狂神说学习笔记:MyBatis
1|0Mybatis
环境:
- JDK1.8
- MySQL5.7
- maven3.6.1
- IDEA
回顾:
- JDBC
- MySQL
- Java基础
- Maven
- Junit
1|11、简介
1.1、什么是Mybatis
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
如何获取MyBatis?
-
maven仓库
1.2、持久化
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
- 数据库(JDBC),IO持久化
- 生活:冷藏,罐头
为什么需要持久化?
-
有一些对象,不能让他丢掉
-
内存太贵
1.3、持久层
- 完成持久化工作的代码块
- 层界限十分明显
1.4、为什么需要MyBatis?
-
帮助程序员将数据存入到数据库中
-
方便
-
传统的JDBC代码太复杂了,简化,框架,自动化
-
不用MyBatis也可以,但是更容易上手(技术没有高低之分)
-
优点:
- 简单易学
- 灵活
- sql和代码的分离,提高了可维护性
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
最重要的一点:使用的人多
1|22、第一个MyBatis
思路:搭建环境--->导入MyBatis-->编写代码-->测试
2.1、搭建环境
搭建数据库
新建项目
-
新建一个普通的maven项目
-
删除src目录
-
导入maven依赖
2.2、创建一个模块
-
编写MyBatis核心配置文件
mybatis-config.xml
-
编写MyBatis工具类
MyBatisUtils.java
2.3、编写代码
-
实体类
User.java
-
Dao接口
UserDao.java
-
接口实现类由原来的UserDaoImpl转换为一个Mapper配置文件
UserMapper.xml
2.4、测试
注意点:
org.apache.ibatis.binding.BindingException: Type interface com.dt.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?Mapper没有注册
解决办法:在MyBatis核心配置文件里注册Mapper
2.4.1、资源导出解决
在每个项目的porn.xml中加入以下代码,以防资源导出失败
-
Junit测试
可能会遇到的问题:
- 配置文件没有注册
- 绑定接口错误
- 方法名不对
- 返回类型不对
- Maven导出资源失败
1|33、CRUD
3.1、namespace
namespace的包名要和Dao/Mapper接口的包名一致!
3.2、select
选择,查询语句
- id:就是对应的namespace中的方法名
- resultType:SQL语句的返回值!
- parameterType:参数类型!
-
编写接口
-
编写对应的mapper中的SQL语句
-
测试
3.3、insert
-
编写接口
-
编写对应的mapper中的SQL语句
-
测试
3.4、update
-
编写接口
-
编写对应的mapper中的SQL语句
-
测试
3.5、delete
-
编写接口
-
编写对应的mapper中的SQL语句
-
测试
注意点:
- 增删改需要提交事务!
3.6、分析错误
- 标签不要匹配错
- resource绑定mapper需要使用路径
/
而不是.
- 程序配置文件必须符合规范
- NullPointerException,没有注册到资源
- 输出的xml文件中存在中文乱码问题!
- maven资源没有导出问题!
3.7、万能Map
假设我们实体类,或数据库中的表,字段或者参数过多时,应当考虑使用Map
-
接口
-
对应的mapper中的SQL语句
-
测试
Map传参与实体类传参的区别:
-
Map传递数据,直接在sql中取出key即可!【parameterType="map"】
-
对象传递参数,直接在sql中取对象的属性即可!【parameterType="Object"】
-
只有一个基本类型参数的情况下,可以直接在sql中取到!(可以省略parameterType)
-
多个参数用Map,或者注解!
3.8、模糊查询
-
Java代码执行的时候,传递通配符% %
-
在SQL拼接中使用通配符!
-
使用concat函数连接字符串
接口:
对应的mapper中的SQL语句:
测试:
1|44、配置解析
4.1、核心配置文件
4.2、环境配置(environments)
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境
学会使用配置多套运行环境!
MyBatis默认的事务管理器:JDBC
,默认连接池:POOLED
4.3、属性(properties)
我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在properties 元素的子元素中设置 [db.properties]
编写一个配置文件:
在核心配置文件中引入:
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件的!
4.4、类型别名(typeAliases)
- 类型别名可为 Java 类型设置一个缩写名字
- 意在降低冗余的全限定类名书写
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
每一个在包中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名(大写也可以,但不推荐)
在实体类比较少的时候,使用第一种方式
如果实体类十分多,建议使用第二种
第一种可以DIY别名,第二种则不行,需要通过注解@Alias
来DIY别名
4.5、设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为
4.6、其他配置
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- mybatis-generator-core
- mybatis-plus
- 通用mapper
4.7、映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件
方式一:【推荐使用】
方式二:使用class文件绑定注册
注意点:
- 接口和他的Mapper配置文件必须同名
- 接口和他的配置文件必须在同一个包下!
方式三:使用扫描包进行注入绑定
注意点:
- 接口和他的Mapper配置文件必须同名
- 接口和他的配置文件必须在同一个包下!
4.8、生命周期和作用域(Scope)
生命周期和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder:
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 局部变量
SqlSessionFactory:
- 可以想象为:数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- 因此 SqlSessionFactory 的最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式
SqlSession:
- 连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完后需要赶紧关闭,否则资源被占用!
1|55、解决属性名和字段名不一致的问题
5.1、问题
数据库中的字段
新建一个项目,拷贝之前的,测试实体类字段不一致的情况
出现问题:
解决方法:
-
起别名
5.2、resultMap
结果集映射
数据库 | id | name | pwd |
---|---|---|---|
实体类 | id | name | password |
resultMap
元素是 MyBatis 中最重要最强大的元素- ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了
ResultMap
的优秀之处——你完全可以不用显式地配置它们
1|66、日志
6.1、日志工厂
如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
在MyBatis中具体使用哪个日志实现,在设置中设定!
STDOUT_LOGGING标准日志输出
在mybatis核心配置文件中,配置我们的日志!
6.2、Log4j
什么是Log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们也可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程
简单使用
-
在要使用Log4j的类中,导入包
import org.apache.log4j.Logger;
-
日志对象,加载参数为当前类的class
-
日志界别
1|77、分页
思考:为什么要分页?
- 减少数据的处理量
7.1、使用Limit分页
使用MyBatis实现分页,核心SQL
-
接口
-
Mapper
-
测试
7.2、RowBounds分页
不使用SQL实现分页(了解)
-
接口
-
Mapper
-
测试
7.3、分页插件
了解即可,万一以后公司的架构师,说要使用,你需要知道它是什么东西
1|88、使用注解开发
8.1、面向接口编程
- 之前学过面向对象编程,也学习过接口,但在真正的开发中,很多时候会选择面向接口编程
- 根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得容易,规范性更好
- 在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了
- 而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程
8.2、使用注解开发
-
注解在接口上实现
-
核心配置
mybatis-config.xml
-
测试
本质:反射机制实现
底层:动态代理!
Mybatis详细执行流程:
- Resource获取全局配置文件
- 实例化SqlsessionFactoryBuilder
- 解析配置文件流XMLConfigBuilder
- Configration所有的配置信息
- SqlSessionFactory实例化
- trasactional事务管理
- 创建executor执行器
- 创建SqlSession
- 实现CRUD
- 查看是否执行成功
- 提交事务
- 关闭

