Mybatis缓存解析

一、Mybatis的缓存概述

声明:本文纯属个人兴趣探究,其中一些概念、理论以及结论并不一定正确,如果有错误,请包涵并告知。

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中的查询语句缓存数据?

之前听到过一种说法,就是在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时也不会执行,只有明确调用某个查询动作时才会执行,并进行缓存,也许是需要进行手动设置,暂时不会用到,便不再继续深入了。

你可能感兴趣的:(Mybatis)