Mybatis之——动态sql与缓存

动态sql

	动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。( 借助了OGNL 的表达式)

常用的标签:

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

1、if标签(常常与where标签一起配合使用)

直接上xml文件演示了

 <!--测试where 和 if标签组合使用
	if标签就和if条件判断语句一样一样的(若只用where标签的话,若一些值为空,可能会造成拼接麻烦,出现多余的and或or关键字)
	where:where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,*where* 元素也会将它们去除。
-->
    <select id="selectByMoreProperties" resultType="com.kaisi.bean.Emp">
        select * from emp 
        <where>
            <if test="empno!=null and empno!=''">
                empno=#{empno}
            </if>
            <if test="ename!=null and ename!=''">
                and ename=#{ename}
            </if>
        </where>
    </select>

2、trim标签,用来截取字符串(自定义的where)

<!--
    trim截取字符串:
    prefix:前缀,为sql整体添加一个前缀
    prefixOverrides:去除整体字符串前面多余的字符
    suffixOverrides:去除后面多余的字符串
    -->
    <select id="getEmpByCondition" resultType="com.kaisi.bean.Emp">
        select * from emp

        <trim prefix="where" prefixOverrides="and" suffixOverrides="and">
            <if test="empno!=null">
                empno > #{empno} and
            </if>
            <if test="ename!=null">
                ename like #{ename} and
            </if>
            <if test="sal!=null">
                sal > #{sal} and
            </if>
        </trim>
    </select>

3、foreach标签
动态sql中唱用于对集合进行遍历(尤其是在构建in条件语句的时候)

 <!--
        这里测试foreach标签:
        collection:传入的集合名
        close:关闭标签
        open:开始标签
        item:定义下集合中的遍历到的数据叫什么,也就是遍历出来的元素值
        separator:分割符
        index:没什么用,就是索引标识。
        注意:item中定义参数名是需要与#{}中参数一致
        还需要注意一点,就是有时候会读取不到你定义的参数名,比如刚才就找不到deptnos
        这个list集合,那么就去接口中加入@Param("deptnos")即可
    -->
    <select id="selectByArry" resultType="com.kaisi.bean.Emp">
        select * from emp where deptno in
        <foreach collection="deptnos" close=")" open="(" item="jqk" separator="," index="idx" >
            #{jqk}
        </foreach>
     </select>

4、choose标签:多种条件,但是只选择其中一种就会用到choose标签

 <select id="selectByChoose" resultType="com.kaisi.bean.Emp">
        select * from emp
        <where>
            <choose>
                <when test="empno!=null and empno!=''">
                    empno >= #{empno}
                </when>
                <when test="sal!=null and sal!=''">
                    and sal > #{sal}
                </when>
               <!--
                    下面这个我是为了防止全表查询,没别的意思
               -->
                <otherwise>
                    1!=1
                </otherwise>
            </choose>
        </where>
    </select>

5、set标签:用于动态更新语句的标签就是set,set元素可以动态的包含需要更新的列,忽略其它不更新的列。

  <!--
        set标签用于动态更新语句的类似解决方案叫
        做 *set*。*set* 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
    -->
    <update id="updateByEmp">
        update emp
        <set>
            <if test="empno!=null and empno!=''">
                empno=#{empno},
            </if>
            <if test="ename!=null and ename!=''">
                ename=#{ename},
            </if>
            <if test="sal!=null and sal!=''">
                sal=#{sal}
            </if>
        </set>
        <where>
            empno=#{empno}
        </where>
    </update>

Mybatis中的缓存

			如果没有缓存,那么每次查询的时候都需要从数据库中加载数据,这回造成io的性能问题,所以,在很多情况下
    如果连续执行两条相同的sql语句,可以直接从缓存中获取,如果获取不到,那么再去查询数据库,这意味着查询完成的结果
    需要放到缓存中。

缓存分类:
1、一级缓存:表示sqlSession级别的缓存,每次查询的时候都会开启一个会话,想到于一次连接,当会话关闭,那么sqlSession中缓存的数据会失效
2、二级缓存:二级缓存是全局范围的缓存,二级缓存有个先决条件就是sqlSession关闭后才会生效。
3、第三方缓存:用第三方缓存组件,来当做缓存

一级缓存:
表示将数据存储在sqlsession中,关闭之后自动失效,默认情况下是开启的
在同一个会话之内,如果执行了多个相同的sql语句,那么除了第一个之外,所有的数据都是从缓存中进行查询的

 //测试一级缓存:理想状态,emp第一次查询会出现sql语句进行去数据库查询,第二次emp2则会走默认的一级缓存
    @Test
    public void t10(){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Emp emp = mapper.selectByEmpno(7369);
        System.out.println(emp);
        Emp emp2 = mapper.selectByEmpno(7369);
        System.out.println(emp2);
        sqlSession.close();
    }

从下面运行结果就可以看出,只执行了一条sql查询语句
在这里插入图片描述

在某些情况下,一级缓存会失效
1、在同一个方法体中,并不是只会有一次会话,不同会话之间的缓存并不会进行共享,记住是sqlSession级别就好理解多了

    //理想状态,2次会话,2次查询,出现2条sql语句
    @Test
    public void t11(){
        //sqlSession一号准备完毕
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        //sqlSession二号准备完毕
        SqlSession sqlSession1 = sqlSessionFactory.openSession();
        EmpDao mapper1 = sqlSession1.getMapper(EmpDao.class);
        Emp emp = mapper.selectByEmpno(7369);
        Emp emp1 = mapper1.selectByEmpno(7369);
        System.out.println(emp);
        System.out.println(emp1);
        sqlSession.close();
    }

