• 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都是同一个,故能用上缓存了

  • 相关阅读:
    基础连接已经关闭: 未能为 SSL/TLS 安全通道建立信任关系。
    Configuration system failed to initialize
    WebException 请求被中止: 操作超时
    The underlying connection was closed: The connection was closed unexpectedly.
    无法将数据写入传输连接: 在一个非套接字上尝试了一个操作
    C# .NET UDP 形式调用 graylog,gelf
    python3存入redis是bytes
    python commands模块在python3.x被subprocess取代
    subprocess
    nginx重新编译添加ssl模块
  • 原文地址:https://www.cnblogs.com/gmhappy/p/11864104.html
Copyright © 2020-2023  润新知