Section VII: mybatis cache

MyBatis has includes a powerful query caching feature which is very configurable and customizable. Caching can greatly improve query efficiency.

MyBatis system defined by default two cache: cache and a secondary cache.

  1, by default, only the first-level cache (SqlSession level cache, also known as a local cache) is turned on.

  2, need to manually open and secondary cache configuration, he is based namespace level cache.

  3, in order to improve scalability. MyBatis defines the cache interface Cache. We can customize the interface to the secondary cache by implementing Cache

1, L1 cache


Level cache (local cache), namely the local cache, the scope defaults to sqlSession. Session When the flush or close, all of the Session Cache will be cleared. Local cache can not be closed, but you can call clearCache () to clear the local cache, cache or change the scope. After mybatis3.1, you can configure the local cache scope, configuration mybatis.xml in. During the same session as long as the queried data will be saved in the current of a Map SqlSession, Key: hashCode + + sqlid queries written in sql query parameters +

Cache usage:

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

        Person person1 = mapper.getPerson(1);
        System.out.println(person1);

        Person person2 = mapper.getPerson(1);
        System.out.println(person2);

        System.out.println(person1 == person2);
    }
    finally
    {
        sqlSession.close();
    }
}

Four cases a cache miss (not close at SqlSession case)

1 except that a buffer corresponding to different SqlSession

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession01 = sqlSessionFactory.openSession();
    SqlSession sqlSession02 = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper = sqlSession01.getMapper(PersonMapper.class);
        Person person1 = mapper.getPerson(1);
        System.out.println(person1);

        mapper = sqlSession02.getMapper(PersonMapper.class);
        Person person2 = mapper.getPerson(1);
        System.out.println(person2);

        System.out.println(person1 == person2);//false
    }
    finally
    {
        sqlSession01.close();
        sqlSession02.close();
    }
}

2, but with a different query SqlSession

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

        PERSON1 the Person = mapper.getPerson (1); // query id is 1 
        System.out.println (PERSON1);

        Person person2 = mapper.getPerson(2);//查询id为2
        System.out.println(person2);

        System.out.println(person1 == person2);//false
    }
    finally
    {
        sqlSession.close();
    }
}

3, additions and deletions to perform the operation at any one time during the same query twice SqlSession

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

        Person person1 = mapper.getPerson(1);
        System.out.println(person1);

        mapper.deletePerson ( 2); // delete a data 

        the Person PERSON2 = mapper.getPerson (. 1 );
        System.out.println(person2);

        System.out.println(person1 == person2);//false
    }
    finally
    {
        sqlSession.commit();
        sqlSession.close();
    }
}

4, with a query manually during SqlSession twice cleared the cache.

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

        Person person1 = mapper.getPerson(1);
        System.out.println(person1);

        sqlSession.clearCache (); // manually cleared cache 

        the Person PERSON2 = mapper.getPerson (. 1 );
        System.out.println(person2);

        System.out.println(person1 == person2);//false
    }
    finally
    {
        sqlSession.close();
    }
}

2, the secondary cache


Secondary cache (cache global): based on a cache-level namespace, a namespace corresponding to a secondary cache.

Working Mechanism:

1, a conversation, a query data, this data will be placed in a cache in the current session;

2, if the session is closed, a cache data is saved in the secondary cache, the new query session information, can reference the contents of secondary cache;

Different namespace will find out on their own data corresponding cache (map), isolated data will be the default first on a cache. Only after the session commit or close, a data cache will be transferred to the secondary cache.

Use :

1), two open global cache configuration : <Setting name = "cacheEnabled" value = "to true" />
2), configured to use the secondary cache mapper.xml: mapper following which only the following configuration, with only the secondary cache, or even open the two global cache, L2 cache does not take effect
    <cache> </ cache>

tag can be configured to cache attributes:

eviction : cache recovery strategy:
    the LRU - Least Recently Used: Removes the maximum time an object is not used.
    FIFO - First In First Out: order by object into the cache to remove them.
    SOFT - Soft Reference: Removes objects based on the garbage collector state and rules of soft references.
    WEAK - Weak Reference: More aggressively removes objects referenced rule-based garbage collector state and weak.
    The default is LRU.
flushInterval : cache flush interval
    cache clear how long a default is not empty, set a value in milliseconds
readOnly : whether the read-only:
    to true: read-only; MyBatis that all acquired data are read from the cache operation of the operation, will not be modified data.
             mybatis order to accelerate the acquisition rate, the data will be directly referenced in the cache to the user. Unsafe speed
    false: non-read only: mybatis think the acquired data may be modified.
            mybatis will use serialization & anti-cloning technology for a new sequence of data to you. Safe, slow and
size : the number of elements in the cache storage;
of the type = "": specify a custom cache full class name: <cache type = "org.mybatis.caches.ehcache.EhcacheCache" > </ cache>
        implement the interface to Cache ;
