Open luozhenyu opened 1 year ago
The cache feature belong the MyBatis core module. Probably, the MyBatis core module does not support to clear cache when rollback savepoint. You can prevent this behavior with localCacheScope
set to STATEMENT
instead of SESSION
(default) or call the SqlSession#clearCache()
.
@harawata Do you have any comments?
MyBatis' cache is not aware of Spring transaction's savepoint. You may have to use the workarounds proposed by @kazuki43zoo .
@harawata How about merge the following code into mybatis-spring-boot-starter? I solved this problem by add a clear cache post processor just like spring-tx did.
import org.aopalliance.intercept.MethodInterceptor;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionalProxy;
import java.lang.reflect.Method;
/**
* Clear mybatis cache after transaction rollback
* <p>Spring will not notify mybatis to clear cache after a savepoint rollback. This class
* catches a exception, clears the mybatis cache and rethrows it back</p>
*
* @author luozhenyu
*/
@Component
public class MybatisClearCachePostProcessor extends AbstractAdvisingBeanPostProcessor implements InitializingBean {
private final AnnotationTransactionAttributeSource annotationTransactionAttributeSource
= new AnnotationTransactionAttributeSource();
private final SqlSessionTemplate sqlSessionTemplate;
@Autowired
public MybatisClearCachePostProcessor(SqlSessionTemplate sqlSessionTemplate) {
this.sqlSessionTemplate = sqlSessionTemplate;
}
@Override
public void afterPropertiesSet() {
StaticMethodMatcherPointcutAdvisor clearCacheAdvisor = new StaticMethodMatcherPointcutAdvisor() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
if (targetClass != null && TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
TransactionAttribute attribute = annotationTransactionAttributeSource.getTransactionAttribute(method, targetClass);
return attribute != null && attribute.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED;
}
};
clearCacheAdvisor.setAdvice((MethodInterceptor) invocation -> {
try {
return invocation.proceed();
} catch (Throwable t) {
Method method = invocation.getMethod();
Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
TransactionAttribute attribute = annotationTransactionAttributeSource.getTransactionAttribute(method, targetClass);
if (attribute != null && attribute.rollbackOn(t)) {
sqlSessionTemplate.clearCache();
}
throw t;
}
});
this.advisor = clearCacheAdvisor;
this.beforeExistingAdvisors = true;
}
}