11【MyBatis的缓存机制】

package com.dfbz.dao;

import com.dfbz.entity.Emp;

/**
* @author lscl
* @version 1.0
* @intro:
*/
public interface EmpDao {

Emp findById(Integer id);

void save(Emp emp);

}


* EmpDao.xml:



<?xml version="1.0" encoding="UTF-8"?>
<select id="findById" resultType="emp">
    select * from emp where id=#{id}
</select>

<insert id="save">
    insert into emp(id,name) values(null,#{name});
</insert>

* 测试类:



/**
* 测试一级缓存
*/
@Test
public void test1() {

// 获取session(开启一级缓存)
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 发送SQL查询数据,将数据存入一级缓存
Emp emp = mapper.findById(1);
System.out.println(emp);

// 从一级缓存中获取数据(没有发送SQL)
Emp emp2 = mapper.findById(1);
System.out.println(emp2);
System.out.println(emp == emp2);            // true

// session关闭,一级缓存清空
session.close();

}


观察日志:


![在这里插入图片描述](https://img-blog.csdnimg.cn/18a9f9b303d64c859a403eca6e9b9e9d.png#pic_center)


#### 2.2.3 一级缓存清空


一级缓存在如下情况下,会情况:


* 1)将一级缓存的作用域设置为语句级别(`localCacheScope`设置为`STATEMENT`)
* 2)清空缓存(clearCache)
* 3)执行任何增删改操作都会导致**整个**一级缓存
* 4)刷新缓存(flushCache)
* 5)session关闭,一级缓存清空


##### 2.2.3.1 设置localCacheScope



<!--

更改一级缓存的作用域
SESSION: 会话级别缓存,默认值
STATEMENT: 语句级别缓存,缓存只对当前执行的语句生效(类似于关闭一级缓存)
–>


* 再次执行上述的测试代码:



/**
* 测试一级缓存
*/
@Test
public void test1() {

// 获取session(开启一级缓存)
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 发送SQL查询数据
Emp emp = mapper.findById(1);
System.out.println(emp);

// 发送SQL查询数据
Emp emp2 = mapper.findById(1);
System.out.println(emp2);
System.out.println(emp == emp2);            // false

// session关闭
session.close();

}


执行程序,观察控制台:


