spring mybatis 통합 후session 캐시가 사용되지 않는 원인 분석

8486 단어 springmybatissession
스프링으로 mybatis를 통합했기 때문에 mybatis의session 캐시를 거의 사용하지 않습니다.습관은 로컬 캐시가 맵으로 쓰거나 제3자의 로컬 캐시 프레임워크를 도입하는 것이다ehcache,Guava
그래서 얘기를 꺼내서 고민을 해봤어요.
실험하(spring 통합 mybatis 약, 인터넷 더미), 먼저 mybatis 레벨의session 캐시 보기
인쇄 sql 문장 내보내기
configuration.xml 가입

<settings>
    <!--   -->
    <setting name="logImpl" value="STDOUT_LOGGING" />
  </settings>
테스트 소스 코드는 다음과 같습니다.
종류

/**
 *  spring mybatis 
 *
 * @author   2017.02.15
 */
@Component
public class TestDao {
  private Logger logger = Logger.getLogger(TestDao.class.getName());
  @Autowired
  private SqlSessionTemplate sqlSessionTemplate;
  @Autowired
  private SqlSessionFactory sqlSessionFactory;
  /**
   *  SQL
   *
   * @param id
   * @return
   */
  public TestDto selectBySpring(String id) {
    TestDto testDto = (TestDto) sqlSessionTemplate.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    testDto = (TestDto) sqlSessionTemplate.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    return testDto;
  }
  /**
   *  SQL
   *
   * @param id
   * @return
   */
  public TestDto selectByMybatis(String id) {
    SqlSession session = sqlSessionFactory.openSession();
    TestDto testDto = session.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    testDto = session.selectOne("com.hejb.TestDto.selectByPrimaryKey", id);
    return testDto;
  }
}
테스트 서비스 클래스

@Component
public class TestService {
  @Autowired
  private TestDao testDao;
  /**
   *  spring Mybatis 
   */
  public void testSpringCashe() {
    // SQL
    testDao.selectBySpring("1");
  }
  /**
   *  spring Mybatis 
   */
  @Transactional
  public void testSpringCasheWithTran() {
    //spring , 1 SQL
    testDao.selectBySpring("1");
  }
  /**
   * mybatis 
   */
  public void testCash4Mybatise() {
    // mybatis, 1 SQL
    testDao.selectByMybatis("1");
  }
}
출력 결과:
testSpringCashe() 메서드는 SQL을 두 번 실행했고 다른 메서드는 한 번 실행되었습니다.
소스 코드 추적:
일단 my batis의 sql Session.
마지막까지 추적해서 org로 호출합니다.apache.ibatis.executor.BaseExecutor의query 방법

try {
   queryStack++;
   list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; // 
   if (list != null) {
    handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); // key CacheKey
   } else {
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
   }
캐시 데이터를 어떻게 꺼내는지 코드 붙이기

private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
  if (ms.getStatementType() == StatementType.CALLABLE) {
   final Object cachedParameter = localOutputParameterCache.getObject(key);// localOutputParameterCache 
   if (cachedParameter != null && parameter != null) {
    final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
    final MetaObject metaParameter = configuration.newMetaObject(parameter);
    for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
     if (parameterMapping.getMode() != ParameterMode.IN) {
      final String parameterName = parameterMapping.getProperty();
      final Object cachedValue = metaCachedParameter.getValue(parameterName);
      metaParameter.setValue(parameterName, cachedValue);
     }
    }
   }
  }
 }
 
발견은local Output Parameter Cache에서 Perpetual Cache이고 Perpetual Cache에서 맵을 유지했습니다. 바로session의 캐시 본질입니다.
중점은 아래의 두 가지 힘든 논리에 주목할 수 있다
PerpetualCache, 두 개의 매개 변수, id 및 map
CacheKey,map에 저장된 키,equas를 덮어쓰는 방법이 있습니다. 캐시를 가져올 때 호출합니다.
이런 로컬 맵 캐시 획득 대상의 단점은 내가 구덩이를 밟은 경험(예전에 나도 맵으로 실현한 로컬 캐시), 바로 획득 대상은 clone이 아니라 돌아오는 두 대상은 모두 하나의 주소이다
스프링에서는 일반적으로 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());
qlSessionProxy는 JDK의 동적 에이전트 방법을 통해 생성된 에이전트 클래스입니다. 주요 논리는 InvocationHandler에서 실행 방법에 대해 앞뒤로 차단했습니다. 주요 논리는 invoke에서 매번 실행할 때마다 qlsesstion에 대한 생성,common, 닫기
코드는 다음과 같습니다.

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);
    }
   }
  }
 }
매번 만들기 때문에 qlSession의 캐시를 사용할 수 없습니다.
사무를 열었는데 왜 사용할 수 있습니까, 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;
 }
안에 Sql Session Holder를 유지했습니다. 업무와 session을 연결했습니다. 존재하면 직접 꺼내고, 그렇지 않으면 session을 새로 만듭니다. 그래서 업무가 있는 모든 session은 동일하기 때문에 캐시를 사용할 수 있습니다.
상기 서술한 것은 편집자가 여러분께 소개한spring 통합 mybatis 후session 캐시를 사용하지 못하는 원인 분석입니다. 여러분께 도움이 되었으면 합니다. 궁금한 점이 있으면 저에게 메시지를 남겨 주십시오. 편집자는 제때에 답장을 드리겠습니다.여기에서도 저희 사이트에 대한 지지에 감사드립니다!

좋은 웹페이지 즐겨찾기