在实际项目开发中,通常对数据库查询的性能要求很高,而MyBatis提供了查询缓存来缓存数据,从而达到提高查询性能的要求。MyBatis的查询缓存分为一级缓存(sqlSession级别)和二级缓存(mapper级别)。
一级缓存(sqlSession级别)
一级缓存的作用域是sqlSession范围的,当在同一个sqlSession中执行两次相同的SQL语句时,第一次执行完毕会将从数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。需要注意的是,如果sqlSession执行了DML操作(insert、update、delete),并提交到数据库,MyBatis则会清空sqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。
当一个sqlSession结束后该sqlSession的一级缓存也就不在了。MyBatis默认开启一级缓存,不需要进行任何配置。
示例:测试MyBatis缓存
程序清单:UserMapper.java
package com.mapper;
import com.po.User;
public interface UserMapper {
//增加用户
void addUser(User user);
//删除用户
void deleteUser(Integer id);
//根据id查询用户
User getUserById(Integer id);
//更新用户
void updateUser(User user);
}
程序清单:UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.UserMapper">
<!-- 删除用户 -->
<delete id="deleteUser" parameterType="Integer">
delete from t_user where id=#{id}
</delete>
<!-- 根据id查询用户 -->
<select id="getUserById" parameterType="Integer" resultType="user" >
select * from t_user where id=#{id}
</select>
</mapper>
程序清单:UserDao.java
@Test
public void getUserById() throws IOException {
session=getSqlSession();
UserMapper um=session.getMapper(UserMapper.class);
User user=um.getUserById(2);
User user1=um.getUserById(2);
System.out.println(user);
System.out.println(user1);
session.close();
}
运行UserDao类的getUserById() 方法,控制台显示如下:
仔细观察MyBatis的执行结果,在第一次查询id为2的User对象时执行了一条select语句,但是第二次获取id为2的User对象时并没有执行select语句。因为此时一级缓存已经缓存了id为2的User对象,MyBatis直接从缓存中将对象取出来,并没有再次去数据库查询,所以第二次没有执行select语句。
程序清单:UserDao.java
@Test
public void test() throws IOException {
session=getSqlSession();
UserMapper um=session.getMapper(UserMapper.class);
User user=um.getUserById(2);
System.out.println(user);
um.deleteUser(3);
session.commit();
User user1=um.getUserById(2);
System.out.println(user1);
}
执行UserDao的test()方法,控制台显示如下:
仔细观察MyBatis的执行结果,在第一次查询id为2的User对象时执行了一条select语句,接下来执行了一个delete操作,MyBatis为了保证缓存中存储的是最新信息,会清空SqlSession缓存。当第二次获取id为2的User对象时一级缓存并没有缓存任何对象,所以MyBatis再次执行select语句去查询id为2的User对象。
程序清单:UserDao.java
@Test
public void test() throws IOException {
session=getSqlSession();
UserMapper um=session.getMapper(UserMapper.class);
User user=um.getUserById(2);
System.out.println(user);
//清空一级缓存
session.clearCache();
um=session.getMapper(UserMapper.class);
User user1=um.getUserById(2);
System.out.println(user1);
}
执行UserDao的test()方法,控制台显示如下:
仔细观察MyBatis的执行结果,在第一次查询id为2的User对象时执行了一条select语句,接下来调用SqlSession的clearCache()方法,该方法会清空SqlSession缓存。第二次获取id为2的User对象时,MyBatis会再次执行select语句。
程序清单:UserDao.java
@Test
public void test() throws IOException {
session=getSqlSession();
UserMapper um=session.getMapper(UserMapper.class);
User user=um.getUserById(2);
System.out.println(user);
//关闭SqlSession连接
session.close();
//重新获取SqlSession
SqlSession session2=getSqlSession();
um=session2.getMapper(UserMapper.class);
User user1=um.getUserById(2);
System.out.println(user1);
}
仔细观察MyBatis的执行结果,在第一次查询id为2的User对象时执行了一条select语句,接下来调用SqlSession的close()方法,该方法会关闭SqlSession缓存。第二次获取id为2的User对象时,MyBatis会再次执行select语句。
二级缓存(mapper级别)
使用二级缓存时,多个Sqlsession使用同一个mapper的SQL语句去操作数据库,得到的数据会存在二级缓存区域,多个SqlSession可以共享二级缓存中的数据。
示例:测试MyBatis二级缓存
在mybatis-config.xml开启二级缓存
<settings>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
配置UserMapper.xml
<!-- 开启当前mapper的namespace下的二级缓存 -->
<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/>
程序清单:MySqlSessionFactory.java
package com.factory;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class MySqlSessionFactory {
private static SqlSessionFactory sqlSessionFactory=null;
static {
try {
InputStream is= Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
public static SqlSessionFactory getSqlSessionFactory() {
return sqlSessionFactory;
}
}
程序清单:UserDao.java
@Test
public void test() throws IOException {
session=MySqlSessionFactory.getSqlSession();
UserMapper um=session.getMapper(UserMapper.class);
User user=um.getUserById(2);
System.out.println(user);
//关闭SqlSession连接
session.close();
//重新获取SqlSession
SqlSession session2=MySqlSessionFactory.getSqlSession();
um=session2.getMapper(UserMapper.class);
User user1=um.getUserById(2);
System.out.println(user1);
session2.close();
}
执行UserDao的test()方法,控制台显示如下:
仔细观察MyBatis的执行结果,日志中有几条以Cache Hit Ratio开头的语句,这行日志后面输出的值为当前执行方法的缓冲命中率。在第一次查询id为2的User对象时执行了一条select语句,接下来调用SqlSession的close()方法,该方法会关闭SqlSession一级缓存,同时会将查询数据保存到二级缓存中。当第二次获取id为2的User对象时,MyBatis会去二级缓存中查找,所以不会再次执行select语句。