精华内容
下载资源
问答
  • 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一级缓存和二级缓存理解

    千次阅读 2018-11-04 17:30:07
    Mybatis提供查询缓存分为一级缓存和二级缓存,用于减轻数据库压力,提高数据库性能。 一级缓存的工作原理 一级缓存是SqlSession级别的缓存  MyBatis的一级查询缓存是由 org.apache.ibatis.cache.impl....

    持久层框架:Mybatis

    Mybatis提供查询缓存分为一级缓存和二级缓存,用于减轻数据库压力,提高数据库性能。

    一级缓存的工作原理

    一级缓存是SqlSession级别的缓存

      MyBatis的一级查询缓存是由 org.apache.ibatis.cache.impl.PerpetualCache 类的 HashMap本地缓存实现的,它的作用域则是SqlSession,它的作用域也就是生命周期,假如同一个SqlSession中执行两次sql语句的查询,这两次的查询的位置是不同的,第一次查询时,由于没有缓存结果则是从数据库中进行查询结果,得到结果后写入缓存并将结果返回给查询语句,而在进行第二次查询时,这时缓存中已经有符合条件的结果集,这次的查询就会在缓存获得结果并返回,而不会向数据库进行查询。当SqlSession结束后相应的缓存也就销毁了。 MyBatis默认一级查询时开启状态,而且不能关闭。

    二级缓存的工作原理

    二级缓存与一级缓存不同在于它更强大一些,强大在它的生命周期比一级缓存长,二级缓存的生命周期是与整个应用同步的,与应用同生共死,这个时候二级缓存就与SqlSession没有关系了。这是从它们两者的生命周期上来讲,除此之外它俩的用途目的也不一样,一级缓存是用来共享数据提升速度和效率的,而二级缓存则是为了延长查询结果的保存时间,从而提高系统性能而已,二级缓存也是可以用于共享数据的。 
           二级缓存在myBatis中有内置的,其实现类为 org.apache.ibatis.cache.impl.PerpetualCache,并且使用的时候实体类需要实现序列化接口,除了内置的之外还有一个ehcache,相对于内置的缓存来说这个是专业的,用起来也比较方便。

    mybatis的二级缓存时mapper范围级别的,处理在sqlMapperConfig中设置二级缓存总开关,还要再具体的mapper中开启二级缓存。

    sqlSession1去查询用户id为1的用户信息,查询到用户信息会将查询数据存储到二级缓存中。

    sqlSession2去查询用户id为1的用户信息,去缓存中找是否存在数据,如果存在直接从缓存中取出数据。

     

    区别:
     

    二级缓存与一级缓存区别,二级缓存的范围更大,是多个sqlSession可以共享同一个二级缓存区域。

    UserMapper有一个二级缓存区域(按namespace区分),其他mapper(按namespace区分)也有自己的二级缓存区域

    每一个namespace的mapper都有一个二级缓存区域,两个mapper的namespace如果相同,这两个mapper执行sql所查询到的数据将存储在相同的二级缓存区域中。

    缓存中重要的属性
    禁用二级缓存
    在statement中设置useCachefalse可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。

    <select id=”findUserById” resultMap=”BaseResultMap” useCache=”false”>

    总结:针对每次查询都需要最新的数据,需要设置成useCache=”false”,禁用二级缓存

    刷新缓存(就是清空缓存)
    在mapper的同一个namespace中如果有其他insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

    设置statement配置中的flushCache=”true”属性,默认情况下为true即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。
     

    二级缓存的应用场景

    很少被修改的数据

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

    不会被并发访问的数据

    常用的缓存插件

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

    redis:是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)

     

    分布式缓存的典型应用场景可分为以下几类:
    页面缓存.用来缓存Web 页面的内容片段,包括HTML、CSS 和图片等,多应用于社交网站等;
    应用对象缓存.缓存系统作为ORM 框架的二级缓存对外提供服务,目的是减轻数据库的负载压力,加速应用访问;
    状态缓存.缓存包括Session 会话状态及应用横向扩展时的状态数据等,这类数据一般是难以恢复的,对可用性要求较高,多应用于高可用集群;
    并行处理.通常涉及大量中间计算结果需要共享;
    事件处理.分布式缓存提供了针对事件流的连续查询(continuous query)处理技术,满足实时性需求;
    极限事务处理.分布式缓存为事务型应用提供高吞吐率、低延时的解决方案,支持高并发事务请求处理,多应用于铁路、金融服务和电信等领域.

    展开全文
  • 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,查看你的配置命中率高不高



    展开全文
  • 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中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且是不能关闭的。一级缓存是指SqlSession级别的缓存,当在同一个SqlSession中进行相同的SQL语句查询时,第二次以后的查询不会从数据库查询,而是直接...
  • Mybatis的一级缓存和二级缓存详解

    千次阅读 多人点赞 2018-11-19 19:11:59
    注:本笔记是根据尚硅谷的MyBatis视频记录的 对于任何个持久层框架,都有缓存机制;缓存在电脑中有块真实的存储空间...关于Mybatis的一级缓存和二级缓存执行顺序具体可参考:Mybatis的一级缓存和二级缓存执行...
  • 2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取 3、一般不会关闭一级缓存 4、二级缓存默认不开启 5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库 ...
  • Hiberante3 一级缓存总结 1. Session 级别的缓存,它同session邦定。它的生命周期session相同。Session消毁,它也同时消...2. 两个session 不能共享一级缓存,因它会伴随session的生命周期的创建消毁; 3. 
  • 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之间的缓存数据区域是互相不影响的。也
  • Mybatis一级缓存和二级缓存

    千次阅读 2021-04-07 09:38:42
    Mybatis中缓存分为一级缓存和二级缓存,主要是通过缓存策略来减少与数据库的交互次数,提高性能 一级缓存一级缓存是SqlSession范围的缓存,当调用SqlSession的增删改,commit(),close()等方法时,一级缓存就...
  • 上一篇:21-Mybatis二级缓存... 关闭二级缓存一级缓存可正常使用 2. <select useCache="false"> 关闭二级缓存一级缓存可正常使用 3. <select flushCache="true"> :一二级均被清空
  • 一级缓存和二级缓存理解

    千次阅读 2018-05-17 18:31:29
    、什么是缓存和内存:1.什么是内存?内存是计算机中重要的部件之,它是与CPU进行沟通的桥梁。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。内存(Memory)也被称为内存储器,...
  • 1.一级缓存和二级缓存的区别 一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。  一级缓存的...
  • 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之间 一级缓存不需要手动开启,属于默认开启状态;...
  • 一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。 一级缓存的作用域是SqlSession范围的,当在...
  • 一级缓存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-02-27 14:01:56
    一级缓存:是SQlSession级别的缓存。在操作数据库时需要构造SqlSession对象,在对象中... 二级缓存:是mapper级别的缓存,多个SqlSession去操作同一个mapper的sql语句,多个SqlSession可以共用二级缓存二级缓存是跨
  • 【MyBatis】MyBatis一级缓存和二级缓存

    千次阅读 2018-01-27 19:35:53
    MyBatis自带的缓存有一级缓存和二级缓存 一级缓存Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。 也就是在同一个SqlSession中,执行相同的查询SQL,第一次会...
  • 关于堆和栈、一级缓存和二级缓存理解 数据结构的堆和栈  堆:先进先出的  栈:先进后出的、自顶向下 内存分布上的堆和栈  堆:也称为动态内存分配,是由我们自己在程序中分配内存和释放的,就是生存期是由我们...
  • mybatis的缓存机制(一级缓存二级缓存和刷新缓存)和mybatis整合ehcache
  • Mybatis一级缓存和二级缓存的区别

    万次阅读 多人点赞 2020-03-11 21:35:06
    1)一级缓存 Mybatis的一级缓存是指SQLSession,一级缓存的作用域是SQlSession, Mabits默认开启一级缓存。 在同一个SqlSession中,执行相同的SQL查询时;第一次会去查询数据库,并写在缓存中,第次会直接从缓存中...
  • Hibernate的Session提供了一级缓存的功能,默认总是有效的,当应用程序保存持久化实体、修改持久化实体时,Session并不会立即把这种改变提交到数据库,而是缓存在当前的Session中,除非显示调用了Session的flush()...
  • MyBatis的一级缓存和二级缓存

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

    千次阅读 多人点赞 2018-06-14 13:49:56
    CPU中的一级缓存二级缓存,三级缓存 缓存又叫高速缓冲存储器,其作用在于缓解主存速度慢、跟不上CPU读写速度要求的矛盾。 缓存的实现原理,是把CPU最近最可能用到的少量信息(数据或指令)从主存复制到CACHE中,当...
  • :mybatis查询缓存基本介绍 缓存:将相同查询条件的sql语句执行遍后所得到的结果存在内存或者某种缓存介质当中,当下次遇到一模一样的查询sql时候不在执行sql与数据库交互,而是直接从缓存中获取结果,减少...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 437,175
精华内容 174,870
关键字:

一级缓存和二级缓存如何理解