Mybatis学习笔记4

75 阅读7分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

动态SQL环境搭建

什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

搭建环境

 CREATE TABLE `blog`(
 `id` VARCHAR(50) NOT NULL COMMENT '博客id',
 `title` VARCHAR(100) NOT NULL COMMENT '博客标题',
 `author` VARCHAR(30) NOT NULL COMMENT '博客作者',
 `create_time` DATETIME NOT NULL COMMENT '创建时间',
 `views` INT(30) NOT NULL COMMENT '浏览量'
 )ENGINE=INNODB DEFAULT CHARSET=utf8

创建一个基础工程

  1. 导包

  2. 编写配置文件

    mybatis-config.xml

     <settings>
         <setting name="logImpl" value="STDOUT_LOGGING"/>
         <!--是否开启驼峰命名自动映射-->
         <setting name="mapUnderscoreToCamelCase" value="true"/>
     </settings>
     ​
     <typeAliases>
         <typeAlias type="com.isabella.pojo.Blog" alias="Blog"/>
     </typeAliases>
    
     <mappers>
         <mapper class="com.isabella.dao.BlogMapper"></mapper>
     </mappers>
    
  3. 编写实体类

    Blog.java

     package com.isabella.pojo;
     ​
     import java.util.Date;
     ​
     public class Blog {
         private String id;
         private String title;
         private String author;
         private Date createTime;
         private int views;
     ​
         public Blog() {
         }
     ​
         public Blog(String id, String title, String author, Date createTime, int views) {
             this.id = id;
             this.title = title;
             this.author = author;
             this.createTime = createTime;
             this.views = views;
         }
     ​
         public String getId() {
             return id;
         }
     ​
         public void setId(String id) {
             this.id = id;
         }
     ​
         public String getTitle() {
             return title;
         }
     ​
         public void setTitle(String title) {
             this.title = title;
         }
     ​
         public String getAuthor() {
             return author;
         }
     ​
         public void setAuthor(String author) {
             this.author = author;
         }
     ​
         public Date getCreateTime() {
             return createTime;
         }
     ​
         public void setCreateTime(Date createTime) {
             this.createTime = createTime;
         }
     ​
         public int getViews() {
             return views;
         }
     ​
         public void setViews(int views) {
             this.views = views;
         }
     ​
         @Override
         public String toString() {
             return "Blog{" +
                     "id='" + id + ''' +
                     ", title='" + title + ''' +
                     ", author='" + author + ''' +
                     ", createTime=" + createTime +
                     ", views=" + views +
                     '}';
         }
     }
     ​
    
  4. 编写实体类对应Mapper接口和Mapper.xml文件

    BlogMapper.java:

     package com.isabella.dao;
     ​
     import com.isabella.pojo.Blog;
     ​
     import java.util.List;
     import java.util.Map;
     ​
     public interface BlogMapper {
         //插入数据
         int addBlog(Blog blog);
     ​
         //查询博客
         List<Blog> querryBlogIF(Map map);
     ​
     }
     ​
    

    BlogMapper.xml:

     <?xml version="1.0" encoding="UTF8" ?>
     <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     <mapper namespace="com.isabella.dao.BlogMapper">
         <insert id="addBlog" parameterType="Blog">
             insert into mybatis.blog (id, title, author, create_time, views)
             values (#{id},#{title},#{author},#{createTime},#{views});
         </insert>
         
         <select id="querryBlogIF" parameterType="map" resultType="Blog">
             select * from mybatis.blog where 1=1
             <if test="title!=null">
                 and title = #{title}
             </if>
             <if test="author != null">
                 and author = #{author}
             </if>
         </select>
     ​
     ​
     </mapper>
    
  5. 编写测试类

    MyTest.java:

     import com.isabella.dao.BlogMapper;
     import com.isabella.pojo.Blog;
     import com.isabella.utils.IDutils;
     import com.isabella.utils.MybatisUtils;
     import org.apache.ibatis.session.SqlSession;
     import org.junit.Test;
     ​
     import java.util.Date;
     import java.util.HashMap;
     import java.util.List;
     ​
     public class MyTest {
         @Test
         public void addBlogTest() {
             SqlSession sqlsession = MybatisUtils.getSqlsession();
             BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
     ​
             Blog blog = new Blog();
             blog.setId(IDutils.getId());
             blog.setTitle("Mybatis如此简单");
             blog.setAuthor("Isabella");
             blog.setCreateTime(new Date());
             blog.setViews(9999);
     ​
             mapper.addBlog(blog);
     ​
             blog.setId(IDutils.getId());
             blog.setTitle("Java如此简单");
             mapper.addBlog(blog);
     ​
             blog.setId(IDutils.getId());
             blog.setTitle("Spring如此简单");
             mapper.addBlog(blog);
     ​
             blog.setId(IDutils.getId());
             blog.setTitle("微服务");
             mapper.addBlog(blog);
     ​
     ​
             sqlsession.close();
         }
     ​
         @Test
         public void queryBlogIF(){
             SqlSession sqlsession = MybatisUtils.getSqlsession();
             BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
             HashMap map = new HashMap();
             map.put("title","Java如此简单");
     ​
             List<Blog> blogs = mapper.querryBlogIF(map);
     ​
             for (Blog blog : blogs) {
                 System.out.println(blog);
             }
     ​
     ​
             sqlsession.close();
         }
     ​
     }
     ​
    

动态SQL常用标签

choose(when,otherwise)

BlogMapper.xml:

 <select id="queryBlogChoose" parameterType="map" resultType="Blog">
         select * from mybatis.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>

MyTest.java:

 @Test
 public void queryBlogChoose(){
     SqlSession sqlsession = MybatisUtils.getSqlsession();
     BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
 ​
     HashMap map = new HashMap();
     map.put("title","Java如此简单");
     map.put("views",9999);
     List<Blog> blogs = mapper.queryBlogChoose(map);
 ​
     for (Blog blog : blogs) {
         System.out.println(blog);
     }
 ​
     sqlsession.close();
 }

trim(where,set)

where

BlogMapper.xml

 <select id="queryBlogIF" parameterType="map" resultType="Blog">
         select * from mybatis.blog
         <where>
             <if test="title!=null">
                 title = #{title}
             </if>
             <if test="author!=null">
                 and author = #{author}
             </if>
         </where>
     </select>

set

BlogMapper.java:

 //更新博客
     int updateBlog(Map map);

BlogMapper.xml:

     <update id="updateBlog" parameterType="map">
         update mybatis.blog
         <set>
             <if test="title!=null">
                 title = #{title},
             </if>
             <if test="author!=null">
                 author = #{author}
             </if>
         </set>
         where id = #{id}
     </update>

所谓的动态SQL,本所还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

if

where,set,choose,when

动态SQL之Foreach

SQL片段

有的时候,我们可能会将一此公共的部分抽取出来,方复用

  1. 使用SQL标签抽取公共的部分

    BlogMapper.xml:

         <sql id="ifTitleAuthor">
             <if test="title!=null">
                 title = #{title}
             </if>
             <if test="author!=null">
                 and author = #{author}
             </if>
         </sql>
    
  2. 在需要使用的地方使用include标签引用即可

    BlogMapper.xml:

         <select id="queryBlogIF" parameterType="map" resultType="Blog">
             select * from mybatis.blog
             <where>
                 <include refid="ifTitleAuthor"></include>
             </where>
         </select>
    

注意事项:

  • 最好 基于单表来定义SQL片段
  • 不要存在where标签

foreach

 select * from user where 1=1 and (id=1 or id=2 or id=3)

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

BlogMapper.java:

     //查询第1-2-3号博客
     List<Blog> queryBlogForeach(Map map);

BlogMapper.xml:

     <select id="queryBlogForeach" parameterType="map" resultType="Blog">
         select * from mybatis.blog
         <where>
              <foreach collection="ids" item="id" open="(" close=")" separator="or">
                  id = #{id}
             </foreach>
         </where>
     </select>

MyTest.java:

 @Test
 public void queryBlogForeach(){
     SqlSession sqlsession = MybatisUtils.getSqlsession();
     BlogMapper mapper = sqlsession.getMapper(BlogMapper.class);
 ​
     HashMap map = new HashMap();
     ArrayList<String> ids = new ArrayList<String>();
     ids.add("1");
     ids.add("2");
     ids.add("3");
 ​
     map.put("ids",ids);
     List<Blog> blogs = mapper.queryBlogForeach(map);
     for (Blog blog : blogs) {
         System.out.println(blog);
     }
 ​
     sqlsession.close();
 }

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

建议:

  • 先在Mysql中写出完整的SQL,再对应地去修改成为我们的动态SQL实现通用即可

缓存简介

查询 :连接数据库,耗资源

一次查询的结果,给它存在一个可以直接取到的地方-->内存:缓存

我们再次查询相同数据的时候,直接走缓存,就不用走数据库了

  1. 什么是缓存[Cache]

    • 存在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
  2. 为什么使用缓存?

    • 减少和数据库的交互次数,减少系统开销,提高系统效率
  3. 什么样的数据能使用缓存?

    • 经常查询并且不经常改变的数据

Mybatis缓存

  • Mybatis包含一个非常强大的查询特性,它可以非常方便地定制和配置缓存。缓存可以级大的提升查询效率

  • Mybatis系统中默认定义了两级缓存:一级缓存* *和二级缓存

    • 默认情况下,只有一级缓存开启(SqlSession级别的缓存,也称本地缓存)
    • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存

  • 一级缓存 也叫本地缓存:

    • 与数据库同一次会话期间查询到的数据会放在本地缓存中
    • 以后如是需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

测试步骤:

  1. 开启日志

  2. 测试在一个Session中查询再次相同记录

  3. 查看日志输出

    image-20210816122126883

(user1==user2)=false的情况:

  1. 查询不同的东西
  2. 增删改操作,可能会改原来的数据,所以必定会刷新缓存
  3. 查询不同的Mapper.xml
  4. 手动清理缓存
 sqlsession.clearCache();//手动清理缓存

小结:一级缓存默认是开启的,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段一级缓存就是一个Map

二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但我们想要的是会话关闭了,一级缓存中的数据被保存到二级缓存中
    • 新的会话查询信息,就可以从二级缓存中获取内容
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中

cache语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

步骤:

  1. 开启全局缓存(虽然默认开启,但显示地写出更利于开发)

             <!--显示的开启全局缓存-->
             <setting name="cacheEnabled" value="true"/>
    
  2. 在要使用二级缓存的Mapper中开启

         <!--在当前Mapper.xml中使用二级缓存-->
         <cache/>
    

    也可以自定义参数

     <cache
       eviction="FIFO"
       flushInterval="60000"
       size="512"
       readOnly="true"/>
    
  3. 测试

    1. 问题:我们现在要将实体类序列化,否则就会报错

       Caused by: java.io.NotSerializableException: com.isabella.pojo.User
      

    User.java:

     public class User implements Serializable {
     //代码
     }
    

    MyTest.java:

     import com.isabella.dao.UserMapper;
     import com.isabella.pojo.User;
     import com.isabella.utils.MybatisUtils;
     import org.apache.ibatis.session.SqlSession;
     import org.junit.Test;
     ​
     public class MyTest {
         @Test
         public void test(){
             SqlSession sqlsession = MybatisUtils.getSqlsession();
             SqlSession sqlsession2 = MybatisUtils.getSqlsession();
             UserMapper mapper = sqlsession.getMapper(UserMapper.class);
             UserMapper mapper2 = sqlsession2.getMapper(UserMapper.class);
     ​
             User user1 = mapper.getUserById(1);
             System.out.println(user1);
             sqlsession.close();
             User user2 = mapper2.getUserById(1);
             System.out.println(user1==user2);
     ​
             sqlsession2.close();
         }
     ​
     ​
     }
     ​
    

小结:

  • 只要开启了二级缓存,在同一个Mapper就有效
  • 所有的数据都会先放在一级缓存中
  • 只有当会话提交,或者关闭的时候,才会交到二级缓存中