从下图不难看出 (- - 哎,是个人都猜到了,我还写出来…)
在这里插入图片描述
2、当传递对象的时候,如果对象中的属性值不同,也不会走缓存
(这句可以当我没说,我查的东西都不一样,缓存中怎么可能会有)

3、在多次的查询中,如果修改了数据,那么缓存会失效。

//理想状态,在一次会话中,俩次查询中间进行修改操作,第二次查询会查询数据库
	@Test
    public void t12 (){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        Emp emp = mapper.selectByEmpno(7369);
        Emp empUpdate = new Emp();
        empUpdate.setEmpno(7369);
        empUpdate.setEname("KAISI");
        int i = mapper.updateByEmp(empUpdate);
        Emp emp1 = null;
        if (i==1){
            emp1 = mapper.selectByEmpno(7369);
        }
        System.out.println(emp);
        System.out.println(emp1);
        sqlSession.commit();
        sqlSession.close();
    }

发生修改后,一级缓存就会失效,第二次的查询依旧需要查询数据库
在这里插入图片描述
4、如果在一个会话过程中,手动清空了缓存,那么缓存也会失效(这句…各位同僚都懂,就不演示了哈)

二级缓存:
二级缓存表示全局缓存,前提的条件就是sqlSession必须关闭才会生效
默认情况下二级缓存是不开启,还需要进行以下配置才能生效
1、修改全局配置文件,在settings中添加配置

2、指定在哪个映射文件中必须添加缓存配置

3、与映射文件对应的实体类必须实现序列化接口
public class Emp implements Serializable(){…}

映射文件中标签中缓存属性:
​eviction:表示缓存回收策略,默认是LRU

​ LRU:最近最少使用的,移除最长时间不被使用的对象

​ FIFO:先进先出,按照对象进入缓存的顺序来移除

​ SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象

​ WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象

​ flushInternal:刷新间隔,单位毫秒

​ 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新

​ size:引用数目,正整数

​ 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

​ readonly:只读,true/false

​ true:只读缓存,会给所有调用这返回缓存对象的相同实例,因此这些对象不能被修改。

​ false:读写缓存,会返回缓存对象的拷贝(序列化实现),这种方式比较安全,默认值

  	//开始测试2级缓存
    //理想状态,mapper2进行查询,会走二级缓存,不会出现sql查询语句
    @Test
    public void t13 (){
        SqlSession sqlSession = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
        Emp emp = mapper.selectByEmpno(7369);
        System.out.println(emp);
        sqlSession.close();
        Emp emp2 = mapper2.selectByEmpno(7369);
        System.out.println(emp2);
        sqlSession2.close();
    }

在这里插入图片描述

从上述代码中,既有一级缓存,也有二级缓存。如何确定缓存的查询是先查询一级缓存还是先查询二级缓存呢?

 @Test
    public void t14(){
        //一号会话
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //二号会话
        SqlSession sqlSession2 = sqlSessionFactory.openSession();
        EmpDao mapper = sqlSession.getMapper(EmpDao.class);
        EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class);
        Emp emp = mapper.selectByEmpno(7566);
        System.out.println("一号会话初次"+emp);
        /*
        * 关闭一号会话,开启二级缓存,将7566对象数据缓存到二级缓存中去,并且查俩次
        * 断个言,3次查询,2次命中,2/3的概率。既然出了3中2的概率,那么查询7566的时候
        * 就可以认定走了二级缓存
        * */
        sqlSession.close();
        Emp emp2 = mapper2.selectByEmpno(7566);
        System.out.println(emp2);
        Emp emp3 = mapper2.selectByEmpno(7566);
        System.out.println(emp3);
        //查询新的对象使之加入缓冲,既没有关闭sqlSession2,那么何来开启二级缓存
        Emp emp4 = mapper2.selectByEmpno(7369);
        System.out.println(emp4);
        Emp emp5 = mapper2.selectByEmpno(7369);
        System.out.println(emp5);
        sqlSession2.close();
    }

从下图中得出的信息可以看出
Mybatis中缓存先后为
二级缓存---->一级缓存----->数据库
在这里插入图片描述

第三方缓存

在一些情况是可以自定义实现缓存,当然了,肯定有可以直接拿来用的
下面用ehcache

 <!-- https://mvnrepository.com/artifact/org.ehcache/ehcache -->
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.8.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
        <dependency>
            <groupId>org.mybatis.caches</groupId>
            <artifactId>mybatis-ehcache</artifactId>
            <version>1.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.0-alpha1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>2.0.0-alpha1</version>
            <scope>test</scope>
        </dependency>

加入ehcache.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\ehcache" />
 
 <defaultCache 
   maxElementsInMemory="1" 
   maxElementsOnDisk="10000000"
   eternal="false" 
   overflowToDisk="true" 
   timeToIdleSeconds="120"
   timeToLiveSeconds="120" 
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>

最后一步,在映射配置文件中,加入type属性

在这里插入图片描述
哈哈哈,是不是感觉没什么特别的,但是打开D盘就会发现,缓存的数据都会存放到你指定的本地文件中
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值