MyBatis(4) ---- 查询缓存
- 用于减轻数据库压力,提高数据库性能
- 如果缓存中又数据,就不用去数据库取数据
一级缓存(是SqlSession级别的缓存, Mybatis 默认支持一级缓存)
一级缓存的原理 ---- sqlSession 级
- 第一次取查询,先去缓存是否存在这个数据,没有
就去操作数据库,并将查询结果存放到一级缓存
如果sqlSession 去执行commit 时(插入、更新、删除),会清空sqlSession 中
的一级缓存 ---- 目的:为了让缓存内存在的数据是最新的信息,避免脏数据
- 第二次取查询,先去缓存是否存在这个数据,如果
缓存内存在数据,就直接从缓存内取出数据
- 一级缓存测试
/*一级缓存测试*/
@Test
public void test() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper dao = sqlSession.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
user.setName("上官铁柱");
PojoVO pojoVO = new PojoVO();
pojoVO.setUser(user);
int i = dao.updateUserById(pojoVO);
//提交,清除一级缓存,避免脏数据
sqlSession.commit();
System.out.println(i);
//第一次查询 --- 查看是否存在缓存,没有就执行sql 查询
User user1 = dao.findUserById(pojoVO);
System.out.println(user1.toString());
//第二次查询 --- 当出现缓存,直接拿取缓存的数据
User user2 = dao.findUserById(pojoVO);
System.out.println(user2.toString());
}
- 注意:
一级缓存在同一个sqlSession 时有效,当sqlSession 关闭时,所对应的一级缓存也会被清空
二级缓存----跨SqlSession(Mapper 级)
- 是Mapper级别的缓存 — 多个sqlSession 去查找mapper 的语句
二级缓存的原理
- 前提:开启mybatis 的二级缓存
- 注意:mybatis 二级缓存默认关闭;
- 多个sqlSession 可以共享一个二级缓存区域;
- 每一个namespace 的mapper 有一个二级缓存区域;
- commit 会清空该mapper 下的二级缓存
- 开启二级缓存
- 开启mybatis 配置文件的缓存开关(默认值为true)
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
- 开启namespace 二级缓存开关
<!-- 开启该namespace 的二级缓存 -->
<cache />
- 设置pojo 实现序列化接口(存放二级缓存的位置不一定在内存(硬盘等))
public class User implements Serializable{}
- 二级缓存测试代码1(没有commit)
@Test
public void testCache_1() throws Exception {
SqlSession sqlSession0 = sqlSessionFactory.openSession();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
/*第一次发起请求*/
UserMapper dao0 = sqlSession0.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
user.setName("上官铁柱");
PojoVO pojoVO = new PojoVO();
pojoVO.setUser(user);
User user_1 = dao0.findUserById(pojoVO);
System.out.println(user_1);
/*将sqlSession 的数据写到而二级缓存区域*/
sqlSession0.close();
/*再次进行查询,查看二级缓存执行过程*/
UserMapper dao2 = sqlSession2.getMapper(UserMapper.class);
User user_2 = dao2.findUserById(pojoVO);
System.out.println(user_2);
sqlSession2.close();
}
- 调试结果
- 二级缓存测试代码1(有commit)
@Test
public void testCache_1() throws Exception {
SqlSession sqlSession0 = sqlSessionFactory.openSession();
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
/*第一次发起请求*/
UserMapper dao0 = sqlSession0.getMapper(UserMapper.class);
User user = new User();
user.setId(1);
user.setName("上官铁柱");
PojoVO pojoVO = new PojoVO();
pojoVO.setUser(user);
User user_1 = dao0.findUserById(pojoVO);
System.out.println(user_1);
/*将sqlSession 的数据写到而二级缓存区域*/
sqlSession0.close();
/*进行更新操作,查看二级缓存数据*/
UserMapper dao1 = sqlSession1.getMapper(UserMapper.class);
int i = dao1.updateUserById(pojoVO);
sqlSession1.commit();
System.out.println(i);
sqlSession1.close();
/*再次进行查询,查看二级缓存执行过程*/
UserMapper dao2 = sqlSession2.getMapper(UserMapper.class);
User user_2 = dao2.findUserById(pojoVO);
System.out.println(user_2);
sqlSession2.close();
}
- 调试结果
二级缓存参水
- 禁用二级缓存(变化频率高的sql 设置成禁用缓存)
<select id="findUserById" parameterType="pojoVO" resultType="user" useCache="false">
SELECT * FROM user
WHERE id=#{user.id}
</select>
- 调试结果
- 刷新缓存(清空缓存)
一般情况下,commit 操作执行后都需要刷新缓存,避免脏数据
<update id="updateUserById" parameterType="pojoVO" flushCache="true">
Mybatis 整合 ehcache
- ehcache 是一个分布式缓存
整合方法
- Mybatis 提供了一个cache 接口
<!-- type:指定cache 接口的实现类型,mybatis 默认使用PerpetualCache
要与ehcache 实现整合,需要配置type 为ehcache 的实现cache 接口的类型 -->
<cache type="" />
- 过程
- 加入ehcache 的依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
- 加入ehcache 的配置文件
- 在resources 下配置ehcache.xml
<ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
/* 当需要将数据存放到硬盘时的位置 */
<diskStore path="D:\ehcache\resources"/>
<defaultCache
maxElementsInMemory="20000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="eternalCache"
maxElementsInMemory="20000"
eternal="true"
overflowToDisk="true"
diskPersistent="false"
timeToLiveSeconds="0"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
- 修改 属性值为***org.mybatis.caches.ehcache.EhcacheCache***
二级缓存的应用场景:
- 对访问次数多、实时性要求不高;
- 耗时查询
二级缓存的局限性:
- 当存在大量数据在二级缓存时,如果存在一个数据使用了commit 操作,那么
整个二级缓存数据都将被清空,导致数据缓存命中率低
Mybatis 逆向工程
- 正对单表自动生成Mybatis 自动创建所需的代码(mapper.java、mapper.xml、opjo);
- 常用的逆向工程的方式是由数据库生成java 代码
使用逆向工程
- 建议使用java 程序的方式生成Java 代码
生成的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 -->
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- Mysql数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/javaend"
userId="root"
password="1006">
</jdbcConnection>
<!-- Oracle数据库
<jdbcConnection driverClass="oracle.jdbc.OracleDriver"
connectionURL="jdbc:oracle:thin:@127.0.0.1:1521:yycg"
userId="yycg"
password="yycg">
</jdbcConnection>
-->
<!-- 默认为false,把JDBC DECIMAL 和NUMERIC类型解析为Integer,为true时
把JDBC DECIMAL 和NUMERIC类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
<!-- targetProject:生成POJO类的位置 -->
<javaModelGenerator targetPackage="com.pojo" targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true"/>
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.dao" targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</sqlMapGenerator>
<!-- targetProject:mapper接口生成的的位置 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.dao" targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false"/>
</javaClientGenerator>
<!-- 指定数据表 -->
<table schema="" tableName="tb_orders"></table>
<table schema="" tableName="tb_product"></table>
<table schema="" tableName="tb_ordersitem"></table>
<table schema="" tableName="tb_user"></table>
<!-- 有些表的字段需要指定java类型
<table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
<property name="useActualColumnNames" value="true"/>
<generatedKey column="ID" sqlStatement="DB2" identity="true" />
<columnOverride column="DATE_FIELD" property="startDate" />
<ignoreColumn column="FRED" />
<columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
</table> -->
</context>
</generatorConfiguration>
执行生成程序
package com.mybatisStudy5_generator; /**
* @ClassName StartGeneratorConfig
* @Author 秃头的JJ
* Date 2019/5/20 0020 22:05
* Version 1.0
*/
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class StartGeneratorConfig {
public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定 逆向工程配置文件
File configFile = new File("mybatisStudy5_generator/src/main/resources/generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
StartGeneratorConfig start = new StartGeneratorConfig();
start.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}