Mybatis特性值缓存和动态SQL

缓存

MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况下是没有开启缓存的,除了局部的session缓存,可以增强变现而且处理循环依赖也是必须的。要开启二级缓存,你需要在你的SQL映射文件中添加一行:

<cache/> 

字面上看就是这样。这个简单语句的效果如下:
 映射语句文件中的所有select语句将会被缓存。
 映射语句文件中的所有insert,update和delete语句会刷新缓存。
 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
 根据时间表(比如no Flush Interval,没有刷新间隔),缓存不会以任何时间顺序来刷新。
 缓存会存储列表集合或对象(无论查询方法返回什么)的1024个引用。
 缓存会被视为是read/write(可读/可写)的缓存,意味着对象检索不是共享的,而且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

所有的这些属性都可以通过缓存元素的属性来修改。比如:

<cache 
    eviction="FIFO" 
    flushInterval="60000" 
    size="512" 
    readOnly="true"/> 

这个更高级的配置创建了一个FIFO缓存,并每隔60秒刷新,存数结果对象或列表的512个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会导致冲突。
可用的收回策略有:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。

默认的是LRU。
flushInterval(刷新间隔)可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。

size(引用数目)可以被设置为任意正整数,要记住你缓存的对象数目和你运行环境的可用内存资源数目。默认值是1024。

readOnly(只读)属性可以被设置为true或false。只读的缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。可读写的缓存会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false。

使用自定义缓存

除了这些自定义缓存的方式,你也可以通过实现你自己的缓存或为其他第三方缓存方案创建适配器来完全覆盖缓存行为。

<cache type=”com.domain.something.MyCustomCache”/> 

这个示例展示了如何使用一个自定义的缓存实现。type属性指定的类必须实现org.mybatis.cache.Cache接口。这个接口是MyBatis框架中很多复杂的接口之一,但是简单给定它做什么就行。

public interface Cache { 
    String getId(); 
    int getSize(); 
    void putObject(Object key, Object value); 
    Object getObject(Object key); 
    boolean hasKey(Object key); 
    Object removeObject(Object key); 
    void clear(); 
    ReadWriteLock getReadWriteLock(); 
} 

要配置你的缓存,简单和公有的JavaBeans属性来配置你的缓存实现,而且是通过cache元素来传递属性,比如,下面代码会在你的缓存实现中调用一个称为“setCacheFile(String file)”的方法:

<cache type=”com.domain.something.MyCustomCache”> 
    <property name=”cacheFile” value=/tmp/my-custom-cache.tmp”/> 
</cache> 

你可以使用所有简单类型作为JavaBeans的属性,MyBatis会进行转换。
记得缓存配置和缓存实例是绑定在SQL映射文件的命名空间是很重要的。因此,所有在相同命名空间的语句正如绑定的缓存一样。语句可以修改和缓存交互的方式,或在语句的语句的基础上使用两种简单的属性来完全排除它们。默认情况下,语句可以这样来配置:

<select ... flushCache=”false” useCache=”true”/> 
<insert ... flushCache=”true”/> 
<update ... flushCache=”true”/> 
<delete ... flushCache=”true”/> 

因为那些是默认的,你明显不能明确地以这种方式来配置一条语句。相反,如果你想改变默认的行为,只能设置flushCache和useCache属性。比如,在一些情况下你也许想排除从缓存中查询特定语句结果,或者你也许想要一个查询语句来刷新缓存。相似地,你也许有一些更新语句依靠执行而不需要刷新缓存。

动态SQL

MyBatis的一个强大的特性之一通常是它的动态SQL能力。如果你有使用JDBC或其他
相似框架的经验,你就明白条件地串联SQL字符串在一起是多么的痛苦,确保不能忘了空格或在列表的最后省略逗号。动态SQL可以彻底处理这种痛苦。

通常使用动态SQL不可能是独立的一部分,MyBatis当然使用一种强大的动态SQL语言来改进这种情形,这种语言可以被用在任意映射的SQL语句中。

动态SQL元素和使用JSTL或其他相似的基于XML的文本处理器相似。在MyBatis之前的版本中,有很多的元素需要来了解。MyBatis 3大大提升了它们,现在用不到原先一半的元素就能工作了。MyBatis采用功能强大的基于OGNL的表达式来消除其他元素。

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

If
在动态SQL中所做的最通用的事情是包含部分where字句的条件。比如:

<select id=”findActiveBlogWithTitleLike” parameterType=”Blog” resultType=”Blog”> 
    SELECT * FROM BLOG 
    WHERE state = "ACTIVE‟ 
    <if test="title != null"> 
        AND title like #{title} 
    </if> 
</select> 

这条语句会提供一个可选的文本查找功能。如果你没有传递title,那么所有激活的博客都会被返回。但是如果你传递了title,那么就会查找相近的title(对于敏锐的检索,这中情况下你的参数值需要包含任意的遮掩或通配符)的博客。
假若我们想可选地搜索title和author呢?首先,要改变语句的名称让它有意义。然后简单加入另外的一个条件。

