Mybatis中具有一级缓存和二级缓存
一级缓存
一级缓存是SqlSession级别的缓存,只要SqlSession缓存没有被清除,则一级缓存就存在
- 编写账户实体类
package com.liang.domain;
import java.io.Serializable;
/**
* 账户实体类
*/
public class Account implements Serializable {
private int id;
private int uid;
private Double money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", uid=" + uid +
", money=" + money +
'}';
}
}
- 编写持久层接口
package com.liang.dao;
import com.liang.domain.Account;
import java.util.List;
/**
* 账户的持久层接口
*/
public interface AccountDao {
/**
* 通过用户id查询账户信息
* @param uid
* @return
*/
List<Account> findByUid(int uid);
}
- 编写用户持久层映射文件
<?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.liang.dao.AccountDao">
<select id="findByUid" parameterType="int" resultType="com.liang.domain.Account">
select *from account where uid = #{uid}
</select>
</mapper>
- 编写测试方法、
/**
* 查询所有用户信息
*/
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findByUid(41);
List<Account> accounts1 = accountDao.findByUid(41);
if(accounts == accounts1)
{
System.out.println("accounts 和 accounts1 是同一个对象");
}
}
测试结果:
用上面的程序可以看出,调用了两次查询方法,但是实际只执行了一次SQL语句,这就是因为一级缓存的作用。一级缓存是SqlSession范围的缓存,所以SqlSession缓存没有被清除,当第二次执行相同的代码,MyBatis会从缓存中获取数据,而不是去数据库。
当对测试代码进行如下修改:
/**
* 查询所有用户信息
*/
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findByUid(41);
sqlSession.clearCache();//清除缓存
List<Account> accounts1 = accountDao.findByUid(41);
if(accounts == accounts1)
{
System.out.println("accounts 和 accounts1 是同一个对象");
}
}
清除了SqlSession的缓存,则测试结果如下:
可见清楚缓存后,会执行两次数据库的查询。
当调用SqlSession的修改,添加,删除,commit(),close等方法时候,就会清除一级缓存,这样主要是为了让缓存中存储的数据是最新的数据,避免脏读。
测试一级缓存的清空
5. 持久层接口中添加方法
/**
* 更新账户信息
* @param account
*/
void updateAccount(Account account);
- 持久层接口对应的映射文件添加
<update id="updateAccount" parameterType="com.liang.domain.Account">
update account set MONEY = #{money} where id = #{id}
</update>
- 测试方法修改
/**
* 查询所有用户信息
*/
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findByUid(41);
testUpdate();
List<Account> accounts1 = accountDao.findByUid(41);
if(accounts == accounts1)
{
System.out.println("accounts 和 accounts1 是同一个对象");
}
}
/**
* 更新账户信息
*/
public void testUpdate()
{
Account account = new Account();
account.setId(1);
account.setMoney(200.0);
accountDao.updateAccount(account);
}
测试如下:
可以看出更新数据后,第二次根据用户id查询账户信息的数据来自于数据库查询,而不是缓存数据。
二级缓存
Mybatis的二级缓存指的是SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
- Mybatis配置文件中开启二级缓存
<settings>
<!--开启二级缓存的支持-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 配置相关的持久层映射文件
- 编写测试方法
@Test
public void testSecondLevelCache()
{
SqlSession sqlSession1 = sqlSessionFactory.openSession();
AccountDao accountDao1 = sqlSession1.getMapper(AccountDao.class);
List<Account> accounts1 = accountDao1.findByUid(41);
System.out.println(accounts1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
AccountDao accountDao2 = sqlSession2.getMapper(AccountDao.class);
List<Account> accounts2 = accountDao2.findByUid(41);
System.out.println(accounts2);
sqlSession1.close();
System.out.println(accounts1 == accounts2);
}
测试结果:
可见:SQL语句执行了一次,说明第二次数据来自于缓存
使用二级缓存时,所缓存的类一定要实现java.io.Serializable接口,因为二级缓存存放的是被序列化数据,而不是对象
通过注解配置二级缓存
1.在MyBatis配置文件中开启二级缓存
<settings>
<!--开启二级缓存的支持-->
<setting name="cacheEnabled" value="true"/>
</settings>
2.在持久层接口中通过注解配置二级缓存