1、缓存
缓存
- 存在与内存中的临时数据。
为什么使用缓存?
- 减少和数据库的交互次数,提高执行的效率。
什么样的数据能使用缓存
- 经常查询且不经常改变的
- 数据的正确与否对最终结果影响不大的
什么样的数据不适用于缓存
- 经常改变的数据
- 数据的正确与否对最终结果影响很大的
- 例如:商品的库存,银行的汇率,股市的牌价
2、一级缓存
什么是一级缓存
它指的是Mybatis中SqlSession对象的缓存,当我们执行查询之后,查询的结果同时会存入到SqlSession一块区域中,该区域的一个结构是Map结构。当我们再次查询同样的数据,mybatis会先去sqlsession中查询是否有,有的话直接拿来用。当SqlSession对象关闭时,或者清空缓存,mybatis的一级缓存也就消失了。
代码查看一级缓存
- 目录结构
- 表结构
- SqlMapConifg.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis主配置文件-->
<configuration>
<!--配置环境-->
<environments default="mysql">
<!-- 配置mysql环境-->
<environment id="mysql">
<!-- 配置事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!-- 配置数据库的基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="111111"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每一个dao独立的配置文件-->
<mappers>
<package name="com.mybatis.dao"/>
</mappers>
</configuration>
- IUserDao接口,在这里只有两个方法。
package com.mybatis.dao;
import com.mybatis.domain.User;
import java.util.List;
public interface IUserDao {
/**
* 查询所有用户,以及账户信息
* @return
*/
List<User> findAll();
/**
* 根据ID查询用户信息
* @param userId
* @return
*/
User findById(Integer userId);
}
- IUserDao.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.mybatis.dao.IUserDao">
<!-- 查询所有-->
<select id="findAll" resultType="com.mybatis.domain.User">
select * from users
</select>
<select id="findById" parameterType="INT" resultType="com.mybatis.domain.User">
select * from users where id = #{uid}
</select>
</mapper>
- MybatisTest测试类
package com.mybatis.test;
import com.mybatis.dao.IUserDao;
import com.mybatis.domain.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class MybatisTest {
private InputStream in;
private SqlSession session;
private IUserDao userDao;
private SqlSessionFactory factory;
@Before
public void init() throws Exception {
this.in = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
System.out.println(in);
this.factory = factoryBuilder.build(in);
// this.session = factory.openSession(true);
this.session = factory.openSession();
this.userDao = session.getMapper(IUserDao.class);
}
@After
public void destory() throws IOException {
session.commit();
this.in.close();
this.session.close();
}
@Test
public void testCache(){
User user1 = userDao.findById(8);
System.out.println(user1);
session.clearCache();
User user2 = userDao.findById(8);
System.out.println(user2);
System.out.println(user1 == user2);
}
}
- 执行完testCache方法后,结果如下:
两个user对象是一样的,说明第一次从数据库中读取后,第二次直接拿的是session的一级缓存。 - 当我们把testCache的方法改成如下两钟时
public void testCache(){
User user1 = userDao.findById(8);
System.out.println(user1);
session.clearCache();
User user2 = userDao.findById(8);
System.out.println(user2);
System.out.println(user1 == user2);
}
public void testCache(){
User user1 = userDao.findById(8);
System.out.println(user1);
session.close();
session = factory.openSession();
userDao = session.getMapper(IUserDao.class);
User user2 = userDao.findById(8);
System.out.println(user2);
System.out.println(user1 == user2);
}
此时已经将缓存session关闭或者清空缓存,结果如下:
此时两个User对象不同,说明是从数据库钟两次读取的结果。
一级缓存如何实现数据同步的呢?
一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除,commit(),close()等方法时,就会清空一级缓存。
这样如果更新了数据,就自动清空缓存,下一次读取的时候就在数据库中读取内容。
3、二级缓存
什么是一级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存,由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤
- 第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
- 第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
- 第三步:让当前的操作支持二级缓存(在Select标签中配置)
代码查看二级缓存
- SqlMapConfig.xml
添加setting标签
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--mybatis主配置文件-->
<configuration>
<!-- 配置参数-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!--配置环境-->
<environments default="mysql">
<!-- 配置mysql环境-->
<environment id="mysql">
<!-- 配置事务类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池)-->
<dataSource type="POOLED">
<!-- 配置数据库的基本信息-->
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="111111"/>
</dataSource>
</environment>
</environments>
<!-- 指定映射配置文件的位置,映射配置文件指的是每一个dao独立的配置文件-->
<mappers>
<package name="com.mybatis.dao"/>
</mappers>
</configuration>
- IUserDao.xml
添加一个cache标签 ,select标签中添加useCache=“true”
<?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.mybatis.dao.IUserDao">
<cache/>
<!-- 查询所有-->
<select id="findAll" resultType="com.mybatis.domain.User">
select * from users
</select>
<select id="findById" parameterType="INT" resultType="com.mybatis.domain.User" useCache="true">
select * from users where id = #{uid}
</select>
</mapper>
测试类:
sqlSession1.close();其中这个时关闭一级缓存对二级缓存的影响。
public void testCache(){
SqlSession sqlSession1 = factory.openSession();
IUserDao userDao1 = sqlSession1.getMapper(IUserDao.class);
User user1 = userDao1.findById(8);
System.out.println(user1);
sqlSession1.close();
SqlSession sqlSession2 = factory.openSession();
IUserDao userDao2 = sqlSession2.getMapper(IUserDao.class);
User user2 = userDao2.findById(8);
System.out.println(user2);
sqlSession2.close();
System.out.println(user1 == user2);
}
运行结果:
如果有日志的话,会看到该两次查询,只从数据库中查询了一次结果,然后从缓存中拿取。
user1 与 user2不相同的原因是,我们在数据库中存储的不是一个对象,而是一组数据,可以理解为json格式的数据,当有请求拿缓存中的数据时,直接将数据给他,然后创建出一个新的对象出来。所以虽然是从缓存中拿取,但是两次的对象肯定是不同的。这一点和一级缓存不同,一级缓存在缓存中存储的就是一个对象。有一个请求缓存过来时,直接用一个引用指向该对象。