mybatis基础之二->Mybatis SQL映射文件详解

在之前我们学习了mybatis的全局配置文件,下面我们开始学习mybatis的映射文件,在映射文件中,可以编写以下的顶级元素标签:

cache – 该命名空间的缓存配置。 cache-ref – 引用其它命名空间的缓存配置。 resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。 parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。 sql – 可被其它语句引用的可重用语句块。 insert – 映射插入语句。 update – 映射更新语句。 delete – 映射删除语句。 select – 映射查询语句。

? 在每个顶级元素标签中可以添加很多个属性,下面我们开始详细了解下具体的配置。
1、insert、update、delete元素
属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap 用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
insert into user(user_name) values(#{userName}) select max(id)+1 from userinsert into user(id,user_name) values(#{id},#{userName})

2、select元素
1、select的参数传递
select * from emp where empno=#{empno} and ename=#{ename}select * from emp where empno=#{empno} and ename=#{ename}

2、参数的取值方式 ? 在xml文件中编写sql语句的时候有两种取值的方式,分别是#{}和${},下面来看一下他们之间的区别:
select * from #{t} where empno=${empno} and ename=${ename}

3、处理集合返回结果 EmpDao.xml
select* from empselect * from emp where empno = #{empno}select * from emp

UserDao.java
package com.mashibing.dao; import com.mashibing.bean.Emp; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface EmpDao {public Emp findEmpByEmpno(Integer empno); public int updateEmp(Emp emp); public int deleteEmp(Integer empno); public int insertEmp(Emp emp); Emp selectEmpByNoAndName(@Param("empno") Integer empno, @Param("ename") String ename,@Param("t") String tablename); Emp selectEmpByNoAndName2(Map map); List selectAllEmp(); Map selectEmpByEmpReturnMap(Integer empno); @MapKey("empno") Map getAllEmpReturnMap(); }

4、自定义结果集---resultMap Dog.java
package com.mashibing.bean; public class Dog { private Integer id; private String name; private Integer age; private String gender; public Integer getId() { return id; }public void setId(Integer id) { this.id = id; }public String getName() { return name; }public void setName(String name) { this.name = name; }public Integer getAge() { return age; }public void setAge(Integer age) { this.age = age; }public String getGender() { return gender; }public void setGender(String gender) { this.gender = gender; }@Override public String toString() { return "Dog{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", gender='" + gender + '\'' + '}'; } }

dog.sql
/* Navicat MySQL Data TransferSource Server: node01 Source Server Version : 50729 Source Host: 192.168.85.111:3306 Source Database: demoTarget Server Type: MYSQL Target Server Version : 50729 File Encoding: 65001Date: 2020-03-24 23:54:22 */SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for `dog` -- ---------------------------- DROP TABLE IF EXISTS `dog`; CREATE TABLE `dog` ( `id` int(11) NOT NULL AUTO_INCREMENT, `dname` varchar(255) DEFAULT NULL, `dage` int(11) DEFAULT NULL, `dgender` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of dog -- ---------------------------- INSERT INTO dog VALUES ('1', '大黄', '1', '雄'); INSERT INTO dog VALUES ('2', '二黄', '2', '雌'); INSERT INTO dog VALUES ('3', '三黄', '3', '雄');

DogDao.java
package com.mashibing.dao; import com.mashibing.bean.Dog; public interface DogDao {public Dog selectDogById(Integer id); }

DogDao.xml
select * from dog where id = #{id}

5、联合查询 emp.java
package com.mashibing.bean; import java.util.Date; public class Emp {private Integer empno; private String ename; private String job; private Integer mgr; private Date hiredate; private Double sal; private Double common; private Dept dept; public Emp() { }public Emp(Integer empno, String ename) { this.empno = empno; this.ename = ename; }public Emp(Integer empno, String ename, String job, Integer mgr, Date hiredate, Double sal, Double common, Dept dept) { this.empno = empno; this.ename = ename; this.job = job; this.mgr = mgr; this.hiredate = hiredate; this.sal = sal; this.common = common; this.dept = dept; }public Integer getEmpno() { return empno; }public void setEmpno(Integer empno) { this.empno = empno; }public String getEname() { return ename; }public void setEname(String ename) { this.ename = ename; }public String getJob() { return job; }public void setJob(String job) { this.job = job; }public Integer getMgr() { return mgr; }public void setMgr(Integer mgr) { this.mgr = mgr; }public Date getHiredate() { return hiredate; }public void setHiredate(Date hiredate) { this.hiredate = hiredate; }public Double getSal() { return sal; }public void setSal(Double sal) { this.sal = sal; }public Double getCommon() { return common; }public void setCommon(Double common) { this.common = common; }public Dept getDept() { return dept; }public void setDept(Dept dept) { this.dept = dept; }@Override public String toString() { return "Emp{" + "empno=" + empno + ", ename='" + ename + '\'' + ", job='" + job + '\'' + ", mgr=" + mgr + ", hiredate=" + hiredate + ", sal=" + sal + ", common=" + common + ", dept=" + dept + '}'; } }

Dept.java
package com.mashibing.bean; public class Dept { private Integer deptno; private String dname; private String loc; public Dept() { }public Dept(Integer deptno, String dname, String loc) { this.deptno = deptno; this.dname = dname; this.loc = loc; }public Integer getDeptno() { return deptno; }public void setDeptno(Integer deptno) { this.deptno = deptno; }public String getDname() { return dname; }public void setDname(String dname) { this.dname = dname; }public String getLoc() { return loc; }public void setLoc(String loc) { this.loc = loc; }@Override public String toString() { return "Dept{" + "deptno=" + deptno + ", dname='" + dname + '\'' + ", loc='" + loc + '\'' + '}'; } }

EmpDao.xml
select * from emp left join dept on emp.deptno = dept.deptno where empno = #{empno};

Test
@Test public void test08() {// 获取数据库的会话 SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmpDao mapper = sqlSession.getMapper(EmpDao.class); Emp emp = mapper.selectEmpAndDept(7369); System.out.println(emp); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

6、获取集合元素 Dept.java
package com.mashibing.bean; import java.util.List; public class Dept { private Integer deptno; private String dname; private String loc; private List emps; public Dept() { }public Dept(Integer deptno, String dname, String loc) { this.deptno = deptno; this.dname = dname; this.loc = loc; }public Integer getDeptno() { return deptno; }public void setDeptno(Integer deptno) { this.deptno = deptno; }public String getDname() { return dname; }public void setDname(String dname) { this.dname = dname; }public String getLoc() { return loc; }public void setLoc(String loc) { this.loc = loc; }public List getEmps() { return emps; }public void setEmps(List emps) { this.emps = emps; }@Override public String toString() { return "Dept{" + "deptno=" + deptno + ", dname='" + dname + '\'' + ", loc='" + loc + '\'' + ", emps=" + emps + '}'; } }

DeptDao.java
package com.mashibing.dao; import com.mashibing.bean.Dept; import com.mashibing.bean.Emp; import java.util.List; public interface DeptDao {public Dept getDeptAndEmps(Integer deptno); }

DeptDao.xml
select * from dept left join emp on dept.deptno = emp.deptno where dept.deptno=#{deptno}

Test
@Test public void test09() {// 获取数据库的会话 SqlSession sqlSession = sqlSessionFactory.openSession(); try { DeptDao mapper = sqlSession.getMapper(DeptDao.class); Dept deptAndEmps = mapper.getDeptAndEmps(10); System.out.println(deptAndEmps); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

7、分步查询 ? 在上述逻辑的查询中,是由我们自己来完成sql语句的关联查询的,那么,我们能让mybatis帮我们实现自动的关联查询吗?
关联查询的分步
DeptDao.java
package com.mashibing.dao; import com.mashibing.bean.Dept; import com.mashibing.bean.Emp; import java.util.List; public interface DeptDao {public Dept getDeptAndEmps(Integer deptno); public Dept getDeptAndEmpsBySimple(Integer deptno); }

EmpDao.java
package com.mashibing.dao; import com.mashibing.bean.Emp; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface EmpDao {Emp selectEmpAndDept(Integer empno); Emp selectEmpAndDeptBySimple(Integer empno); }

DeptDao.xml
select * from dept where deptno = #{deptno}

EmpDao.xml
select * from emp where empno = #{empno}

Test
@Test public void test08() {// 获取数据库的会话 SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmpDao mapper = sqlSession.getMapper(EmpDao.class); //Emp emp = mapper.selectEmpAndDept(7369); Emp emp = mapper.selectEmpAndDeptBySimple(7369); System.out.println(emp); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

【mybatis基础之二->Mybatis SQL映射文件详解】集合的分步查询
EmpDao.java
package com.mashibing.dao; import com.mashibing.bean.Emp; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface EmpDao { Emp selectEmpAndDeptBySimple(Integer empno); Emp selectEmpByStep(Integer empno); }

DeptDao.java
package com.mashibing.dao; import com.mashibing.bean.Dept; import com.mashibing.bean.Emp; import java.util.List; public interface DeptDao {public Dept getDeptAndEmps(Integer deptno); public Dept getDeptAndEmpsBySimple(Integer deptno); public Dept getDeptAndEmpsByStep(Integer deptno); }

EmpDao.xml
select * from emp where deptno = #{deptno}

DeptDao.xml
select * from dept where deptno = #{deptno}

Test
@Test public void test09() {// 获取数据库的会话 SqlSession sqlSession = sqlSessionFactory.openSession(); try { DeptDao mapper = sqlSession.getMapper(DeptDao.class); //Dept deptAndEmps = mapper.getDeptAndEmps(10); Dept deptAndEmpsByStep = mapper.getDeptAndEmpsByStep(10); System.out.println(deptAndEmpsByStep); } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

8、延迟查询 ? 当我们在进行表关联的时候,有可能在查询结果的时候不需要关联对象的属性值,那么此时可以通过延迟加载来实现功能。在全局配置文件中添加如下属性
mybatis-config.xml

如果设置了全局加载,但是希望在某一个sql语句查询的时候不适用延时策略,可以添加如下属性:

3、动态sql
? 动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
? 使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
? 如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
1、if EmpDao.xml
select * from emp where empno > #{empno} and ename like #{ename} and sal > #{sal}

EmpDao.java
public List getEmpByCondition(Emp emp);

Test.java
@Test public void test10() {SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmpDao mapper = sqlSession.getMapper(EmpDao.class); Emp emp = new Emp(); emp.setEmpno(6500); emp.setEname("%E%"); emp.setSal(500.0); List empByCondition = mapper.getEmpByCondition(emp); for (Emp emp1 : empByCondition) { System.out.println(emp1); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

? 看起来测试是比较正常的,但是大家需要注意的是如果我们传入的参数值有缺失会怎么呢?这个时候拼接的sql语句就会变得有问题,例如不传参数或者丢失最后一个参数,那么语句中就会多一个where或者and的关键字,因此在mybatis中也给出了具体的解决方案:
? where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
select * from emp empno > #{empno} and ename like #{ename} and sal > #{sal}

? 现在看起来没有什么问题了,但是我们的条件添加到了拼接sql语句的前后,那么我们该如何处理呢?
select * from emp empno > #{empno} and ename like #{ename} and sal > #{sal} and

2、foreach ? 动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
select * from emp where deptno in #{deptno}

3、choose ? 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
select * from emp empno > #{empno} ename like #{ename} sal > #{sal} 1=1

4、set ? 用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
update emp empno=#{empno}, ename = #{ename}, sal = #{sal} empno = #{empno}

4、缓存
? MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
? 默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

当添加上该标签之后,会有如下效果:
  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
在进行配置的时候还会分为一级缓存和二级缓存:
一级缓存:线程级别的缓存,是本地缓存,sqlSession级别的缓存
二级缓存:全局范围的缓存,不止局限于当前会话
1、一级缓存的使用 ? 一级缓存是sqlsession级别的缓存,默认是存在的。在下面的案例中,大家发现我发送了两个相同的请求,但是sql语句仅仅执行了一次,那么就意味着第一次查询的时候已经将结果进行了缓存。
@Test public void test01() {SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmpDao mapper = sqlSession.getMapper(EmpDao.class); List list = mapper.selectAllEmp(); for (Emp emp : list) { System.out.println(emp); } System.out.println("--------------------------------"); List list2 = mapper.selectAllEmp(); for (Emp emp : list2) { System.out.println(emp); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }

? 在大部分的情况下一级缓存是可以的,但是有几种特殊的情况会造成一级缓存失效:
1、一级缓存是sqlSession级别的缓存,如果在应用程序中只有开启了多个sqlsession,那么会造成缓存失效
@Test public void test02(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class); List list = mapper.selectAllEmp(); for (Emp emp : list) { System.out.println(emp); } System.out.println("================================"); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class); List list2 = mapper2.selectAllEmp(); for (Emp emp : list2) { System.out.println(emp); } sqlSession.close(); sqlSession2.close(); }

2、在编写查询的sql语句的时候,一定要注意传递的参数,如果参数不一致,那么也不会缓存结果
3、如果在发送过程中发生了数据的修改,那么结果就不会缓存
@Test public void test03(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class); Emp empByEmpno = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno); System.out.println("================================"); empByEmpno.setEname("zhangsan"); int i = mapper.updateEmp(empByEmpno); System.out.println(i); System.out.println("================================"); Emp empByEmpno1 = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno1); sqlSession.close(); }

4、在两次查询期间,手动去清空缓存,也会让缓存失效
@Test public void test03(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class); Emp empByEmpno = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno); System.out.println("================================"); System.out.println("手动清空缓存"); sqlSession.clearCache(); System.out.println("================================"); Emp empByEmpno1 = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno1); sqlSession.close(); }

2、二级缓存 ? 二级缓存是全局作用域缓存,默认是不开启的,需要手动进行配置。
? Mybatis提供二级缓存的接口以及实现,缓存实现的时候要求实体类实现Serializable接口,二级缓存在sqlSession关闭或提交之后才会生效。
1、缓存的使用 ? 步骤:
? 1、全局配置文件中添加如下配置:

? 2、需要在使用二级缓存的映射文件出使用标签标注
? 3、实体类必须要实现Serializable接口
@Test public void test04(){ SqlSession sqlSession = sqlSessionFactory.openSession(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class); EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class); Emp empByEmpno = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno); sqlSession.close(); Emp empByEmpno1 = mapper2.findEmpByEmpno(1111); System.out.println(empByEmpno1); sqlSession2.close(); }

