Mybatis中的动态SQL和缓存

Mybatis中的动态SQL

在之前使用jdbc时不同条件拼接SQL语句是非常痛苦的,

String sql = "SELECT" +
                    " s.id , " +
                    "s.num , " +
                    "s.name , " +
                    "s.gender ," +
                    " phone , " +
                    "g.grade ," +
                    " a.account , " +
                    "s.oper_time " +
                    "FROM students s " +
                    "LEFT JOIN gradetb g ON s.gradeid = g.id " +
                    "LEFT JOIN admin a ON s.adminid = a.id where 1=1";
            if (num != null && num != "") {
                sql = sql + " and num='" + num+"'" ;
            }
            if (name != null && name != "") {
                sql = sql + " and s.name='" + name+"'";
            }

在拼接时需要十分小心,一个空格使用不当就会导致错误。使用动态SQL是十分有必要的。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

建议直接看官方文档:https://mybatis.org/mybatis-3/zh/dynamic-sql.html

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

<select id="findStudent" parameterType="Student" resultType="com.ffyc.mybatis.modle.Student">
        SELECT
        id,
        NAME,
        num,
        student_phone
        FROM
        student
        WHERE 1=1
        <if test="num!='' &amp; num!=null">
            and num = #{num}
        </if>
        <if test="name!='' &amp; name !=null">
            and NAME = #{name}
        </if>
    </select>

这样查是可以的,但是会多余一个"1=1" 的恒等式,如果去掉这个“1=1”,后面两个if成立与否我们是不知道的,可能两个if都不成立 ,此时就不需要where,可能成立一个,where后面就不需要加and,可能成立两个,就需要and,至于给哪一个if加and是未知的。

where

Mybatis动态SQL中提供了where标签可以解决:

<select id="findStudent1" parameterType="Student" resultType="com.ffyc.mybatis.modle.Student">
        SELECT
        id,
        NAME,
        num,
        student_phone
        FROM
        student
        <where>
            <if test="num!='' &amp; num!=null">
                 num = #{num}
            </if>
            <if test="name!='' &amp; name !=null">
                and NAME = #{name}
            </if>
        </where>

    </select>

当两个if都不成立时,就不会出现where,当只有第二个if成立时,“AND NAME = #{name}”中的and 也会取消。

trim

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>
 <!-- 
trim prefix=“自定义的前缀” prefixOverrides=“覆盖指定关键字”
-->

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

choose

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句,choose标签中没有属性,只有两个子标签:when、otherwise。

<select id="findStudent1" parameterType="Student" resultType="com.ffyc.mybatis.modle.Student">
        SELECT
        id,
        NAME,
        num,
        student_phone
        FROM
        student
       <where>
           <choose>
               <when test="num!=null&amp;num!=null">
                   num = #{num}
               </when>
               <otherwise>
                   num = "1001"
               </otherwise>
           </choose>
       </where>
    </select>
 <!-- 
choose
	when test=“条件” 成立则执行 (when可以有多个)
	otherwise when都不成立时执行 (只有一个)
-->

set

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

 <update id="upDateStudent">
        UPDATE
          student
        <set>
            <if test="name!='' &amp; name !=null">NAME = #{name},</if>
            <if test="num!='' &amp; num !=null">num  = #{num},</if>
            <if test="gender!='' &amp; gender !=null">gender=#{gender},</if>
            <if test="studentPhone!='' &amp; studentPhone !=null">student_phone=#{studentPhone}</if>
        </set>

        WHERE id = #{id}

    </update>

这个例子中,set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

或者,可以通过使用trim元素来达到同样的效果:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。如我们一般会有批量操作的查询,删除。

<select id="findStudentListByRange" resultType="com.ffyc.mybatis.modle.Student">

        SELECT
          id,name,gender,num,student_phone
        FROM
          student
        <where>
            <foreach item="item" collection="list"
                     open="id in (" separator="," close=")" >
                #{item}
            </foreach>
        </where>
    </select>