![在这里插入图片描述](https://img-blog.csdnimg.cn/9a6205ecb3d74c5b8f9a8f5ca91d2ddb.png#pic_center)


##### 2.2.3.2 clearCache


一级缓存清空测试:



@Test
public void test2() throws Exception { // 测试clearCache

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 首先从一级缓存里面查询,没查询到,然后去数据库查询,将查询的结果放入一级缓存
Emp emp = mapper.findById(1);
System.out.println(emp);

session.clearCache();           // 清空一级缓存

// 首先从一级缓存里面查询(没有),去数据库查询,将查询的结果放入一级缓存
Emp emp2 = mapper.findById(1);
System.out.println(emp2);

System.out.println(emp == emp2);        // false

// 关闭session(一级缓存清空)
session.close();

}


执行结果,查看控制台:


![在这里插入图片描述](https://img-blog.csdnimg.cn/ecf4bb325d8c4b0cb83e3667974365f7.png#pic_center)


##### 2.2.3.3 关闭session清空缓存


一级缓存是基于session级别的,如果session关闭,那么一级缓存将会失效!


* 测试代码:



@Test
public void test3() throws Exception { // 测试session关闭清空一级缓存

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 将查询的结果放入一级缓存
Emp emp = mapper.findById(1);
System.out.println(emp);

// session关闭一级缓存清空(session关闭了代表与数据库的会话一级结束了,不可以再发送任何的SQL语句了)
session.close();

// 重新获取一次会话
SqlSession session2 = factory.openSession();
EmpDao mapper2 = session2.getMapper(EmpDao.class);

// 新的session的一级缓存中并没有数据,发送SQL去数据库查询
Emp emp2 = mapper2.findById(1);
System.out.println(emp2);
System.out.println(emp == emp2);

// session关闭,一级缓存清空
session2.close();

}


执行程序,查看控制台:


![在这里插入图片描述](https://img-blog.csdnimg.cn/73aa4216024949ddaf7fd33d59de317a.png#pic_center)


##### 2.2.3.4 执行任何的增删改清空缓存


在MyBatis中,执行任何的增删改都会导致一级缓存清空!


* 测试代码:



@Test
public void test4() throws Exception { // 测试增删改清空缓存

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 将查询结果放入一级缓存
Emp emp = mapper.findById(1);
System.out.println(emp);

Emp saveEmp = new Emp();
saveEmp.setName("test");

mapper.save(saveEmp);           // 任何的增删改都会导致缓存失效

// 首先从一级缓存里面查询(没有),去数据库查询,将查询的结果放入一级缓存
Emp emp2 = mapper.findById(1);
System.out.println(emp2);

System.out.println(emp == emp2);        // false

// 关闭session(一级缓存清空)
session.close();

}


![在这里插入图片描述](https://img-blog.csdnimg.cn/ab7ce4493d8f463cba1051fa433e69ff.png#pic_center)


##### 2.2.3.5 flushCache清空缓存


flushCache属性用于控制执行完操作后是否要刷新缓存,对于增删改语句,**flushCache的值默认是true**,对于查询语句,flushCache的值默认是false,**但是MyBatis不支持将任何的增删改语句设置为false**;


* EmpDao.xml:



<?xml version="1.0" encoding="UTF-8"?>
<!--

flushCache=“true”:每次执行完这个查询语句都清空一级缓存
–>

select * from emp where id=#{id}

<!--

flushCache: 执行完本次SQL语句是否要刷新缓存
insert/update/delete:默认为true,对于增删改的操作,MyBatis不支持将其设置为false
select:默认为false
–>

insert into emp values(null,#{name},#{age},#{addr},#{salary},null)



> 
> Tips:在MyBatis中,不支持将任何的增删改语句的flushCache属性设置为false
> 
> 
> 


* 测试代码:



@Test
public void test5() throws Exception { // 测试flushCache清空缓存(对于增删改操作无法设置为false)

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

Emp emp = mapper.findById(1);
System.out.println(emp);

Emp emp2 = mapper.findById(1);
System.out.println(emp2);

System.out.println(emp == emp2);

// 关闭session(一级缓存清空)
session.close();

}


执行程序,观察控制台:


![在这里插入图片描述](https://img-blog.csdnimg.cn/5d7f5bfbde764f96b082fe6d92945790.png#pic_center)


**需要注意的是MyBatis并不支持将任何的增删改语句的flushCache设置为false**


* 测试代码(此时save语句的flushCache为false):



/**
* 测试增删改的flushCache属性
*/
@Test
public void test6() {

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao empDao = session.getMapper(EmpDao.class);

// 将查询到的结果存入一级缓存缓存(发送一次SQL)
Emp emp = empDao.findById(1);       // flushCache="true",因此会清空一级缓存
System.out.println(emp);

// 进行增删改操作(一级缓存清空,即使设置了flushCache=false也不管用)
empDao.save(new Emp(null, "test", 20, null, null, null));

// 重新发送SQL语句
Emp emp2 = empDao.findById(1);
System.out.println(emp2);

// session关闭,一级缓存清空
session.close();

}


![在这里插入图片描述](https://img-blog.csdnimg.cn/a788a03e698b45d09d4f2ac3a870d7fc.png#pic_center)


#### 2.2.4 一级缓存的引用


MyBatis将数据存入一级缓存时,是将对象的引用(内存地址)存入一级缓存;在获取一级缓存中的数据时,MyBatis将返回当初存入一级缓存的那个内存地址值,也就是说,一级缓存中的数据是同一个;这样一来就会出现内存地址值引用问题;


* 测试代码:



// 缓存的引用
@Test
public void test7() throws Exception {

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 去数据库查询,将查询结果存入一级缓存
Emp emp = mapper.findById(1);
System.out.println(emp);                // name=张三

// 修改emp对象的name为abc(一级缓存中的emp也会修改)
emp.setName("abc");

// 从一级缓存中查询emp对象
Emp emp2 = mapper.findById(1);
System.out.println(emp2);               // name=abc

System.out.println(emp == emp2);

// 关闭session(一级缓存清空)
session.close();

}


#### 2.2.5 PerpetualCache缓存类


MyBatis 跟缓存相关的类都在cache 包里面,其中有一个Cache 接口,只有一个默认的实现类 PerpetualCache,改类是MyBatis的缓存实现类;包括一级缓存和二级缓存都是采用PerpetualCache类来实现的;


![在这里插入图片描述](https://img-blog.csdnimg.cn/22df3b5e210d48b48e544791d8dac167.png#pic_center)


* 测试代码:



@Test
public void test1() throws Exception {
// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

/\*

首先从一级缓存里面查询
查询到了: 返回
没查询到: 去数据库查询,之后将查询的结果放入一级缓存
*/
Emp emp = mapper.findById(1);
System.out.println(emp);

/\*

从一级缓存里面查询,直接返回
*/
Emp emp2 = mapper.findById(1);
System.out.println(emp2);

System.out.println(emp == emp2);        // true

// 关闭session(一级缓存清空)
session.close();

}


一级缓存执行流程:


![在这里插入图片描述](https://img-blog.csdnimg.cn/1a0bd7dd885a4140922cc4ed0bca70f0.png#pic_center)


### 2.3 二级缓存


我们刚刚学习完了一级缓存,一级缓存是session级别的缓存,不同的session一级缓存是不能共享的;


**二级缓存是mapper级别的缓存**,多个session去操作同一个Mapper映射的SQL语句时,多个session可以共用二级缓存,二级缓存是跨SqlSession 的。


**当二级缓存和一级缓存同时存在时,先查询二级缓存,再查询一级缓存;**


![在这里插入图片描述](https://img-blog.csdnimg.cn/b4933a8ce8f645ecaf8c107c26746983.png#pic_center)


#### 2.3.1 二级缓存相关配置


* 全局配置:


MyBatis默认是开启二级缓存的,我可以通过`cacheEnabled`参数来控制二级缓存的开关;**此配置默认开启**


* 修改SqlMapConfig配置文件:



<!--开启二级缓存,默认是开启状态,false为关闭二级缓存-->
<setting name="cacheEnabled" value="true"/>

* Mapper配置以及SQL语句配置:


**在MyBatis核心配置文件中开启二级缓存后(此配置默认开启状态),还需要在对应的mapper.xml文件中激活缓存;否则二级缓存并不会生效;**



<?xml version="1.0" encoding="UTF-8"?>
  <!--

useCache: 当一级缓存关闭时,是否将本次SQL语句的结果集存入二级缓存
true: 存入(默认值)
false: 不存入
–>

select * from emp where id=#{id}

<insert id="save" >
    insert into emp(id,name) values(null,#{name});
</insert>

**存入二级缓存中的对象必须实现序列化接口:**


![在这里插入图片描述](https://img-blog.csdnimg.cn/f86b779b62ea4ddf80532de36f2e3a73.png#pic_center)


#### 2.3.2 二级缓存测试


##### 2.3.2.1 二级缓存代码测试


**当二级缓存和一级缓存同时存在时,先查询二级缓存,再查询一级缓存;**


当session关闭时,将一级缓存中的数据写入二级缓存;



@Test
public void test1() throws Exception { // 测试二级缓存

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

/*
先从二级缓存里面查询–>再查询一级缓存–>再查询数据库(查询到了把结果放入一级缓存)
*/
Emp emp = mapper.findById(1);
System.out.println(emp);

// 关闭一级缓存,把数据写入二级缓存(这个时候才会把数据写入二级缓存(序列化))
session.close();

// session.clearCache(); // session关闭才会将一级缓存的内容写入二级缓存,clearCache并不会

// 开启一级缓存
SqlSession session2 = factory.openSession();

EmpDao mapper2 = session2.getMapper(EmpDao.class);

// 先查询二级缓存(有),反序列化
Emp emp2 = mapper2.findById(1);
System.out.println(emp2);
System.out.println(emp == emp2);            // false ,两个对象的属性值一样,但是内存地址值不一样(反序列化出来的)

session2.close();

}


观察日志:


![在这里插入图片描述](https://img-blog.csdnimg.cn/a0998f290f17482585f3368dd77b934b.png#pic_center)


##### 2.3.2.2 useCache参数


useCache可以控制当前SQL语句的结果集是否要存入二级缓存;默认情况下为true



select * from emp where id=#{id}


> 
> Tips:
> 
> 
> * 1)useCache只能控制二级缓存,并不会影响一级缓存;
> * 2)useCache需要当前的mapper.xml开启二级缓存功能后才能使用;
> 
> 
> 


#### 2.3.3 二级缓存失效情况


二级缓存在如下情况下,会情况:


* 1)执行任何**增删改**操作
* 2)刷新缓存(flushCache)


##### 2.3.3.1 执行增删改


**执行任何的增删改操作,不仅会导致一级缓存清空,也会导致二级缓存清空!**


* 测试代码:



/**
* 测试任何增删改清空二级缓存
*/
@Test
public void test2() throws Exception {

// 开启一级缓存
SqlSession session = factory.openSession(true);

EmpDao mapper = session.getMapper(EmpDao.class);

// 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),将查询到的结果集存入一级缓存
Emp emp = mapper.findById(1);
System.out.println(emp);

// 关闭一级缓存,把数据写入二级缓存(这个时候才会把数据写入二级缓存(序列化))
session.close();

// 开启一级缓存
SqlSession session2 = factory.openSession(true);

EmpDao mapper2 = session2.getMapper(EmpDao.class);

Emp saveEmp = new Emp();
saveEmp.setName("aaa");

mapper2.save(saveEmp);                  // 执行任何的增删改清空所有缓存(一级缓存和二级缓存)

// 查询二级缓存(没有),一级缓存(没有),发送SQL
Emp emp2 = mapper2.findById(1);
System.out.println(emp2);

session2.close();

}


执行程序,日志如下:



Created connection 368342628.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
> Preparing: select * from dept where id=? # 第一次发送SQL
> Parameters: 1(Integer)
<
Columns: id, name, location
<
Row: 1, 研发部, 中国台湾
<== Total: 1
研发部
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664] # 关闭session,将数据写入二级缓存
Returned connection 368342628 to pool.
Opening JDBC Connection
Checked out connection 368342628 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
> Preparing: insert into dept values(null,?,?) # 执行任何增删改清空一级/二级缓存
> Parameters: test(String), test(String)
<
Updates: 1
Cache Hit Ratio [com.dfbz.dao.DeptDao]: 0.5
> Preparing: select * from dept where id=? # 再次发送SQL查询
> Parameters: 1(Integer)
<
Columns: id, name, location
<
Row: 1, 研发部, 中国台湾
<
Total: 1
false # 返回false
Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15f47664]
Returned connection 368342628 to pool.
Disconnected from the target VM, address: ‘127.0.0.1:58814’, transport: ‘socket’

Process finished with exit code 0


##### 2.3.3.2 flushCache


flushCache不仅会清空一级缓存,而且还会清空二级缓存


![在这里插入图片描述](https://img-blog.csdnimg.cn/81488e6613ca4582bca762e5a3bc4971.png#pic_center)



> 
> Tips:flushCache控制二级缓存时可以设置任何的**增删改查**是否清空二级缓存
> 
> 
> 


* 测试代码:



/*
测试flushCache
*/
@Test
public void test4() throws Exception {

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),之后flushCache,一级缓存数据清空
Emp emp = mapper.findById(1);
System.out.println(emp);

// 一级缓存关闭,将一级缓存的数据写入二级缓存(此时一级缓存并没有数据)
session.close();

// 获取session,开启新的一级缓存
SqlSession session2 = factory.openSession();

EmpDao mapper2 = session2.getMapper(EmpDao.class);

// 此时二级缓存,一级缓存均没有数据,最终去数据库查询
Emp emp2 = mapper2.findById(1);
System.out.println(emp2);

session2.close();

}


执行程序,日志如下:



Created connection 527829831.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
> Preparing: select * from emp where id=? # 发送SQL语句查询,并没有将结果存入一级缓存(flushCache)
> Parameters: 1(Integer)
<
Columns: id, name, age, addr, salary, dept_id
<
Row: 1, 张三, 20, 广西来宾, 7600.00, 1
<== Total: 1
Emp(id=1, name=张三, age=20, addr=广西来宾, salary=7600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

sesison关闭,将一级缓存的数据写入二级缓存(此时一级缓存中并没有数据)

Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.5
Opening JDBC Connection # 重新开启一个session
Checked out connection 527829831 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

先查询二级缓存(没查询到),再查询一级缓存(没查询到),最终去查询数据库

> Preparing: select * from emp where id=?
> Parameters: 1(Integer)
<
Columns: id, name, age, addr, salary, dept_id
<
Row: 1, 张三, 20, 广西来宾, 7600.00, 1
<== Total: 1
Emp(id=1, name=张三, age=20, addr=广西来宾, salary=7600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Disconnected from the target VM, address: ‘127.0.0.1:52082’, transport: ‘socket’

Process finished with exit code 0


**flushCache不仅会清空一级缓存,而且也会清空二级缓存**


* 扩展一个方法:



Emp findByName(String name);


* EmpDao.xml:



select * from emp where name=#{name}

![在这里插入图片描述](https://img-blog.csdnimg.cn/8855e65cdcce49edb2869ca02e9d9e24.png#pic_center)


* 测试代码:



@Test
public void test5() throws Exception {

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),将查询到的结果集存入一级缓存
Emp emp = mapper.findByName("小明");
System.out.println(emp);

// 一级缓存关闭,将一级缓存的数据写入二级缓存
session.close();

// 获取session,开启新的一级缓存
SqlSession session2 = factory.openSession();

EmpDao mapper2 = session2.getMapper(EmpDao.class);

// flushCache清空一级缓存(会清空二级缓存)
Emp emp2 = mapper2.findById(1);
System.out.println(emp2);

// 去二级缓存查询,查询不到,从一级缓存查询,查询不到,发送SQL语句
Emp emp3 = mapper2.findByName("小明");
System.out.println(emp3);

session2.close();

}


执行程序,日志如下:



Created connection 527829831.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

将数据写入一级缓存

> Preparing: select * from emp where name=?
> Parameters: 小明(String)
<
Columns: id, name, age, addr, salary, dept_id
<
Row: 3, 小明, 25, 广东云浮, 6600.00, 2
<== Total: 1
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

连接关闭,将数据写入二级缓存

Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.0
Opening JDBC Connection
Checked out connection 527829831 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]

执行findById(flushCache清空一级缓存和二级缓存)

> Preparing: select * from emp where id=?
> Parameters: 1(Integer)
<
Columns: id, name, age, addr, salary, dept_id
<
Row: 1, 张三, 20, 广西来宾, 7600.00, 1
<== Total: 1
Emp(id=1, name=张三, age=20, addr=广西来宾, salary=7600.0, deptId=null)
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.3333333333333333

再次发送SQL去数据库查询

> Preparing: select * from emp where name=?
> Parameters: 小明(String)
<
Columns: id, name, age, addr, salary, dept_id
<
Row: 3, 小明, 25, 广东云浮, 6600.00, 2
<== Total: 1
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Disconnected from the target VM, address: ‘127.0.0.1:52207’, transport: ‘socket’

Process finished with exit code 0


**前面我们学习一级缓存的时候,flushCache对于增删改语句是无法设置为false(设置了不生效),即执行任何增删改的时候一定会清空一级缓存,但flushCache却可以控制二级缓存的增删改;**


将insert语句的flushCache设置为false(不清空缓存):


![在这里插入图片描述](https://img-blog.csdnimg.cn/b959552afd704e29a8670137a2feac23.png#pic_center)


* 测试代码:



@Test
public void test6() throws Exception {

// 开启一级缓存
SqlSession session = factory.openSession();

EmpDao mapper = session.getMapper(EmpDao.class);

// 先从二级缓存里面查询,再从一级缓存里面查询,再从数据库查询(发送SQL),将查询到的结果集存入一级缓存
Emp emp = mapper.findByName("小明");
System.out.println(emp);

// 一级缓存关闭,将一级缓存的数据写入二级缓存
session.close();

// 获取session,开启新的一级缓存
SqlSession session2 = factory.openSession();

EmpDao mapper2 = session2.getMapper(EmpDao.class);

// 执行新增(flushCache为false,并不会清空二级缓存)
mapper2.save(new Emp(null,"xxx",null,null,null,null));

// 先查询二级缓存(查询到了)
Emp emp2 = mapper2.findByName("小明");
System.out.println(emp2);

session2.close();

}


日志如下:



Created connection 527829831.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
> Preparing: select * from emp where name=?
> Parameters: 小明(String)
<
Columns: id, name, age, addr, salary, dept_id
<
Row: 3, 小明, 25, 广东云浮, 6600.00, 2
<== Total: 1
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Opening JDBC Connection
Checked out connection 527829831 from pool.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
==> Preparing: insert into emp(id,name) values(null,?);
> Parameters: xxx(String)
<
Updates: 1
Cache Hit Ratio [com.dfbz.dao.EmpDao]: 0.5
Emp(id=3, name=小明, age=25, addr=广东云浮, salary=6600.0, deptId=null)
Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@1f760b47]
Returned connection 527829831 to pool.
Disconnected from the target VM, address: ‘127.0.0.1:52373’, transport: ‘socket’

Process finished with exit code 0


#### 2.3.4 PerpetualCache的二级缓存



## 写在最后

**在结束之际,我想重申的是,学习并非如攀登险峻高峰,而是如滴水穿石般的持久累积。尤其当我们步入工作岗位之后,持之以恒的学习变得愈发不易,如同在茫茫大海中独自划舟,稍有松懈便可能被巨浪吞噬。然而,对于我们程序员而言,学习是生存之本,是我们在激烈市场竞争中立于不败之地的关键。一旦停止学习,我们便如同逆水行舟,不进则退,终将被时代的洪流所淘汰。因此,不断汲取新知识,不仅是对自己的提升,更是对自己的一份珍贵投资。让我们不断磨砺自己,与时代共同进步,书写属于我们的辉煌篇章。**


需要完整版PDF学习资源私我



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值