1. 什么是缓存 113
缓存:cache
1.1 缓存的作⽤:113
通过减少IO的⽅式,来提⾼程序的执⾏效率。
1.2 mybatis的缓存:113
将select语句的查询结果放到缓存(内存)当中,下⼀次还是这条select语句的话,直接从缓存中取,不再查数据库。⼀⽅⾯是减少了IO。另⼀⽅⾯不再执⾏繁琐的查找算法。效率⼤⼤提升。
1.3 mybatis缓存包括:114
⼀级缓存:将查询到的数据存储到SqlSession中。
⼆级缓存:将查询到的数据存储到SqlSessionFactory中。
或者集成其它第三⽅的缓存:⽐如EhCache【Java语⾔开发的】、Memcache【C语⾔开发的】
等。
缓存只针对于DQL语句,也就是说缓存机制只对应select语句。
2. 一级缓存 113
⼀级缓存默认是开启的。不需要做任何配置。
原理:只要使⽤同⼀个SqlSession对象执⾏同⼀条SQL语句,就会⾛缓存。
<select id="selectById" resultType="Car">
select * from t_car where id = #{id}
</select>
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(43L);
System.out.println(car1);
CarMapper mapper2 = sqlSession.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(43L);
System.out.println(car2);
//sqlSession.commit();
sqlSession.close();
}
2.1 什么情况下不⾛缓存?115
2.1.1 第⼀种: 不同的SqlSession对象。 115
//不走缓存
// 如果要获取不同的SqlSession对象,不能使用以下代码。 115
@Test
public void testSelectById() throws Exception{
//SqlSession sqlSession = SqlSessionUtil.openSession();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(43L);
System.out.println(car1);
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(43L);
System.out.println(car2);
sqlSession1.close();
sqlSession2.close();
}
2.1.2 第⼆种:查询条件变化了。 116
2.1.3 ⼀级缓存失效情况包括两种: 116
第一次DQL和第二次DQL之间你做了以下两件事中的任意一件,都会让一级缓存清空:
1. 执行了sqlSession的clearCache()方法,这是手动清空缓存。
2. 执行了INSERT或DELETE或UPDATE语句。不管你是操作哪张表的,都会清空一级缓存。
第⼀种:第⼀次查询和第⼆次查询之间,⼿动清空了⼀级缓存。
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(43L);
System.out.println(car1);
// 手动清空一级缓存 116A
sqlSession.clearCache();
CarMapper mapper2 = sqlSession.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(43L);
System.out.println(car2);
//sqlSession.commit();
sqlSession.close();
}
第⼆种:第⼀次查询和第⼆次查询之间,执⾏了增删改操作。【这个增删改和哪张表没有关系,只要 有insert delete update操作,⼀级缓存就失效。】
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(43L);
System.out.println(car1);
// 手动清空一级缓存 116A
//sqlSession.clearCache();
// 在这里执行了INSERT DELETE UPDATE中的任意一个语句。并且和表没有关系。116
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
mapper.insertClazz(2000, "高三三班");
CarMapper mapper2 = sqlSession.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(43L);
System.out.println(car2);
sqlSession.commit();
sqlSession.close();
}
3. 二级缓存 117
⼆级缓存的范围是SqlSessionFactory。
3.1 使⽤⼆级缓存需要具备以下⼏个条件:117
1. 全局性地开启或关闭所有映射器配置⽂件中已配置的任何缓存。默认就是true,⽆需设置。
2. 在需要使⽤⼆级缓存的SqlMapper.xml⽂件中添加配置:
3. 使⽤⼆级缓存的实体类对象必须是可序列化的,也就是必须实现java.io.Serializable接⼝
4. SqlSession对象关闭或提交之后,⼀级缓存中的数据才会被写⼊到⼆级缓存当中。此时⼆级缓存才可⽤。
<!--测试二级缓存 117-->
<select id="selectById2" resultType="Car">
select * from t_car where id = #{id}
</select>
//测试二级缓存 117
@Test
public void testSelectById2() throws Exception{
// 这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
// 这行代码执行结束之后,实际上数据是缓存到一级缓存当中了。(sqlSession1是一级缓存。)
Car car1 = mapper1.selectById2(43L);
System.out.println(car1);
// 如果这里不关闭SqlSession1对象的话,二级缓存中还是没有数据的。
// 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中。
sqlSession1.close();
// 这行代码执行结束之后,实际上数据会缓存到一级缓存当中。(sqlSession2是一级缓存。)
Car car2 = mapper2.selectById2(43L);
System.out.println(car2);
// 程序执行到这里的时候,会将sqlSession1这个一级缓存中的数据写入到二级缓存当中。
//sqlSession1.close();
// 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中。
sqlSession2.close();
}
3.2 ⼆级缓存的失效: 118
只要两次查询之间出现了增删改操作。⼆级缓存就会失效。【⼀级缓存也会失效】
3.3 ⼆级缓存的相关配置: 118
1. eviction:指定从缓存中移除某个对象的淘汰算法。默认采⽤LRU策略。
a. LRU:Least Recently Used。最近最少使⽤。优先淘汰在间隔时间内使⽤频率最低的对象。(其实还有⼀种淘汰算法LFU,最不常⽤。)
b. FIFO:First In First Out。⼀种先进先出的数据缓存器。先进⼊⼆级缓存的对象最先被淘汰。
c. SOFT:软引⽤。淘汰软引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
d. WEAK:弱引⽤。淘汰弱引⽤指向的对象。具体算法和JVM的垃圾回收算法有关。
2. flushInterval:
a. ⼆级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存⾜够⼤,⼀直会向⼆级缓存中缓存数据。除⾮执⾏了增删改。
3. readOnly:
a. true:多条相同的sql语句执⾏之后返回的对象是共享的同⼀个。性能好。但是多线程并发可能会存在安全问题。
b. false:多条相同的sql语句执⾏之后返回的对象是副本,调⽤了clone⽅法。性能⼀般。但安全。
4. size:
a. 设置⼆级缓存中最多可存储的java对象数量。默认值1024。
4. MyBatis集成EhCache(集成第三方的缓存机制) 119
集成EhCache是为了代替mybatis⾃带的⼆级缓存。⼀级缓存是⽆法替代的。
mybatis对外提供了接⼝,也可以集成第三⽅的缓存组件。⽐如EhCache、Memcache等。都可以。EhCache是Java写的。Memcache是C语⾔写的。所以mybatis集成EhCache较为常⻅,按照以下步骤操作,就可以完成集成:
第⼀步:引⼊mybatis整合ehcache的依赖。
pom.xml
<!--mybatis集成ehcache的组件-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
第⼆步:在类的根路径下新建echcache.xml⽂件,并提供以下配置信息。
echcache.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"
updateCheck="false">
<!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
<diskStore path="e:/ehcache"/>
<!--defaultCache:默认的管理策略-->
<!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
<!--maxElementsInMemory:在内存中缓存的element的最大数目-->
<!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
<!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-->
<!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问-->
<!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问-->
<!--memoryStoreEvictionPolicy:缓存的3 种清空策略-->
<!--FIFO:first in first out (先进先出)-->
<!--LFU:Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存-->
<!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存-->
<defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
</ehcache>
第三步:修改SqlMapper.xml⽂件中的标签,添加type属性。
CarMapper.xml
<!--集成Ehcache组件-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
第四步:编写测试程序使⽤。
//测试二级缓存 117
@Test
public void testSelectById2() throws Exception{
// 这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
// 这行代码执行结束之后,实际上数据是缓存到一级缓存当中了。(sqlSession1是一级缓存。)
Car car1 = mapper1.selectById2(43L);
System.out.println(car1);
// 如果这里不关闭SqlSession1对象的话,二级缓存中还是没有数据的。
// 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中。
sqlSession1.close();
// 这行代码执行结束之后,实际上数据会缓存到一级缓存当中。(sqlSession2是一级缓存。)
Car car2 = mapper2.selectById2(43L);
System.out.println(car2);
// 程序执行到这里的时候,会将sqlSession1这个一级缓存中的数据写入到二级缓存当中。
//sqlSession1.close();
// 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中。
sqlSession2.close();
}
5. 代码汇总
main中com.powernode.mybatis.mapper
CarMapper
package com.powernode.mybatis.mapper;
import com.powernode.mybatis.pojo.Car;
import org.apache.ibatis.annotations.Param;
//演示缓存机制 115
public interface CarMapper {
/**
* 测试二级缓存 117
* @param id
* @return
*/
Car selectById2(Long id);
/**
* 保存班级信息 116
* @param cid
* @param cname
* @return
*/
int insertClazz(@Param("cid") Integer cid, @Param("cname") String cname);
/**
* 根据id获取Car信息。115
* @param id
* @return
*/
Car selectById(Long id);
}
CarMapper.xml
<?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.powernode.mybatis.mapper.CarMapper">
<!--
默认情况下,二级缓存机制是开启的。
只需要在对应的SqlMapper.xml文件中添加以下标签。用来表示“我”使用该二级缓存。
-->
<!-- <cache/>-->
<!--集成Ehcache组件 119-->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<insert id="insertClazz">
insert into t_clazz values(#{cid},#{cname})
</insert>
<!--测试一级缓存 116-->
<select id="selectById" resultType="Car">
select * from t_car where id = #{id}
</select>
<!--测试二级缓存 117-->
<select id="selectById2" resultType="Car">
select * from t_car where id = #{id}
</select>
</mapper>
test中com.powernode.mybatis.test
CarMapperTest
package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
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;
//研究缓存机制 115
public class CarMapperTest {
//测试二级缓存 117
@Test
public void testSelectById2() throws Exception{
// 这里只有一个SqlSessionFactory对象。二级缓存对应的就是SqlSessionFactory。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
// 这行代码执行结束之后,实际上数据是缓存到一级缓存当中了。(sqlSession1是一级缓存。)
Car car1 = mapper1.selectById2(43L);
System.out.println(car1);
// 如果这里不关闭SqlSession1对象的话,二级缓存中还是没有数据的。
// 如果执行了这行代码,sqlSession1的一级缓存中的数据会放到二级缓存当中。
sqlSession1.close();
// 这行代码执行结束之后,实际上数据会缓存到一级缓存当中。(sqlSession2是一级缓存。)
Car car2 = mapper2.selectById2(43L);
System.out.println(car2);
// 程序执行到这里的时候,会将sqlSession1这个一级缓存中的数据写入到二级缓存当中。
//sqlSession1.close();
// 程序执行到这里的时候,会将sqlSession2这个一级缓存中的数据写入到二级缓存当中。
sqlSession2.close();
}
//不走缓存
// 如果要获取不同的SqlSession对象,不能使用以下代码。 115
/* @Test
public void testSelectById() throws Exception{
//SqlSession sqlSession = SqlSessionUtil.openSession();
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(43L);
System.out.println(car1);
CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(43L);
System.out.println(car2);
sqlSession1.close();
sqlSession2.close();
}*/
// 思考:什么时候不走缓存?
// SqlSession对象不是同一个,肯定不走缓存。
// 查询条件不一样,肯定也不走缓存。
// 思考:什么时候一级缓存失效?
// 第一次DQL和第二次DQL之间你做了以下两件事中的任意一件,都会让一级缓存清空:
// 1. 执行了sqlSession的clearCache()方法,这是手动清空缓存。
// 2. 执行了INSERT或DELETE或UPDATE语句。不管你是操作哪张表的,都会清空一级缓存。
//根据id获取Car信息。115
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper1 = sqlSession.getMapper(CarMapper.class);
Car car1 = mapper1.selectById(43L);
System.out.println(car1);
// 手动清空一级缓存 116A
//sqlSession.clearCache();
// 在这里执行了INSERT DELETE UPDATE中的任意一个语句。并且和表没有关系。116
/*CarMapper mapper = sqlSession.getMapper(CarMapper.class);
mapper.insertClazz(2000, "高三三班");*/
CarMapper mapper2 = sqlSession.getMapper(CarMapper.class);
Car car2 = mapper2.selectById(43L);
System.out.println(car2);
sqlSession.commit();
sqlSession.close();
}
}
com.powernode.mybatis.pojo
Car
package com.powernode.mybatis.pojo;
import java.io.Serializable;
/**
* 封装汽车相关信息的pojo类。普通的java类。
* @author 动力节点
* @version 1.0
* @since 1.0
*/
public class Car implements Serializable {
// 数据库表当中的字段应该和pojo类的属性一一对应。
// 建议使用包装类,这样可以防止null的问题。
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
@Override
public String toString() {
return "Car{" +
"id=" + id +
", carNum='" + carNum + '\'' +
", brand='" + brand + '\'' +
", guidePrice=" + guidePrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarNum() {
return carNum;
}
/*public String getXyz() {
return carNum;
}*/
public void setCarNum(String carNum) {
this.carNum = carNum;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Double getGuidePrice() {
return guidePrice;
}
public void setGuidePrice(Double guidePrice) {
this.guidePrice = guidePrice;
}
public String getProduceTime() {
return produceTime;
}
public void setProduceTime(String produceTime) {
this.produceTime = produceTime;
}
public String getCarType() {
return carType;
}
public void setCarType(String carType) {
this.carType = carType;
}
public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
this.id = id;
this.carNum = carNum;
this.brand = brand;
this.guidePrice = guidePrice;
this.produceTime = produceTime;
this.carType = carType;
}
public Car() {
}
}
mybatis-config.xml
<?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 resource="jdbc.properties"/>
<!--mapUnderscoreToCamelCase开启驼峰命名自动映射-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.powernode.mybatis.pojo"/>
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"/>
<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.powernode.mybatis.mapper"/>
</mappers>
</configuration>
echcache.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"
updateCheck="false">
<!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存-->
<diskStore path="e:/ehcache"/>
<!--defaultCache:默认的管理策略-->
<!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断-->
<!--maxElementsInMemory:在内存中缓存的element的最大数目-->
<!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上-->
<!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false-->
<!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问-->
<!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问-->
<!--memoryStoreEvictionPolicy:缓存的3 种清空策略-->
<!--FIFO:first in first out (先进先出)-->
<!--LFU:Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存-->
<!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存-->
<defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"
timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/>
</ehcache>
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.powernode</groupId>
<artifactId>course22</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<!--mybatis集成ehcache的组件-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
<properties>
<!-- 编译代码使用的jdk版本-->
<maven.compiler.source>1.8</maven.compiler.source>
<!-- 运行程序使用的jdk版本-->
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>
剩余的
utils
logback.xml
jdbc.properties
不做赘述
标签:缓存,Car,sqlSession,mybatis,CarMapper,public From: https://blog.51cto.com/u_15784725/6442694