zoukankan      html  css  js  c++  java
  • spring整合mybatis后,mybatis一级缓存失效的原因

    一般来说,可以在5个方面进行缓存的设计:

    1. 最底层可以配置的是数据库自带的query cache,
    2. mybatis的一级缓存,默认情况下都处于开启状态,只能使用自带的PerpetualCache,无法配置第三方缓存
    3. mybatis的二级缓存,可以配置开关状态,默认使用自带的PerpetualCache,但功能比较弱,能够配置第三方缓存,
    4. service层的缓存配置,结合spring,可以灵活进行选择
    5. 针对实际业务情况,直接缓存部分html页面,直接返回给客户端。

    在测试过程中,发现mybatis的一级缓存没有起作用,失效了。经过调研,发现是由于以下原因引起的:

    1.mybatis的一级缓存生效的范围是sqlsession,是为了在sqlsession没有关闭时,业务需要重复查询相同数据使用的。一旦sqlsession关闭,则由这个sqlsession缓存的数据将会被清空。

    2.spring对mybatis的sqlsession的使用是由template控制的,sqlSessionTemplate又被spring当作resource放在当前线程的上下文里(threadlocal),spring通过mybatis调用数据库的过程如下:

    1. 我们需要访问数据
    2. spring检查到了这种需求,于是去申请一个mybatis的sqlsession(资源池),并将申请到的sqlsession与当前线程绑定,放入threadlocal里面
    3. sqlSessionTemplate从threadlocal获取到sqlsession,去执行查询
    4. 查询结束,清空threadlocal中与当前线程绑定的sqlsession,释放资源
    5. 我们又需要访问数据
    6. 返回到步骤b

    通过以上步骤后发现,同一线程里面两次查询同一数据所使用的sqlsession是不相同的,所以,给人的印象就是结合spring后,mybatis的一级缓存失效了。

    而在spring中一般都是用sqlSessionTemplate,如下

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:configuration.xml" />
        <property name="mapperLocations">
            <list>
                <value>classpath*:com/hejb/sqlmap/*.xml</value>
            </list>
        </property>
    </bean>
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg ref="sqlSessionFactory" />
    </bean>

    在SqlSessionTemplate中执行SQL的session都是通过sqlSessionProxy来,sqlSessionProxy的生成在构造函数中赋值,如下:

    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());

    sqlSessionProxy通过JDK的动态代理方法生成的一个代理类,主要逻辑在InvocationHandler对执行的方法进行了前后拦截,主要逻辑在invoke中,包好了每次执行对sqlsesstion的创建,commit,关闭

    代码如下:

    private class SqlSessionInterceptor implements InvocationHandler {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       // 每次执行前都创建一个新的sqlSession
       SqlSession sqlSession = getSqlSession(
         SqlSessionTemplate.this.sqlSessionFactory,
         SqlSessionTemplate.this.executorType,
         SqlSessionTemplate.this.exceptionTranslator);
       try {
       // 执行方法
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
         // force commit even on non-dirty sessions because some databases require
         // a commit/rollback before calling close()
         sqlSession.commit(true);
        }
        return result;
       } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
         // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
         closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
         sqlSession = null;
         Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
         if (translated != null) {
          unwrapped = translated;
         }
        }
        throw unwrapped;
       } finally {
        if (sqlSession != null) {
         closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
       }
      }
     }

    因为每次都进行创建,所以就用不上sqlSession的缓存了.

    对于开启了事务为什么可以用上呢, 跟入getSqlSession方法

    如下:

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
      notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
      notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
      SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
      // 首先从SqlSessionHolder里取出session
      SqlSession session = sessionHolder(executorType, holder);
      if (session != null) {
       return session;
      }
      if (LOGGER.isDebugEnabled()) {
       LOGGER.debug("Creating a new SqlSession");
      }
      session = sessionFactory.openSession(executorType);
      registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
      return session;
     }

    在里面维护了个SqlSessionHolder,关联了事务与session,如果存在则直接取出,否则则新建个session,所以在有事务的里,每个session都是同一个,故能用上缓存了

  • 相关阅读:
    (转)【web前端培训之前后端的配合(中)】继续昨日的故事
    ural(Timus) 1136. Parliament
    scau Josephus Problem
    ACMICPC Live Archive 6204 Poker End Games
    uva 10391 Compound Words
    ACMICPC Live Archive 3222 Joke with Turtles
    uva 10132 File Fragmentation
    uva 270 Lining Up
    【转】各种字符串哈希函数比较
    uva 10905 Children's Game
  • 原文地址:https://www.cnblogs.com/gmhappy/p/11864104.html
Copyright © 2011-2022 走看看