<!-- 
可以传入数组和集合:当传入数组时 collection=“arry” 当传入集合时 collection="list"
open = "id in (" 表示语句以id in ( 开头
separator=",":表示没循环一次用逗号隔开
close=")" :表示循环结束时用)结尾
-->

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

特殊符号的处理

在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示:

<&lt;
>&gt;
"&quot;
&apos;
&&amp;

除了可以使用上述转义字符外,还可以使用<![CDATA[]]>来包裹特殊字符。如下所示:

<if test="id != null"> 
    AND <![CDATA[ id <> #{id} ]]> 
</if>
<!-- 
<![CDATA[ ]]>是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略。

有个问题那就是 <if> </if> <where> </where> <choose> </choose> <trim> </trim> 等这些标签都不会被解析,所以 我们只把有特殊字符的语句放在 <![CDATA[ ]]> 尽量缩小<![CDATA[ ]]> 的范围。
-->

Mybatis的缓存

为什么使用缓存

​ 缓存(cache)的作用是为了减去数据库的压力,提高查询性能。缓存实现的原理是从数据库中查询出来的对象在使用完后不要销毁,而是存储在内存(缓存)中,当再次需要获取该对象时,直接从内存(缓存)中直接获取,不再向数据库执行select 语句,从而减少了对数据库的查询次数,因此提高了数据库的性能。

Mybatis 有一级缓存和二级缓存。一级缓存的作用域是同一个 SqlSession,在同一个 sqlSession 中两次执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查 询,从而提高查询效率。当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。

二级缓存是多个 SqlSession 共享的,其作用域是同一个 namespace,不同的sqlSession 两次执行相同 namespace 下的 sql 语句且向 sql 中传递参数也相同即最终执行相同的 sql 语句,第一次执行完毕会将数据库中查询的数据写到缓存 (内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。Mybatis 默认没有开启二级缓存需要在 setting 全局参数中配置开启二级缓存。

第一步:

 <!--二级缓存-->
<setting name="cacheEnabled" value="true"/>

第二步:

在 Mapper 映射文件中添加,表示此 mapper 开启二级缓存。当 SqlSeesion 关闭时,会将数据存入到二级缓存. 其中的属性值目前可以直接使用默认值。

<cache></cache>
<!--
    flushInterval :缓存存放时间
	eviction : 清除策略 LRU(最近最少用) FIFO(先进先出) SOFT(软引用) WEAK(弱引用)   
	size : 引用存储数量
	readOnly : 只读
    -->

第三步:

所有的实现类都需要实现java.io. Serializable。

一级缓存

Mybatis中的一级缓存是SqlSession级别的,第一次查询的数据会保存在SqlSession中,在没有超出缓存时限的情况下,第二次如果查询相同的数据,不会从数据库查询,直接从SqlSession中获取。

一级缓存的生命周期

a、MyBatis 在开启一个数据库会话时,会 创建一个新的 SqlSession 对象,SqlSession 对象中会有一个新的 Executor 对象。Executor 对象中持有一个新

的 PerpetualCache 对象,如果 SqlSession 调用了 close()方法,会释放掉一级

缓存 PerpetualCache 对象,一级缓存将不可用。

b、如果 SqlSession 调用了 clearCache(),会清空 PerpetualCache 对象中的数据,但是该对象仍可使用。

c、SqlSession 中执行了任何一个 update 操作(update()、delete()、insert()) ,都会清空缓存的数据,但是该对象可以继续使用。

二级缓存

二级缓存是 SqlSessionFactory 级别的,根据 mapper 的 namespace 划分区域的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分。

每次查询会先从缓存区域查找,如果找不到则从数据库查询,并将查询到数据写入缓存。Mybatis 内部存储缓存使用一个 HashMap,key 为hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。sqlSession 执行 insert、update、delete 等操作 commit 提交后会清空缓存区域。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值