spring mybatis 통합 후session 캐시가 사용되지 않는 원인 분석
그래서 얘기를 꺼내서 고민을 해봤어요.
실험하(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 캐시를 사용하지 못하는 원인 분석입니다. 여러분께 도움이 되었으면 합니다. 궁금한 점이 있으면 저에게 메시지를 남겨 주십시오. 편집자는 제때에 답장을 드리겠습니다.여기에서도 저희 사이트에 대한 지지에 감사드립니다!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
thymeleaf로 HTML 페이지를 동적으로 만듭니다 (spring + gradle)지난번에는 에서 화면에 HTML을 표시했습니다. 이번에는 화면을 동적으로 움직여보고 싶기 때문에 입력한 문자를 화면에 표시시키고 싶습니다. 초보자의 비망록이므로 이상한 점 등 있으면 지적 받을 수 있으면 기쁩니다! ...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.