前言:
如果大家使用过Spring事务管理,会发现Spring提供的事务分为“只读”和“读写”事务两类。这不免就会疑问这两种事务会有什么不同?本文则通过对Spring和Hibernate源代码的剖析来找出这两种事务的区别。特别是运行性能方面的区别。
解读的源代码版本为 Spring 2.5.6.SEC01 ,Hibernate 3.3.2.GA。
Spring对事务的支持也分编程式和声明式,本文以基于Annotation方式的声明式事务为例:
Spring的配置如下:
<bean
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
<property name="proxyTargetClass" value="true"></property>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="entityManager" />
<property name="jpaProperties">
<props>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributeSource">
<bean
class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
</property>
</bean>
<bean id="transactionAttributeSourceAdvisor"
class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor" ref="transactionInterceptor" />
</bean>
从配置中,可以看到事务的拦截,都由
TransactionInterceptor 类进行处理
下面是invoke方法的核心处理过程:
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be <code>null</code>.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class targetClass = (invocation.getThis() != null ? invocation.getThis().getClass() : null);
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final String joinpointIdentification = methodIdentification(invocation.getMethod());
if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//处理事务的操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
.省略
}
针对事务的操作,就是调用
commitTransactionAfterReturning 方法进行事务的处理。
该方法会调用AbstractPlatformTransactionManager类的commit和processCommit方法。processCommit方法是真正调用Hibernate事务处理的实现。
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) { //如果是一个新启的事务
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}//则进行事务的提交处理
doCommit(status);
}
代码省略
}
doCommit 方法的调用 会触发 Hibernate的JDBCTransaction的commit方法调用
public void commit() throws HibernateException {
if (!begun) {
throw new TransactionException("Transaction not successfully started");
}
log.debug("commit");
//如果是只读事务,Spring会将transactionContext的 isFlushModeNever 设置为true
if ( !transactionContext.isFlushModeNever() && callback ) {
//刷新一级缓存中的持久对象,向数据库发送sql语句
transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
}
notifyLocalSynchsBeforeTransactionCompletion();
if ( callback ) {
jdbcContext.beforeTransactionCompletion( this );
}
try {
commitAndResetAutoCommit();
log.debug("committed JDBC Connection");
committed = true;
if ( callback ) {
jdbcContext.afterTransactionCompletion( true, this );
}
notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_COMMITTED );
}
catch (SQLException e) {
log.error("JDBC commit failed", e);
commitFailed = true;
if ( callback ) {
jdbcContext.afterTransactionCompletion( false, this );
}
notifyLocalSynchsAfterTransactionCompletion( Status.STATUS_UNKNOWN );
throw new TransactionException("JDBC commit failed", e);
}
finally {
closeIfRequired();
}
}
关键点已经在上面的注释中说明。
当事务被标识为只读事务时,Spring可以对某些可以针对只读事务进行优化的资源就可以执行相应的优化措施,上面Spring告之
hibernate的session在只读事务模式下不用尝试检测和同步持久对象的状态的更新。
总结:
如果在使用事务的情况下,所有操作都是读操作,那建议把事务设置成只读事务,或者事务的传播途径最好能设置为 supports (运行在当前的事务范围内,如果当前没有启动事务,那么就不在事务范围内运行)或者 not supports (不在事务范围内执行,如果当前启动了事务,那么挂起当前事务),这样不在事务下,就不会调用
transactionContext.managedFlush(); 方法。
所有只读事务与读写事务的比较大的运行性能区别就是只读事务避免了Hibernate的检测和同步持久对象的状态的更新,提升了运行性能。
Good Luck!
Yours Matthew!