Mybatis二级缓存
项目结构
缓存的概念
什么是缓存?
存在于内存中的临时数据.
为什么使用缓存?
减少和数据库的交互次数, 提供执行效率.
什么样的数据使用缓存, 什么样的数据不能使用缓存?
适用于缓存:
经常查询并且不经常改变的.
数据的正确与否对最终结果影响不大的.
不适用于缓存:
经常改变的数据
数据的正确与否对最终结影响很大的.
列如: 商品的库存, 银行的汇率, 故事的牌价.
Mybatis中的一级缓存和二级缓存
一级缓存:
它只的是Mybatis中SqlSession对象的缓存.
当我们执行查询之后, 查询的结果会同时存入到SqlSession为我们提供一块区域中.
该区域的结构是一个Map. 当我们再次查询同样的数据, mybatis会先去SqlSession
中查询是否有, 有的话直接拿出来用.
当SqlSession对象消失时, mybatis的以及缓存也就消失了.
二级缓存:
它指的是Mybatis中SqlSessionFactory对象的缓存.
由同一个SqlSessionFactory对象创建的SqlSession共享其缓存
二级缓存的使用步骤:
第一步: 让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
第二步: 让当前的映射文件支持二级缓存(在userDao.xml中配置)
第三步: 让当前的操作支持二级缓存(在select标签中配置)
二级缓存的存在
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
配置文件
-
pom.xml
<?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.xiaoge</groupId> <artifactId>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>8.0.13</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>
-
jdbcConfig.properties
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis jdbc.username=root jdbc.password=123456
-
SqlMapConfig.xml(重点settings)
<?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> <!--配置properties--> <properties resource="jdbcConfig.properties"> </properties> <!-- 开启配置缓存 --> <settings> <setting name="cacheEnabled" value="true"/> </settings> <!-- 使用typeAliases配置别名, 它只能配置domain中类的别名 --> <typeAliases> <package name="com.xiaoge.domain"></package> </typeAliases> <!--配置环境--> <environments default="mysql"> <!--配置mysql环境--> <environment id="mysql"> <!--配置事务--> <transactionManager type="JDBC"></transactionManager> <!--配置连接池--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.username}" /> <property name="password" value="${jdbc.password}" /> </dataSource> </environment> </environments> <mappers> <package name="com.xiaoge.dao"></package> </mappers> </configuration>
-
UserDao.xml(重点cache 还有 useCache)
<?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.xiaoge.dao.UserDao"> <!-- 开启user支持二级缓存 --> <cache/> <!-- 查询所有用户操作 --> <select id="findAll" resultType="user"> select * from user </select> <!-- 查询条记录信息 useCache="true": 开启二级缓存 --> <select id="findById" parameterType="java.lang.Integer" resultType="user" useCache="true"> select * from user where id = #{userId} </select> <!-- 修改用户信息 --> <update id="updateUser" parameterType="user"> update user set username = #{username}, address = #{address} where id = #{id} </update> </mapper>
实体类
-
User(注意: 使用二级缓存是一定要继承Serializable, 要不然就会报一个序列化的错误)
package com.xiaoge.domain; import java.io.Serializable; import java.util.Date; /** * @Author: 潇哥 * @DateTime: 2020/3/4 下午4:03 * @Description: TODO */ public class User implements Serializable { private Integer id; private String username; private String address; private String sex; private Date birthday; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
持久层接口
-
UserDao
package com.xiaoge.dao; import com.xiaoge.domain.User; import java.util.List; /** * @Author: 潇哥 * @DateTime: 2020/3/4 下午4:19 * @Description: 用户的持久层接口 */ public interface UserDao { /** * 查询所有用户, 同时获取到用户下所有账户的信息 * @return */ public List<User> findAll(); /** * 获取用户信息 * @return */ public User findById(Integer id); /** * 修改用户信息 * @param user */ public void updateUser(User user); }
测试持久层接口方法
-
SecondLevelCacheTest
package com.xiaoge.test; import com.xiaoge.dao.UserDao; import com.xiaoge.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; /** * @Author: 潇哥 * @DateTime: 2020/3/16 下午6:12 * @Description: TODO */ public class SecondLevelCacheTest { private InputStream is; private SqlSessionFactory factory; @Before public void init() throws IOException { is = Resources.getResourceAsStream("SqlMapConfig.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); factory = builder.build(is); } @After public void destroy() throws IOException { if (is != null) { is.close(); } } /** * 测试二级缓存 * 注意: 二级缓存存的是数据, 而不是对象 */ @Test public void findFirstLevelCacheTest(){ SqlSession sqlSession1 = factory.openSession(); UserDao dao1 = sqlSession1.getMapper(UserDao.class); User user1 = dao1.findById(41); System.out.println(user1); sqlSession1.close(); // 一级缓存消失 SqlSession sqlSession2 = factory.openSession(); UserDao dao2 = sqlSession2.getMapper(UserDao.class); User user2 = dao2.findById(41); System.out.println(user2); sqlSession2.close(); System.out.println(user1 == user2); // 运行结果(只会去数据库查询一次) 2020-03-18 15:55:27,489 1460 [ main] DEBUG om.xiaoge.dao.UserDao.findById - ==> Preparing: select * from user where id = ? // 这里 2020-03-18 15:55:27,539 1510 [ main] DEBUG om.xiaoge.dao.UserDao.findById - ==> Parameters: 41(Integer) 2020-03-18 15:55:27,589 1560 [ main] DEBUG om.xiaoge.dao.UserDao.findById - <== Total: 1 com.xiaoge.domain.User@55f616cf 2020-03-18 15:55:27,597 1568 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f59a598] 2020-03-18 15:55:27,598 1569 [ main] DEBUG ansaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f59a598] 2020-03-18 15:55:27,598 1569 [ main] DEBUG source.pooled.PooledDataSource - Returned connection 525968792 to pool. 2020-03-18 15:55:27,607 1578 [ main] DEBUG com.xiaoge.dao.UserDao - Cache Hit Ratio [com.xiaoge.dao.UserDao]: 0.5 com.xiaoge.domain.User@2beee7ff false // 这里是因为二级缓存存储的是数据, 而不是对象, 所以是false } }