一文讲明Mybatis 的使用 超详细 【爆肝两万字教程】下篇

我 | 在这里

🕵️ 读书 | 长沙 ⭐软件工程 ⭐ 本科

🏠 工作 | 广州 ⭐ 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 代码片段(复用)

有的时候,我们可以能会将一些功能的部分抽取出来,方便复用!

  1. 使用SQL标签抽取公共的部分
bash 复制代码
  <sql id="if-title-author">
        <if test="title != null">
            title = #{title}
        </if>
        <if test="author != null">
            and author = #{author}
        </if>
    </sql>
  1. 在需要使用的地方使用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]? ● 存在内存中的临时数据。 ● 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库查询文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。

  1. 为什么使用缓存? ● 减少和数据库的交互次数,减少系统开销,提高系统效率。

  2. 什么样的数据能使用缓存? ● 经常查询并且不经常改变的数据。【可以使用缓存】

资料参考: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 缓存成功

测试步骤:

  1. 开启日志!
  2. 测试在一个Session中查询两次相同记录
  3. 查看日志输出
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 缓存失效的情况

一级缓存失效的情况:

  1. 查询不同的东西;
  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存!
  3. 查询不同的Mapper.xml
  4. 手动清理缓存!
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 测试案例

步骤:

  1. 在mybatis-config.xml开启全局缓存
xml 复制代码
   <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  1. 在要使用二级缓存的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"/>
     -->

测试

  1. 问题:如果没有自定义参数,则会报错,我们需要将实体类序列化 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]
         **/

    }
相关推荐
有梦想的攻城狮15 分钟前
spring中的@MapperScan注解详解
java·后端·spring·mapperscan
柚个朵朵1 小时前
Spring的Validation,这是一套基于注解的权限校验框架
java·后端·spring
Asus.Blogs2 小时前
为什么go语言中返回的指针类型,不需要用*取值(解引用),就可以直接赋值呢?
开发语言·后端·golang
C_V_Better2 小时前
Java Spring Boot 控制器中处理用户数据详解
java·开发语言·spring boot·后端·spring
胡子洲2 小时前
Spring Boot 应用中实现基本的 SSE 功能
java·spring boot·后端
贰拾wan3 小时前
【Java-EE进阶】SpringBoot针对某个IP限流问题
java·spring boot·后端·idea
Paran-ia3 小时前
【2025版】Spring Boot面试题
java·spring boot·后端
sufu10654 小时前
SpringAI更新:废弃tools方法、正式支持DeepSeek!
人工智能·后端
嘵奇4 小时前
Spring Boot拦截器详解:原理、实现与应用场景
java·spring boot·后端
秋野酱6 小时前
基于javaweb的SpringBoot自习室预约系统设计与实现(源码+文档+部署讲解)
java·spring boot·后端