11. 动态SQL & 缓存
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。
动态SQL就是指根据不同查询条件,生成不同的SQL语句
在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
if 提供了一种可选的查找文本功能
参数test:里面的表达式如果为ture则执行,否则不执行
<select id="findActiveBlogLike"
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 author_name like #{author.name}
</if>
</select>
choose ,where,otherwise:有点像java中的switch语句
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
官方文档示例:
<select id="findActiveBlogLike"
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 author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
switch (exp){
case 1:
break;
case 2:
break;
}
</select>
trim,where,set:解决几个例子中where条件不匹配的情况,SQL语句报错的问题
where
元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where
元素也会将它们去除。
官方文档示例:
<select id="findActiveBlogLike"
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 author_name like #{author.name}
</if>
</where>
</select>
如果 where 元素没有按正常套路出牌,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:如果 where 元素没有按正常套路出牌,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
官方文档示例:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
set元素可以用于动态包含需要更新的列,而舍弃其他的。
官方文档示例:
<update id="updateAuthorIfNecessary">
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>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
foreach
foreach允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。foreach允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
官方文档示例:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
代码测试
测试模糊查询
接口编写
package com.westos.dao;
import com.westos.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserDao {
//获取全部的用户
List<User> getAllUser();
//模糊查询.
List<User> getUserByLike(Map<String,Object> map);
}
映射文件编写
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.westos.dao.UserDao">
<select id="getAllUser" resultType="com.westos.pojo.User">
select * from user
</select>
<select id="getUserByLike" resultType="User" parameterType="Map">
select * from mybatis.user
<where>
<if test="name!=null">
name like CONCAT('%',#{name},'%')
</if>
<if test="id!=null">
and id = #{id}
</if>
</where>
</select>
</mapper>
测试类
package com.westos.dao;
import com.westos.pojo.User;
import com.westos.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
public class test {
@Test
public void getAllUser(){
SqlSessionFactory sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> allUser = mapper.getAllUser();
for (User user : allUser) {
System.out.println("id号\t"+user.getId()+"\t姓名:"+user.getName()+"\t密码:"+user.getPwd());
}
}
@Test
public void getUserByLike(){
SqlSessionFactory sqlSessionFactory = MyBatisUtils.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("name","秦");
List<User> allUser = mapper.getUserByLike(map);
for (User user : allUser) {
System.out.println(user);
}
}
}
注意点:太过复杂的逻辑不建议使用动态SQL,简单的话可以直接使用动态SQL实现;
缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在SQL的映射文件中添加一行:
<!--开启缓存-->
<cache/>
这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他- - 调用者或线程所做的潜在修改。
注意事项: 缓存只作用于 cache 标签所在的映射文件中的语句。
cache 元素的属性
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
这些属性配置表示创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
一般在执行查询的时候,才会使用缓存,使用的方法是:将useCache属性设为true
<select id="getUser" resultType="User" useCache="true">
select * from mybatis.user
</select>
使用缓存可以将查询出来的结果暂时保存着,如果短时间查询同样的语句比较多,使用缓存就可以提高查询速度,但是使用缓存会消耗内存资源。
缓存只作用于 cache 标签所在的映射文件中的语句。
在实际开发环境中,很少使用SQL层面的缓存来提高查询速度。