精华内容
下载资源
问答
  • 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. 查询多于修改时使用二级缓存
      在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
    展开全文
  • 配套博客内容,mybatis一级缓存和二级缓存简单示例,供初学童鞋予以参考。 博客地址 https://blog.csdn.net/magi1201/article/details/85524712
  • hibernate的一级缓存和二级缓存,hibernate的一级缓存和二级缓存,hibernate的一级缓存和二级缓存,hibernate的一级缓存和二级缓存,hibernate的一级缓存和二级缓存
  • MyBatis【四】一级缓存和二级缓存

    千次阅读 多人点赞 2020-04-22 10:18:33
    Mybatis中的一级缓存和二级缓存: 【一级缓存】 它指的是Mybatis中SqlSession对象的缓存 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。 当我们再次查询...

    MyBatis缓存

    • 一级缓存默认是开启的
    • 二级缓存在映射配置文件中开启

    Mybatis中的一级缓存和二级缓存:

    【一级缓存】

    它指的是Mybatis中SqlSession对象的缓存

    • 当我们执行查询之后,查询的结果会同时存入到SqlSession为我们提供一块区域中。该区域的结构是一个Map。

    • 当我们再次查询同样的数据,Mybatis会先去SqlSession中查询是否有,有的话直接拿出来用。

    • 当SqlSession对象消失时,Mybatis的一级缓存也就消失了。

    【二级缓存】

    它指的是Mybatis中SqlSessionFactory对象的缓存。

    由同一个SqlSessionFactory对象创建的SqlSession共享其缓存

    二级缓存的使用步骤:

    1. 让Mybatis框架支持二级缓存(在SqlMapConfig.xml中配置)
    2. 让当前的映射文件支持二级缓存(在IUserDao.xml中配置)
    3. 让当前的操作支持二级缓存(在select标签中配置)

    1. 一级缓存

    主配置文件SqlMapConfig.xml中开启缓存(默认是开启的)


    MyBatis提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。 Mybatis中缓存分为一级缓存,二级缓存。


    Mybatis的一级缓存的作用域是session,是SqlSession级别的缓存,只要SqlSession没有flushclose,它就存在。

    如果执行相同的SQL(相同语句和参数), MyBatis不进行执行SQL,而是从缓存中命中返回查询;如果命中直接返回,没有命中则执行SQL,从数据库中査询

    一级缓存存在测试


    我们可以发现,虽然在上面的代码中我们查询了两次,但最后只执行了一次数据库操作,这就是Mybatis提供给我们的一级缓起作用了。因为一级缓存的存在,导致第二次查询id为51的记录时,并没有发出SQL语句从数据库中查询数据,而是从一级缓存中查询。

    一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。

    一级缓存清空测试


    当执行sqlSession.close()后,再次获取sqlSession并查询id=51的User对象时,又重新执行了SQL 语句,从数据库进行了查询操作。

    2. 二级缓存

    MyBatis 的二级缓存是mapper映射级别的缓存,作用域是同一个mapper的namespace ,同一个namespace中查询SQL可以从缓存中命中,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。



    二级缓存测试

    映射配置文件IUserDao.xml中开启二级缓存

    开启user支持二级缓存
    <cache/>
    


    展开全文
  • Mybatis的一级缓存和二级缓存详解

    千次阅读 多人点赞 2018-11-19 19:11:59
    注:本笔记是根据尚硅谷的MyBatis视频记录的 对于任何个持久层框架,都有缓存机制;缓存在电脑中有块真实的存储空间...关于Mybatis的一级缓存和二级缓存执行顺序具体可参考:Mybatis的一级缓存和二级缓存执行...

    注:本笔记是根据尚硅谷的MyBatis视频记录的

    对于任何一个持久层框架,都有缓存机制;缓存在电脑中有一块真实的存储空间(https://baike.baidu.com/item/%E7%BC%93%E5%AD%98/100710?fr=aladdin);

    两个关于mybatis缓存额外的链接:

    关于Mybatis的一级缓存和二级缓存执行顺序具体可参考:Mybatis的一级缓存和二级缓存执行顺序

     Mybatis整合第三方缓存步骤具体可参考:Mybatis整合第三方缓存ehcache

    缓存原理图:

    一、一级缓存(本地缓存)

    sqlSession级别的缓存。(相当于一个方法内的缓存)

    每一次会话都对应自己的一级缓存,作用范围比较小,一旦会话关闭就查询不到了;

    一级缓存默认是一直开启的,是SqlSession级别的一个Map;
    与数据库同一次会话期间查询到的数据会放在本地缓存中。
    以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;

    测试:

    取缓存中的数据:

    说明:当再次查询发现已经有数据了,就直接在缓存中返回之前查的数据,而不再访问数据库;

    二、一级缓存失效的四种情况:

    没有使用到当前一级缓存的情况,效果就是:还需要再向数据库发出查询

    1、sqlsession不同(会话不同)

    结果:

    说明:不同的会话的缓存不共享数据

    2、sqlsession相同,查询缓存中没有的数据

    @Test
    public void testFirstLevelCache() throws IOException{
    	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
    	try{
    		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    		Employee emp01 = mapper.getEmpById(1);
    		System.out.println(emp01);
    		
    		//sqlSession相同,查询条件不同
    		Employee emp03 = mapper.getEmpById(3);
    		System.out.println(emp03);
    		System.out.println(emp01==emp03);
    
    	}finally{
    		openSession.close();
    	}
    }

    结果:

    说明:当缓存中没有数据时,会重新查数据库

    3、sqlsession相同,但两次查询之间执行了增删改操作

    @Test
    public void testFirstLevelCache() throws IOException{
    	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
    	try{
    		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    		Employee emp01 = mapper.getEmpById(1);
    		System.out.println(emp01);
    		
    		//sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)
    		mapper.addEmp(new Employee(null, "testCache", "cache", "1"));
    		System.out.println("数据添加成功");
    		
    		Employee emp02 = mapper.getEmpById(1);
    		System.out.println(emp02);
    		System.out.println(emp01==emp02);
    		
    	}finally{
    		openSession.close();
    	}
    }

    结果:

    说明:为了防止增删改对当前数据的影响,即使查的同一个对象,也会重新查数据库

    原因:每个增删改标签都有默认清空缓存配置:flushCache="true",不过这是默认的是一级和二级缓存都清空

    4、sqlsession相同,但手动清楚了一级缓存(缓存清空)

    清空缓存:openSession.clearCache();

    @Test
    public void testFirstLevelCache() throws IOException{
    	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
    	try{
    		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    		Employee emp01 = mapper.getEmpById(1);
    		System.out.println(emp01);
    		
    		//sqlSession相同,手动清除了一级缓存(缓存清空)
    		openSession.clearCache();
    		
    		Employee emp02 = mapper.getEmpById(1);
    		System.out.println(emp02);
    		System.out.println(emp01==emp02);
    		
    	}finally{
    		openSession.close();
    	}
    }

    结果:

    说明:手动清空缓存后,需要重新查数据库

    三、二级缓存(全局缓存)

    基于namespace名称空间级别的缓存:一个namespace对应一个二级缓存

    即一个mapper.xml对应一个缓存:

    1、工作机制:

    *         1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    *         2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
    *         3、sqlSession===EmployeeMapper==>Employee
    *                         DepartmentMapper===>Department
    *             不同namespace查出的数据会放在自己对应的缓存中(map)
    *             效果:数据会从二级缓存中获取
    *                 查出的数据都会被默认先放在一级缓存中。
    *                 只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

    2、 使用:
    *             1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
    *             2)、去mapper.xml中配置使用二级缓存:
    *                 <cache></cache>
    *             3)、我们的POJO需要实现序列化接口

    1)在mybatis全局配置文件中开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>

    2)在mapper.xml中配置使用二级缓存

    直接加上: <cache><cache/>

    <?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="com.atguigu.mybatis.dao.EmployeeMapper">
    	
    	 <cache><cache/>
    
     	<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);  -->
     	<select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
     		select * from tbl_employee where last_name like #{lastName}
     	</select>
    </mapper>

    或者 <cache>中配置一些参数:

    eviction:缓存的回收策略:
        • LRU – 最近最少使用的:移除最长时间不被使用的对象。
        • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
        • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
        • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
        • 默认的是 LRU。
    flushInterval:缓存刷新间隔
        缓存多长时间清空一次,默认不清空,设置一个毫秒值
    readOnly:是否只读:
        true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
                 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
        false:非只读:mybatis觉得获取的数据可能会被修改。
                mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
    size:缓存存放多少元素;
    type="":指定自定义缓存的全类名;
            实现Cache接口即可;

    <?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="com.atguigu.mybatis.dao.EmployeeMapper">
    
    	 <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> 
     
     	<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);  -->
     	<select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
     		select * from tbl_employee where last_name like #{lastName}
     	</select>
    </mapper>

    3)POJO需要实现序列化接口

    测试:

    注意:需要openSession.close();后,才能从二级缓存中查数据;

    @Test
    public void testSecondLevelCache() throws IOException{
    	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
    	SqlSession openSession2 = sqlSessionFactory.openSession();
    	try{
    
    		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    		EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
    		
    		Employee emp01 = mapper.getEmpById(1);
    		System.out.println(emp01);
    		openSession.close();
    		
    		//第二次查询是从二级缓存中拿到的数据,并没有发送新的sql
    		Employee emp02 = mapper2.getEmpById(1);
    		System.out.println(emp02);
    		openSession2.close();
    		
    	}finally{
    		
    	}
    }

    结果:

    说明:第二次查询是从二级缓存中拿到的数据,并没有发送新的sql;

    注意:

    如果openSession.close();在第二次查询之后才关闭,则第二次查询会从一级缓存中查,如果不是一个session,则查询不到数据:

    @Test
    public void testSecondLevelCache() throws IOException{
    	SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    	SqlSession openSession = sqlSessionFactory.openSession();
    	SqlSession openSession2 = sqlSessionFactory.openSession();
    	try{
    
    		EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
    		EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class);
    		
    		Employee emp01 = mapper.getEmpById(1);
    		System.out.println(emp01);
    		
    		Employee emp02 = mapper2.getEmpById(1);
    		System.out.println(emp02);
    		openSession.close();
    		openSession2.close();
    		
    	}finally{
    		
    	}
    }

    结果:

    说明:第二次又重新发送了sql,因为从二级缓存中取数据时,会话没关闭所以二级缓存中没数据,所以又去一级缓存中查询,也没有数据则发送了sql查数据库;

    所以,只有会话关闭或提交后,一级缓存中的数据才会转移到二级缓存中,然后因为是同一个namespace所以可以获取到数据;

    关于Mybatis的一级缓存和二级缓存执行顺序具体可参考:Mybatis的一级缓存和二级缓存执行顺序

    四、和缓存有关的设置/属性

    1)、mybatis全局配置文件中配置全局缓存开启和清空

    1.1)控制二级缓存的开启和关闭

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

             cacheEnabled=true:开启缓存;false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)

    1.2)控制一级缓存的开启和关闭

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

             localCacheScope:本地缓存作用域(一级缓存SESSION);

             当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;

    注意:一级缓存关闭后,二级缓存自然也无法使用;  

    2)、方法中sqlSession清除缓存测试

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

    如果openSession清空了缓存,即执行了openSession.clearCache()方法:

    结果:

     说明:openSession清空缓存不影响二级缓存;只清空了一级缓存;因为在openSession.close()时,就将一级缓存保存至了二级缓存; 

    3)、mapper.xml中也可以配置一级和二级缓存开启和使用

    3.1)每个select标签都默认配置了useCache="true":
             如果useCache= false:则表示不使用缓存(一级缓存依然使用,二级缓存不使用)
    3.2)每个增删改标签默认配置了flushCache="true":(一级二级都会清除)


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

    测试:默认flushCache="true":一级缓存和二级缓存都会被清空;

    执行增加操作:

    结果:

    注意:查询标签<select>默认flushCache="false":如果flushCache=true;每次查询之后都会清空缓存;一级和二级缓存都无法使用;

    五、整合第三方缓存

    mybatis通过map实现的缓存,很不专业;此时可以通过整合第三方缓存来达到缓存的目的;

    做法:实现Cache.java接口中的方法即可:

    如实现putObject()方法,往缓存中写数据;实现getObject()方法,从缓存中获取数据;至于什么时候调用,由mybatis决定;

     具体可参考:Mybatis整合第三方缓存ehcache

    展开全文
  • Hibernate一级缓存和二级缓存详解

    万次阅读 多人点赞 2014-10-14 09:02:38
    一、一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个session)又做了同一个操作,...

    一、一级缓存二级缓存的概念解释

    (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个

    session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据;

    (2)二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory

    创建的某个session执行了相同的操作,hibernate就会从二级缓存中拿结果,而不会再去连接数据库;

    (3)Hibernate中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理

    的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓

    存可以进行配置和更改,并且可以动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存;


    二、一级缓存和二级缓存的比较


    (1)第一级缓存 第二级缓存 存放数据的形式相互关联的持久化对象 对象的散装数据 缓存的范围事务范围,每个事务都有单独的第一级

    缓存进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享并发访问策略由于每个事务都拥有单独的第一级缓存,不

    会出现并发问题,无需提供并发访问策略由于多个事务会同时访问第二级缓存中相同数据,因此必须提供适当的并发访问策略,来保

    证特定的事务隔离级别数据过期策略没有提供数据过期策略。

    (2)处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者

    清除特定的对象必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处

    于缓存中的最长空闲时间物理存储介质内存内存和硬盘。

    (3)对象的散装数据首先存放在基于内存的缓存中,当内存中对象的数目达到数

    据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。

    (4)缓存的软件实现在Hibernate的Session的实现中包含了缓存的

    实现由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider)。用于把特定的缓存插件集成到Hibernate中。

    (5)启用缓存的方式

    只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库

    中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接

    通过JDBC API来执行指操作。

    (6)用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就

    可以考虑使用第二级缓存。

    (7)只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。

    用户管理缓存的方式第一级缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。

    Session的 evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。第二级缓存的物理介质可以是内存和硬盘,因此第二

    级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。

    (8)管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。


    三、 一级缓存的管理


    (1)当应用程序调用Session的save()、update()、savaeOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或

    filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,

    Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj)

    :从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。

    (2)save、update、saveOrupdate、load、list、iterate、lock会向一级缓存存放数据;

    save 案例:
    	//添加一个学生
    			Student student=new Student();
    			student.setName("小东");
    			
    			s.save(student);//放入一级缓存
    			
    			//我马上查询
    			Student stu2=(Student) s.get(Student.class, student.getId()); //select
    			System.out.println("你刚刚加入的学生名字是"+stu2.getName());

    (3) 什么操作会从一级缓存取数据 get、load、list

     

    get / load 会首先从一级缓存中取,如没有.再有不同的操作[get 会立即向数据库发请求,而load 会返回一个代理对象,直到用户真的去使用数据,才会向数据库发请求

    //查询45号学生
    		
    			Student stu=(Student) s.get(Student.class, 45);
    			
    			System.out.println("|||||||||||||||||||");
    			
    			String hql="from Student where id=45";
    			
    			
    			Student stu2=(Student) s.createQuery(hql).uniqueResult();
    			
    			System.out.println(stu2.getName());

    从上面的案例,我们看出 query.list() query.uniueResut() 不会从一级缓取数据但是query.list 或者query.uniqueRestu() 会向一级缓存放数据的.

    注意:

    ① 一级缓存不需要配置,就可以使用,它本身没有保护机制,所以我们程序员要考虑这个问题,我们可以同 evict 或者 clear来清除session缓存中对象. evict 是清除一个对象,clear是清除所有的sesion缓存对象

    ② session级缓存中对象的生命周期session关闭后,就自动销毁.

    ③ 我们自己用HashMap来模拟一个Session缓存,加深对缓存的深入.

    四、Hibernate二级缓存的管理


    1. Hibernate二级缓存策略的一般过程如下:

    1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。

    2) 把获得的所有数据对象根据ID放入到第二级缓存中。

    3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

    4) 删除、更新、增加数据的时候,同时更新缓存。

    Hibernate二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。

    5) 二级缓存的对象可能放在内存,也可能放在磁盘.


    2. 什么样的数据适合存放到第二级缓存中? 

    1) 很少被修改的数据 

    2) 不是很重要的数据,允许出现偶尔并发的数据 

    3) 不会被并发访问的数据 

    4) 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。

    3. 不适合存放到第二级缓存的数据? 

    1) 经常被修改的数据 

    2) 财务数据,绝对不允许出现并发 

    3) 与其他应用共享的数据。

    4. 常用的缓存插件 Hibernater二级缓存是一个插件,下面是几种常用的缓存插件:

    ◆EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。

    ◆OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询

    缓存提供了支持。

    ◆SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。

    ◆JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。


    5. 配置Hibernate二级缓存的主要步骤:

    1) 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。

    2) 选择合适的缓存插件,然后编辑该插件的配置文件。

    <property name="hbm2ddl.auto">update</property>
    	<!-- 启动二级缓存 -->
    	<property name="cache.use_second_level_cache">true</property>
    	<!-- 指定使用哪种二级缓存 -->
    	<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
    	<mapping resource="com/hsp/domain/Department.hbm.xml" />
    	<mapping resource="com/hsp/domain/Student.hbm.xml" />
    	<!-- 指定哪个domain启用二级缓存 
    	特别说明二级缓存策略:
    	1. read-only
    	2. read-write
    	3. nonstrict-read-write
    	4. transcational
    	-->
    	<class-cache class="com.hsp.domain.Student" usage="read-write"/>

    3) 可以把oscache.properties文件放在  src 目录下,这样你可以指定放入二级缓存的对象 capacity  大小 默认 1000

    6.使用二级缓存:

    // TODO Auto-generated method stub
    		//通过获取一个sesion,让hibernate框架运行(config->加载hibernate.cfg.xml)
    		Session s=null;
    		Transaction tx=null;
    		
    		try {
    			//我们使用基础模板来讲解.
    			s=HibernateUtil.openSession();
    			tx=s.beginTransaction();
    			
    			//查询45号学生
    		
    			Student stu1=(Student) s.get(Student.class, 45);//45->一级缓存		
    			System.out.println(stu1.getName());
    			
    			
    			tx.commit();
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    			if(tx!=null){
    				tx.rollback();
    			}
    		}finally{
    			
    			if(s!=null && s.isOpen()){
    				s.close();
    			}
    		}
    		
    		System.out.println("*********************************");
    		try {
    			//我们使用基础模板来讲解.
    			s=HibernateUtil.openSession();
    			tx=s.beginTransaction();
    			
    			//查询45号学生
    		
    			Student stu1=(Student) s.get(Student.class, 45);	
    			System.out.println(stu1.getName());
    			
    			Student stu3=(Student) s.get(Student.class, 46);	
    			System.out.println(stu3.getName());
    				tx.commit();
    			
    		} catch (Exception e) {
    			e.printStackTrace();
    			if(tx!=null){
    				tx.rollback();
    			}
    		}finally{
    			
    			if(s!=null && s.isOpen()){
    				s.close();
    			}
    		}
    		
    		//完成一个统计,统计的信息在Sessfactory
    		//SessionFactory对象.
    		Statistics statistics= HibernateUtil.getSessionFactory().getStatistics();
    		System.out.println(statistics);
    		System.out.println("放入"+statistics.getSecondLevelCachePutCount());
    		System.out.println("命中"+statistics.getSecondLevelCacheHitCount());
    		System.out.println("错过"+statistics.getSecondLevelCacheMissCount());

    在配置了二级缓存后,请大家要注意可以通过 Statistics,查看你的配置命中率高不高



    展开全文
  • Hibernate 一级缓存和二级缓存的区别
  • 2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取 3、一般不会关闭一级缓存 4、二级缓存默认不开启 5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库 ...
  • Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存是指SqlSession级别的缓存,当在同一个SqlSession中进行相同的SQL语句查询时,第二次以后的查询不会从数据库查询,而是直接...
  • Mybatis一级缓存和二级缓存

    千次阅读 2021-04-07 09:38:42
    Mybatis中缓存分为一级缓存和二级缓存,主要是通过缓存策略来减少与数据库的交互次数,提高性能 一级缓存一级缓存是SqlSession范围的缓存,当调用SqlSession的增删改,commit(),close()等方法时,一级缓存就...
  • Mybatis 一级缓存和二级缓存的使用

    万次阅读 2021-01-19 10:28:52
    目录Mybatis缓存一级缓存二级缓存缓存原理 Mybatis缓存 官方文档:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html#cache MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置定制 Mybatis中...
  • MyBatis中的一级缓存和二级缓存介绍

    千次阅读 多人点赞 2017-06-13 20:07:22
    先说缓存,合理使用缓存是优化...一级缓存 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据区域是互相不影响的。也
  • 上一篇:21-Mybatis二级缓存... 关闭二级缓存一级缓存可正常使用 2. <select useCache="false"> 关闭二级缓存一级缓存可正常使用 3. <select flushCache="true"> :一二级均被清空
  • hibernate二级缓存(一)一级缓存二级缓存 1.hibernate一级缓存 hibernate的一级缓存是session级别的缓存,一级缓存hibernate默认启用且不能被卸载,一个事务内有效。 特点: 使用一级缓存的目的是为了...
  • 一级缓存和二级缓存(面试题)

    千次阅读 2019-09-22 20:35:40
    一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。 一级缓存的作用域是SqlSession范围的,当在...
  • Mybatis一级缓存二级缓存

    千次阅读 2019-01-01 15:31:16
    mybatis的有两种缓存,一级缓存和二级缓存。两个缓存的不同点和相同点总结如下 不同点: 一级缓存存在于一个SqlSession之内,二级缓存存在于不同的SqlSession之间 一级缓存不需要手动开启,属于默认开启状态;...
  • 【MyBatis】MyBatis一级缓存和二级缓存

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

    万次阅读 多人点赞 2017-02-27 14:01:56
    一级缓存:是SQlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中... 二级缓存:是mapper级别的缓存,多个SqlSession去操作同一个mapper的sql语句,多个SqlSession可以共用二级缓存二级缓存是跨
  • Mybatis一级缓存和二级缓存的区别

    万次阅读 多人点赞 2020-03-11 21:35:06
    1)一级缓存 Mybatis的一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。 在同一个SqlSession中,执行相同的SQL查询时;第一次会去查询数据库,并写在缓存中,第次会直接从缓存中...
  • 一级缓存2.1 一级缓存的命中场景2.2 触发清空一级缓存2.3 一级缓存源码分析3 二级缓存3.1 二级缓存的设计3.2 二级缓存的使用3.3 二级缓存的命中场景3.4 二级缓存源码分析3.4.1 query查询操作。3.4.2 commit提交操作...
  • MyBatis的一级缓存和二级缓存

    千次阅读 2017-04-12 15:20:41
    mybatis缓存介绍如下图,是mybatis一级缓存和二级缓存的区别图解:一级缓存:Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到...
  • mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache
  • Mybatis的一级缓存和二级缓存

    千次阅读 2018-05-16 01:20:22
    1、简介MyBatis提供了支持一级缓存和二级缓存
  • hibernate一级缓存和二级缓存的区别

    千次阅读 2019-02-01 11:21:39
    hibernate一级缓存和二级缓存的区别
  • mybatis 一级缓存和二级缓存简介

    万次阅读 2015-11-24 23:10:36
    正如大多数持久层框架一样,MyBatis 同样提供了一级缓存和二级缓存的支持 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的...
  • 一级缓存和二级缓存区别

    千次阅读 2016-08-16 21:06:14
    级缓存和二级缓存区别 一级缓存:  就是Session级别的缓存。一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中。  如果短时间内这个session(一定要同一个session)又做了同一个操作,那么...
  • CPU中的一级缓存二级缓存,三级缓存

    千次阅读 多人点赞 2018-06-14 13:49:56
    CPU中的一级缓存二级缓存,三级缓存 缓存又叫高速缓冲存储器,其作用在于缓解主存速度慢、跟不上CPU读写速度要求的矛盾。 缓存的实现原理,是把CPU最近最可能用到的少量信息(数据或指令)从主存复制到CACHE中,当...
  • Hibernate的Session提供了一级缓存的功能,默认总是有效的,当应用程序保存持久化实体、修改持久化实体时,Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中,除非显示调用了Session的flush()...
  • :mybatis查询缓存基本介绍 缓存:将相同查询条件的sql语句执行遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询sql时候不在执行sql与数据库交互,而是直接从缓存中获取结果,减少...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 448,761
精华内容 179,504
关键字:

一级缓存和二级缓存