罗里吧嗦
什么是缓存
Mybatis中的缓存
什么是缓存
存在于内存中的临时数据。
为什么使用缓存
减少和数据库的交互次数,提高执行效率。
什么样的数据能使用缓存,什么样的数据不能使用
适用于缓存:
经常查询并且不经常改变的。
数据的正确与否对最终结果影响不大的。
不适用于缓存:
经常改变的数据
数据的正确与否对最终结果影响很大的。
例如:商品的库存,银行的汇率,股市的牌价。
Mybatis中的一级缓存和二级缓存
一级缓存:
它指的是Mybatis中SqlSession对象的缓存。
当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。
该区域的结构是一个Map。当我们再次查询同样的数据,mybatis会先去sqlsession中
查询是否有,有的话直接拿出来用。
当SqlSession对象消失时,mybatis的一级缓存也就消失了。
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存的使用步骤:
第一步:让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步:让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
第三步:让当前的操作支持二级缓存(在select标签中配置)
一级缓存
一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除, commit(), close()等方法时,就会清空一级缓存。
第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。
得到用户信息,将用户信息存储到一级缓存中。
如果此时 sqlSession 去执行 commit 操作(执行插入、更新、删除),
就会清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。
证明一级缓存的存在
执行了两次查询同一个id的对象,只执行了一个sql语句,也就是第二次没有执行sql语句,而是直接拿第一次的结果
如果在查询了第一个的时候,关闭sqlsession,然后再次开启,则执行了两次sql语句,也就是没有缓存
执行更新操作,查询同一个id,执行了两次sql语句,也就是清除了一级缓存
二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,
多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的
首先开启 mybatis 的二级缓存。
sqlSession1 去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
如果 SqlSession3 去执行相同 mapper 映射下 sql,执行 commit 提交, 将会清空该 mapper 映射下的二级缓存区域的数据。
sqlSession2 去查询与 sqlSession1 相同的用户信息, 首先会去缓存中找是否存在数据,如果存在直接从缓存中取出数据。
如何开启二级缓存
* 步骤: * 1.缓存类要实现序列化接口,也就是public class User implements Serializable * 2.mybatis配置文件支持二级缓存,也就是<setting name="cacheEnabled" value="true"/>(默认支持) * 3.让当前配置文件支持二级缓存,也就是UserMapper.xml中<cache></cache> * 4.让当前操作支持二级缓存,也就是在select标签中useCache="true" * 另外,二级缓存,存的是数据,而不是对象
经过上面的测试,我们发现执行了两次查询,并且在执行第一次查询后,我们关闭了一级缓存,再去执行第二
次查询时,我们发现并没有对数据库发出 sql 语句,所以此时的数据就只能是来自于我们所说的二级缓存。
完整代码
1.创建maven项目导入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cong</groupId> <artifactId>mybatis_cache</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
2.User类,以及代理mapper接口
package com.cong.pojo; import java.io.Serializable; public class User implements Serializable{ private int uid; private String name; private char sex; private String addr; // @Override // public String toString() { // return "User{" + // "uid=" + uid + // ", name='" + name + '\'' + // ", sex=" + sex + // ", addr='" + addr + '\''+ // '}'; // } public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public String getAddr() { return addr; } public void setAddr(String addr) { this.addr = addr; } } package com.cong.mapper; import com.cong.pojo.User; import java.util.List; public interface UserMapper { List<User> findAll();//查询所有user,同时获取user含有的account信息 User findById(int id); User findById2(int id); void updateUser(User user); }
3.mybatis配置文件
<?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"> <configuration> <settings> <!-- 开启二级缓存的支持 因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。 为 true 代表开启二级缓存;为false 代表不开启二级缓存。 --> <setting name="cacheEnabled" value="true"/> </settings> <typeAliases> <package name="com.cong.pojo"></package> </typeAliases> <environments default="mysql"> <environment id="mysql"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/rainbow?characterEncoding=UTF-8"></property> <property name="username" value="root"></property> <property name="password" value="123456"></property> </dataSource> </environment> </environments> <mappers> <!--<mapper class="com.cong.mapper.UserMapper"></mapper>--> <package name="com.cong.mapper"></package> </mappers> </configuration>
4.映射文件
<?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.cong.mapper.UserMapper"> <!-- 开启二级缓存的支持,测试一级缓存要删掉 --> <cache></cache> <select id="findAll" resultType="user"> select * from user; </select> <select id="findById" resultType="user" parameterType="int"> select * from user where uid = #{uid}; </select> <!--useCache=”true”代表当前这个 statement 要使用二级缓存,如果不使用二级缓存可以设置为 false--> <!--注意: 针对每次查询都需要最新的数据 sql,要设置成 useCache=false,禁用二级缓存。--> <select id="findById2" resultType="user" parameterType="int" useCache="true"> select * from user where uid = #{uid}; </select> <update id="updateUser" parameterType="user"> update user set name = #{name},addr = #{addr} where uid = #{uid}; </update> </mapper>
5.测试一级缓存类
import com.cong.mapper.UserMapper; import com.cong.pojo.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.InputStream; import java.util.List; public class TestFirstLevelCache { private SqlSessionFactory factory; private InputStream inputStream; private SqlSession sqlSession; private UserMapper userMapper; /** *测试一级缓存是否存在 * 当把sqlsession关闭再重新打开的时候,就会发现最终得到的结果是false */ @Test public void testExistFirstLevelCache0(){ User user = userMapper.findById(5); User user1 = userMapper.findById(5); System.out.println(user); System.out.println(user1); System.out.println(user == user1); } @Test public void testExistFirstLevelCache(){ User user = userMapper.findById(5); //再次开启sqlsession sqlSession.close(); sqlSession = factory.openSession(); userMapper = sqlSession.getMapper(UserMapper.class); //sqlSession.clearCache();//此方法就是清空一级缓存,跟上面代码同样的效果 User user1 = userMapper.findById(5); System.out.println(user); System.out.println(user1); System.out.println(user == user1); } /** * 测试一级缓存的触发条件 */ @Test public void testClearCache(){ User user = userMapper.findById(5); user.setName("new rainbow"); user.setAddr("耀华里5-1"); //更新操作,会触发清空一级缓存 userMapper.updateUser(user); //再次查询 User user1 = userMapper.findById(5); System.out.println(user); System.out.println(user1); System.out.println(user == user1); } @Test public void findAllUser(){ List<User> users = userMapper.findAll(); for (User user : users) { System.out.println(user.toString()); } } @Before public void init() throws Exception{ inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); factory = new SqlSessionFactoryBuilder().build(inputStream); sqlSession = factory.openSession(); userMapper = sqlSession.getMapper(UserMapper.class); } @After public void destroy() throws Exception{ sqlSession.commit(); sqlSession.close(); inputStream.close(); } }
7.测试二级缓存类
import com.cong.mapper.UserMapper; import com.cong.pojo.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.InputStream; public class TestSecondLevelCache { private InputStream in; private SqlSessionFactory factory; /** * 测试二级缓存 * 步骤: * 1.缓存类要实现序列化接口,也就是public class User implements Serializable * 2.mybatis配置文件支持二级缓存,也就是<setting name="cacheEnabled" value="true"/>(默认支持) * 3.让当前配置文件支持二级缓存,也就是<cache></cache> * 4.让当前操作支持耳机缓存,也就是useCache="true" * 另外,二级缓存,存的是数据,而不是对象,所以最终结果还会使false */ @Test public void test2(){ SqlSession sqlSession = factory.openSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.findById2(5); sqlSession.close();//一级缓存消失 SqlSession newSqlSession = factory.openSession(); UserMapper mapper1 = newSqlSession.getMapper(UserMapper.class); User user1 = mapper1.findById2(5); System.out.println(user); System.out.println(user1); newSqlSession.close(); System.out.println(user == user1);//false } @Before//用于在测试方法执行之前执行 public void init()throws Exception{ //1.读取配置文件,生成字节输入流 in = Resources.getResourceAsStream("SqlMapConfig.xml"); //2.获取 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in); } @After//用于在测试方法执行之后执行 public void destroy()throws Exception{ in.close(); } }