blocking: If you can not find the corresponding cache key, whether it will have been blocking, until the corresponding data into the cache.

3), POJO interfaces need to implement serialization

Secondary cache code sample

1)<setting name="cacheEnabled" value="true"/>
2)public class Person implements Serializable{...}
3)mapper映射文件加入<cache></cache>

In order to observe the log case, we simply configure your log print:

<setting name="logImpl" value="STDOUT_LOGGING" />

Here is the test code

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper1 = sqlSession1.getMapper(PersonMapper.class);
        Person person1 = mapper1.getPerson(1);
        System.out.println(person1);

        sqlSession1.close();

        PersonMapper mapper2 = sqlSession2.getMapper(PersonMapper.class);
        Person person2 = mapper2.getPerson(1);
        System.out.println(person2);

    }
    finally
    {
        sqlSession2.close();
    }
}

解释:sqlSession1查询结果之后,关闭sqlSession1,会将结果写入二级缓存,然后sqlSession2查询会从二级缓存中查询,不从数据查询数据了。下面日志可以证明:

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.
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Sun Jun 16 17:16:22 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 327177752.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
==>  Preparing: select * from person where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, first_name, last_name, age, email, address
<==        Row: 1, tom, Carine, 25, null, beijing
<==      Total: 1
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Returned connection 327177752 to pool.
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.5
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}

注意:只有一级缓存关闭的情况下二级缓存才会生效,下面演示中一级缓存没有关闭,二级缓存没有起作用,注意sqlSession1.close()的位置

public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession1 = sqlSessionFactory.openSession();
    SqlSession sqlSession2 = sqlSessionFactory.openSession();
    try
    {
        PersonMapper mapper1 = sqlSession1.getMapper(PersonMapper.class);
        Person person1 = mapper1.getPerson(1);
        System.out.println(person1);
        
        PersonMapper mapper2 = sqlSession2.getMapper(PersonMapper.class);
        Person person2 = mapper2.getPerson(1);
        System.out.println(person2);

    }
    finally
    {
        sqlSession1.close();
        sqlSession2.close();
    }
}

结果则是没有命中二级缓存:

Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Sun Jun 16 17:22:38 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 327177752.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
==>  Preparing: select * from person where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, first_name, last_name, age, email, address
<==        Row: 1, tom, Carine, 25, null, beijing
<==      Total: 1
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
Cache Hit Ratio [com.yefengyu.mybatis.mapper.PersonMapper]: 0.0
Opening JDBC Connection
Sun Jun 16 17:22:38 CST 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
Created connection 1589683045.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ec0a365]
==>  Preparing: select * from person where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, first_name, last_name, age, email, address
<==        Row: 1, tom, Carine, 25, null, beijing
<==      Total: 1
Person{id=1, firstName='tom', lastName='Carine', age=25, email='null', address='beijing'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@13805618]
Returned connection 327177752 to pool.
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ec0a365]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@5ec0a365]
Returned connection 1589683045 to pool.

其它知识点:

     *             和缓存有关的设置/属性:

     *             1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)

     *             2)、每个select标签都有useCache="true":

     *                     false:不使用缓存(一级缓存依然使用,二级缓存不使用) :在全局开启的情况下可以禁止部分查询使用二级缓存

     *             3)、【每个增删改标签的:flushCache="true":(一级二级都会清除)】

     *                     增删改执行完成后就会清除缓存;

     *                     测试:flushCache="true":一级缓存就清空了;二级也会被清除;

     *                     查询标签:flushCache="false":

     *                         如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;

     *             4)、sqlSession.clearCache();只是清除当前session的一级缓存;

     *             5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;

     *                                 STATEMENT:可以禁用一级缓存;

3、外部缓存


外部缓存可以使用第三方提供的缓存包,比如EhCache:

1、首先在类路径下面添加ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
 <!-- 磁盘保存路径 -->
 <diskStore path="D:\ehcache" />

 <defaultCache
   maxElementsInMemory="10000"
   maxElementsOnDisk="10000000"
   eternal="false"
   overflowToDisk="true"
   timeToIdleSeconds="120"
   timeToLiveSeconds="120"
   diskExpiryThreadIntervalSeconds="120"
   memoryStoreEvictionPolicy="LRU">
 </defaultCache>
</ehcache>

属性说明:

diskStore:指定数据在磁盘中的存储位置。

defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略

以下属性是必须的:

maxElementsInMemory - 在内存中缓存的element的最大数目

maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大

eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断

overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上

以下属性是可选的:

timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大

timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大

diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区.

diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。

diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作

memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)

2、在mapper文件下面使用下面的缓存

<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

3、注意依赖包,主要是缓存包,适配包

Guess you like

Origin www.cnblogs.com/ye-feng-yu/p/11032253.html