MyBatis
1、MyBatis简介
1.1、MyBatis特性
MyBatis是一个基于java的持久层框架,包括SQL Maps和Data Access Object(DAO)
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
1.2、MyBatis下载
https://github.com/mybatis/mybatis-3
1.3、和其它持久化层技术对比
-
JDBC
-
SQL 夹杂在Java代码中耦合度高,导致硬编码内伤
-
维护不易且实际开发需求中 SQL 有变化,频繁修改的情况多见
-
代码冗长,开发效率低
-
-
Hibernate 和 JPA
-
操作简便,开发效率高
-
程序中的长难复杂 SQL 需要绕过框架
-
内部自动生产的 SQL,不容易做特殊优化
-
基于全映射的全自动框架,大量字段的 POJO 进行部分映射时比较困难。
-
反射操作太多,导致数据库性能下降
-
-
MyBatis
-
轻量级,性能出色
-
SQL 和 Java 编码分开,功能边界清晰。Java代码专注业务、SQL语句专注数据
-
开发效率稍逊于HIbernate,但是完全能够接受
-
2、搭建MyBatis
2.1、创建工程
创建Maven工程,设置打包方式为jar,引入依赖
2.2、创建MyBatis核心配置文件
核心配置文件中主要用于配置连接数据库的环境以及MyBatis的全局配置信息
习惯上命名为mybatis-config.xml,这个文件名仅仅只是建议,并非强制要求。将来整合Spring 之后,这个配置文件可以省略,所以操作时可以直接复制、粘贴。
核心配置文件存放的位置是src/main/resources目录下
2.3、创建mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
2.4、创建MyBatis的映射文件
-
相关概念:ORM(Object Relationship Mapping)对象关系映射。
-
对象:Java的实体类对象
-
关系:关系型数据库
-
映射:二者之间的对应关系
-
Java概念 | 数据库概念 |
---|---|
类 | 表 |
属性 | 字段 / 列 |
对象 | 记录 / 行 |
映射文件的命名规则:
表所对应的实体类的类名+Mapper.xml 例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
一个映射文件对应一个实体类,对应一张表的操作
MyBatis映射文件用于编写SQL,访问以及操作表中的数据
MyBatis映射文件存放的位置是src/main/resources/mappers目录下
MyBatis中可以面向接口操作数据,要保证两个一致:
mapper接口的全类名和映射文件的命名空间(namespace)保持一致
mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
2.5、通过jUnit测试功能
报错Cannot find class: com.mysql.cj.jdbc.Driver
:数据库连接驱动的版本不正确,在pom.xml
中将mysql:mysql-connector-java:8.0.29
改为最新版本即可
SqlSession:代表Java程序和数据库之间的会话。(HttpSession是Java程序和浏览器之间的 会话)
SqlSessionFactory:是“生产”SqlSession的“工厂”。
工厂模式:如果创建某一个对象,使用的过程基本固定,那么我们就可以把创建这个对象的 相关代码封装到一个“工厂类”中,以后都使用这个工厂类来“生产”我们需要的对象。
2.6、加入log4j日志功能
加入依赖:
加入log4j的配置文件:
log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下
日志的级别:FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试) 从左到右打印的内容越来越详细
3、核心配置文件
4、MyBatis增删改查
注意:
查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射关系
resultType:自动映射,用于属性名和表中字段名一致的情况
resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异常TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合作为返回值
封装SqlSessionUtils工具类
5、获取参数值的两种方式(重点)
MyBatis获取参数值的两种方式:${}
和#{}
${}
的本质就是字符串拼接,#{}
的本质就是占位符赋值
${}
使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}
使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
能用#{}
就不要用${}
5.1、单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}
和#{}
以任意的名称获取参数的值,注意${}
需要手动加单引号
5.2、多个字面量类型的参数
-
若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,默认以两种方式进行存储:
-
以
arg0,arg1...
为键,以参数为值; -
以
param1,param2...
为键,以参数为值;
因此只需要通过
${}
和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号 -
5.3、map集合类型的参数
若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中,只需要通过${}
和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号
手动创建的map集合可以通过自定义的键来访问
5.4、实体类类型的参数
若mapper接口中的方法参数为实体类对象时 此时可以使用${}
和#{}
,通过访问实体类对象中的属性名获取属性值,注意${}
需要手动加单引号
属性主要是由get/set方法决定的,而不是成员变量,可能存在有get/set方法但没有成员变量的情况
5.5、使用@Param标识参数(推荐)
可以通过 @Param 注解标识mapper接口中的方法参数,此时,会将这些参数放在map集合中: 以 @Param 注解的value属性值为键,以参数为值; 以 param1,param2... 为键,以参数为值; 只需要通过${}
和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号
如果需要传输多个字面量类型的参数,建议使用 @Param 方式
6、MyBatis的各种查询功能
6.1、查询一个实体类对象
若查询出的数据只有一条,可以通过实体类对象或list集合接收
若查询出的数据有多条,只能通过list集合接收,不能通过实体类对象接收,否则会抛出异常TooManyResultsException
6.2、查询一个list集合
6.3、查询单个数据
在MyBatis中,对于Java中常用的类型都设置了类型别名
例如:
java.lang.Integer
-->int
,integer
例如:
int
-->_int
,_integer
例如:
Map
-->map
,List
-->list
,String
-->string
6.4、查询一条数据为map集合
6.5、查询多条数据为map集合
以map集合的形式查询多条数据
-
方式一:将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取
-
方式二:将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过
@MapKey
注解设置map集合的键,值是每条数据所对应的map集合
7、特殊SQL的执行
7.1、模糊查询
模糊查询建议使用“%”#{ }"%"
这种方式
7.2、批量删除
由于#{}
在sql语句中会自动被解析为一对单引号(最终使占位符被解析为一个问号字符),所以批量删除只能使用${}
7.3、动态设置表名
设置要查询的表的表名,因为表名不能带有单引号,所以动态设置表名只能使用${}
7.4、添加功能获取自增的主键
获取添加时自增的键对应的值,如user的id
useGeneratedKeys:true表示当前标签中的使用了自增的主键
keyProperty:将获取的自增的主键放在传输的参数user对象的某个属性中
8、自定义映射resultMap
数据库的命名规范:所有标识符都使用小写,单词之间下划线连接
由于数据库字段的命名规范和java命名规范不同,所以需要使用自定义映射 例如:数据库中的字段emp_name
和java pojo中的属性empName
8.1、resultMap处理字段和属性的映射关系
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
如果使用resultMap,建议将所有字段名和属性名的映射都设置出来(不论名字是否相同)
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用
_
),实体类中的属性名符合Java的规则(使用驼峰)
此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系:
可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
可以在MyBatis的核心配置文件中设置一个全局配置信息
mapUnderscoreToCamelCase
,可以在查询表中数据时,自动将_
类型的字段名转换为驼峰
例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为userName
8.2、多对一映射处理
多中创建一的对象,一中设置多的集合 例如:多个员工对应一个部门,在员工类中设置部门对象,在部门中设置员工集合
-
多对一映射处理:
-
方式一:级联方式处理
-
方式二:使用association处理
-
方式三:分步查询
-
第一步:查询员工信息
-
第二步:根据员工所对应的部门id查询部门信息
-
-
8.3、一对多映射处理
-
一对多映射处理:
-
collection
-
分步查询
-
第一步:查询部门信息
-
第二步:根据部门id查询部门中的所有员工
-
-
分步查询的优点:可以实现延迟加载,在分步查询中只加载查询的信息(否则会加载所有步骤的查询信息),但是必须在核心配置文件中设置全局配置信息
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading
:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载,此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql开启了全局延迟加载后,可通过association和collection中的
fetchType
属性设置当前的分步查询是否使用延迟加载 fetchType=”lazy”
(延迟加载),”eager”
(立即加载)
9、动态SQL
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
动态SQL指的是映射文件中的一系列标签
9.1、if
if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行
9.2、where
where和if一般结合使用:
a>若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字
b>若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and去掉,例如:
and age = #{age}
注意:where标签不能去掉条件最后多余的and,例如:
age = #{age} and
9.3、trim
trim用于去掉或添加标签中的内容(只能去掉最开头和最末尾的内容,不是对每一个if进行处理)
常用属性:
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
9.4、choose、when、otherwise
choose、when、otherwise相当于if...else if..else
choose是父标签,when=if,otherwise=else
9.5、foreach
foreach的属性:
collection
:设置要循环的数组或集合
item
:表示集合或数组中的每一个数据
separator
:设置循环体之间的分隔符
open
:设置foreach标签中的内容的开始符
close
:设置foreach标签中的内容的结束符
9.6、SQL片段
sql片段,可以记录一段公共sql片段,在使用的地方通过include
标签进行引入
10、MyBatis的缓存
缓存只对查询功能有效
10.1、MyBatis的一级缓存
一级缓存是SqlSession级别的,默认开启,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
-
使一级缓存失效的四种情况:
-
不同的SqlSession对应不同的一级缓存
-
同一个SqlSession但是查询条件不同
-
同一个SqlSession两次查询期间执行了任何一次增删改操作
-
同一个SqlSession两次查询期间手动清空了缓存,例如:
sqlSession.clearCache();
-
10.2、MyBatis的二级缓存
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
-
二级缓存开启的条件:
-
在核心配置文件中,设置全局配置属性
cacheEnabled="true"
(默认为true,不需要额外设置) -
在映射文件中设置标签
<cache/>
-
二级缓存必须在SqlSession关闭或提交之后有效(数据在关闭或提交了SqlSession之后才会被保存在二级缓存中),如
sqlSession.close();
-
查询的数据所转换的实体类类型必须实现序列化的接口,如
public class Emp implements Serializable {}
-
-
使二级缓存失效的情况:
-
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
-
10.3、二级缓存的相关配置
在mapper配置文件中添加的cache标签可以设置一些属性:
-
eviction:缓存回收策略,
-
LRU(Least Recently Used):最近最少使用的,移除最长时间不被使用的对象。
-
FIFO(First in First out):先进先出,按对象进入缓存的顺序来移除它们。
-
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象。
-
WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
-
-
flushInterval属性:刷新间隔,单位毫秒
-
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
-
-
size属性:引用数目,正整数
-
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
-
-
readOnly属性:只读,true/false
-
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
-
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。
-
10.4、MyBatis缓存查询的顺序
-
二级缓存-->一级缓存-->数据库
-
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
-
如果二级缓存没有命中,再查询一级缓存
-
如果一级缓存也没有命中,则查询数据库
-
SqlSession关闭之后,一级缓存中的数据会写入二级缓存
10.5、整合第三方缓存EHCache
第三方缓存只能代替MyBatis的二级缓存
-
添加依赖
-
各jar包功能
-
在资源目录下创建EHCache的配置文件ehcache.xml
-
在mapper配置文件设置二级缓存的类型
-
加入logback日志
存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。
创建logback的配置文件logback.xml
-
EHCache配置文件说明
-
测试
11、MyBatis的逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:Java实体类、Mapper接口、Mapper映射文件
11.1、创建逆向工程的步骤
-
添加依赖和插件
-
创建MyBatis的核心配置文件
jdbc.properties log4j.xml mybatis-config.xml
-
创建逆向工程的配置文件
文件名必须是:generatorConfig.xml
-
执行MBG插件的generate目标
11.2、QBC查询
QBC:根据条件查询
要使用QBC,首先需要修改generatorConfig.xml中context标签为
<context id="DB2Tables" targetRuntime="MyBatis3">
条件增删改查中,
null
表示不对该字段进行增删改查,而普通增删改查中,null
表示该字段值为null
12、分页插件
12.1、分页插件使用步骤
-
添加依赖
-
配置分页插件
12.2、分页插件的使用
分页相关数据
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]
假设navigatePages=5,则pageNum=1~3时,navigatepageNums=[1,2,3,4,5];pageNum=4时,navigatepageNums=[2,3,4,5,6]
13、常见错误
Cause: java.lang.IllegalArgumentException: Result Maps collection already contains value
__EOF__

本文链接:https://www.cnblogs.com/colinpersonalblog/p/16641535.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix