1. 简介
什么是Mybatis
官方文档:https://mybatis.org/mybatis-3/zh/index.html
持久层解释
- 持久化就是将数据在持久化状态和瞬时状态转化的过程
- 持久化就是保存的意思
- 持久层就是完成持久化的代码
2 第一个Mybatis项目
2.1 创建一个普通的maven项目,配置依赖
<dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.3</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.13</version> </dependency> </dependencies> <properties> <maven.compiler.source>13</maven.compiler.source> <maven.compiler.target>13</maven.compiler.target> </properties>
2.2 编写核心xml文件
<?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"> <!--核心配置文件--> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/student?useSSL=true&useUnicode=true&characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value="123"/> </dataSource> </environment> </environments> </configuration>
2.3 编写mybatis工具类
public class mybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//1.获取sqlsessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//2.可以执行crud的对象
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}2.4 编写代码
实体类
//实体类 public class user { private int id; private String username; private String pwd; public user() { } public user(int id, String username, String pwd) { this.id = id; this.username = username; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "user{" + "id=" + id + ", username='" + username + '\'' + ", pwd='" + pwd + '\'' + '}'; } }接口
public interface userDao { List<user> getUserList(); }设置对应的mapping配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace绑定一个对应的接口--> <mapper namespace="com.sun.dao.userDao"> <!--select查询语句--> <select id="getUserList" resultType="com.sun.pojo.user"> select * from student.user </select> </mapper>
2.4 测试
编写一个测试类
public class UserDao_Test { @Test public void test(){ SqlSession sqlSession= mybatisUtils.getSqlSession(); UserDao userDao=sqlSession.getMapper(UserDao.class); List<User> userList=userDao.getUserList(); for(User user : userList) { System.out.println(user); } } }注册mapping
<mappers> <mapper resource="com/sun/dao/UserMapper.xml"></mapper> </mappers>
处理找不到文件的问题
<build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
2.5 步骤总结
- 写一个工具类
- 写一个mybatis-config.xml配置文件
- 写一个实体类
- 写接口
- 写mapping.xml配置
- 写测试类
3. CRUD
3.1 基本语法
namespace
namespace中的包名要和mapper接口的包名一致
select
步骤:
- id:就是对应的namespace中的方法名一致
- resulttype:sql语句的返回值!
- parameterType:参数的类型!
编写接口
List<User> getUserList();
编写对应的mapper中的sql语句
<select id="getUserList" resultType="com.sun.pojo.User"> select * from student.user </select>
测试
@Test public void test(){ //第一步,获得sqlSession对象 SqlSession sqlSession= mybatisUtils.getSqlSession(); try { UserMapper userMapper =sqlSession.getMapper(UserMapper.class); List<User> userList= userMapper.getUserList(); for(User user : userList) { System.out.println(user); } } catch (Exception e) { e.printStackTrace(); } finally { //关闭sqlSession sqlSession.close(); } }
注意:增删改需要 提交事务!
3.2 查找
<select id="getUserbyId" resultType="com.sun.pojo.User" parameterType="int">
select * from student.user where id=#{id}
</select>3.3 添加
<select id="insertUser" parameterType="com.sun.pojo.User">
insert into student.user (id,username ,pwd) values (#{id},#{username},#{pwd})
</select>3.4 修改
<update id="updateUser" parameterType="com.sun.pojo.User">
update student.user set username=#{username},pwd=#{pwd} where id=#{id};
</update>3.5 删除
<delete id="deletebyId" parameterType="int">
delete from student.user where id=#{id}
</delete>3.6 Map
map传递参数,直接在SQL中取出key即可!
parameterType="map"
对象传递参数,直接在SQL中取对象的属性即可 !
parameterType="com.sun.pojo.User">
只有一个基本类型参数的情况下,可以直接在SQL中取到!
多个参数用map或注解!
3.7 模糊查询
- java代码在执行的时候,传递通配符%%
- 在sql中拼接使用通配符!
4 配置文件
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
4.1 属性
1 可以用db.properties方法来优化
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/student?useSSL=true&useUnicode=true username=root password=123
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>2 优先级问题
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 在 properties 元素体内指定的属性首先被读取。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。
4. 2 类型别名(typeAliases)
类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余
第一种:
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type=""/> <typeAlias alias="Comment" type="domain.blog.Comment"/> <typeAlias alias="Post" type="domain.blog.Post"/> <typeAlias alias="Section" type="domain.blog.Section"/> <typeAlias alias="Tag" type="domain.blog.Tag"/> </typeAliases>
第二种:
<typeAliases> <package name="domain.blog"/> </typeAliases>
在实体类比较少的时候,使用第一类;
如果实体类十分多,建议使用第二类;
第一章可以DIY(自定义),第二种则不行,如果非要改,可以在实体中增加注解
4.3 设置(setting)
4.4 mapping(映射器)
注册绑定xml
每一个Mapper.xml都必须在Mybatis的核心配置文件下进行注册
方法一:通过xml【推荐使用】
<mappers> <mapper resource="com/sun/dao/UserMapper.xml"></mapper> </mappers>
方式二:使用class文件注册
<mappers> <mapper class="com.sun.dao.UserMapper"></mapper> </mappers>
注意:
- 接口和他的mapper配置文件必须同名!
- 接口和他的mappper配置文件必须在一个包下!
方法三:
<mappers> <package name="com.sun.dao"/> </mappers>
注意:
- 接口和他的mapper配置文件必须同名!
- 接口和他的mappper配置文件必须在一个包下!
4.5 生命周期和作用域
作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。

SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory
- 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- SqlSessionFactory 的最佳作用域是应用作用域。
- 最简单的就是使用单例模式或者静态单例模式。
- 可以理解为连接池
SqlSession
- 连接到连接池的一个请求!
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后赶紧关闭,否则资源被占用!
这里的每一个Mapper都代表一个具体的业务!
5. 解决属性名和字段名不一致的问题
1. 方法:
原来这样的sql语句
select * from student.user where id=#{id}如果直接执行,就会出现错误,对应的属性值可能为空
改成这样即可,起别名
select id,username,pwd as password from student.user where id=#{id}
id,username,pwd就是数据库中的字段名
password是pwd对应的属性名2. 结果集映射resultMap
id username pwd 数据库中的字段名 id username password 对应的属性名

- resultMap是MyBatis中最重要强大的元素
- ResultMap的设计思想是,对于简单的语句根本不需要显示配置结果映射,而对于复杂的语句,只描述他们的关系就行了。
- 一样的就可以不用映射了
6. 日志
日志工厂
如果一个数据库操作,出现了异常,我们需要排错,日志就是最好的助手!
- SLF4J
- LOG4J (掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING (掌握)
- NO_LOGGING

注意:value后面小心空格!
LOG4j

导入jar包
配置log4j.properties (新建以这个为名)这个配置文件
配置log4j为日志的实现
<settings> <setting name="logImpl" value="LOG4j"/> </settings>
测试
7. 分页
接口
List<User> getUserbyLimit(Map<String,Object> map);
Mapper.xml
<select id="getUserbyLimit" parameterType="map" resultType="com.sun.pojo.User"> select * from student.user limit #{startIndex},#{pageslll} </select>测试
@Test public void getUserbyLimit(){ SqlSession sqlSession = null; try { sqlSession= mybatisUtils.getSqlSession(); UserMapper mapper= sqlSession.getMapper(UserMapper.class); Map<String,Object> map=new HashMap<String, Object>(); map.put("startIndex",0); map.put("pageslll",2); List<User> userList= mapper.getUserbyLimit(map); for (User user : userList) { System.out.println(user); } ; } catch (Exception e) { e.printStackTrace(); } sqlSession.close(); }
8. 注解开发
1. 面向接口编程
2. 使用注解开发

本质:反射机制体现!
底层:动态代理!
3. CRUD
查询
接口实现
//方法存在多个参数,一定要用@Param,方法中的参数和@Param中的参数相比,以@Param中的参数为主 @Select("select * from user where id=#{id}") User getiUserID(@Param("id") int id);核心配置文件绑定接口
<mappers> <mapper class="com.sun.dao.UserMapper"></mapper> </mappers>
测试
//注解开发 @Test public void getiUserID(){ SqlSession sqlSession=mybatisUtils.getSqlSession(); UserMapper mapper=sqlSession.getMapper(UserMapper.class); User user=mapper.getiUserID(1); System.out.println(user); sqlSession.close(); }
添加
接口实现
@Insert("insert into user(id,username,pwd) values(#{id},#{username},#{pwd})") int insertUserBy(User user);如查询的步骤二一样,绑定过一次就不用每次都绑定了!
测试
@Test public void insertUserBy(){ SqlSession sqlSession=mybatisUtils.getSqlSession(); UserMapper mapper=sqlSession.getMapper(UserMapper.class); mapper.insertUserBy(new User(22,"李红","890")); sqlSession.close(); }注意:正常情况下,增删改都需要提交事务才能够真正修改数据库!现在不用,是因为设置了自动提交事务!
public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(true); }
修改
接口实现
@Update("update user set username=#{username},pwd=#{pwd} where id=#{id}") int updateByzhujie(User user);如查询的步骤二一样,绑定过一次就不用每次都绑定了!
测试
@Test public void insertUserBy(){ SqlSession sqlSession=mybatisUtils.getSqlSession(); UserMapper mapper=sqlSession.getMapper(UserMapper.class); mapper.updateByzhujie(new User(9,"时代","457")); sqlSession.close(); }
删除
接口实现
@Delete("delete from user where id=#{id}") int deleteByzhujie(int id);如查询的步骤二一样,绑定过一次就不用每次都绑定了!
测试
@Test public void insertUserBy(){ SqlSession sqlSession=mybatisUtils.getSqlSession(); UserMapper mapper=sqlSession.getMapper(UserMapper.class); mapper.deleteByzhujie(22); sqlSession.close(); }
关于@Param注解
- 基本类型的参数或者string类型,需要加上
- 引用类型不用加上
- 如果只有一个基本类型参数,可以不用!
- 我们在SQL中引用的就是这里的@param设置的属性名!
9. Lombok
目的是为了简化开发,它是个java库,是个构建工具,也是个插件
9.1 步骤
idea中加入插件

在项目中导入jar包
在实体类中加注解即可
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
Code inspections
Refactoring actions (lombok and delombok)
10. 多对一
多个学生,一个老师
- 对于学生而言,一个老师关联多个学生
- 对于老师而言,集合,一个老师管理多个学生
按照查询嵌套处理

按照结果嵌套处理

11. 一对多
环境搭建
实体类


按照结果集查询

按照查询嵌套处理

小结
- 关联-associate 【多对一】
- 集合-collection· 【一对多】
- javaType&ofType
- javaType用来指定实体类中属性的类型
- ofType 用来指定映射到list或者集合中的pojo类型,泛型中的约束类型!
12. 动态SQL
定义:自动生成SQL语句
if
<select id="queryByIF" resultType="com.sun.pojo.User" parameterType="map">
select * from student.user where 1=1
<if test="id!=null">
and id=#{id}
</if>
</select>注意:resultType中应该写成com.sun.pojo.User,而不是简单的User就可以了!
where
<select id="queryByIF" resultType="com.sun.pojo.User" parameterType="map">
select * from student.user
<where>
<if test="id!=null">
and id=#{id}
</if>
</where>
</select>注意:where主要是可以自动添加或删除and,可以一个条件成立,也可以多个条件成立
trim, where, set
set 的作用就是只能选择一个条件成立
trim:定制 where 元素的功能
foreach
SQL片段
注意事项:
- 最好基于单表来定义sql片段!
- 不要存在where标签!
13. 缓存
13.1 什么样的数据适合缓存?
经常查询但是不经常改变的数据
Mybatis中默认有两个缓存,一级缓存和二级缓存
- 一级缓存就是本地缓存,默认开启,sqlsession级别的缓存
- 二级缓存需要手动配置启动,是基于namespace级别的缓存
- 有缓存接口cache,我们可以通过cache自定义二级缓存
13.2 一级缓存
- 与数据库同一次会话期间查询的数据会放在本地缓存中
- 以后如果需要相同的信息,直接从缓存中获取,没有必要再去查询数据库
测试步骤:
开启日志
测试在一个session中查询两次相同记录
查看日志,分析
通过id查询两次id=1的数据

**通过查看日志可以看出SQL·语句执行了一次,第二次直接从缓冲中获取了
缓存失效:
小结:一次缓存默认是开启的,只在一次sqlsession中有效,也就是拿到连接到关闭连接的区间段!一级缓存就是一个mapper!
查看id=1和id=2的数据

可以发现,SQL语句执行了两次!因为这是不同的数据,查询过id=1的数据之后,缓存中并没有id=2的数据,因此需要再次查询数据库
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的mapper.xml
- 手动清理缓存
13.3 二级缓存
- 二级缓存也叫全局缓存,一级缓存的作用域太低了,所以诞生了二级缓存!
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一个数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存会保存到二级缓存中
- 新的会话查询信息,就可以从二级缓冲中查询信息
- 不同的mapper查出的信息会放在对应的缓存(mapper)中
步骤:
显示开启全局缓存( 默认就是开启的,现在只是显示出来而已)
<settings> <!-- 显示的开启全局缓存--> <setting name="cacheEnabled" value="true"/> </settings>
在一个mapper中开启二级缓存
<!-- 在当前maper中开启二级缓存--> <cache/>
也可以自定义参数
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
测试
问题是我们需要将实体类序列化!否则就会报错!

改为
小结:
- 只有开启了二级缓存,在同一个mapper有效!
- 所有的数据都会先放到一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!
13.4 缓存原理
缓存顺序:
- 用户先查询二级缓存
- 接着查询一级缓存
- 缓存中没有的话,从数据库中查询,放在一级缓存中
- 会话关闭的话,数据从一级缓存放到二级缓存中
13.5 自定义缓存-ehcahe
ehcache是一种广泛使用的开源java分布式缓存,主要用于通用缓存
要在程序中使用
