8.3、CRUD
我们可以在工具类创建的时候实现自动提交事务!
MyBatisUtils
-
编写接口
-
测试类
【注意:我们必须要将接口注册绑定到我们的核心配置文件中】
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但是建议大家都加上
- 我们在SQL中引用的就是我们这里的
@Param()
中设定的属性名
#{}和${}区别
-
2|0{}能防止sql注入
- MyBatis排序时使用order by 动态参数时需要注意,使用${}而不用#{}
2|19、Lombok
Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样
板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。
使用步骤:
2|210、多对一处理
多对一:
- 多个学生,对应一个老师
- 对于学生而言,关联-多个学生,关联一个老师【多对一】
- 对于老师而言,集合-一个老师,关联多个学生【一对多】
SQL:
10.1、测试环境搭建
-
导入Lombok依赖
-
新建实体类 Teacher,Student
Teacher.java
Student.java
-
编写实体类对应的Mapper接口【两个】
TeacherMapper.java
StudentMapper.java
-
编写Mapper接口对应的 mapper.xml配置文件【两个】
TeacherMapper.xml
StudentMapper.xml
-
在核心配置文件中绑定注册Mapper接口或者文件!【方式很多,随心选】
mybatis-config.xml
-
测试查询是否能够成功!
TeacherMapper.java
MyTest.java
10.2、按照查询嵌套处理
-
给StudentMapper接口增加方法
-
编写对应打的Mapper文件
-
编写完毕去Mybatis配置文件中,注册Mapper
-
测试
-
注意点拓展
10.3、按照结果嵌套处理
-
接口方法编写
-
编写对应的mapper文件
-
编写完毕去Mybatis配置文件中,注册Mapper
-
测试
小结
- 按照查询进行嵌套处理就像SQL中的子查询
- 按照结果进行嵌套处理就像SQL中的联表查询
回顾MySQL多对一查询方式:
- 子查询
- 联表查询
2|311、一对多处理
比如:一个老师拥有多个学生
对于老师而言,就是一对多的关系!
11.1、环境搭建
-
环境搭建,和刚才一样(跳转到环境搭建)
实体类:
11.2、按结果嵌套处理
-
TeacherMapper接口编写方法
-
编写接口对应的Mapper配置文件
-
将Mapper文件注册到MyBatis-config文件中
-
测试
11.3、按查询嵌套处理
-
TeacherMapper接口编写方法
-
编写接口对应的Mapper配置文件
-
将Mapper文件注册到MyBatis-config文件中
-
测试
小结
- 关联-
association
【多对一】 - 集合-
collection
【一对多】 javaType
&ofType
JavaType
用来指定实体类中属性的类型ofType
用来指定映射到List或者集合中的POJO类型
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对多和多对一中,属性和字段对应的问题
- 如果问题不好排查,可以使用日志【推荐使用Log4j】
2|412、动态SQL
什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
12.1、搭建环境
新建一个数据库表:blog
字段:id,title,author,create_time,views
创建一个基础工程
-
导包
-
编写核心配置文件,设置下划线驼峰自动转换
-
编写实体类
-
编写实体类对应的Mapper接口 和 Mapper.xml文件
-
编写IDUtils工具类
-
插入初始数据
编写接口及它的xml文件
初始化博客方法
12.2、If
需求:根据作者名字和博客名字来查询博客!如果作者名字不为空,就根据作者名字来查询。如果博客名字不为空,就根据博客名字来查询。如果二者都为空,那就把表的所有内容都查询出来
-
编写接口
-
编写xml文件的SQL语句
-
测试
12.3、where
修改上面的SQL语句
这个where
标签会知道如果它包含的标签中有返回值的话,它就插入一个where
。此外,如果标签返回的内容是以AND
或OR
开头的,则它会剔除掉。
12.4、choose (when, otherwise)
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
-
编写接口
-
编写xml文档的SQL语句
-
测试
补充:
-
MyBatis 提供了 choose 元素。if标签是与(and)的关系,而 choose 是或(or)的关系。
-
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default
12.5、Set
-
编写接口
-
编写xml文档的SQL语句
set
元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的) -
测试
所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码
12.6、trim
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
同理,set 元素等价的自定义 trim 元素
注意,我们覆盖了后缀值设置,并且自定义了前缀值。
12.7、SQL片段
有的时候,我们可能会将一些功能的部分抽取出来,方便复用
提取SQL片段:
引用SQL片段:
注意点:
- 最好基于 单表来定义 sql 片段,提高片段的可重用性
- 在 sql 片段中不要包括 where
12.8、Foreach
-
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
-
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
-
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
-
编写接口
-
编写xml文档的SQL语句
-
测试
小结
其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写出原生的 sql 语句,然后改成通用的 mybatis 动态sql ,防止出错。
2|513、缓存
13.1、简介
- 什么是缓存[Cache]?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库查询文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
- 什么样的数据能使用缓存
- 经常查询并且不经常改变的数据
13.2、Mybatis缓存
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率
- MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
13.3、一级缓存
- 一级缓存也叫本地缓存
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库
测试步骤:
一级缓存失效的四种情况
-
SqlSession相同,查询条件不同
结果:通过日志看出,发送了两条SQL语句
存在缓存中查询不到的数据,会刷新缓存
-
SqlSession相同,两次查询之间进行了增删改操作
结果:通过日志看出,发送了三条SQL语句
增删改操作,可能会改变原来的数据,所以一定会刷新缓存
-
SqlSession不同,查询条件相同
结果:通过日志看出,发送了两条SQL语句
每个SQLSession的缓存相互独立
-
SqlSession相同,查询条件相同,手动清理缓存
结果:通过日志看出,发送了两条SQL语句
小结
一级缓存是SqlSession级别的缓存,是一直开启的,我们关闭不了它;
一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请求!
一级缓存相当于一个Map
13.4、二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中
- 新的会话查询信息,就可以从二级缓存中获取内容
- 不同的mapper查出的数据会放在自己对应的缓存(map)中
使用步骤:
-
在核心配置文件中开启全局缓存
-
所有的实体类先实现序列化接口(提示:
cache
标签中的readonly
属性等于 false 才需要序列化) -
去每个mapper.xml中配置使用二级缓存,这个配置非常简单
官方自定义参数
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新
-
测试
结果:通过日志看出,不同的SqlSession只发送了一条SQL语句
正好符合以下特性
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中。
- 新的会话查询信息,就可以从二级缓存中获取内容
前提:必须在同一个Mapper中,二级缓存只针对一个namespace
小结
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者会话关闭的时候,才会保存到二级缓存中
13.5、缓存原理图
缓存顺序:
- 先去二级缓存中查找
- 再去一级缓存中查找
- 最后再查询数据库
13.6、自定义缓存-Ehcache(了解即可)
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存
要在程序中使用ehcache,先要导包
在mapper中指定使用我们的ehcache缓存实现
配置文件
ehcache.xml
目前:Redis来做缓存 K-V
__EOF__

本文链接:https://www.cnblogs.com/dt746294093/p/16768030.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· DeepSeek在M芯片Mac上本地化部署