前言:
如果大家使用過Spring事務(wù)管理,會(huì)發(fā)現(xiàn)Spring提供的事務(wù)分為“只讀”和“讀寫”事務(wù)兩類。這不免就會(huì)疑問這兩種事務(wù)會(huì)有什么不同?本文則通過對Spring和Hibernate源代碼的剖析來找出這兩種事務(wù)的區(qū)別。特別是運(yùn)行性能方面的區(qū)別。
解讀的源代碼版本為 Spring 2.5.6.SEC01 ,Hibernate 3.3.2.GA。
Spring對事務(wù)的支持也分編程式和聲明式,本文以基于Annotation方式的聲明式事務(wù)為例:
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>
從配置中,可以看到事務(wù)的攔截,都由
TransactionInterceptor 類進(jìn)行處理下面是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);
}
//處理事務(wù)的操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
.省略
} 針對事務(wù)的操作,就是調(diào)用
commitTransactionAfterReturning 方法進(jìn)行事務(wù)的處理。
該方法會(huì)調(diào)用AbstractPlatformTransactionManager類的commit和processCommit方法。processCommit方法是真正調(diào)用Hibernate事務(wù)處理的實(shí)現(xiàn)。
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()) { //如果是一個(gè)新啟的事務(wù)
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}//則進(jìn)行事務(wù)的提交處理
doCommit(status);
}
代碼省略
} doCommit 方法的調(diào)用 會(huì)觸發(fā) Hibernate的JDBCTransaction的commit方法調(diào)用
public void commit() throws HibernateException {
if (!begun) {
throw new TransactionException("Transaction not successfully started");
}
log.debug("commit");
//如果是只讀事務(wù),Spring會(huì)將transactionContext的 isFlushModeNever 設(shè)置為true
if ( !transactionContext.isFlushModeNever() && callback ) {
//刷新一級緩存中的持久對象,向數(shù)據(jù)庫發(fā)送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();
}
}
關(guān)鍵點(diǎn)已經(jīng)在上面的注釋中說明。
當(dāng)事務(wù)被標(biāo)識(shí)為只讀事務(wù)時(shí),Spring可以對某些可以針對只讀事務(wù)進(jìn)行優(yōu)化的資源就可以執(zhí)行相應(yīng)的優(yōu)化措施,上面Spring告之
hibernate的session在只讀事務(wù)模式下不用嘗試檢測和同步持久對象的狀態(tài)的更新。
總結(jié):
如果在使用事務(wù)的情況下,所有操作都是讀操作,那建議把事務(wù)設(shè)置成只讀事務(wù),或者事務(wù)的傳播途徑最好能設(shè)置為 supports (運(yùn)行在當(dāng)前的事務(wù)范圍內(nèi),如果當(dāng)前沒有啟動(dòng)事務(wù),那么就不在事務(wù)范圍內(nèi)運(yùn)行)或者 not supports (不在事務(wù)范圍內(nèi)執(zhí)行,如果當(dāng)前啟動(dòng)了事務(wù),那么掛起當(dāng)前事務(wù)),這樣不在事務(wù)下,就不會(huì)調(diào)用
transactionContext.managedFlush(); 方法。所有只讀事務(wù)與讀寫事務(wù)的比較大的運(yùn)行性能區(qū)別就是只讀事務(wù)避免了Hibernate的檢測和同步持久對象的狀態(tài)的更新,提升了運(yùn)行性能。