MyBatis
缓存
MyBatis 的缓存级别
MyBatis 提供两种级别的缓存:一级缓存、二级缓存
- 一级缓存:MyBatis 默认开启一级缓存,一级缓存是 SqlSession 级别的缓存。
- 二级缓存:需要手动开启和配置,二级缓存是 namespace(映射文件)级别的缓存。
一级缓存
一个 SqlSession 在执行查询语句之后,会将该查询语句的结果保存在本地缓存中,当该 SqlSession 再次执行相同的查询语句时,会直接在缓存中获取查询结果。
一级缓存失效的几种情况:
- 不同的 SqlSession 对应不同的一级缓存。
- 同一个 SqlSession 但是查询条件不同
- 同一个 SqlSession 两次查询期间执行了增删改操作
- 同一个 SqlSession 两次查询期间手动清空了缓存
sqlSession.clearCache()
二级缓存
二级缓存是映射文件级别的,不论 SqlSession 是否相同,只要 SqlSession 加载的是同一个 Mapper 接口sqlSession.getMapper(EmpMapper.class)
,则所有 SqlSession 共享缓存数据。
二级缓存又叫全局缓存,默认关闭,需要手动开启,二级缓存在 SqlSession 提交或关闭之后才会生效sqlSession.commit()或sqlSession.close()
。
二级缓存的实现步骤:
- 在 MyBatis 全局配置文件中开启二级缓存
<settings><setting name="cacheEnabled" value="true"/></settings>
。 - 在需要使用二级缓存的映射文件中使用
<cache/>
配置二级缓存。 - Bean 类需要实现
Serializable接口
。 - 二级缓存在 sqlSession 提交或关闭时生效。
mybatis-config.xml
<!-- 设置 -->
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
EmpMapper.xml
<mapper namespace="com.mcc.MyBatis.mapper.EmpMapper">
<!--使用二级缓存-->
<cache/>
...
</mapper>
Emp.java
package com.mcc.MyBatis.bean;
import java.io.Serializable;
public class Emp implements Serializable {
private Integer id;
private String eName;
private Integer age;
private String sex;
private Dept dept;
}
二级缓存的相关属性
在<cache/>标签中
eviction="FIFO"
:缓存回收策略:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认为 LRU。flushInterval
:刷新间隔,单位毫秒
默认情况是不设置,即没有刷新间隔,缓存只在调用语句时被刷新。size
:缓存数目,正整数。
代表缓存最多可以存储多少个对象,太大容易导致内存溢出。readOnly
:只读,true/false,默认为 false。
true:只读缓存。会给所有调用者返回缓存对象的相同实例,这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存。会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全。
在增删改查标签中
-
全局 setting 的 cacheEnable:
开启二级缓存,一级缓存一直是打开的。 -
select 标签的
useCache
属性:
在开启全局二级缓存的情况下,设置本条 select 语句是否使用二级缓存,一级缓存一直是使用的。
-
增删改查标签的
flushCache
属性:
增删改默认 flushCache=“true”,语句执行以后,会同时清空一级和二级缓存。
查询默认 flushCache=“false”。 -
sqlSession.clearCache()
:只能用来清除一级缓存。
整合第三方缓存
在映射文件中使用<cache type=""/>
,实现使用第三方缓存工具管理本映射文件的缓存数据。
以 ehcache 为例:
步骤:
-
引入相关 jar 包。
-
创建 ehcache 配置文件,ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="D:\Java\ehcache\temp"/>
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
属性说明:
l diskStore:指定数据在磁盘中的存储位置。
l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略
以下属性是必须的:
l maxElementsInMemory - 在内存中缓存的element的最大数目
l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大
l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断
l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上
以下属性是可选的:
l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大
l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大
diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.
l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。
l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作
l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
-->
- 在核心配置文件已经开启二级缓存的条件下,在映射文件中使用
<cache type=""/>
,指定要使用的第三方缓存类的全类名,即<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
逆向工程
介绍
MyBatis Generator:简称 MBG,是一个专门为 MyBatis 框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及 bean 类。 支持基本的增删改查,以及 QBC 风格的条件查询。但是表连接、存储过程等这些复杂 sql 的定义需要我们手工编写。
mbg 配置文件
(1)导入 jar 包:mybatis-generator-core-x.x.x.jar。
(2)mbg-config.xml 配置文件。
<?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">
<!--设置数据库连接信息-->
<jdbcConnection
driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&rewriteBatchedStatements=true&allowMultiQueries=true"
userId="root"
password="macheng_0213">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--javaBean的生成策略-->
<javaModelGenerator targetPackage="com.mcc.mbg.pojo" targetProject=".\src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--映射文件的生成策略-->
<sqlMapGenerator targetPackage="com.mcc.mbg.mapper" targetProject=".\conf">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--Mapper接口的生成策略-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.mcc.mbg.mapper" targetProject=".\src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--设置将数据库中的哪张表对应生成哪个JavaBean-->
<table tableName="employee" domainObjectName="Emp" />
<table tableName="department" domainObjectName="Dept" />
</context>
</generatorConfiguration>
(3)测试。
package com.mcc.mbg.generator;
import org.junit.Test;
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;
public class Generator {
@Test
public void generatorXML() throws Exception {
ArrayList<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("./conf/mbg-config.xml");
ConfigurationParser parser = new ConfigurationParser(warnings);
Configuration config = parser.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
生成的 javaBean、映射文件、Mapper接口。
Example、Criteria 条件查询
使用 targetRuntime="MyBatis3Simple"
时,生成的是简单的 sql 语句;使用 targetRuntime="MyBatis3"
时,在 JavaBean 中会额外生成 XxxExample.java 类,该类可以支持带条件的查询操作,即 QBC 风格的条件查询。
QBC 条件查询演示:
package com.mcc.mbg.test;
import com.mcc.mbg.mapper.EmpMapper;
import com.mcc.mbg.pojo.Emp;
import com.mcc.mbg.pojo.EmpExample;
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.Test;
import java.io.InputStream;
import java.util.List;
public class TestGenerator {
@Test
public void testGeneratorXml() throws Exception {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = factory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//创建Example对象,该对象用于创建Criteria对象
EmpExample empExample = new EmpExample();
EmpExample.Criteria c1 = empExample.createCriteria();
//Criteria对象可以拼接一系列sql操作,同一个Criteria对象拼接的sql之间只能是and逻辑
c1.andEnameLike("%人%");
c1.andDepartmentidEqualTo("JS");
//在同一个Example对象下创建第二个Criteria对象
EmpExample.Criteria c2 = empExample.createCriteria();
c2.andAgeEqualTo(21);
//同一个Example对象的不同Criteria对象之间可以进行or操作
empExample.or(c2);//这里参数传递c1或c2均可
List<Emp> emps = mapper.selectByExample(empExample);
for(Emp emp : emps){
System.out.println(emp);
}
}
}
特点:
- 创建 Example 对象,该对象用于创建 Criteria 对象。
- Criteria 对象可以拼接一系列 sql 操作,同一个 Criteria 对象拼接的 sql 之间只能是 and 逻辑。
- 在同一个 Example 对象下创建第二个 Criteria 对象。
- 同一个 Example 对象的不同Criteria对象之间可以进行 or 操作。
PageHelper 分页
配置插件
MyBatis 提供插件:PageHelper,实现分页操作。
步骤:
-
导入 jar 包。
-
在 核心配置文件中配置插件 plugins。注意配置文件各个标签的顺序。
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
PageHelper、PageInfo
PageHelper.startPage(int pageNum, int PageSize)
:设置起始页码,每页显示的信息数目,需要放在查询语句之前才能生效。PageInfo<T>(List list, int navigatePages)
:使用 PageInfo 包装查询后的结果,封装了详细的查询数据,T 为当前页面显示的数据类型。
list:需要分页的数据集合
navigatePages:搜索栏中显示的页码个数(12345),当前页的页码始终在中间
//需要放在查询语句之前才能生效
PageHelper.startPage(3, 2);//设置页面信息
//查询全部员工
List<Emp> empList = mapper.getAllEmps();
for(Emp emp : empList){
System.out.println(emp);//显示在第2页的Emp有哪些
}
//PageInfo<T>(List list, int navigatePages)类:获取当前页面信息,T为当前页面保存的数据类型
//list:查询到的所有信息,navigatePages:搜索栏中显示的页码个数,当前页的页码始终在中间。
PageInfo<Emp> pageInfo = new PageInfo<>(empList, 3);//显示当前页:第2页的分页信息
System.out.println(Arrays.toString(pageInfo.getNavigatepageNums()));
模拟分页
PageUtils.java
package com.mcc.MyBatis.page;
import com.github.pagehelper.PageInfo;
public class PageUtils {
//分页
public static<T> String getPage(PageInfo<T> pageInfo){
StringBuilder stringBuilder = new StringBuilder();
String path = "http://loclalhost:8080/ssm/";
//首页
if(pageInfo.isHasPreviousPage()){
stringBuilder.append("<a href='"+path+"emps/1"+"'>"+"首页"+"</a>");
}
stringBuilder.append(" ");
//上一页
if(pageInfo.isHasPreviousPage()){
stringBuilder.append("<a href='"+path+"emps/"+pageInfo.getPrePage()+"'>"+"上一页"+"</a>");
}
stringBuilder.append(" ");
//页码
int[] nums = pageInfo.getNavigatepageNums();
for (int num : nums) {
if(num==pageInfo.getPageNum()){//获取当前页码
stringBuilder.append("<a href='"+path+"emps/"+num+"' style='color:red;'>"+num+"</a>");
stringBuilder.append(" ");
}else{
stringBuilder.append("<a href='"+path+"emps/"+num+"'>"+num+"</a>");
stringBuilder.append(" ");
}
}
//下一页
if(pageInfo.isHasNextPage()){
stringBuilder.append("<a href='"+path+"emps/"+pageInfo.getNextPage()+"'>"+"下一页"+"</a>");
}
stringBuilder.append(" ");
//尾页
if(pageInfo.isHasNextPage()){
stringBuilder.append("<a href='"+path+"emps/"+pageInfo.getPages()+"'>"+"尾页"+"</a>");
}
//StringBuilder转化为字符串返回
String pageStr = stringBuilder.toString();
return pageStr;
}
}
测试:
package com.mcc.MyBatis.test;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mcc.MyBatis.bean.Emp;
import com.mcc.MyBatis.mapper.EmpMapper;
import com.mcc.MyBatis.page.PageUtils;
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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class TestPages {
@Test
public void testPages() throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession = sqlSessionFactory.openSession(true);
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//设置页面信息
PageHelper.startPage(1, 3);
//查询全部员工
List<Emp> emps = mapper.getAllEmps();
//将全部员工分页显示,栏有5个
PageInfo<Emp> pageInfo = new PageInfo<>(emps, 5);
String page = PageUtils.getPage(pageInfo);
System.out.println(page);
}
}
结果: