声明:本文纯属个人兴趣探究,其中一些概念、理论以及结论并不一定正确,如果有错误,请包涵并告知。
Mybatis作为持久层框架,要提升性能必然离不开缓存,Mybatis为我们提供了两级缓存,称为一级缓存和二级缓存,另外Mybatis还支持自定义缓存,本文仅探究一级缓存和二级缓存。
一级缓存和二级缓存是分别作用于不同的工作域,要了解缓存就需要明白Mybatis工作时基本的实例。
SqlSession:从字面可以看出,这个对象负责一次与数据库的会话,会话结束后即会被销毁,然后数据会进行提交或回滚,可以理解为它代表着一次事务。
SqlSessionFactory:顾名思义,就是生产SqlSession的工厂,所有的SqlSession都从这里获得,一个数据库连接对应着一个SqlSessionFactory,所以如果系统中需要同时连接多个数据库,那么就需要保存多个SqlSessionFactory的实例。
Mapper:Mapper对应的是一个SQL映射文件,包含增删改查等具体操作,Mybatis的二级缓存便是对应一个个的Mapper。
再来看Mybatis的缓存。
一级缓存是SqlSession级别的,作用在一个SqlSession内,即在一个SqlSession内,重复提交完全一样的sql,不会再重复执行,而是从缓存中去获取,SqlSession关闭后对应于这个SqlSession的缓存就会被清理,一级缓存默认开启。
我们通过一些简单的测试代码来了解Mybatis的一级缓存。
1、执行两次相同的查询,观察SQL执行情况
测试代码:
public Map queryCustomer(Integer customerId) throws IOException{
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
//第一次查询
System.out.println("-----------queryCustomer1-----------");
Map customer1 = testMapper.queryCustomer(customerId);
System.out.println("-----------queryCustomer2-----------");
//第二次查询,执行完全相同查询语句,来测试是否会从缓存中获取数据
Map customer2 = testMapper.queryCustomer(customerId);
return customer2;
} finally {
sqlSession.close();
}
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------queryCustomer1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 188576144.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
-----------queryCustomer2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.658 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:可以看到,再次执行相同查询语句时,Mybatis就从一级缓存中去获取,不会重复执行SQL。
2、在两次完全相同的查询中插入一个修改的操作,查询与修改是同一张数据表。
测试代码:
public Map queryCustomer(Integer customerId) throws IOException{
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
//第一次查询
System.out.println("-----------queryCustomer1-----------");
Map customer1 = testMapper.queryCustomer(customerId);
System.out.println("-----------updateCustomer-----------");
Map map = new HashMap<>();
map.put("customerId", customerId);
map.put("birthday", "1992-09-01");
//进行一次update操作,select和update是同一张数据表
testMapper.updateCustomerBirthday(map);
System.out.println("-----------queryCustomer2-----------");
//第二次查询
Map customer2 = testMapper.queryCustomer(customerId);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.close();
}
return null;
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------queryCustomer1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 188576144.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
-----------updateCustomer-----------
==> Preparing: update tb_customer_info t set t.customerbirthday = ? where t.customerid = ?
==> Parameters: 1992-09-01(String), 269(Integer)
<== Updates: 1
-----------queryCustomer2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1992-09-01, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Rolling back JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.669 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:在两次完全相同的查询操作中插入一次update操作,update和select操作均为同一张数据表,在update操作后Mybatis就会将该SqlSession的一级缓存清理,所以在第二次执行相同的SQL时没有在一级缓存中找到结果,再次执行了SQL。
3、在两次完全相同的查询中插入一个修改的操作,查询与修改不是同一张数据表。
测试代码:
public Map queryCustomer(Integer customerId) throws IOException{
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
System.out.println("-----------queryCustomer1-----------");
//第一次查询
Map customer1 = testMapper.queryCustomer(customerId);
System.out.println("-----------updateCustomer-----------");
Map map = new HashMap<>();
map.put("zoneCode", 1);
map.put("zoneName", "区域1");
//进行一次update操作,select和update不是同一张数据表
testMapper.updateZoneName(map);
System.out.println("-----------queryCustomer2-----------");
//第二次查询
Map customer2 = testMapper.queryCustomer(customerId);
sqlSession.commit();
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.close();
}
return null;
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------queryCustomer1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 188576144.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
-----------updateZone-----------
==> Preparing: update tb_initzone t set t.zone_name = ? where t.zone_code = ?
==> Parameters: 区域1(String), 1(Integer)
<== Updates: 1
-----------queryCustomer2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Committing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.643 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:在两次完全相同的查询操作中插入一次update操作,update和select操作不为同一张数据表,在update操作后Mybatis仍然会将该SqlSession的一级缓存清理,也就是在SqlSession中只要涉及到数据改动,Mybatis就会将其一级缓存清理。
二级缓存是Mapper级别的,每一个Mapper(对应于一份SQL映射文件)都有自己的一份二级缓存,并且是所有SqlSession共享的,不会随着数据库会话的关闭而被清理,启用二级缓存后,在一定的时间内,全局的SqlSession使用该Mapper执行相同的查询操作会优先从该Mapper的二级缓存中查找获取,该Mapper执行增删改操作后会将其二级缓存清空。二级缓存默认关闭,需要手动在配置文件中开启,Mybatis为二级缓存提供了一些配置项,可以针对实际需求自行配置。
开启二级缓存
1、Mybatis配置文件settings节点下设置cacheEnabled,开启二级缓存
<setting name="cacheEnabled" value="true" />
2、SQL映射文件中增加cache节点,声明此映射文件中配置的sql需要使用二级缓存
<mapper namespace="com.study.mybatis.mapper.TestMapper">
<cache/>
mapper>
通过以上两步即可开启使用Mybatis的二级缓存。
我们通过一些简单的程序来测试Mybatis的二级缓存。
1、用两个SqlSession先后执行相同的查询语句,观察是否会从缓存中获取数据
业务层代码:
public Map query1(Integer customerId) throws IOException{
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
System.out.println("-----------query1-----------");
return testMapper.queryCustomer(customerId);
} finally {
sqlSession.close();
}
}
public Map query2(Integer customerId) throws IOException{
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
System.out.println("-----------query2-----------");
return testMapper.queryCustomer(customerId);
} finally {
sqlSession.close();
}
}
测试代码:
@Test
public void testSecondCache() {
TestService service = new TestService();
try {
//第一次查询
Map list1 = service.query1(269);
//第二次查询
Map list2 = service.query2(269);
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------query1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 188576144.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
-----------query2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.5
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.919 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:在query1时Mybatis执行了SQL,而query2时没有执行SQL,而是从缓存中获取到了数据,说明二级缓存已经成功开启。
2、在两次查询中增加一次修改操作,观察query2是否会再次执行SQL,修改的表与查询的表不是同一张数据表
业务层代码:
public int update1() throws IOException {
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
TestMapper testMapper = sqlSession.getMapper(TestMapper.class);
System.out.println("-----------update-----------");
Map map = new HashMap<>();
map.put("zoneCode", 1);
map.put("zoneName", "区域1");
int line = testMapper.updateZoneName(map);
sqlSession.commit();
return line;
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.close();
}
return 0;
}
/*query1与query2无变动*/
测试代码:
@Test
public void testSecondCache() {
TestService service = new TestService();
try {
Map list1 = service.query1(269);
int line = service.update1();
Map list2 = service.query2(269);
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------query1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 188576144.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
-----------update1-----------
Opening JDBC Connection
Checked out connection 188576144 from pool.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: update tb_initzone t set t.zone_name = ? where t.zone_code = ?
==> Parameters: 区域1(String), 1(Integer)
<== Updates: 1
Committing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
-----------query2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Checked out connection 188576144 from pool.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@b3d7190]
Returned connection 188576144 to pool.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.685 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:在执行了update操作后,query2再次执行了SQL,说明修改操作触发Mybatis清理二级缓存,即使修改与查询的不是同一张表。
3、在Mapper1中查询,然后在Mapper2中进行修改操作,然后再用Mapper执行相同查询,观察query2时是否会再次执行SQL,修改与查询的不是同一张表
业务层代码:
public int update2() throws IOException {
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
//注意,此处获取的是Test2Mapper的实例
Test2Mapper test2Mapper = sqlSession.getMapper(Test2Mapper.class);
System.out.println("-----------update2-----------");
Map map = new HashMap<>();
map.put("zoneCode", 1);
map.put("zoneName", "区域1");
int line = test2Mapper.updateZoneName(map);
sqlSession.commit();
return line;
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.close();
}
return 0;
}
/*query1与query2无变动*/
测试代码:
@Test
public void testSecondCache() {
TestService service = new TestService();
try {
Map list1 = service.query1(269);
int line = service.update2();
Map list2 = service.query2(269);
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------query1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 1873859565.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Returned connection 1873859565 to pool.
-----------update2-----------
Opening JDBC Connection
Checked out connection 1873859565 from pool.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
==> Preparing: update tb_initzone t set t.zone_name = ? where t.zone_code = ?
==> Parameters: 区域1(String), 1(Integer)
<== Updates: 1
Committing JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Returned connection 1873859565 to pool.
-----------query2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.5
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.653 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:从执行的结果来看,query1后执行update2,再执行query2,query2仍然是从缓存中在获取数据,TestMapper的缓存并没有因为Test2Mapper中的update2而被清理。
4、在Mapper1中查询,然后在Mapper2中进行修改操作,然后再用Mapper执行相同查询,观察query2时是否会再次执行SQL,修改与查询的是同一张表
业务层代码:
public int update3() throws IOException {
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
//注意,此处获取的是Test2Mapper的实例
Test2Mapper test2Mapper = sqlSession.getMapper(Test2Mapper.class);
System.out.println("-----------update3-----------");
Map map = new HashMap<>();
map.put("customerid", 269);
map.put("birthday", "1993-09-08");
int line = test2Mapper.updateCustomerBirthday(map);
sqlSession.commit();
return line;
} catch (Exception e) {
sqlSession.rollback();
} finally {
sqlSession.close();
}
return 0;
}
/*query1与query2无变动*/
测试代码:
@Test
public void testSecondCache() {
TestService service = new TestService();
try {
Map list1 = service.query1(269);
int line = service.update3();
Map list2 = service.query2(269);
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
-----------query1-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.0
Opening JDBC Connection
Created connection 1873859565.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
==> Preparing: select * from tb_customer_info t where t.customerid = ?
==> Parameters: 269(Integer)
<== Columns: CUSTOMERID, CUSTOMERNAME, CUSTOMERCERTTYPE, CUSTOMERCERTNO, CUSTOMERTEL, CUSTOMERMOBILE, CUSTOMERBIRTHDAY, CUSTOMEREMAIL, CUSTOMERADDR, CUSTOMERDEPT, CUSTOMERSEX, CUSTOMERNATION, CUSTOMERBIRTHPLACE, CUSTOMEREDUCATION, CUSTOMERINDUSTRYNO, CUSTOMER_PTO, CUSTOMER_TYPE, CUSTOMER_STATE, CARDTYPEID, INIT_MONEY, FISBIND, FBANKNO, QINS, HOUSE_ID
<== Row: 269, 黄锦华, 13_000001, null, null, null, 1900-01-0, null, null, 00010015, 19_000002, null, null, null, 6000252, null, null, 1, 16, 0, 0, null, null, null
<== Total: 1
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Returned connection 1873859565 to pool.
-----------update3-----------
Opening JDBC Connection
Checked out connection 1873859565 from pool.
Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
==> Preparing: update tb_customer_info t set t.customerbirthday = ? where t.customerid = ?
==> Parameters: 1993-09-08(String), 269(Integer)
<== Updates: 1
Committing JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@6fb0d3ed]
Returned connection 1873859565 to pool.
-----------query2-----------
Cache Hit Ratio [com.study.mybatis.mapper.TestMapper]: 0.5
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.693 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:从一系列测试结果可以看出,Mybatis的缓存和具体的数据表没有关系,TestMapper和Test2Mapper之间不是共享缓存的,Test2Mapper中进行的修改操作,不会影响到TestMapper的缓存,我们在使用就需要小心,因为这个有可能会导致脏数据的发生,应该避免一个数据表在多个Mapper中被操作。
通过一系列的测试可以看出,Mybatis的二级缓存是对应于Mapper的,每个Mapper都有自己的二级缓存,在同一个Mapper下进行增删改操作,会触发清理其二级缓存的动作,如果在一个Mapper中先查询出一份数据,在另一个Mapper中进行增删改操作,而后再用前一个Mapper执行同样的查询动作,这时就出现脏数据,所以我们在使用Mybatis时应该避免在多个Mapper中操作同一个数据表。
之前听到过一种说法,就是在Mybatis初始化时会执行Mapper中的查询语句缓存数据,一直半信半疑,因为大多数查询语句都是带参数的,如何进行预加载呢?
于是通过简单代码进行了测试,我特意在TestMapper.xml中放置了一条无需任何参数的查询语句,TestMapper.java中也有对应的抽象函数,以观察它是否会被预先加载并缓存。
业务代码:
/*仅开启了一个数据库会话,以初始化Mybatis的一系列实例*/
public void load() throws IOException{
SqlSession sqlSession = Context.getSqlSessionFactory().openSession();
try {
} finally {
sqlSession.close();
}
}
测试代码:
@Test
public void testLoad() {
TestService service = new TestService();
try {
System.out.println("-----------load-----------");
service.load();
} catch (IOException e) {
e.printStackTrace();
}
}
输出结果:
T E S T S
-------------------------------------------------------
Running com.study.mybatis.service.TestServiceTester
-----------load-----------
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.484 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
结论:从测试结果来看,Mybatis在初始化SqlSessionFactory时不会自动执行SQL映射文件中的查询语句,开启SqlSession时也不会执行,只有明确调用某个查询动作时才会执行,并进行缓存,也许是需要进行手动设置,暂时不会用到,便不再继续深入了。