缓存
缓存是指存在于内存中的临时数据,使用缓存可以减少和数据库的交互次数,提高执行效率。
什么样的数据适用缓存
经常查询且不经常改变的;数据的正确与否对最终结果影响不大的。
一级缓存
一级缓存是指MyBatis中SqlSession对象的缓存,当SqlSession消失时,一级缓存随之消失。
当我们执行查询后,查询的结果会同时存入到SqlSession中的一块区域。当我们再次查询同样数据时,MyBatis会先从SqlSession查询,若存在结果则使用。
证明一级缓存的存在
详情配置文件参照前面几章节
public class Person {
private Integer id;
private Date birthday;
private String name;
//省略get /set方法
}
dao层接口
public interface PersonDao {
Person findById(Integer pid);
}
映射文件PersonDao.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.on1.dao.PersonDao">
<select id="findById" resultType="person" parameterType="int">
select * from person where id = #{pid}
</select>
</mapper>
测试方法:
public class day5 {
InputStream in;
SqlSession sqlSession;
PersonDao personDao;
@Before
public void init() throws Exception {
// 加载配置文件
in = Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
// 获取代理对象
personDao = sqlSession.getMapper(PersonDao.class);
}
@After
public void destroy() throws Exception {
sqlSession.commit();
sqlSession.close();
in.close();
}
@Test
public void testFirstLevelCache() {
Person p1 = personDao.findById(41);
System.out.println(p1);
Person p2 = personDao.findById(41);
System.out.println(p2);
System.out.println(p1 == p2);
}
}
控制台输出如下,可以发现,两个对象是相同的,且只执行了一句查询语句
证明一级缓存的清空
映射文件PersonDao.xml中添加更新操作:
<update id="updatePerson" parameterType="Person">
update person set name = #{name} where id = #{id}
</update>
测试方法:
@Test
public void testUpdate() {
Person p1 = personDao.findById(42);
System.out.println(p1);
p1.setName("on1");
p1.setBirthday(new Date());
personDao.updatePerson(p1);
Person p2 = personDao.findById(42);
System.out.println(p2);
System.out.println(p1 == p2);
}
控制台输出如下,两个对象并不相同。
当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存。
示例
在配置文件中开启二级缓存
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
映射文件PersonDao.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.on1.dao.PersonDao">
<cache></cache>
<!-- useCache=”true”代表当前这个 statement 要使用二级缓存; -->
<select id="findById" resultType="person" parameterType="int"
useCache="false">
select * from person where id = #{pid}
</select>
</mapper>
测试:
public class day5 {
InputStream in;
SqlSession sqlSession;
SqlSessionFactory factory;
PersonDao personDao;
@Before
public void init() throws Exception {
// 加载配置文件
in = Resources.getResourceAsStream("mybatisConfig.xml");
factory = new SqlSessionFactoryBuilder().build(in);
sqlSession = factory.openSession();
// 获取代理对象
personDao = sqlSession.getMapper(PersonDao.class);
}
@After
public void destroy() throws Exception {
in.close();
}
@Test
public void testTwoLevelCache() {
Person p1 = personDao.findById(42);
sqlSession.commit();
System.out.println(p1);
sqlSession.close(); //清空一级缓存
SqlSession ss2 = factory.openSession();
PersonDao personDao2 = ss2.getMapper(PersonDao.class);
Person p2 = personDao2.findById(42);
System.out.println(p2);
System.out.println(p1 == p2);
}
}
控制台输出如下,发现它一共执行了两条查询语句
如果我们在映射文件中使用二级缓存:
<!-- 每次查询都需要最新数据,因此要禁用二级缓存 -->
<select id="findById" resultType="person" parameterType="int"
useCache="true">
select * from person where id = #{pid}
</select>
则控制台输出如下,它只执行一条查询语句
无论是否使用二级缓存,你会发现两个对象p1和p2都是不相同的,这是因为二级缓存存放的内容是数据,类似于:
"id": 42, "name":"on1"
每当有要返回bean类时,它就会新建一个数据是二级缓存的数据的bean类,然后返回该对象,因此p1,p2对象是不同的。
当我们在使用二级缓存时,所缓存的bean类一定要实现 java.io.Serializable 接口,这样就可以使用序列化的方式来保存对象
public class Person implements Serializable { ... }