精华内容
下载资源
问答
  • diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区。 diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 ...
  • 像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能 Mybatis 中缓存分为一级缓存,二级缓存 Mybatis级缓存 Mybatis级缓存 ...

    像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能

    Mybatis 中缓存分为一级缓存,二级缓存
    在这里插入图片描述

    Mybatis 一级缓存

    一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。

    证明一级缓存的存在

    新建数据库映射实体类 User.java

    package cn.lemon.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class User implements Serializable {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public Date getBirthday() {
            return birthday;
        }
    
        public void setBirthday(Date birthday) {
            this.birthday = birthday;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        public String getAddress() {
            return address;
        }
    
        public void setAddress(String address) {
            this.address = address;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", birthday=" + birthday +
                    ", sex='" + sex + '\'' +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    

    新建持久层接口 IUserDao.java

    package cn.lemon.dao;
    
    import cn.lemon.domain.User;
    
    public interface IUserDao {
        User findById(Integer userId);
    }
    

    新建配置文件 SqlMapConfig.xml 和 jdbc.properties

    <?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"/>
        <typeAliases>
            <package name="cn.lemon.domain"/>
        </typeAliases>
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"></transactionManager>
                <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="cn.lemon.dao"/>
        </mappers>
    </configuration>
    
    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql:///db_mybatis
    jdbc.username=root
    jdbc.password=lemon
    

    新建接口映射文件 IUserDao.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="cn.lemon.dao.IUserDao">
        <select id="findById" parameterType="int" resultType="user">
            select * from user where id = #{id}
        </select>
    </mapper>
    

    测试类

    package cn.lemon.dao;
    
    import cn.lemon.domain.User;
    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;
    
    import java.io.InputStream;
    
    public class IUserDaoTest {
        private InputStream inputStream;
        private SqlSessionFactory sqlSessionFactory;
        private SqlSession sqlSession;
        private IUserDao iUserDao;
    
        @Test
        public void findById() throws Exception{
            inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
    
            /*第一次查询*/
            User user1 = iUserDao.findById(58);
            System.out.println("第一次查询:" + user1);
            System.out.println("-------------------------------------------");
    
            /*第二次查询*/
            User user2 = iUserDao.findById(58);
            System.out.println("第二次查询:" + user2);
    
            sqlSession.close();
            inputStream.close();
        }
    }
    

    运行结果:
    在这里插入图片描述
    关闭缓存,或者使用sqlSession.clearCache 清除缓存,或者对查询的这条语句执行修改,就会出现两次查询
    在这里插入图片描述

    Mybatis 二级缓存 (重要)

    二级缓存的特点

    • 二级缓存是 mapper 映射级别的缓存
    • 多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句
    • 多个SqlSession 可以共用二级缓存
    • 二级缓存是跨 SqlSession 的。
      在这里插入图片描述

    二级缓存的配置

    第一步:在 SqlMapConfig.xml 文件开启二级缓存

        <settings>
            <!-- 
                开启二级缓存的支持
                因为 cacheEnabled 的取值默认就为 true,所以这一步可以省略不配置。为 true 代表开启二级缓存;为 false 代表不开启二级缓存
             -->
            <setting name="cacheEnabled" value="true"/>
        </settings>
    

    第二步:在 IUserDao.xml 中,配置相关的 Mapper 映射文件

        <!--
            开启二级缓存的支持
            <cache>标签表示当前这个 mapper 映射将使用二级缓存,区分的标准就看 mapper 的 namespace
        -->
        <cache></cache>
    

    第三步:在 IUserDao.xml 中,配置 statement 上面的 useCache 属性
    在这里插入图片描述
    第四步:测试类

    package cn.lemon.dao;
    
    import cn.lemon.domain.User;
    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;
    
    import java.io.InputStream;
    
    public class IUserDaoTest {
    
        @Test
        public void findById() throws Exception {
            InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            /*第一次查询*/
            SqlSession sqlSession1 = sqlSessionFactory.openSession(true);
            IUserDao iUserDao1 = sqlSession1.getMapper(IUserDao.class);
            User user1 = iUserDao1.findById(58);
            System.out.println("第一次查询:" + user1);
            System.out.println("-------------------------------------------");
            sqlSession1.close();//关闭资源
    
            /*第二次查询*/
            SqlSession sqlSession2 = sqlSessionFactory.openSession(true);//重新开启
            IUserDao iUserDao2 = sqlSession2.getMapper(IUserDao.class);
            User user2 = iUserDao2.findById(58);
            System.out.println("第二次查询:" + user2);
            System.out.println("-------------------------------------------");
            sqlSession2.close();
    
            /*第三次查询*/
            SqlSession sqlSession3 = sqlSessionFactory.openSession(true);//重新开启
            IUserDao iUserDao3 = sqlSession3.getMapper(IUserDao.class);
            User user3 = iUserDao3.findById(58);
            System.out.println("第三次查询:" + user3);
            System.out.println("-------------------------------------------");
            sqlSession3.close();
    
            inputStream.close();
        }
    }
    

    运行结果:
    在这里插入图片描述
    特别提醒:

    • 二级缓存要实现序列化接口,也就是 implements Serializable
    • 二级缓存要在同一个命名空间下,不用的命名空间有不同的缓存,如上面的例子 namespace="cn.lemon.dao.IUserDao"
    展开全文
  • MyBatis级缓存和二级缓存

    万次阅读 多人点赞 2018-09-05 22:44:30
    MyBatis自带的缓存有一级缓存和二级缓存级缓存 Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中,执行相同的查询SQL,第一次...

    MyBatis自带的缓存有一级缓存和二级缓存

    一级缓存

    Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。
    也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;
    第二次以后是直接去缓存中取。
    当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。

    一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。
    如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,在下通过localCacheScope指定。

    1

    <setting name="localCacheScope" value="STATEMENT"/>

    建议不需要修改

    需要注意的是
    当Mybatis整合Spring后,直接通过Spring注入Mapper的形式,如果不是在同一个事务中每个Mapper的每次查询操作都对应一个全新的SqlSession实例,这个时候就不会有一级缓存的命中,但是在同一个事务中时共用的是同一个SqlSession。
    如有需要可以启用二级缓存。

    二级缓存

    Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。

    二级缓存是默认启用的(要生效需要对每个Mapper进行配置),如想取消,则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。

    1

    2

    3

    <settings>

      <setting name="cacheEnabled" value="false" />

    </settings>

    cacheEnabled默认是启用的,只有在该值为true的时候,底层使用的Executor才是支持二级缓存的CachingExecutor。具体可参考Mybatis的核心配置类org.apache.ibatis.session.Configuration的newExecutor方法实现。
    可以通过源码看看

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    ...

        public Executor newExecutor(Transaction transaction) {

            return this.newExecutor(transaction, this.defaultExecutorType);

        }

     

        public Executor newExecutor(Transaction transaction, ExecutorType executorType) {

            executorType = executorType == null this.defaultExecutorType : executorType;

            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

            Object executor;

            if (ExecutorType.BATCH == executorType) {

                executor = new BatchExecutor(this, transaction);

            else if (ExecutorType.REUSE == executorType) {

                executor = new ReuseExecutor(this, transaction);

            else {

                executor = new SimpleExecutor(this, transaction);

            }

     

            if (this.cacheEnabled) {//设置为true才执行的

                executor = new CachingExecutor((Executor)executor);

            }

     

            Executor executor = (Executor)this.interceptorChain.pluginAll(executor);

            return executor;

        }

    ...

    要使用二级缓存除了上面一个配置外,我们还需要在我们每个DAO对应的Mapper.xml文件中定义需要使用的cache

    1

    2

    3

    4

    5

    ...

    <mapper namespace="...UserMapper">

        <cache/><!-- 加上该句即可,使用默认配置、还有另外一种方式,在后面写出 -->

        ...

    </mapper>

    具体可以看org.apache.ibatis.executor.CachingExecutor类的以下实现
    其中使用的cache就是我们在对应的Mapper.xml中定义的cache。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {

        BoundSql boundSql = ms.getBoundSql(parameterObject);

        CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);

        return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

    }

     

    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {

        Cache cache = ms.getCache();

        if (cache != null) {//第一个条件 定义需要使用的cache 

            this.flushCacheIfRequired(ms);

            if (ms.isUseCache() && resultHandler == null) {//第二个条件 需要当前的查询语句是配置了使用cache的,即下面源码的useCache()是返回true的  默认是true

                this.ensureNoOutParams(ms, parameterObject, boundSql);

                List<E> list = (List)this.tcm.getObject(cache, key);

                if (list == null) {

                    list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

                    this.tcm.putObject(cache, key, list);

                }

     

                return list;

            }

        }

     

        return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

    }

    还有一个条件就是需要当前的查询语句是配置了使用cache的,即上面源码的useCache()是返回true的,默认情况下所有select语句的useCache都是true,如果我们在启用了二级缓存后,有某个查询语句是我们不想缓存的,则可以通过指定其useCache为false来达到对应的效果。
    如果我们不想该语句缓存,可使用useCache="false

    1

    2

    3

    4

    5

    6

    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" useCache="false">

            select

            <include refid="Base_Column_List"/>

            from tuser

            where id = #{id,jdbcType=VARCHAR}

        </select>

    cache定义的两种使用方式

    上面说了要想使用二级缓存,需要在每个DAO对应的Mapper.xml文件中定义其中的查询语句需要使用cache来缓存数据的。
    这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。
    需要注意的是
    对于同一个Mapper来讲,只能使用一个Cache,当同时使用了和时,定义的优先级更高(后面的代码会给出原因)。
    Mapper使用的Cache是与我们的Mapper对应的namespace绑定的,一个namespace最多只会有一个Cache与其绑定。

    cache元素定义

    使用cache元素来定义使用的Cache时,最简单的做法是直接在对应的Mapper.xml文件中指定一个空的元素(看前面的代码),这个时候Mybatis会按照默认配置创建一个Cache对象,准备的说是PerpetualCache对象,更准确的说是LruCache对象(底层用了装饰器模式)。
    具体的可看org.apache.ibatis.builder.xml.XMLMapperBuilder中的cacheElement()方法解析cache元素的逻辑。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    ...

        private void configurationElement(XNode context) {

            try {

                String namespace = context.getStringAttribute("namespace");

                if (namespace.equals("")) {

                    throw new BuilderException("Mapper's namespace cannot be empty");

                else {

                    this.builderAssistant.setCurrentNamespace(namespace);

                    this.cacheRefElement(context.evalNode("cache-ref"));

                    this.cacheElement(context.evalNode("cache"));//执行在后面

                    this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));

                    this.resultMapElements(context.evalNodes("/mapper/resultMap"));

                    this.sqlElement(context.evalNodes("/mapper/sql"));

                    this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

                }

            catch (Exception var3) {

                throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);

            }

        }

    ...

        private void cacheRefElement(XNode context) {

            if (context != null) {

                this.configuration.addCacheRef(this.builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));

                CacheRefResolver cacheRefResolver = new CacheRefResolver(this.builderAssistant, context.getStringAttribute("namespace"));

     

                try {

                    cacheRefResolver.resolveCacheRef();

                catch (IncompleteElementException var4) {

                    this.configuration.addIncompleteCacheRef(cacheRefResolver);

                }

            }

     

        }

     

        private void cacheElement(XNode context) throws Exception {

            if (context != null) {

                String type = context.getStringAttribute("type""PERPETUAL");

                Class<? extends Cache> typeClass = this.typeAliasRegistry.resolveAlias(type);

                String eviction = context.getStringAttribute("eviction""LRU");

                Class<? extends Cache> evictionClass = this.typeAliasRegistry.resolveAlias(eviction);

                Long flushInterval = context.getLongAttribute("flushInterval");

                Integer size = context.getIntAttribute("size");

                boolean readWrite = !context.getBooleanAttribute("readOnly"false).booleanValue();

                Properties props = context.getChildrenAsProperties();

                this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);

    //如果同时存在<cache>和<cache-ref>,这里的设置会覆盖前面的cache-ref的缓存

            }

     

        }

    空cache元素定义会生成一个采用最近最少使用算法最多只能存储1024个元素的缓存,而且是可读写的缓存,即该缓存是全局共享的,任何一个线程在拿到缓存结果后对数据的修改都将影响其它线程获取的缓存结果,因为它们是共享的,同一个对象。

    cache元素可指定如下属性,每种属性的指定都是针对都是针对底层Cache的一种装饰,采用的是装饰器的模式。

    1. blocking:默认为false,当指定为true时将采用BlockingCache进行封装,blocking,阻塞的意思,使用BlockingCache会在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,这样可以阻止并发情况下多个线程同时查询数据,详情可参考BlockingCache的源码。
      简单理解,也就是设置true时,在进行增删改之后的并发查询,只会有一条去数据库查询,而不会并发

       

    2. eviction:eviction,驱逐的意思。也就是元素驱逐算法,默认是LRU,对应的就是LruCache,其默认只保存1024个Key,超出时按照最近最少使用算法进行驱逐,详情请参考LruCache的源码。如果想使用自己的算法,则可以将该值指定为自己的驱逐算法实现类,只需要自己的类实现Mybatis的Cache接口即可。除了LRU以外,系统还提供了FIFO(先进先出,对应FifoCache)、SOFT(采用软引用存储Value,便于垃圾回收,对应SoftCache)和WEAK(采用弱引用存储Value,便于垃圾回收,对应WeakCache)这三种策略。
      这里,根据个人需求选择了,没什么要求的话,默认的LRU即可

    3. flushInterval:清空缓存的时间间隔,单位是毫秒,默认是不会清空的。当指定了该值时会再用ScheduleCache包装一次,其会在每次对缓存进行操作时判断距离最近一次清空缓存的时间是否超过了flushInterval指定的时间,如果超出了,则清空当前的缓存,详情可参考ScheduleCache的实现。

    4. readOnly:是否只读
      默认为false。当指定为false时,底层会用SerializedCache包装一次,其会在写缓存的时候将缓存对象进行序列化,然后在读缓存的时候进行反序列化,这样每次读到的都将是一个新的对象,即使你更改了读取到的结果,也不会影响原来缓存的对象,即非只读,你每次拿到这个缓存结果都可以进行修改,而不会影响原来的缓存结果;
      当指定为true时那就是每次获取的都是同一个引用,对其修改会影响后续的缓存数据获取,这种情况下是不建议对获取到的缓存结果进行更改,意为只读(不建议设置为true)。
      这是Mybatis二级缓存读写和只读的定义,可能与我们通常情况下的只读和读写意义有点不同。每次都进行序列化和反序列化无疑会影响性能,但是这样的缓存结果更安全,不会被随意更改,具体可根据实际情况进行选择。详情可参考SerializedCache的源码。

    5. size:用来指定缓存中最多保存的Key的数量。其是针对LruCache而言的,LruCache默认只存储最多1024个Key,可通过该属性来改变默认值,当然,如果你通过eviction指定了自己的驱逐算法,同时自己的实现里面也有setSize方法,那么也可以通过cache的size属性给自定义的驱逐算法里面的size赋值。

    6. type:type属性用来指定当前底层缓存实现类,默认是PerpetualCache,如果我们想使用自定义的Cache,则可以通过该属性来指定,对应的值是我们自定义的Cache的全路径名称。

    cache-ref元素定义

    cache-ref元素可以用来指定其它Mapper.xml中定义的Cache,有的时候可能我们多个不同的Mapper需要共享同一个缓存的
    是希望在MapperA中缓存的内容在MapperB中可以直接命中的,这个时候我们就可以考虑使用cache-ref,这种场景只需要保证它们的缓存的Key是一致的即可命中,二级缓存的Key是通过Executor接口的createCacheKey()方法生成的,其实现基本都是BaseExecutor,源码如下。

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {

        if (this.closed) {

            throw new ExecutorException("Executor was closed.");

        else {

            CacheKey cacheKey = new CacheKey();

            cacheKey.update(ms.getId());

            cacheKey.update(rowBounds.getOffset());

            cacheKey.update(rowBounds.getLimit());

            cacheKey.update(boundSql.getSql());

            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

            TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();

     

            for(int i = 0; i < parameterMappings.size(); ++i) {

                ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);

                if (parameterMapping.getMode() != ParameterMode.OUT) {

                    String propertyName = parameterMapping.getProperty();

                    Object value;

                    if (boundSql.hasAdditionalParameter(propertyName)) {

                        value = boundSql.getAdditionalParameter(propertyName);

                    else if (parameterObject == null) {

                        value = null;

                    else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {

                        value = parameterObject;

                    else {

                        MetaObject metaObject = this.configuration.newMetaObject(parameterObject);

                        value = metaObject.getValue(propertyName);

                    }

     

                    cacheKey.update(value);

                }

            }

     

            return cacheKey;

        }

    }

    打个比方我想在MenuMapper.xml中的查询都使用在UserMapper.xml中定义的Cache,则可以通过cache-ref元素的namespace属性指定需要引用的Cache所在的namespace,即UserMapper.xml中的定义的namespace,假设在UserMapper.xml中定义的namespace是cn.chenhaoxiang.dao.UserMapper,则在MenuMapper.xml的cache-ref应该定义如下。

    1

    <cache-ref namespace="cn.chenhaoxiang.dao.UserMapper"/>

    这样这两个Mapper就共享同一个缓存了

    自定义cache就不介绍了。

    测试二级缓存

    查询测试

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    <br />/**

     * Created with IntelliJ IDEA.

     * User: 陈浩翔.

     * Date: 2018/1/10.

     * Time: 下午 10:15.

     * Explain:

     */

    @RunWith(SpringJUnit4ClassRunner.class)

    //配置了@ContextConfiguration注解并使用该注解的locations属性指明spring和配置文件之后

    @ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-mybatis.xml"})

    public class MyBatisTestBySpringTestFramework {

     

        //注入userService

        @Autowired

        private UserService userService;

     

        @Test

        public void testGetUserId(){

            String userId = "4e07f3963337488e81716cfdd8a0fe04";

            User user = userService.getUserById(userId);

            System.out.println(user);

     

     

            //前面说到spring和MyBatis整合

            User user2 = userService.getUserById(userId);

            System.out.println("user2:"+user2);

        }

    }

    接下来我们把Mapper中的cache元素删除,不使用二级缓存

    再运行测试

    对二级缓存进行了以下测试,获取两个不同的SqlSession(前面有说,Spring和MyBatis集成,每次都是不同的SqlSession)执行两条相同的SQL,在未指定Cache时Mybatis将查询两次数据库,在指定了Cache时Mybatis只查询了一次数据库,第二次是从缓存中拿的。

    Cache Hit Ratio 表示缓存命中率。
    开启二级缓存后,每执行一次查询,系统都会计算一次二级缓存的命中率。
    第一次查询也是先从缓存中查询,只不过缓存中一定是没有的。
    所以会再从DB中查询。由于二级缓存中不存在该数据,所以命中率为0.但第二次查询是从二级缓存中读取的,所以这一次的命中率为1/2=0.5。
    当然,若有第三次查询,则命中率为1/3=0.66
    0.5这个值可以从上面开启cache的图看出来,0.0的值未截取到~漏掉了~

    注意:
    增删改操作,无论是否进行提交sqlSession.commit(),均会清空一级、二级缓存,使查询再次从DB中select。
    说明:
    二级缓存的清空,实质上是对所查找key对应的value置为null,而非将对,即entry对象删除。
    从DB中进行select查询的条件是:缓存中根本不存在这个key或者缓存中存在该key所对应的entry对象,但value为null。

    设置增删改操作不刷新二级缓存:
    若要使某个增、删或改操作不清空二级缓存,则需要在其或或中添加属性flushCache="false",默认为true。

    二级缓存的使用原则

    1. 只能在一个命名空间下使用二级缓存
      由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
    2. 在单表上使用二级缓存
      如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象。
    3. 查询多于修改时使用二级缓存
      在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
    展开全文
  • MyBatisMyBatis级缓存和二级缓存

    千次阅读 2018-01-27 19:35:53
    MyBatis自带的缓存有一级缓存和二级缓存级缓存Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中,执行相同的查询SQL,第一次会...

    MyBatis自带的缓存有一级缓存和二级缓存

    一级缓存

    Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。
    也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;
    第二次以后是直接去缓存中取。
    当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。

    一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。
    如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,在下通过localCacheScope指定。

     <setting name="localCacheScope" value="STATEMENT"/>

    建议不需要修改

    需要注意的是
    当Mybatis整合Spring后,直接通过Spring注入Mapper的形式,如果不是在同一个事务中每个Mapper的每次查询操作都对应一个全新的SqlSession实例,这个时候就不会有一级缓存的命中,但是在同一个事务中时共用的是同一个SqlSession。
    如有需要可以启用二级缓存。

    二级缓存

    Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。

    二级缓存是默认启用的(要生效需要对每个Mapper进行配置),如想取消,则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。

    <settings>
      <setting name="cacheEnabled" value="false" />
    </settings>

    cacheEnabled默认是启用的,只有在该值为true的时候,底层使用的Executor才是支持二级缓存的CachingExecutor。具体可参考Mybatis的核心配置类org.apache.ibatis.session.Configuration的newExecutor方法实现。
    可以通过源码看看

    ...
        public Executor newExecutor(Transaction transaction) {
            return this.newExecutor(transaction, this.defaultExecutorType);
        }
    
        public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
            executorType = executorType == null ? this.defaultExecutorType : executorType;
            executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
            Object executor;
            if (ExecutorType.BATCH == executorType) {
                executor = new BatchExecutor(this, transaction);
            } else if (ExecutorType.REUSE == executorType) {
                executor = new ReuseExecutor(this, transaction);
            } else {
                executor = new SimpleExecutor(this, transaction);
            }
    
            if (this.cacheEnabled) {//设置为true才执行的
                executor = new CachingExecutor((Executor)executor);
            }
    
            Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
            return executor;
        }
    ...
    

    要使用二级缓存除了上面一个配置外,我们还需要在我们每个DAO对应的Mapper.xml文件中定义需要使用的cache

    ...
    <mapper namespace="...UserMapper">
        <cache/><!-- 加上该句即可,使用默认配置、还有另外一种方式,在后面写出 -->
        ...
    </mapper>

    具体可以看org.apache.ibatis.executor.CachingExecutor类的以下实现
    其中使用的cache就是我们在对应的Mapper.xml中定义的cache。

        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
            BoundSql boundSql = ms.getBoundSql(parameterObject);
            CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
            return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
    
        public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
            Cache cache = ms.getCache();
            if (cache != null) {//第一个条件 定义需要使用的cache  
                this.flushCacheIfRequired(ms);
                if (ms.isUseCache() && resultHandler == null) {//第二个条件 需要当前的查询语句是配置了使用cache的,即下面源码的useCache()是返回true的  默认是true
                    this.ensureNoOutParams(ms, parameterObject, boundSql);
                    List<E> list = (List)this.tcm.getObject(cache, key);
                    if (list == null) {
                        list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                        this.tcm.putObject(cache, key, list);
                    }
    
                    return list;
                }
            }
    
            return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        }
    

    还有一个条件就是需要当前的查询语句是配置了使用cache的,即上面源码的useCache()是返回true的,默认情况下所有select语句的useCache都是true,如果我们在启用了二级缓存后,有某个查询语句是我们不想缓存的,则可以通过指定其useCache为false来达到对应的效果。
    如果我们不想该语句缓存,可使用useCache=”false

    <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" useCache="false">
            select
            <include refid="Base_Column_List"/>
            from tuser
            where id = #{id,jdbcType=VARCHAR}
        </select>

    cache定义的两种使用方式

    上面说了要想使用二级缓存,需要在每个DAO对应的Mapper.xml文件中定义其中的查询语句需要使用cache来缓存数据的。
    这有两种方式可以定义,一种是通过cache元素定义,一种是通过cache-ref元素来定义。
    需要注意的是
    对于同一个Mapper来讲,只能使用一个Cache,当同时使用了和时,定义的优先级更高(后面的代码会给出原因)。
    Mapper使用的Cache是与我们的Mapper对应的namespace绑定的,一个namespace最多只会有一个Cache与其绑定。

    cache元素定义

    使用cache元素来定义使用的Cache时,最简单的做法是直接在对应的Mapper.xml文件中指定一个空的元素(看前面的代码),这个时候Mybatis会按照默认配置创建一个Cache对象,准备的说是PerpetualCache对象,更准确的说是LruCache对象(底层用了装饰器模式)。
    具体的可看org.apache.ibatis.builder.xml.XMLMapperBuilder中的cacheElement()方法解析cache元素的逻辑。

    ...
        private void configurationElement(XNode context) {
            try {
                String namespace = context.getStringAttribute("namespace");
                if (namespace.equals("")) {
                    throw new BuilderException("Mapper's namespace cannot be empty");
                } else {
                    this.builderAssistant.setCurrentNamespace(namespace);
                    this.cacheRefElement(context.evalNode("cache-ref"));
                    this.cacheElement(context.evalNode("cache"));//执行在后面
                    this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
                    this.resultMapElements(context.evalNodes("/mapper/resultMap"));
                    this.sqlElement(context.evalNodes("/mapper/sql"));
                    this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
                }
            } catch (Exception var3) {
                throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
            }
        }
    ...
        private void cacheRefElement(XNode context) {
            if (context != null) {
                this.configuration.addCacheRef(this.builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
                CacheRefResolver cacheRefResolver = new CacheRefResolver(this.builderAssistant, context.getStringAttribute("namespace"));
    
                try {
                    cacheRefResolver.resolveCacheRef();
                } catch (IncompleteElementException var4) {
                    this.configuration.addIncompleteCacheRef(cacheRefResolver);
                }
            }
    
        }
    
        private void cacheElement(XNode context) throws Exception {
            if (context != null) {
                String type = context.getStringAttribute("type", "PERPETUAL");
                Class<? extends Cache> typeClass = this.typeAliasRegistry.resolveAlias(type);
                String eviction = context.getStringAttribute("eviction", "LRU");
                Class<? extends Cache> evictionClass = this.typeAliasRegistry.resolveAlias(eviction);
                Long flushInterval = context.getLongAttribute("flushInterval");
                Integer size = context.getIntAttribute("size");
                boolean readWrite = !context.getBooleanAttribute("readOnly", false).booleanValue();
                Properties props = context.getChildrenAsProperties();
                this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, props);
    //如果同时存在<cache>和<cache-ref>,这里的设置会覆盖前面的cache-ref的缓存
            }
    
        }

    空cache元素定义会生成一个采用最近最少使用算法最多只能存储1024个元素的缓存,而且是可读写的缓存,即该缓存是全局共享的,任何一个线程在拿到缓存结果后对数据的修改都将影响其它线程获取的缓存结果,因为它们是共享的,同一个对象。

    cache元素可指定如下属性,每种属性的指定都是针对都是针对底层Cache的一种装饰,采用的是装饰器的模式。

    1. blocking:默认为false,当指定为true时将采用BlockingCache进行封装,blocking,阻塞的意思,使用BlockingCache会在查询缓存时锁住对应的Key,如果缓存命中了则会释放对应的锁,否则会在查询数据库以后再释放锁,这样可以阻止并发情况下多个线程同时查询数据,详情可参考BlockingCache的源码。
      简单理解,也就是设置true时,在进行增删改之后的并发查询,只会有一条去数据库查询,而不会并发

    2. eviction:eviction,驱逐的意思。也就是元素驱逐算法,默认是LRU,对应的就是LruCache,其默认只保存1024个Key,超出时按照最近最少使用算法进行驱逐,详情请参考LruCache的源码。如果想使用自己的算法,则可以将该值指定为自己的驱逐算法实现类,只需要自己的类实现Mybatis的Cache接口即可。除了LRU以外,系统还提供了FIFO(先进先出,对应FifoCache)、SOFT(采用软引用存储Value,便于垃圾回收,对应SoftCache)和WEAK(采用弱引用存储Value,便于垃圾回收,对应WeakCache)这三种策略。
      这里,根据个人需求选择了,没什么要求的话,默认的LRU即可

    3. flushInterval:清空缓存的时间间隔,单位是毫秒,默认是不会清空的。当指定了该值时会再用ScheduleCache包装一次,其会在每次对缓存进行操作时判断距离最近一次清空缓存的时间是否超过了flushInterval指定的时间,如果超出了,则清空当前的缓存,详情可参考ScheduleCache的实现。

    4. readOnly:是否只读
      默认为false。当指定为false时,底层会用SerializedCache包装一次,其会在写缓存的时候将缓存对象进行序列化,然后在读缓存的时候进行反序列化,这样每次读到的都将是一个新的对象,即使你更改了读取到的结果,也不会影响原来缓存的对象,即非只读,你每次拿到这个缓存结果都可以进行修改,而不会影响原来的缓存结果;
      当指定为true时那就是每次获取的都是同一个引用,对其修改会影响后续的缓存数据获取,这种情况下是不建议对获取到的缓存结果进行更改,意为只读(不建议设置为true)。
      这是Mybatis二级缓存读写和只读的定义,可能与我们通常情况下的只读和读写意义有点不同。每次都进行序列化和反序列化无疑会影响性能,但是这样的缓存结果更安全,不会被随意更改,具体可根据实际情况进行选择。详情可参考SerializedCache的源码。

    5. size:用来指定缓存中最多保存的Key的数量。其是针对LruCache而言的,LruCache默认只存储最多1024个Key,可通过该属性来改变默认值,当然,如果你通过eviction指定了自己的驱逐算法,同时自己的实现里面也有setSize方法,那么也可以通过cache的size属性给自定义的驱逐算法里面的size赋值。

    6. type:type属性用来指定当前底层缓存实现类,默认是PerpetualCache,如果我们想使用自定义的Cache,则可以通过该属性来指定,对应的值是我们自定义的Cache的全路径名称。

    cache-ref元素定义

    cache-ref元素可以用来指定其它Mapper.xml中定义的Cache,有的时候可能我们多个不同的Mapper需要共享同一个缓存的
    是希望在MapperA中缓存的内容在MapperB中可以直接命中的,这个时候我们就可以考虑使用cache-ref,这种场景只需要保证它们的缓存的Key是一致的即可命中,二级缓存的Key是通过Executor接口的createCacheKey()方法生成的,其实现基本都是BaseExecutor,源码如下。

        public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
            if (this.closed) {
                throw new ExecutorException("Executor was closed.");
            } else {
                CacheKey cacheKey = new CacheKey();
                cacheKey.update(ms.getId());
                cacheKey.update(rowBounds.getOffset());
                cacheKey.update(rowBounds.getLimit());
                cacheKey.update(boundSql.getSql());
                List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
                TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    
                for(int i = 0; i < parameterMappings.size(); ++i) {
                    ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
                    if (parameterMapping.getMode() != ParameterMode.OUT) {
                        String propertyName = parameterMapping.getProperty();
                        Object value;
                        if (boundSql.hasAdditionalParameter(propertyName)) {
                            value = boundSql.getAdditionalParameter(propertyName);
                        } else if (parameterObject == null) {
                            value = null;
                        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                            value = parameterObject;
                        } else {
                            MetaObject metaObject = this.configuration.newMetaObject(parameterObject);
                            value = metaObject.getValue(propertyName);
                        }
    
                        cacheKey.update(value);
                    }
                }
    
                return cacheKey;
            }
        }

    打个比方我想在MenuMapper.xml中的查询都使用在UserMapper.xml中定义的Cache,则可以通过cache-ref元素的namespace属性指定需要引用的Cache所在的namespace,即UserMapper.xml中的定义的namespace,假设在UserMapper.xml中定义的namespace是cn.chenhaoxiang.dao.UserMapper,则在MenuMapper.xml的cache-ref应该定义如下。

    <cache-ref namespace="cn.chenhaoxiang.dao.UserMapper"/>

    这样这两个Mapper就共享同一个缓存了

    自定义cache就不介绍了。

    测试二级缓存

    查询测试

    
    /**
     * Created with IntelliJ IDEA.
     * User: 陈浩翔.
     * Date: 2018/1/10.
     * Time: 下午 10:15.
     * Explain:
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    //配置了@ContextConfiguration注解并使用该注解的locations属性指明spring和配置文件之后
    @ContextConfiguration(locations = {"classpath:spring.xml","classpath:spring-mybatis.xml"})
    public class MyBatisTestBySpringTestFramework {
    
        //注入userService
        @Autowired
        private UserService userService;
    
        @Test
        public void testGetUserId(){
            String userId = "4e07f3963337488e81716cfdd8a0fe04";
            User user = userService.getUserById(userId);
            System.out.println(user);
    
    
            //前面说到spring和MyBatis整合
            User user2 = userService.getUserById(userId);
            System.out.println("user2:"+user2);
        }
    }

    接下来我们把Mapper中的cache元素删除,不使用二级缓存

    再运行测试

    对二级缓存进行了以下测试,获取两个不同的SqlSession(前面有说,Spring和MyBatis集成,每次都是不同的SqlSession)执行两条相同的SQL,在未指定Cache时Mybatis将查询两次数据库,在指定了Cache时Mybatis只查询了一次数据库,第二次是从缓存中拿的。

    Cache Hit Ratio 表示缓存命中率。
    开启二级缓存后,每执行一次查询,系统都会计算一次二级缓存的命中率。
    第一次查询也是先从缓存中查询,只不过缓存中一定是没有的。
    所以会再从DB中查询。由于二级缓存中不存在该数据,所以命中率为0.但第二次查询是从二级缓存中读取的,所以这一次的命中率为1/2=0.5。
    当然,若有第三次查询,则命中率为1/3=0.66
    0.5这个值可以从上面开启cache的图看出来,0.0的值未截取到~漏掉了~

    注意:
    增删改操作,无论是否进行提交sqlSession.commit(),均会清空一级、二级缓存,使查询再次从DB中select。
    说明:
    二级缓存的清空,实质上是对所查找key对应的value置为null,而非将

    二级缓存的使用原则

    1. 只能在一个命名空间下使用二级缓存
      由于二级缓存中的数据是基于namespace的,即不同namespace中的数据互不干扰。在多个namespace中若均存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。
    2. 在单表上使用二级缓存
      如果一个表与其它表有关联关系,那么久非常有可能存在多个namespace对同一数据的操作。而不同namespace中的数据互补干扰,所以就有可能出现多个namespace中的数据不一致现象。
    3. 查询多于修改时使用二级缓存
      在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。

    本文章由[谙忆]编写, 所有权利保留。
    欢迎转载,分享是进步的源泉。

    转载请注明出处:http://chenhaoxiang.cn/2018/01/11/1222-3/
    本文源自谙忆的博客

    展开全文
  • mybatis级缓存

    2017-10-11 11:35:43
    mybatis级缓存 mybatis的一级缓存 sqlsession 二级缓存的同一命名空间(namespace)mapper mybatis 默认二级缓存未开启。内置是支持二级缓存的。但是由于本事是数据库管理组件,所以缓存并不好用,还要用第方。...

    mybatis 二级缓存

    1. mybatis的一级缓存 sqlsession
    2. 二级缓存的同一命名空间(namespace)mapper

    mybatis 默认二级缓存未开启。内置是支持二级缓存的。但是由于本身是数据库管理组件,所以缓存并不好用,还要用第三方。典型的ehcache。

    二级缓存的常见算法

    • 缓存算法有:
      1. LRU(Least Recently Used):这种算法是在每个对象中维护一个访问的时间变量,每次访问后,时间都会更新,当新的对象需要存放到缓存时,替换那个按时间排序最后的对象。
      2. LFU(Least Frequently Used):这种算法是每个对象记录了对象访问的次数(即命中率),当新的对象需要存放到缓存时,替换那个访问次数最少的对象。
      3. FIFO(First In First Out):这种算法是将缓存中的对象存放成一个数组,当新的对象需要存放到内存中是,替换最先存放到缓存的对象。

    mybatis 中开启二级缓存

    1. 对象要实现序列化接口Serializable
    2. 在mapper映射文件添加
      <!-- 开启二级缓存 -->
      <cache/>
    3. 在mybatis-config.xml文件中添加
    <!-- 设置启动二级缓存 -->
    <setting name="cacheEnabled" value="true"/>

    与ehcache集成

    1. 导入jar包
      这里写图片描述
    2. 生成ehcache.xml配置文件
    3. setting中开启二级缓存
    4. 在mapper文件中指定cache类型为ehcache
    <!-- 开启二级缓存 -->
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    展开全文
  • MyBatis级缓存与二级缓存级缓存与二级缓存1 一级缓存的使用1.1一级缓存的生命周期1.2 如何判断两次查询是完全相同的查询2 二级缓存的使用2.1二级缓存的配置方式2.2二级缓存特点 一级缓存与二级缓存 Mybatis缓存...
  • mybatis级缓存级缓存级缓存
  • MyBatis级缓存

    2020-03-31 09:52:30
    MyBatis级缓存
  • 深入了解MyBatis级缓存

    万次阅读 多人点赞 2015-03-30 08:57:42
    MyBatis深入了解二级缓存
  • mybatis级缓存级缓存级缓存  Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个...
  • Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,也就是说一级缓存默认是开启的,一级缓存只是相对于同一个SqlSession而言。所以在参数和SQL完全一样的情况下,我们使用同一个SqlSession对象...
  • Mybatis级缓存

    2021-09-28 09:55:43
    Mybatis级缓存级缓存应有的需求 二级缓存也称作是应用级缓存,与一级缓存不同的是它的作用范围是整个应用,而且可以跨线程使用。所以二级缓存有更高的命中率,适合缓存一些修改较少的数据 存储方式:内存、...
  • 05-Mybatis级缓存,二级缓存详解 一,mybatis级缓存 mybatis提供查询缓存,如果缓存中又数据,就不用从数据库中获取,用于减轻数据压力,提高系统性能。 我们用一张图来表示一级缓存和二级缓存的位置: 1.一...
  • mybatis缓存 mybatis作为一个流行的持久化工具,缓存必然是缺少不了的组件。通过这篇文章,就让我们来了解一下mybatis的缓存。...第二种,就是非sqlSession级别的缓存了,我们通常称为二级缓存mybatis中的二级缓
  • Mybatis级缓存与二级缓存

    千次阅读 2019-01-01 15:31:16
    mybatis的有两种缓存,一级缓存和二级缓存。两个缓存的不同点和相同点总结如下 不同点: 一级缓存存在于一个SqlSession之内,二级缓存存在于不同的SqlSession之间 一级缓存不需要手动开启,属于默认开启状态;二...
  • Mybatis级缓存(默认开启) 是SqlSession级别的缓存, 默认开启 Mybatis级缓存(默认关闭,一般不建议使用) namspace/mapper级别(跨sqlSession)的缓存, 默认关闭
  • Mybatis级缓存机制

    2019-09-16 13:21:05
    Mybatis级缓存机制 一级缓存:(本地缓存) sqlSession级别的缓存。一级缓存是一直开启的,没法关闭。SqlSession级别的一个Map 与数据库统一次会话(openSession)期间查询到的数据会放在本地缓存中的。 以后如果...
  • Mybatis级缓存和二级缓存的区别

    万次阅读 多人点赞 2020-03-11 21:35:06
    1)一级缓存 Mybatis的一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。 在同一个SqlSession中,执行相同的SQL查询时;第一次会去查询数据库,并写在缓存中,第二次会直接从缓存中...
  • 文章目录一级缓存缓存实现缓存作用域sessionstatement缓存key生成策略添加缓存清空缓存例子解析总结 一级缓存 官方解释: MyBatis uses local cache to prevent circular references and speed up repeated nested ...
  • mybatis 为我们提供了一级缓存和二级缓存,可以通过下图来理解:    ①、一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的...
  • SpringBoot+Mybatis级缓存和二级缓存详解 一、概念介绍 什么是一级缓存 在日常开发过程中,经常会有相同的sql执行多次查询的情况,mybatis提供了一级缓存来优化这些查询,避免多次请求数据库。 一级缓存在mybatis...
  • mybatis级缓存,二级缓存 http://blog.csdn.net/u011403655/article/details/46696065 总结: 1. 一级缓存只针对单个sqlsession,多个sqlsession之间不能共享 2. 在sqlsession.commit和close后,一级缓存会...
  • mybatis级缓存

    2021-01-28 15:56:06
    mybatis级缓存的作用范围是session,单使用mybatis时默认是开启的; 当第一次查询时,mybatis会把查询结果已Map<cacheKey,value>的形式保存到Local cache中;当再次查询时,会先去Local cache中查询,当...
  • Mybatis级缓存

    2018-01-25 15:49:06
    在日常工作中,开发人员多数情况下是使用MyBatis的默认缓存配置,但是MyBatis缓存机制有一些不足之处,在使用中容易引起脏数据,形成一些潜在的隐患。个人在业务开发中也处理过一些由于MyBatis缓存

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,428
精华内容 14,971
关键字:

mybatis三级缓存