理解:一个特定问题的解决方案,底层实现了重复的功能代码,我们在这个框架的基础上,遵守框架的使用规范,添加自定义代码,完成自己的功能。
框架=jar+API文档+示例代码+源码
1.MyBatis框架
作用:数据持久层框架,就是封装JDBC
工作流程:
- 通过Reader对象读取Mybatis映射主配置文件
- 通过SqlSessionFactoryBuilder对象创建SqlSessionFactory对象
- 事务默认开启
- 通过SQLSession读取映射文件中的操作编号,从而读取SQL语句
- 提交事务
- 关闭资源
1.1框架搭建
jar包下载:https://qingzheng.lanzous.com/b01bkd0gh
jar包结构图:
框架使用日志来实现输出,不同框架使用的日志技术不同:
日志门面: 接口
日志实现:
log4j MyBatis和Spring
log4j2 hibernate
logback SpringBoot
主配置文件mybatis.xml:放在项目src下面
jdbc.properties文件:放在项目src下<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">//可在eclipse的widow->perferences->XML Catalog界面配置本地dtd文件
<!-- 主配置文件名字可以任意,有意义,位置在src目录下 -->
<configuration>
<!-- jdbc的四大参数配置文件 -->
<properties resource="jdbc.properties"/>
<!-- 别名设置 -->
<typeAliases>
<!--typeAlias、package选其一-->
<!-- 每个实体类都要配置,此处实体类为Student -->
<typeAlias type="com.woniuxy.entity.Student" alias="Student"/>
<!--或者可以设置检测包下的所有实体对象-->
<package name="com.woniuxy.entity"/>
</typeAliases>
<!-- 运行的基本环境配置 -->
<environments default="development">
<environment id="development">
<!-- 事务管理器,默认为JDBC的事务管理 -->
<transactionManager type="JDBC"/>
<!-- 数据源type配置连接池,默认使用内置连接池 -->
<dataSource type="POOLED">
<!-- JDBC的四大参数 -->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 指定映射文件所在包,名字必须和接口一样,包括大小写 -->
<package name="com.woniuxy.dao"/>
</mappers>
</configuration>
StudentDao.xml映射文件(insert为例):
MyBatisUtil(单例,用来创建sqlSession对象) ```java package com.woniuxy.util;<?xml version="1.0" encoding="UTF-8"?>
<!-- 根节点就是mapper -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">//和主配置文件中的设置一样
<!--
Mapper动态代理:框架自动生成Dao接口实现类
1.映射文件放在dao接口的包下
2.namespace必须是对应接口的全名
3.sql语句的id要和对应的方法名一样
-->
<!-- 每个Dao有个映射文件 -->
<mapper namespace="com.woniuxy.dao.StudentDao">//此处的名称必须和对应的Dao接口名相同
<!-- parameterType使用方法参数的全类名,此处在主配置文件里面里设置了别名 -->
<insert id="insertStudent" parameterType="Student">//sql语句的id必须和Dao接口中对应方法名相同
insert into student values(null,#{name},#{age})
</insert>
</mapper>
import java.io.IOException; import java.io.InputStream;
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MyBatisUtil { private static SqlSessionFactory sqlSessionFactor = null;
// 禁止外界通过new方法创建,实现单例
private MyBatisUtil() {
}
public static SqlSession getSqlSession() {
SqlSession sqlSession = null;
try {
if (sqlSessionFactor == null) {
// 加载主配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
// 创建sqlSessionFactor
sqlSessionFactor = new SqlSessionFactoryBuilder().build(inputStream);
}
// 创建sqlSession对象
sqlSession = sqlSessionFactor.openSession();
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
}
<a name="Iy47q"></a>
## 1.2日志输出
MyBatais使用的是log4j<br />添加日志的配置文件log4j.properties,名字不能改,位置必须在src下<br />日志输出级别<br />ALL>TRACE>DEBUG>INFO>WARM>ERROR>FATAL>NONE<br />log4j.properties:<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1335005/1594812728934-07589a41-bdbe-4243-9a22-7f5fe46ac0a0.png#height=209&id=fNsdD&margin=%5Bobject%20Object%5D&name=image.png&originHeight=209&originWidth=982&originalType=binary&ratio=1&size=33221&status=done&style=none&width=982)
<a name="3ESDA"></a>
## 1.3查询
本次实验是学生表和班级表的查询<br />表结构如图:<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1335005/1595055497084-6819512b-7147-47cf-b20a-d346bb694ae2.png#height=133&id=NzQcC&margin=%5Bobject%20Object%5D&name=image.png&originHeight=133&originWidth=297&originalType=binary&ratio=1&size=6203&status=done&style=none&width=297)![image.png](https://cdn.nlark.com/yuque/0/2020/png/1335005/1595055542217-283114ea-1fbd-428f-b5e0-f637d2a12173.png#height=155&id=DQuzp&margin=%5Bobject%20Object%5D&name=image.png&originHeight=155&originWidth=439&originalType=binary&ratio=1&size=10989&status=done&style=none&width=439)<br />实体类:<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/1335005/1595055884749-ed4f65c8-cf0b-4282-9986-3be4ca8e3848.png#height=191&id=XuXit&margin=%5Bobject%20Object%5D&name=image.png&originHeight=191&originWidth=285&originalType=binary&ratio=1&size=9174&status=done&style=none&width=285)![image.png](https://cdn.nlark.com/yuque/0/2020/png/1335005/1595055895977-8b4f240f-52df-42c4-8ffe-506cc19d33d6.png#height=119&id=Jn9FG&margin=%5Bobject%20Object%5D&name=image.png&originHeight=119&originWidth=325&originalType=binary&ratio=1&size=7062&status=done&style=none&width=325)
<a name="dkJx7"></a>
### 一对多查询(collection)
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 根节点就是mapper -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- Mapper动态代理:框架自动生成Dao接口实现类
1.映射文件放在dao接口的包下
2.namespace必须是对应接口的全名
3.sql语句的id要和对应的方法名一样 -->
<mapper namespace="com.woniuxy.dao.ClassDao">
<!-- parameterType使用方法参数的全类名,此处在主配置文件里面里设置了别名 -->
<!-- 连接查询的一对多 -->
<!--id对应方法名,resultMap查询结果是cid编号班级下的所有学生信息,无法直接赋值-->
<select id="selectClassById" resultMap="ClassMapper">
select * from student stu,class cl where stu.cid=cl.cid and cl.cid=#{cid}
</select>
<!-- type返回类型,id对应resultMap -->
<resultMap type="Class" id="ClassMapper">
<id column="cid" property="cid" />
<result column="cname" property="cname" />
<!-- 一对多用collection -->
<!--property是对象的属性,ofType是查询结果的返回类型,column对应数据库中的列属性-->
<collection property="students" ofType="Student">
<id column="sid" property="sid" />
<result column="sname" property="sname" />
<result column="sage" property="sage" />
</collection>
</resultMap>
<!-- 分步查询的一对多 -->
<select id="selectClassById" resultMap="classMapper">
select * from class where cid=#{cid}
</select>
<resultMap type="Class" id="classMapper">
<id column="cid" property="cid" />
<result column="cname" property="cname" />
<!-- 一对多 -->
<!-- column中的cid在前一次的查询结果中要出现,并且作为参数传递到下一次查询中作为查询条件 -->
<collection property="students" ofType="Student" select="selectStudentById" column="cid" />
</resultMap>
<select id="selectStudentById" resultType="Student">
select * from student where cid=#{cid}
</select>
</mapper>
1.4一对一查询(association)
<?xml version="1.0" encoding="UTF-8"?>
<!-- 根节点就是mapper -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- Mapper动态代理:框架自动生成Dao接口实现类
1.映射文件放在dao接口的包下
2.namespace必须是对应接口的全名
3.sql语句的id要和对应的方法名一样 -->
<mapper namespace="com.woniuxy.dao.ClassDao">
<!-- parameterType使用方法参数的全类名,此处在主配置文件里面里设置了别名 -->
<!-- 连接查询的一对一 -->
<!--id对应方法名,resultMap查询结果是cid编号班级下的所有学生信息,无法直接赋值-->
<select id="selectStudentById" resultMap="studentMapper">
select * from student stu,class cl where stu.cid=cl.cid and stu.id=#{id}
</select>
<!-- type返回类型,id对应resultMap -->
<resultMap type="Student" id="studentMapper">
<id column="id" property="id" />
<result column="sname" property="sname" />
<!-- 一对一用association -->
<!--property是对象的属性,javaType是查询结果的返回类型,column对应数据库中的列属性-->
<association property="class" javaType="Class">
<id column="id" property="id" />
<result column="cname" property="cname" />
</association>
</resultMap>
动态sql
<?xml version="1.0" encoding="UTF-8"?>
<!-- 根节点就是mapper -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- Mapper动态代理:框架自动生成Dao接口实现类
1.映射文件放在dao接口的包下
2.namespace必须是对应接口的全名
3.sql语句的id要和对应的方法名一样 -->
<mapper namespace="com.woniuxy.dao.StudentDao">
<!-- parameterType使用方法参数的全类名,此处在主配置文件里面里设置了别名 -->
<insert id="insertStudent" parameterType="Student">
insert into student values(null,#{name},#{age})
</insert>
<update id="updateStudent" parameterType="Student">
update student set name=#{name},age=#{age} where id=#{id}
</update>
<delete id="deleteStudentById">
delete from student where id=#{id}
</delete>
<select id="selectStudentById" resultType="Student">
select * from student where id=#{id}
</select>
<select id="selectAllStudents" resultType="Student">
select * from student
</select>
<select id="selectStudentByName" resultType="Student">
select * from student where name like '%' #{nam} '%'
</select>
<!-- 动态sql查询 -->
<select id="selectStudentByIf" resultType="Student">
<include refid="mysql"></include>
<where>
<if test="name!=null and name!=''">
and name like '%' #{name} '%'
</if>
<if test="age>0">
and age>#{age}
</if>
</where>
</select>
<!-- sql:select * from student where id in (?,?,?) -->
<select id="selectStudentByForeachArray" resultType="Student">
<include refid="mysql"></include>
<where>
id in
<foreach collection="array" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</where>
</select>
<select id="selectStudentByChoose" resultType="Student">
<include refid="mysql"></include>
<where>
<choose>
<when test="name!=null and name!=''">
name like '%' #{name} '%'
</when>
<when test="age>0">
age>#{age}
</when>
</choose>
</where>
</select>
<!--重复sql片段-->
<sql id="mysql">
select * from student
</sql>
</mapper>
1.5延迟加载
使用延迟加载的前提:多表关联查询使用分步。
直接加载:多条语句一起执行。
在Mybatis的文件中配置全局延迟加载在configuration标签里面,注意标签是有顺序的
侵入式延迟,深度延迟的区别:
侵入式延迟加载执行查询时只执行第一条sql语句,但是只要你访问了查询对象的属性(无论什么属性)都会执行关联查询。而深度延迟加载只有当你访问了查询对象除第一次查询以外的属性时才会执行关联查询。
缓存(了解)
基本原理:查询数据,先判断缓存(内存,硬盘)中是否存在,缓存中没有,就查询数据库,存入缓存,缓存中有,就读取缓存数据。
作用:减小数据库的压力
一级缓存
又叫SqlSession缓存,默认开启。SqlSession关闭,缓存中的数据就没有了。
注意:只要执行了增删改的操作,sqlSession中的缓存将被清空,无论是否提交。
原理:
第一次发出一个查询sql,sql查询结果写入sqlsession的一级缓存中,缓存使用的数据结构是一个map
• key:hashcode+sql+sql输入参数+输出参数(sql的唯一标识)
• value:用户信息
同一个sqlsession再次发出相同的sql,就从缓存中取不走数据库。如果两次中间出现commit操作(修改、添加、删除),本sqlsession中的一级缓存区域全部清空,下次再去缓存中查询不到所以要从数据库查询,从数据库查询到再写入缓存。
二级缓存
和SQLSession无关,SqlSession关闭也可以使用。
注意:只要执行了增删改的操作,二级缓存一样会被清空。
原理:
二级缓存的范围是mapper级别(mapper同一个命名空间),mapper以命名空间为单位创建缓存数据结构,结构是map
主配置文件设置:
映射文件配置:
实体类需实现序列化:
注解开发(了解)
框架开发一般都支持配置开发和注解开发
配置
Servlet2.5
优点:独立的配置文件,修改配置后只需重启服务器,不需要打包和部署项目。
注解
Servlet3.0
优点:简单方便
MyBatis注解功能弱,官方不推荐。
实现步骤:
删除映射文件,直接在接口中抽象方法前使用对应注解。
逆向工程
开源项目,自动生成MyBatis单表的Dao接口和映射文件
java工程附件下载:
generatorSqlmapCustom.zip
修改附件中的xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码,此处需自己更改 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatisdb" userId="root"
password="123">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成POJO类的位置 -->
<javaModelGenerator targetPackage="com.woniuxy.entity"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.woniuxy.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.woniuxy.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表,此处需自己更改 -->
<table schema="student" tableName="student"></table>
<table schema="course" tableName="course"></table>
<table schema="dep" tableName="dep"></table>
<table schema="emp" tableName="emp"></table>
<table schema="score" tableName="score"></table>
<table schema="stu" tableName="stu"></table>
</context>
</generatorConfiguration>
分页
逆向工程more不支持分页,可以使用PageHleper
分页插件jar下载:https://qingzheng.lanzous.com/b01bkqtdg
My Batis主配置文件添加插件配置
测试: