我 | 在这里
🕵️ 读书 | 长沙 ⭐软件工程 ⭐ 本科
🏠 工作 | 广州 ⭐ Java 全栈开发(软件工程师)
🎃 爱好 | 研究技术、旅游、阅读、运动、喜欢流行歌曲
🏷️ 标签 | 男 自律狂人 目标明确 责任心强
✈️公众号 | 热爱技术的小郑
🚀 邮箱 | [email protected]
✈️ GitHub项目仓库 开源项目 + 实战Demo
为何而写?
🍍 好记性不如烂笔头,记录学习的相关知识 、项目 BUG 解决
🍇 复盘总结,加深记忆,方便自己查看
🍑 分享知识,咱就是这么乐于助人、专注填坑20年、哈哈哈哈
目标描述
🏆 没有伞的孩子、只能用力奔跑。向着架构师的方向努力、做一个有始有终的人。
11、一对多处理
具体搭建过程同 多对一处理
1、实体类TeacherT,StudentT 同一个环境测试,实体类的字段有所不同。通过起别名的方式完善。
less
@Data //get,set
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class StudentT {
private Integer id;
private String name;
private Integer tid;
}
@Data //get,set
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class TeacherT {
private Integer id;
private String name;
private List<StudentT> studentTs;
}
2、mybatis 核心配置文件
ini
<typeAliases>
<package name="com.zyz.mybatis.entity" />
<package name="com.zyz.mybatis.vo" />
</typeAliases>
<mappers>
<mapper resource="com/zyz/mybatis/mapper/StudentMapper.xml"/>
<mapper resource="com/zyz/mybatis/mapper/TeacherMapper.xml"/>
</mappers>
3、mapper 接口
java
public interface TeacherMapper {
/**
* @description: 1、按照结果嵌套处理 多对一
* @author: zhengyuzhu
* @date: 2023/11/22 23:50
* @return: com.zyz.mybatis.vo.Teacher
**/
TeacherT getTeacherById(Integer id);
/**
* @description: 2、按照查询嵌套处理 一对多
* @author: zhengyuzhu
* @date: 2023/11/23 0:12
* @param: id
* @return: com.zyz.mybatis.vo.TeacherT
**/
TeacherT getTeacher2(Integer id);
}
4、mapper.xml
xml
<?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.zyz.mybatis.mapper.TeacherMapper">
<!--
1、 按结果嵌套查询
-->
<select id="getTeacherById" resultMap="TeacherById">
select t.id id, t.name tname, s.id sid,s.name sname,s.tid tid
from tb_teacher t
join tb_student s
on t.id = s.tid;
</select>
<resultMap id="TeacherById" type="TeacherT">
<result property="id" column="id"/>
<result property="name" column="tname"/>
<!--获取List<Student>中的泛型使用 ofType-->
<collection property="studentTs" ofType="StudentT" javaType="java.util.List">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<!--
2、按照查询嵌套处理
-->
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from tb_teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent2" type="TeacherT">
<result property="id" column="id"/>
<collection property="studentTs" javaType="java.util.List" ofType="StudentT" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="StudentT">
select * from tb_student where tid = #{tid}
</select>
</mapper>
5、测试单元
ini
/**
* @description: 1、 按结果嵌套查询 一对多
* @author: zhengyuzhu
* @date: 2023/11/22 23:07
**/
@Test
public void testDemo3(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
TeacherT teacher = teacherMapper.getTeacherById(1);
System.out.println(teacher);
sqlSession.close();
/**
* 输出如下:
*
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1486566962.
[com.zyz.mybatis.mapper.TeacherMapper.getTeacherById]-==> Preparing: select t.id id, t.name tname, s.id sid,s.name sname,s.tid tid from tb_teacher t join tb_student s on t.id = s.tid;
[com.zyz.mybatis.mapper.TeacherMapper.getTeacherById]-==> Parameters:
[com.zyz.mybatis.mapper.TeacherMapper.getTeacherById]-<== Total: 8
TeacherT(id=1, name=玉小刚, studentTs=[
StudentT(id=1, name=唐三, tid=1),
StudentT(id=2, name=小舞, tid=1),
StudentT(id=3, name=戴沐白, tid=1),
StudentT(id=4, name=朱朱清, tid=1),
StudentT(id=5, name=奥斯卡, tid=1),
StudentT(id=6, name=宁荣荣, tid=1),
StudentT(id=7, name=马红俊, tid=1),
StudentT(id=8, name=白尘香, tid=1)
])
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@589b3632]
*
**/
}
/**
* @description: 2、 按照查询嵌套处理 一对多
* @author: zhengyuzhu
* @date: 2023/11/22 23:07
**/
@Test
public void testDemo4(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
TeacherT teacher = teacherMapper.getTeacher2(1);
System.out.println(teacher);
sqlSession.close();
/**
* 输出如下:
*
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1486566962.
[com.zyz.mybatis.mapper.TeacherMapper.getTeacher2]-==> Preparing: select * from tb_teacher where id = ?
[com.zyz.mybatis.mapper.TeacherMapper.getTeacher2]-==> Parameters: 1(Integer)
[com.zyz.mybatis.mapper.TeacherMapper.getStudentByTeacherId]-====> Preparing: select * from tb_student where tid = ?
[com.zyz.mybatis.mapper.TeacherMapper.getStudentByTeacherId]-====> Parameters: 1(Integer)
[com.zyz.mybatis.mapper.TeacherMapper.getStudentByTeacherId]-<==== Total: 8
[com.zyz.mybatis.mapper.TeacherMapper.getTeacher2]-<== Total: 1
TeacherT(id=1, name=玉小刚, studentTs=[
StudentT(id=1, name=唐三, tid=1),
StudentT(id=2, name=小舞, tid=1),
StudentT(id=3, name=戴沐白, tid=1),
StudentT(id=4, name=朱朱清, tid=1),
StudentT(id=5, name=奥斯卡, tid=1),
StudentT(id=6, name=宁荣荣, tid=1),
StudentT(id=7, name=马红俊, tid=1),
StudentT(id=8, name=白尘香, tid=1)
])
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@589b3632]
*
**/
}
12、动态SQL
所谓的动态SQL,本质还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码 什么是动态SQL:动态SQL就是 指根据不同的条件生成不同的SQL语句 查询搜索的时候用的多
12.1 搭建环境
大致过程 ● 1、创建数据表 ● 2、新建实体类 ● 3、编写实体类对应Mapper接口 ● 4、和Mapper.XML文件 ● 5、修改mybatis 核心配置文件 ● 6、测试单元
1、创建数据表
sql
CREATE TABLE `blog` (
`id` int(10) NOT NULL COMMENT '博客id',
`title` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '博客标题',
`author` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '博客作者',
`create_time` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '创建时间',
`views` int(30) NOT NULL COMMENT '浏览量'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2、创建 Blog 实体类
less
@Data //get,set
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class Blog {
private String id;
private String title;
private String author;
private Date createTime; //属性名和字段名不一致
private int views;
}
3、编写实体类对应Mapper接口
typescript
/**
* @author zyz
* @version 1.0
* @data 2023/11/23 11:18
* @Description:
*/
public interface BlogMapper {
/**
* @description: IF的使用 查询博客
* @author: zhengyuzhu
* @date: 2023/11/23 11:27
* @param: map
* @return: com.zyz.mybatis.entity.Blog
**/
Blog queryBlogIF(HashMap<String,Object> map);
/**
* @description: choose (when, otherwise)的使用 查询博客
* @author: zhengyuzhu
* @date: 2023/11/23 11:30
* @param: map
* @return: com.zyz.mybatis.entity.Blog
**/
Blog queryBlogChoose(HashMap<String,Object> map);
/**
* @description: trim (where, set)的使用 查询
* @author: zhengyuzhu
* @date: 2023/11/23 11:34
* @param: map
* @return: com.zyz.mybatis.entity.Blog
**/
Blog queryBlogIFTwo(HashMap<String,Object> map);
/**
* @description: trim (where, set)的使用 修改
* @author: zhengyuzhu
* @date: 2023/11/23 11:35
* @param: map
**/
void updateBlog(HashMap<String,Object> map);
/**
* @description: Foreach 的使用
* @author: zhengyuzhu
* @date: 2023/11/23 14:02
* @param: map
* @return: java.util.List<com.zyz.mybatis.entity.Blog>
**/
List<Blog> queryBlogForeach(HashMap<String,Object> map);
/**
* @description: IF的使用 使用代码片段
* @author: zhengyuzhu
* @date: 2023/11/23 11:27
* @param: map
* @return: com.zyz.mybatis.entity.Blog
**/
Blog queryBlogIFThree(HashMap<String,Object> map);
}
4、修改mybatis 核心配置文件
xml
<!-- 1、使用映射器 -->
<mappers>
<mapper resource="com/zyz/mybatis/mapper/BlogMapper.xml"/>
</mappers>
具体的动态SQL 编写以及具体的 测试单元分成如下小节
12.2 IF
mapper.xml 文件 由于 实体类 和 数据库 字段不一致,这里进行了映射
xml
<!-- 结果集映射 -->
<resultMap id="BlogMap" type="Blog">
<!--column数据库中的字段,property实体类中的属性-->
<result column="id" property="id" />
<result column="title" property="title" />
<result column="author" property="author" />
<result column="create_time" property="createTime" />
<result column="views" property="views" />
</resultMap>
<!--IF的使用 查询博客-->
<select id="queryBlogIF" parameterType="map" resultMap="BlogMap">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
测试单元
ini
/**
* @description: IF 测试
* @author: zhengyuzhu
* @date: 2023/11/23 12:46
**/
@Test
public void testDemo1(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> blogMap = new HashMap<>();
blogMap.put("title","张麻子娶亲");
blogMap.put("author","张老头");
Blog blog = blogMapper.queryBlogIF(blogMap);//查询
System.out.println(blog);
sqlSession.close();
/**
*
*
* blogMap.put("title","张麻子娶亲");
* blogMap.put("author","张老头");
*
* 查询条件两个:输出如下:
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ? and author = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 张麻子娶亲(String), 张老头(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]
*
*
* blogMap.put("title","张麻子娶亲");
* 查询条件一个输出如下
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 张麻子娶亲(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]
*
*
*
*
**/
}
12.3 choose (when, otherwise)
mapper.xml 文件 这里也进行了结果集映射,如上
xml
<!--choose (when, otherwise)的使用 查询博客-->
<select id="queryBlogChoose" parameterType="map" resultMap="BlogMap">
select * from blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
测试单元
ini
/**
* @description: choose 测试 when otherwise
* @author: zhengyuzhu
* @date: 2023/11/23 13:34
**/
@Test
public void testDemo2(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> blogMap = new HashMap<>();
// blogMap.put("title","张麻子娶亲");
// blogMap.put("author","张老头");
blogMap.put("views",30);
Blog blog = blogMapper.queryBlogChoose(blogMap);
System.out.println(blog);
sqlSession.close();
/**
*
* 1、多个参数不会拼接,哪个有值 用哪个
*
* blogMap.put("title","张麻子娶亲");
* blogMap.put("author","张老头");
*
* 查询条件两个 但是拼接的时候 第一个有值,就不会在拼接接下来的数据
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Preparing: select * from blog WHERE title = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Parameters: 张麻子娶亲(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]
*
*
* blogMap.put("author","张老头");
* 2、查询条件一个输出如下
*
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.
[com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Preparing: select * from blog WHERE author = ?
[com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Parameters: 张老头(String)
[com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-<== Total: 1
Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]
*
*
*
* 3、查询条件 一个 都不满足情况 otherwise
* blogMap.put("views",30);
*
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Preparing: select * from blog WHERE views = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Parameters: 30(Integer)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]
*
**/
}
12.4 trim (where, set)
mapper.xml 文件 这里也进行了结果集映射,如上
xml
<!--trim (where, set)的使用 查询-->
<select id="queryBlogIFTwo" parameterType="map" resultMap="BlogMap">
select * from blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
<!--trim (where, set)的使用 修改-->
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id = #{id}
</update>
测试单元
ini
/**
* @description: trim (where, set)的使用 查询
* @author: zhengyuzhu
* @date: 2023/11/23 13:36
**/
@Test
public void testDemo3(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> blogMap = new HashMap<>();
blogMap.put("title","张麻子娶亲");
blogMap.put("author","张老头");
Blog blog = blogMapper.queryBlogIFTwo(blogMap);//查询
System.out.println(blog);
sqlSession.close();
/**
*
*
* blogMap.put("title","张麻子娶亲");
* blogMap.put("author","张老头");
* 查询条件两个:输出如下:
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Preparing: select * from blog WHERE title = ? and author = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Parameters: 张麻子娶亲(String), 张老头(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]
*
*
* blogMap.put("title","张麻子娶亲");
* 查询条件一个输出如下
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 张麻子娶亲(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]
*
*
*
*
**/
}
/**
* @description: trim (where, set)的使用 修改
* @author: zhengyuzhu
* @date: 2023/11/23 13:36
**/
@Test
public void testDemo4(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> blogMap = new HashMap<>();
blogMap.put("title","张麻子娶亲");
blogMap.put("author","张老头");
Blog blog = blogMapper.queryBlogIFTwo(blogMap);//查询
System.out.println("修改前的数据:" + blog);
HashMap<String, Object> blogMapTwo = new HashMap<>();
blogMapTwo.put("id",1);
blogMapTwo.put("title","如何学号java ?");
blogMapTwo.put("author","小明");
Blog blog1 = new Blog();
blog1.setAuthor("如何学号java ?");
blog1.setAuthor("小明");
blogMapper.updateBlog(blogMapTwo);//修改
HashMap<String, Object> blogMap3 = new HashMap<>();
blogMap3.put("title","如何学号java ?");
Blog blog2 = blogMapper.queryBlogIFTwo(blogMap3);//查询
System.out.println("修改后的数据:" + blog2);
sqlSession.close();
/**
*
*
* blogMapTwo.put("id",1);
* blogMapTwo.put("title","如何学号java ?");
* blogMapTwo.put("author","小明");
* 查询条件两个:输出如下:
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Preparing: select * from blog WHERE title = ? and author = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Parameters: 张麻子娶亲(String), 张老头(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-<== Total: 1
* 修改前的数据:Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [com.zyz.mybatis.mapper.BlogMapper.updateBlog]-==> Preparing: update blog SET title = ?, author = ? where id = ?
* [com.zyz.mybatis.mapper.BlogMapper.updateBlog]-==> Parameters: 如何学号java ?(String), 小明(String), 1(Integer)
* [com.zyz.mybatis.mapper.BlogMapper.updateBlog]-<== Updates: 1
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Preparing: select * from blog WHERE title = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Parameters: 如何学号java ?(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-<== Total: 1
* 修改后的数据:Blog(id=1, title=如何学号java ?, author=小明, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]
*
*
*
**/
}
12.5 foreach
更详细的资料参考:mybatis之foreach用法 foreach元素的属性主要有item,index,collection,open,separator,close。 ● item:集合中元素迭代时的别名,该参数为必选。 ● index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选 ● open:foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选 ● separator:元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用","隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。 ● close: foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。 ● collection: 要做foreach的对象,作为入参时,List对象默认用"list"代替作为键,数组对象有"array"代替作为键,Map对象没有默认的键。当然在作为入参时可以使用@Param("keyName")来设置键,设置keyName后,list,array将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:如果User有属性List ids。入参是User对象,那么这个collection = "ids".如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = "ids.id" 在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况: ● 如果传入的是单参数且参数类型是一个List的时候,collection属性值为list . ● 如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array . ● 如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key. 针对最后一条,我们来看一下官方说法: 注意 你可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以"list"作为键,而数组实例的键将是"array"。 所以,不管是多参数还是单参数的list,array类型,都可以封装为map进行传递。如果传递的是一个List,则mybatis会封装为一个list为key,list值为object的map,如果是array,则封装成一个array为key,array的值为object的map,如果自己封装呢,则colloection里放的是自己封装的map里的key值。
mapper.xml 文件 这里也进行了结果集映射
perl
<!--
foreach元素的属性主要有item,index,collection,open,separator,close。
● item:集合中元素迭代时的别名,该参数为必选。
● index:在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选
● open:foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。该参数可选
● separator:元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用","隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。
● close: foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。该参数可选。
● collection: 要做foreach的对象,作为入参时,List对象默认用"list"代替作为键,数组对象有"array"代替作为键,Map对象没有默认的键。
当然在作为入参时可以使用@Param("keyName")来设置键,设置keyName后,list,array将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。
举个例子:如果User有属性List ids。入参是User对象,那么这个collection = "ids".如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;
入参是User对象,那么collection = "ids.id"
select * from blog where 1=1 and (id=1 or id=2 or id=3)
我们现在传递一个万能的map,这map中可以存在一个集合!
-->
<select id="queryBlogForeach" parameterType="map" resultMap="BlogMap">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
测试单元
ini
/**
* @description: Foreach 的使用
* @author: zhengyuzhu
* @date: 2023/11/23 14:03
**/
@Test
public void testDemo5(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
ArrayList<Object> idList = new ArrayList<>();
idList.add(1);
idList.add(2);
idList.add(3);
HashMap<String, Object> blogMap = new HashMap<>();
blogMap.put("ids",idList);
List<Blog> blogList = blogMapper.queryBlogForeach(blogMap);//查询
for(Blog blog : blogList){
System.out.println(blog);
}
sqlSession.close();
/**
*
*
* 查询条件两个:输出如下:
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 360062456.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogForeach]-==> Preparing: select * from blog WHERE ( id = ? or id = ? or id = ? )
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogForeach]-==> Parameters: 1(Integer), 2(Integer), 3(Integer)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogForeach]-<== Total: 3
* Blog(id=1, title=如何学号java ?, author=小明, createTime=2023-11-23 00:00:00, views=30)
* Blog(id=2, title=张麻子学java, author=李老头, createTime=2023-11-22 00:00:00, views=560)
* Blog(id=3, title=张麻子学数据库, author=米老头, createTime=2023-11-22 00:00:00, views=760)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15761df8]
*
*
*
**/
}
12.5 代码片段(复用)
有的时候,我们可以能会将一些功能的部分抽取出来,方便复用!
- 使用SQL标签抽取公共的部分
bash
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
- 在需要使用的地方使用Include标签引用即可 这里有对结果集进行映射
xml
<!--复用 SQL代码片段的方式-->
<select id="queryBlogIFThree" parameterType="map" resultMap="BlogMap">
select * from blog
<where>
<include refid="if-title-author"></include>
</where>
</select>
测试单元
ini
/**
* @description: IF 的使用 复用代码片段
* @author: zhengyuzhu
* @date: 2023/11/23 14:33
**/
@Test
public void testDemo6(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);
HashMap<String, Object> blogMap = new HashMap<>();
blogMap.put("title","张麻子娶亲");
blogMap.put("author","张老头");
Blog blog = blogMapper.queryBlogIF(blogMap);//查询
System.out.println(blog);
sqlSession.close();
/**
*
*
* blogMap.put("title","张麻子娶亲");
* blogMap.put("author","张老头");
*
* 查询条件两个:输出如下:
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ? and author = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 张麻子娶亲(String), 张老头(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]
*
*
* blogMap.put("title","张麻子娶亲");
* 查询条件一个输出如下
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ?
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 张麻子娶亲(String)
* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1
* Blog(id=1, title=张麻子娶亲, author=张老头, createTime=2023-11-23 00:00:00, views=30)
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]
*
*
*
*
**/
}
13、缓存
13.1 简介
1、什么是缓存[Cache]? ● 存在内存中的临时数据。 ● 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库查询文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
-
为什么使用缓存? ● 减少和数据库的交互次数,减少系统开销,提高系统效率。
-
什么样的数据能使用缓存? ● 经常查询并且不经常改变的数据。【可以使用缓存】
资料参考:mybatis一级缓存二级缓存 MYSQL缓存:一级缓存和二级缓存
13.2 Mybatis缓存
● Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。 ● Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存 1、默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存) 2、二级缓存需要手动开启和配置,它是基于namespace级别的缓存。 3、为了提高扩展性,Mybatis定义了缓存接口Cache,我们可以通过实现Cache接口来自 定义二级缓存。
13.2.1 一级缓存
13.2.1.1 原理说明
Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象调用一个Mapper方法,往往只执行一次SQL,因为使用SelSession第一次查询后,MyBatis会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession都会取出当前缓存的数据,而不会再次发送SQL到数据库。
为什么要使用一级缓存,不用多说也知道个大概。但是还有几个问题我们要注意一下。 1、一级缓存的生命周期有多长? a、MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。 b、如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。 c、如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。 d、SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用 2、怎么判断某两次查询是完全相同的查询? mybatis认为,对于两次查询,如果以下条件都完全一样,那么就认为它们是完全相同的两次查询。 2.1 传入的statementId 2.2 查询时要求的结果集中的结果范围 2.3. 这次查询所产生的最终要传递给JDBC java.sql.Preparedstatement的Sql语句字符串(boundSql.getSql() ) 2.4 传递给java.sql.Statement要设置的参数值
13.2.1.2 缓存成功
测试步骤:
- 开启日志!
- 测试在一个Session中查询两次相同记录
- 查看日志输出
ini
/**
* @description: 测试缓存 同一个session 连续查询两次
* @author: zhengyuzhu
* @date: 2023/11/23 15:02
**/
@Test
public void test07(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.queryStudentById(1);
System.out.println(student);
System.out.println("-----------------");
Student student1 = studentMapper.queryStudentById(1);
System.out.println(student1);
//关闭SqlSession
sqlSession.close();
/**
* 只进行了一次查询 输出如下 :
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* -----------------
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
*
**/
}
13.2.1.3 缓存失效的情况
一级缓存失效的情况:
- 查询不同的东西;
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
- 查询不同的Mapper.xml
- 手动清理缓存!
ini
/**
* @description: 测试缓存失效的情况 1、进行不同的数据查询
* @author: zhengyuzhu
* @date: 2023/11/23 15:02
**/
@Test
public void test08(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.queryStudentById(1);
System.out.println(student);
System.out.println("-----------------");
Student student1 = studentMapper.queryStudentById(2);
System.out.println(student1);
//关闭SqlSession
sqlSession.close();
/**
* 1、查询不同的数据的时候,缓存会失效
*
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* -----------------
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 2(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* Student{stuNo=2, stuName='李四', cardID=1116, classID=2}
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
*
**/
}
/**
* @description: 测试缓存失效的情况 2、进行了删除或者修改操作
* @author: zhengyuzhu
* @date: 2023/11/23 15:02
**/
@Test
public void test09(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student1 = studentMapper.queryStudentById(1);
System.out.println("修改前数据:" + student1);
System.out.println("-----------------");
Student student = new Student();
student.setStuNo(1);
student.setStuName("张麻子666");
student.setClassID(8);
student.setCardID(8);
studentMapper.updateStudent(student); //修改
System.out.println("-----------------");
Student student2 = studentMapper.queryStudentById(1);
System.out.println("修改后数据:" + student2);
//关闭SqlSession
sqlSession.close();
/**
* 1、查询相同的数据,但是中间进行了修改操作
*
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* 修改前数据:Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* -----------------
* [com.zyz.mybatis.mapper.StudentMapper.updateStudent]-==> Preparing: update student set stuNo = ?, stuName = ?, cardid = ?, classid = ? where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.updateStudent]-==> Parameters: 1(Integer), 张麻子666(String), 8(Integer), 8(Integer), 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.updateStudent]-<== Updates: 1
* -----------------
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* 修改后数据:Student{stuNo=1, stuName='张麻子666', cardID=8, classID=8}
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
*
**/
}
手动清理缓存!
sqlSession.clearCache(); //手动清除缓存
ini
/**
* @description: 手动清除缓存 缓存失效。查询两次
* @author: zhengyuzhu
* @date: 2023/11/23 15:21
**/
@Test
public void test10(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.queryStudentById(1);
System.out.println(student);
System.out.println("-----------------");
sqlSession.clearCache(); //手动清除缓存
Student student1 = studentMapper.queryStudentById(1);
System.out.println(student1);
//关闭SqlSession
sqlSession.close();
/**
* 1、同一个连接,连续两次查询。第二次查询拿缓存数据
*
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* -----------------
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
*
*
*
* 2、手动清除缓存
*
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* -----------------
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]
*
**/
}
扩展
ini
@Test
public void test01() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession session = sqlSessionFactory.openSession();
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
try {
Employee map = mapper.getEmployeeById(1);
session.clearCache();
Employee map2 = mapper.getEmployeeById(1);
System.out.println(map == map2);
session.commit();
} finally {
session.close();
}
}
输出结果为false. 因为手动清清除缓存,缓存失效
13.2.2 二级缓存
13.2.2.1 原理说明
MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能。
SqlSessionFactory层面上的二级缓存默认是不开启的,二级缓存的开席需要进行配置,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。 也就是要求实现Serializable接口,配置方法很简单,只需要在映射XML文件配置就可以开启缓存了,如果我们配置了二级缓存就意味着: ● 映射语句文件中的所有select语句将会被缓存。 ● 映射语句文件中的所欲insert、update和delete语句会刷新缓存。 ● 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法来收回。 ● 根据时间表,比如No Flush Interval,(CNFI没有刷新间隔),缓存不会以任何时间顺序来刷新。 ● 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用 ● 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全的被调用者修改,不干扰其他调用者或线程所做的潜在修改。
13.2.2.1 详细说明
详细说明: 二级缓存:全局缓存;基于namespace级别的缓存。一个namespace对应一个二级缓存。 工作机制:1.一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中。 2,如果会话被关闭了,一级缓存中的数据会被保存带二级缓存。新的会话查询信息就会参照二级缓存。 3.sqlSession ====> Employee====>employee sqlSession ====>DepartmentMapper=====>Department 不同的namespace查出的数据会放在自己对应的缓存中。 效果:查出的数据首先放在一级缓存中,只有一级缓存被关闭或者提交以后,一级缓存数据才会转移到二级缓存 使用步骤: 1.开启全局缓存配置。 2.因为是namespace级别,需要搭配每个xxxMapper.xml中配置二级缓存
ini
<cache flushInterval="60000" size="512" readOnly="true" eviction="FIFO" type="" />
eviction:缓存的回收策略:
LRU -- 最近最少使用的:移除最长时间不被使用的对象。
FIFO -- 先进先出:按对象进入缓存的顺序来移除它们。
SOFT -- 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK -- 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval:缓存刷新间隔。缓存多久清空一次,默认不清空。设置一个毫秒值。
readOnly:是否只读。true:mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快。
false:mybatis觉得获取的数据可能被修改。mybatis会利用序列化和反序列化的技术克隆一份新的数据给用户。安全,速度快。
size:缓存放多少元素。
type:指定自定义缓存全类名。实现cache接口即可。
3.pojo需要实现序列换接口。
和缓存相关的配置/属性:
1.cacheEnabled:如果是false,关闭二级缓存,不关闭一级缓存。
2.每个select标签都有userCache="true"属性:对一级缓存没有影响。设置为false,二级缓存失效。
3.每个增删改标签都有flushCache="true"属性:一级缓存和二级缓存都会被清空。
4.在查询标签中flushCache="false"属性:如果设置为true,查完会清空,一级二级缓存都会被清空,都不会用缓存。
5.sqlSession.clearn():跟session有关,只会清除一级缓存。
6.localCacheScope:<settings><setting name="localCacheScope" value="SESSION"/></settings>本地缓存作用域。
一级缓存SESSION:当前会话的所有数据保存到回话缓存中。STATEMENT:禁用一级缓存。
缓存首先一进来去查二级缓存,二级缓存没有去找一级缓存,一级缓存没有去找数据库。二级缓存----->一级缓存-------->数据库。 自定义缓存 implements Cache,重写接口中的保存等方法,比如说保存到redis.
13.2.2.2 测试案例
步骤:
- 在mybatis-config.xml开启全局缓存
xml
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 在要使用二级缓存的Mapper中开启
xml
<!--在当前Mapper.xml中使用二级缓存-->
<cache/>
也可以自定义参数
<!--开启本mapper的namespace下的二级缓存-->
<!--
eviction:代表的是缓存回收策略,目前MyBatis提供以下策略。
(1) LRU,最近最少使用的,一处最长时间不用的对象
(2) FIFO,先进先出,按对象进入缓存的顺序来移除他们
(3) SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
(4) WEAK,弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象。这里采用的是LRU,
移除最长时间不用的对形象
flushInterval:刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果你不配置它,那么当
SQL被执行的时候才会去刷新缓存。
size:引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。
这里配置的是1024个对象
readOnly:只读,意味着缓存数据只能读取而不能修改,这样设置的好处是我们可以快速读取缓存,缺点是我们没有
办法修改缓存,他的默认值是false,不允许我们修改
-->
<cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/>
<!--
在当前Mapper.xml中使用二级缓存:
1、默认:<cache/>
2、自定义参数 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-->
测试
- 问题:如果没有自定义参数,则会报错,我们需要将实体类序列化 Cause: java.io.NotSerializableException: com.zyz.mybatis.entity.Student 小结: ● 只要开启了二级缓存,在同一个Mapper下就有效; ● 所有的数据都会先放在一级缓存中; ● 只有当会话提交或者关闭的时候,才会提交到二级缓存中!
单元测试 一定要提交或者关闭
ini
/**
* @description: 二级缓存 创建两个sqlSession
* @author: zhengyuzhu
* @date: 2023/11/23 15:51
**/
@Test
public void test11(){
SqlSessionFactory sqlSessionFactory = null;
try {
//使用Mybatis第一步:获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
//第一步:获得SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//方式一:getMapper
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student student1 = studentMapper.queryStudentById(1);
System.out.println("第1次查询:" + student1);
Student student2 = studentMapper.queryStudentById(1);
System.out.println("第2次查询:" + student2);
// 效果:查出的数据首先放在一级缓存中,只有一级缓存被关闭或者提交以后,
// 一级缓存数据才会转移到二级缓存
sqlSession.commit();
System.out.println("二级缓存观测点");
SqlSession sqlSession2 = sqlSessionFactory.openSession();
StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);
Student student3 = studentMapper2.queryStudentById(1);
System.out.println("第一次执行:"+ student3);
Student student4 = studentMapper2.queryStudentById(1);
System.out.println("第2次执行:"+ student4);
sqlSession2.commit();
System.out.println("四个对象是否相同:" + ((student1 == student2) && (student3 == student4) &&(student1 == student4)));
//关闭SqlSession
sqlSession.close();
sqlSession2.close();
/**
* 二级缓存开启
*
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 921760190.
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@36f0f1be]
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)
* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1
* 第1次查询:Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* [com.zyz.mybatis.mapper.StudentMapper]-Cache Hit Ratio [com.zyz.mybatis.mapper.StudentMapper]: 0.0
* 第2次查询:Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* 二级缓存观测点
* [com.zyz.mybatis.mapper.StudentMapper]-Cache Hit Ratio [com.zyz.mybatis.mapper.StudentMapper]: 0.3333333333333333
* 第一次执行:Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* [com.zyz.mybatis.mapper.StudentMapper]-Cache Hit Ratio [com.zyz.mybatis.mapper.StudentMapper]: 0.5
* 第2次执行:Student{stuNo=1, stuName='张三', cardID=1115, classID=1}
* 四个对象是否相同:true
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@36f0f1be]
* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@36f0f1be]
**/
}