<select id=”findActiveBlogLike” parameterType=”Blog” resultType=”Blog”> 
    SELECT * FROM BLOG WHERE state = „ACTIVE‟ 
    <if test=”title != null”> 
        AND title like #{title} 
    </if> 
    <if test=”author != null and author.name != null”> 
        AND title like #{author.name} 
    </if> 
</select> 

choose, when, otherwise
有时我们不想应用所有的条件,相反我们想选择很多情况下的一种。和Java中的switch语句相似,MyBatis提供choose元素。
我们使用上面的示例,但是现在我们来搜索当title提供时仅有title条件,当author提供时仅有author条件。如果二者都没提供,只返回featured blogs(也许是由管理员策略地选择的结果列表,而不是返回大量没有意义的随机博客结果列表)。

<select id=”findActiveBlogLike” 
    parameterType=”Blog” resultType=”Blog”> 
    SELECT * FROM BLOG WHERE state = „ACTIVE‟ 
    <choose> 
        <when test=”title != null”> 
            AND title like #{title} 
        </when> 
        <when test=”author != null and author.name != null”> 
            AND title like #{author.name} 
        </when> 
        <otherwise> 
            AND featured = 1 
        </otherwise> 
    </choose> 
</select> 

trim, where, set
前面的例子已经方便地处理了一个臭名昭著的动态SQL问题。要考虑我们回到“if”示例后会发生什么,但是这次我们将“ACTIVE = 1”也设置成动态的条件。

<select id=”findActiveBlogLike” parameterType=”Blog” resultType=”Blog”> 
    SELECT * FROM BLOG 
    WHERE 
    <if test=”state != null”> 
        state = #{state} 
    </if> 
    <if test=”title != null”> 
        AND title like #{title} 
    </if> 
    <if test=”author != null and author.name != null”> 
        AND title like #{author.name} 
    </if> 
</select> 

如果这些条件都没有匹配上将会发生什么?这条SQL结束时就会成这样:

SELECT * FROM BLOG 
WHERE 

这会导致查询失败。如果仅仅第二个条件匹配是什么样的?这条SQL结束时就会是这样:

SELECT * FROM BLOG 
WHERE 
AND title like „someTitle‟ 

这个查询也会失败。这个问题不能简单的用条件来解决,如果你从来没有这样写过,那么你以后也不会这样来写。
MyBatis有一个简单的处理,这在90%的情况下都会有用。而在不能使用的地方,你可以自定义处理方式。加上一个简单的改变,所有事情都会顺利进行:

<select id=”findActiveBlogLike” parameterType=”Blog” resultType=”Blog”> 
    SELECT * FROM BLOG 
    <where> 
        <if test=”state != null”> 
            state = #{state} 
        </if> 
        <if test=”title != null”> 
            AND title like #{title} 
        </if> 
        <if test=”author != null and author.name != null”> 
            AND title like #{author.name} 
        </if> 
    </where> 
</select> 

where元素知道如果由被包含的标记返回任意内容,就仅仅插入“WHERE”。而且,如果以“AND”或“OR”开头的内容,那么就会跳过WHERE不插入。
如果where元素没有做出你想要的,你可以使用trim元素来自定义。比如,和where元素相等的trim元素是:

<trim prefix="WHERE" prefixOverrides="AND |OR "></trim> 

overrides属性采用管道文本分隔符来覆盖,这里的空白也是重要的。它的结果就是移除在overrides属性中指定的内容,插入在with属性中的内容。
和动态更新语句相似的解决方案是set。set元素可以被用于动态包含更新的列,而不包含不需更新的。比如:

<update id="updateAuthorIfNecessary" parameterType="domain.blog.Author"> 
    update Author 
    <set> 
        <if test="username != null">username=#{username},</if> 
        <if test="password != null">password=#{password},</if> 
        <if test="email != null">email=#{email},</if> 
        <if test="bio != null">bio=#{bio}</if> 
    </set> 
    where id=#{id} 
</update> 

这里,set元素会动态前置SET关键字,而且也会消除任意无关的逗号,那也许在应用条件之后来跟踪定义的值。
如果你对和这相等的trim元素好奇,它看起来就是这样的:

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

注意这种情况下我们覆盖一个后缀,而同时也附加前缀。

foreach
另外一个动态SQL通用的必要操作是迭代一个集合,通常是构建在IN条件中的。比如:

<select id="selectPostIn" resultType="domain.blog.Post"> 
    SELECT * 
    FROM POST P 
    WHERE ID in 
    <foreach item="item" index="index" collection="list" 
        open="(" separator="," close=")"> 
    </foreach> 
</select> 

foreach元素是非常强大的,它允许你指定一个集合,声明集合项和索引变量,它们可以用在元素体内。它也允许你指定开放和关闭的字符串,在迭代之间放置分隔符。这个元素是很智能的,它不会偶然地附加多余的分隔符。

注意:你可以传递一个List实例或者数组作为参数对象传给MyBatis。当你这么做的时候,MyBatis会自动将它包装在一个Map中,用名称在作为键。List实例将会以“list”作为键,而数组实例将会以“array”作为键。

这个部分是对关于XML配置文件和XML映射文件的而讨论的。下一部分将详细讨论Java API,所以你可以得到你已经创建的最有效的映射。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

徐刘根

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值