如果对一些基础理论感兴趣可以看这一期👇
SSM【Spring SpringMVC Mybatis】——Mybatis
目录
1、Mybatis中参数传递问题
1.1 单个普通参数
1.2 多个普通参数
1.3 命名参数
1.4 POJO参数
1.5 Map参数
1.6 Collection|List|Array等参数
2、Mybatis参数传递【#与$区别】
2.1 回顾JDBC
2.2 #与$区别
2.3 #与$使用场景
3、Mybatis查询中返回值四种情况
3.1 查询单行数据返回单个对象
3.2 查询多行数据返回对象的集合
3.3 查询单行数据返回Map集合
3.4 查询多行数据返回Map集合
4、Mybatis中自动映射与自定义映射
4.1 自动映射与自定义映射
4.2 自定义映射-级联映射
4.3 自定义映射-association映射
4.4 自定义映射-collection映射
4.5 ResultMap相关标签及属性
4.6 Mybatis中分步查询
4.7 Mybatis延迟加载【懒加载】
5、Mybatis动态SQL【重点】
5.1 动态SQL概述
5.2 常用标签
5.3 示例代码
6、 Mybatis中缓存机制
6.1 缓存概述
6.2 Mybatis中的缓存概述
6.3 Mybatis缓存机制之一级缓存
6.4 Mybatis缓存机制之二级缓存
6.5 Mybatis中缓存机制之第三方缓存
1、Mybatis中参数传递问题
1.1 单个普通参数
可以任意使用:参数数据类型、参数名称不用考虑
1.2 多个普通参数
Mybatis底层封装Map结构,封装key为param1、param2....【支持:arg0、arg1、...】
1.3 命名参数
语法:
@Param(value="参数名")
@Param("参数名")
位置:参数前面
注意:
底层封装Map结构
命名参数,依然支持参数【param1,param2,...】
示例代码
/**
* 通过员工姓名及薪资查询员工信息【命名参数】
* @return
*/
public List<Employee> selectEmpByNamed(@Param("lName")String lastName,
@Param("salary") double salary);
<select id="selectEmpByNamed" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
last_name=#{param1}
AND
salary=#{param2}
</select>
源码分析
MapperMethod对象:【命名参数底层代码入口】
命名参数底层封装map为ParamMap,ParamMap继承HashMap
ParamNameResolver对象:命名参数底层实现逻辑
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
1.4 POJO参数
Mybatis支持POJO【JavaBean】入参,参数key是POJO中属性
1.5 Map参数
Mybatis支持直接Map入参,map的key=参数key
1.6 Collection|List|Array等参数
参数名:collection、list、array
2、Mybatis参数传递【#与$区别】
2.1 回顾JDBC
在MyBatis和JDBC的背景下,让我们深入了解每个组件:
1.DriverManager(驱动管理器):这是JDBC API的一部分,负责管理数据库驱动程序的列表。当你使用DriverManager时,可以通过URL字符串获取与特定数据库的连接。DriverManager类动态加载JDBC驱动程序,这使得JDBC API可以通过JDBC驱动程序连接支持SQL的任何数据库。
2.Connection(连接):连接表示与特定数据库的会话。在MyBatis的上下文中,连接对象是从DriverManager获取的。它提供了用于创建Statement和PreparedStatement对象的方法。Connection接口允许你与数据库进行通信,并执行各种操作,如执行SQL语句、提交事务和管理连接属性。
3.Statement(语句):Statement对象用于执行静态SQL语句并返回其产生的结果。当你使用Statement时,SQL查询通常通过串联字符串构造,如果不正确处理,可能会导致SQL注入漏洞。这是在JDBC中执行SQL查询的最基本方法,但由于安全方面的考虑,不建议使用。
4.PreparedStatement(预编译语句):PreparedStatement扩展了Statement,用于执行带参数的SQL查询。与Statement不同,PreparedStatement中创建带有参数占位符(通常用问号?表示)的SQL语句。这样做可以提高性能和安全性,因为SQL语句会被数据库预编译和缓存,减少解析开销,并保护免受SQL注入攻击。
5.ResultSet(结果集):ResultSet对象表示SQL查询的结果。它提供了用于遍历和访问数据库执行查询后返回的数据的方法。通过ResultSet,你可以遍历数据行,检索列值,并对结果集执行各种操作。
2.2 #与$区别
【#】底层执行SQL语句的对象,使用PreparedStatementd,预编译SQL,防止SQL注入安全隐患,相对比较安全。
【$】底层执行SQL语句的对象使用Statement对象,未解决SQL注入安全隐患,相对不安全。
2.3 #与$使用场景
查询SQL:select col,col2 from table1 where col=? and col2=? group by ?, order by ? limit ?,?
#使用场景,sql占位符位置均可以使用#
$使用场景,#解决不了的参数传递问题,均可以交给$处理【如:form 动态化表名】
/**
* 测试$使用场景
*/
public List<Employee> selectEmpByDynamitTable(@Param("tblName") String tblName);
<select id="selectEmpByDynamitTable" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
${tblName}
</select>
3、Mybatis查询中返回值四种情况
3.1 查询单行数据返回单个对象
/**
* 通过id获取员工信息
*/
public Employee selectEmpById(int empId);
<select id="selectEmpById" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
id=#{empId}
</select>
3.2 查询多行数据返回对象的集合
/**
* 查询所有员工信息
*/
public List<Employee> selectAllEmps();
<select id="selectAllEmps" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
</select>
注意:如果返回的是集合,那应该设置为**集合包含的类型**,而不是集合本身的类型。
3.3 查询单行数据返回Map集合
Map<String key,Object value>
字段作为Map的key,查询结果作为Map的Value
示例代码
/**
* 查询单行数据返回Map集合
* @return
*/
public Map<String,Object> selectEmpReturnMap(int empId);
<!-- 查询单行数据返回Map集合-->
<select id="selectEmpReturnMap" resultType="map">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
id=#{empId}
</select>
3.4 查询多行数据返回Map集合
Map<Integer key,Employee value>
对象的id作为key
对象作为value
示例代码
/**
* 查询多行数据返回Map
* Map<Integer,Object>
* Map<Integer,Employee>
* 对象Id作为:key
* 对象作为:value
* @return
*/
@MapKey("id")
public Map<Integer,Employee> selectEmpsReturnMap();
<select id="selectEmpsReturnMap" resultType="map">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
</select>
4、Mybatis中自动映射与自定义映射
自动映射【resultType】
自定义映射【resultMap】
4.1 自动映射与自定义映射
自动映射【resultType】:指的是自动将表中的字段与类中的属性进行关联映射
自动映射解决不了两类问题
多表连接查询时,需要返回多张表的结果集
单表查询时,不支持驼峰式自动映射【不想为字段定义别名】
自定义映射【resultMap】:自动映射解决不了问题,交给自定义映射
注意:resultType与resultMap只能同时使用一个
4.2 自定义映射-级联映射
<!-- 自定义映射 【员工与部门关系】-->
<resultMap id="empAndDeptResultMap" type="employee">
<!-- 定义主键字段与属性关联关系 -->
<id column="id" property="id"></id>
<!-- 定义非主键字段与属性关联关系-->
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="salary" property="salary"></result>
<!-- 为员工中所属部门,自定义关联关系-->
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="selectEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
SELECT
e.`id`,
e.`email`,
e.`last_name`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
e.`id` = #{empId}
</select>
4.3 自定义映射-association映射
特点:解决一对一映射关系【多对一】
示例代码
<!-- 自定义映射 【员工与部门关系】-->
<resultMap id="empAndDeptResultMapAssociation" type="employee">
<!-- 定义主键字段与属性关联关系 -->
<id column="id" property="id"></id>
<!-- 定义非主键字段与属性关联关系-->
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="salary" property="salary"></result>
<!-- 为员工中所属部门,自定义关联关系-->
<association property="dept"
javaType="com.atguigu.mybatis.pojo.Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
4.4 自定义映射-collection映射
示例代码
/**
* 通过部门id获取部门信息,及部门所属员工信息
*/
public Dept selectDeptAndEmpByDeptId(int deptId);
<resultMap id="deptAndempResultMap" type="dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
<collection property="empList"
ofType="com.atguigu.mybatis.pojo.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="salary" property="salary"></result>
</collection>
</resultMap>
<select id="selectDeptAndEmpByDeptId" resultMap="deptAndempResultMap">
SELECT
e.`id`,
e.`email`,
e.`last_name`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
d.dept_id = #{deptId}
</select>
4.5 ResultMap相关标签及属性
resultMap标签:自定义映射标签
id属性:定义唯一标识
type属性:设置映射类型
resultMap子标签
id标签:定义主键字段与属性关联关系
result标签:定义非主键字段与属性关联关系
column属性:定义表中字段名称
property属性:定义类中属性名称
association标签:定义一对一的关联关系
property:定义关联关系属性
javaType:定义关联关系属性的类型
select:设置分步查询SQL全路径
colunm:设置分步查询SQL中需要参数
fetchType:设置局部延迟加载【懒加载】是否开启
collection标签:定义一对多的关联关系
property:定义一对一关联关系属性
ofType:定义一对一关联关系属性类型
fetchType:设置局部延迟加载【懒加载】是否开启
4.6 Mybatis中分步查询
为什么使用分步查询【分步查询优势】?
将多表连接查询,改为【分步单表查询】,从而提高程序运行效率
示例代码
一对一
/**
* 通过员工id获取员工信息及员工所属的部门信息【分步查询】
1. 先通过员工id获取员工信息【id、last_name、email、salary、dept_id】
2. 再通过部门id获取部门信息【dept_id、dept_name】
*/
public Employee selectEmpAndDeptByEmpIdAssociationStep(int empId);
<select id="selectEmpAndDeptByEmpIdAssociationStep" resultMap="empAndDeptResultMapAssocationStep">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
where
id=#{empId}
</select>
/**
* 通过部门id获取部门信息
*/
public Dept selectDeptByDeptId(int deptId);
<select id="selectDeptByDeptId" resultType="dept">
select
dept_id,
dept_name
from
tbl_dept
where
dept_id=#{deptId}
</select>
一对多
/**
* 通过部门id获取部门信息,及部门所属员工信息【分步查询】
1. 通过部门id获取部门信息
2. 通过部门id获取员工信息
*/
public Dept selectDeptAndEmpByDeptIdStep(int deptId);
<!-- 通过部门id获取部门信息,及部门所属员工信息【分步查询】-->
<!-- 1. 通过部门id获取部门信息-->
<!-- 2. 通过部门id获取员工信息-->
<select id="selectDeptAndEmpByDeptIdStep" resultMap="deptAndEmpResultMapStep">
select
dept_id,
dept_name
from
tbl_dept
where
dept_id=#{deptId}
</select>
/**
* 通过部门Id获取员工信息
* @param deptId
* @return
*/
public List<Employee> selectEmpByDeptId(int deptId);
<select id="selectEmpByDeptId" resultType="employee">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
where
dept_id=#{deptId}
</select>
4.7 Mybatis延迟加载【懒加载】
需要时加载,不需要暂时不加载
优势:提升程序运行效率
语法
全局设置
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置加载的数据是按需加载3.4.2及以后的版本该步骤可省略-->
<setting name="aggressiveLazyLoading" value="false"/>
局部设置
fetchType
eager:关闭局部延迟加载
lazy:开启局部延迟加载
示例代码
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.selectDeptByDeptId"
column="dept_id"
fetchType="eager">
</association>
4.8 扩展
如果分步查询时,需要传递给调用的查询中多个参数,则需要将多个参数封装成
Map来进行传递,语法如下**: {k1=v1, k2=v2....}
5、Mybatis动态SQL【重点】
5.1 动态SQL概述
动态SQL指的是:SQL语句可动态化
Mybatis的动态SQL中支持OGNL表达式语言,OGNL( Object Graph Navigation Language )对象图导航语言
5.2 常用标签
if标签:用于完成简单的判断
where标签:用于解决where关键字及where后第一个and或or的问题
trim标签: 可以在条件判断完的SQL语句前后添加或者去掉指定的字符
prefix: 添加前缀
prefixOverrides: 去掉前缀
suffix: 添加后缀
suffixOverrides: 去掉后缀
set标签:主要用于解决set关键字及多出一个【,】问题
choose标签:类似java中if-else【switch-case】结构
foreach标签:类似java中for循环
collection: 要迭代的集合
item: 当前从集合中迭代出的元素
separator: 元素与元素之间的分隔符
open: 开始字符
close:结束字符
sql标签:提取可重用SQL片段
5.3 示例代码
<?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">
<mapper namespace="com.atguigu.mybatis.mapper.EmployeeMapper">
<sql id="emp_col">
id,
last_name,
email,
salary
</sql>
<sql id="select_employee">
select
id,
last_name,
email,
salary
from
tbl_employee
</sql>
<!-- 按条件查询员工信息【条件不确定】-->
<select id="selectEmpByOpr" resultType="employee">
<include refid="select_employee"></include>
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="lastName != null">
and last_name = #{lastName}
</if>
<if test="email != null">
and email = #{email}
</if>
<if test="salary != null">
and salary = #{salary}
</if>
</where>
</select>
<select id="selectEmpByOprTrim" resultType="employee">
<include refid="select_employee"></include>
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id = #{id} and
</if>
<if test="lastName != null">
last_name = #{lastName} and
</if>
<if test="email != null">
email = #{email} and
</if>
<if test="salary != null">
salary = #{salary}
</if>
</trim>
</select>
<update id="updateEmpByOpr">
update
tbl_employee
<set>
<if test="lastName != null">
last_name=#{lastName},
</if>
<if test="email != null">
email=#{email},
</if>
<if test="salary != null">
salary=#{salary}
</if>
</set>
where
id = #{id}
</update>
<select id="selectEmpByOneOpr" resultType="employee">
select
<include refid="emp_col"></include>
from
tbl_employee
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="lastName != null">
last_name = #{lastName}
</when>
<when test="email != null">
email = #{email}
</when>
<when test="salary != null">
salary = #{salary}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
<select id="selectEmpByIds" resultType="employee">
select
id,
last_name,
email,
salary
from
tbl_employee
<where>
id in(
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</where>
</select>
<insert id="batchInsertEmp">
INSERT INTO
tbl_employee(last_name,email,salary)
VALUES
<foreach collection="employees" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.salary})
</foreach>
</insert>
</mapper>
6、 Mybatis中缓存机制
6.1 缓存概述
生活中缓存
缓存一些音频、视频优势
节约数据流量
提高播放性能
程序中缓存【Mybatis缓存】
使用缓存优势
提高查询效率
降低服务器压力
6.2 Mybatis中的缓存概述
一级缓存
二级缓存
第三方缓存
6.3 Mybatis缓存机制之一级缓存
概述:一级缓存【本地缓存(Local Cache)或SqlSession级别缓存】
特点
一级缓存默认开启
不能关闭
可以清空
缓存原理
第一次获取数据时,先从数据库中加载数据,将数据缓存至Mybatis一级缓存中【缓存底层实现原理Map,key:hashCode+查询的SqlId+编写的sql查询语句+参数】
以后再次获取数据时,先从一级缓存中获取,**如未获取到数据**,再从数据库中获取数据。
一级缓存五种失效情况
1) 不同的SqlSession对应不同的一级缓存
2) 同一个SqlSession但是查询条件不同
3) 同一个SqlSession两次查询期间执行了任何一次增删改操作
4) 同一个SqlSession两次查询期间手动清空了缓存
sqlSession.clearCache()
5) 同一个SqlSession两次查询期间提交了事务
sqlSession.commit()
6.4 Mybatis缓存机制之二级缓存
二级缓存【second level cache】概述
二级缓存【全局作用域缓存】
SqlSessionFactory级别缓存
二级缓存特点
二级缓存默认关闭,需要开启才能使用
二级缓存需要提交sqlSession或关闭sqlSession时,才会缓存。
二级缓存使用的步骤:
① 全局配置文件中开启二级缓存<setting name="cacheEnabled" value="true"/>
② 需要使用二级缓存的**映射文件处**使用cache配置缓存<cache />
③ 注意:POJO需要实现Serializable接口
④ 关闭sqlSession或提交sqlSession时,将数据缓存到二级缓存
二级缓存底层原理
第一次获取数据时,先从数据库中获取数据,将数据缓存至一级缓存;当提交或关闭SqlSession时,将数据缓存至二级缓存
以后再次获取数据时,先从一级缓存中获取数据,如一级缓存没有指定数据,再去二级缓存中获取数据。如二级缓存也没有指定数据时,需要去数据库中获取数据,......
二级缓存相关属性
eviction=“FIFO”:缓存清除【回收】策略。
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
flushInterval:刷新间隔,单位毫秒
size:引用数目,正整数
readOnly:只读,true/false
二级缓存的失效情况
在两次查询之间,执行增删改操作,会同时清空一级缓存和二级缓存
sqlSession.clearCache():只是用来清除一级缓存。
6.5 Mybatis中缓存机制之第三方缓存
第三方缓存:EhCache
EhCache 是一个纯Java的进程内缓存框架
使用步骤
导入jar包
<!-- mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
<!-- slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
编写配置文件【ehcache.xml】
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="E:\mybatis\ehcache" />
<defaultCache
maxElementsInMemory="512"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
加载第三方缓存【映射文件】
开始使用
注意事项
第三方缓存,需要建立在二级缓存基础上【需要开启二级缓存,第三方缓存才能生效】
如何让第三方缓存失效【将二级缓存设置失效即可】