2、缓存的属性 ? eviction:表示缓存回收策略,默认是LRU
? LRU:最近最少使用的,移除最长时间不被使用的对象
? FIFO:先进先出,按照对象进入缓存的顺序来移除
? SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
? WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
? flushInternal:刷新间隔,单位毫秒
? 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
? size:引用数目,正整数
? 代表缓存最多可以存储多少个对象,太大容易导致内存溢出
? readonly:只读,true/false
? true:只读缓存,会给所有调用这返回缓存对象的相同实例,因此这些对象不能被修改。
? false:读写缓存,会返回缓存对象的拷贝(序列化实现),这种方式比较安全,默认值
//可以看到会去二级缓存中查找数据,而且二级缓存跟一级缓存中不会同时存在数据,因为二级缓存中的数据是在sqlsession 关闭之后才生效的 @Test public void test05(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class); Emp empByEmpno = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class); Emp empByEmpno2 = mapper2.findEmpByEmpno(1111); System.out.println(empByEmpno2); Emp empByEmpno3 = mapper2.findEmpByEmpno(1111); System.out.println(empByEmpno3); sqlSession2.close(); }// 缓存查询的顺序是先查询二级缓存再查询一级缓存 @Test public void test05(){ SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class); Emp empByEmpno = mapper.findEmpByEmpno(1111); System.out.println(empByEmpno); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class); Emp empByEmpno2 = mapper2.findEmpByEmpno(1111); System.out.println(empByEmpno2); Emp empByEmpno3 = mapper2.findEmpByEmpno(1111); System.out.println(empByEmpno3); Emp empByEmpno4 = mapper2.findEmpByEmpno(7369); System.out.println(empByEmpno4); Emp empByEmpno5 = mapper2.findEmpByEmpno(7369); System.out.println(empByEmpno5); sqlSession2.close(); }

3、二级缓存的作用范围:
? 如果设置了全局的二级缓存配置,那么在使用的时候需要注意,在每一个单独的select语句中,可以设置将查询缓存关闭,以完成特殊的设置
? 1、在setting中设置,是配置二级缓存开启,一级缓存默认一直开启

? 2、select标签的useCache属性:
? 在每一个select的查询中可以设置当前查询是否要使用二级缓存,只对二级缓存有效
? 3、sql标签的flushCache属性
? 增删改操作默认值为true,sql执行之后会清空一级缓存和二级缓存,而查询操作默认是false
? 4、sqlSession.clearCache()
? 只是用来清楚一级缓存
3、整合第三方缓存 ? 在某些情况下我们也可以自定义实现缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
? 1、导入对应的maven依赖
org.ehcache ehcache 3.8.1 org.mybatis.caches mybatis-ehcache 1.2.0 org.slf4j slf4j-api 2.0.0-alpha1 org.slf4j slf4j-log4j12 2.0.0-alpha1 test

? 2、导入ehcache配置文件

? 3、在mapper文件中添加自定义缓存

    推荐阅读