使用hibernate和spring的項(xiàng)目大部分都有可能會(huì)用到OpenSessionInViewFilter和Transactioninterceptor。OpenSessionInViewFilter的主要目的是為了解決hibernate在view層的進(jìn)行懶加載的時(shí)候會(huì)遇到session被close的問(wèn)題。Transactioninterceptor事務(wù)攔截器為spring的聲明式事務(wù),方便開(kāi)啟和提交事務(wù),不用每次都手動(dòng)編寫hibernate事務(wù)相關(guān)代碼。
先看OpenSessionInViewFilter,在web.xml中的filter配置如下:
web.xml
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
<init-param>
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
初始化參數(shù)singleSession可選值有true(單session)和false(多session),打開(kāi)源碼,找到入口方法doFilterInternal如下:
OpenSessionInViewFilter
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
if (isSingleSession()) {
//此處省略n行代碼......
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
else {
//此處省略n行代碼......
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(sessionHolder.getSession(), sessionFactory);
}
else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
}
單session的情況下,請(qǐng)求開(kāi)始時(shí),先通過(guò)getSession方法取到一個(gè)session,然后將sessionFactory作為key,SessionHolder作為value綁定到resources中,請(qǐng)求結(jié)束的時(shí)候,解除綁定session并關(guān)閉session。多session的時(shí)候,請(qǐng)求開(kāi)始時(shí)執(zhí)行initDeferredClose方法標(biāo)記請(qǐng)求過(guò)程中產(chǎn)生的所有session都延遲關(guān)閉,請(qǐng)求結(jié)束時(shí)才關(guān)閉所有的session,也就是關(guān)閉請(qǐng)求中所有被保存起來(lái)的session。中間的filterChain.doFilter(request, response)方法是執(zhí)行其他filter的攔截方法以及請(qǐng)求對(duì)應(yīng)的具體處理內(nèi)容。
先看一下單session的bindResource方法如下:
bindResource
public static void bindResource(Object key, Object value) throws IllegalStateException {
//此處省略n行代碼......
Map<Object, Object> map = resources.get();
// set ThreadLocal Map if none found
if (map == null) {
map = new HashMap<Object, Object>();
resources.set(map);
}
Object oldValue = map.put(actualKey, value);
//此處省略n行代碼......
}
可以發(fā)現(xiàn)resources是一個(gè)ThreadLocal類型的變量,其內(nèi)部實(shí)現(xiàn)是一個(gè)Map,以當(dāng)前線程作為key,來(lái)達(dá)到[整個(gè)請(qǐng)求線程共享變量并且多個(gè)請(qǐng)求之間不會(huì)相互影響]的作用:
ThreadLocal
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
單session情況下,請(qǐng)求開(kāi)始就獲取session,而多session時(shí)候則是執(zhí)行的initDeferredClose這個(gè)方法:
initDeferredClose
public static void initDeferredClose(SessionFactory sessionFactory) {
Assert.notNull(sessionFactory, "No SessionFactory specified");
logger.debug("Initializing deferred close of Hibernate Sessions");
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
if (holderMap == null) {
holderMap = new HashMap<SessionFactory, Set<Session>>();
deferredCloseHolder.set(holderMap);
}
holderMap.put(sessionFactory, new LinkedHashSet<Session>(4));
}
這個(gè)方法首先判斷deferredCloseHolder中是否含有holderMap,如果沒(méi)有,就新建一個(gè)map變量并set到deferredCloseHolder,這個(gè)map中的Set<Session>集合后面會(huì)用來(lái)保存請(qǐng)求過(guò)程中所有產(chǎn)生的session,請(qǐng)求中的數(shù)據(jù)庫(kù)操作只要發(fā)現(xiàn)deferredCloseHolder中有值,就不會(huì)去關(guān)閉session,而是將session保存到set集合里,等到請(qǐng)求結(jié)束才關(guān)閉session。同樣可以發(fā)現(xiàn)這個(gè)deferredCloseHolder也是一個(gè)ThreadLocal類型的變量:
deferredCloseHolder
private static final ThreadLocal<Map<SessionFactory, Set<Session>>> deferredCloseHolder =
new NamedThreadLocal<Map<SessionFactory, Set<Session>>>("Hibernate Sessions registered for deferred close");
多session請(qǐng)求結(jié)束時(shí)候,執(zhí)行延遲關(guān)閉方法:
processDeferredClose
public static void processDeferredClose(SessionFactory sessionFactory) {
Assert.notNull(sessionFactory, "No SessionFactory specified");
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
if (holderMap == null || !holderMap.containsKey(sessionFactory)) {
throw new IllegalStateException("Deferred close not active for SessionFactory [" + sessionFactory + "]");
}
logger.debug("Processing deferred close of Hibernate Sessions");
Set<Session> sessions = holderMap.remove(sessionFactory);
for (Session session : sessions) {
closeSession(session);
}
if (holderMap.isEmpty()) {
deferredCloseHolder.remove();
}
}
可以看到,此處的holderMap就是請(qǐng)求開(kāi)始時(shí)往deferredCloseHolder中put的變量,然后從holderMap取出請(qǐng)求過(guò)程中產(chǎn)生的所有session并關(guān)閉。
接著回頭看單session的時(shí)候,會(huì)用getSession方法取到一個(gè)sesson:
getSession
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = getFlushMode();
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
private FlushMode flushMode = FlushMode.MANUAL;
此處有個(gè)細(xì)節(jié)就是,獲取到session的時(shí)候會(huì)把這個(gè)session的FlushMode設(shè)置為默認(rèn)的Manual狀態(tài),這個(gè)Manual狀態(tài)會(huì)導(dǎo)致執(zhí)行HibernateTemplate的增刪改方法(無(wú)事務(wù)的dao方法)報(bào)錯(cuò),只能執(zhí)行查詢方法。解決方案有兩個(gè):
可以通過(guò)將HibernateTemplate的增刪改方法(常稱dao方法)該為執(zhí)行spring事務(wù)攔截方法(常稱service方法),事務(wù)攔截器會(huì)把session的flushMode改為auto,事務(wù)結(jié)束之后再重新設(shè)置為原來(lái)的Manual狀態(tài)。
新建一個(gè)java類繼承OpenSessionInViewFilter,改寫getSession方法的FlushMode,將該java類代替OpenSessionInViewFilter配置到web.xml中
跟蹤到getSession方法的內(nèi)部實(shí)現(xiàn)doGetSession方法,這個(gè)方法在HibernateTemplate的增刪改查和事務(wù)攔截器Transactioninterceptor創(chuàng)建事務(wù)的時(shí)候也都會(huì)用到:
doGetSession
private static Session doGetSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
throws HibernateException, IllegalStateException {
Assert.notNull(sessionFactory, "No SessionFactory specified");
Object resource = TransactionSynchronizationManager.getResource(sessionFactory);
if (resource instanceof Session) {
return (Session) resource;
}
SessionHolder sessionHolder = (SessionHolder) resource;
if (sessionHolder != null && !sessionHolder.isEmpty()) {
// pre-bound Hibernate Session
Session session = null;
if (TransactionSynchronizationManager.isSynchronizationActive() &&
sessionHolder.doesNotHoldNonDefaultSession()) {
// Spring transaction management is active ->
// register pre-bound Session with it for transactional flushing.
session = sessionHolder.getValidatedSession();
if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
}
if (session != null) {
return session;
}
}
logger.debug("Opening Hibernate Session");
Session session = (entityInterceptor != null ?
sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
// Use same Session for further Hibernate actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// We're within a Spring-managed transaction, possibly from JtaTransactionManager.
logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
SessionHolder holderToUse = sessionHolder;
if (holderToUse == null) {
holderToUse = new SessionHolder(session);
}
else {
holderToUse.addSession(session);
}
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != sessionHolder) {
TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
}
// Check whether we are allowed to return the Session.
if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
closeSession(session);
throw new IllegalStateException("No Hibernate Session bound to thread, " +
"and configuration does not allow creation of non-transactional one here");
}
return session;
}
這段代碼比較長(zhǎng),主要邏輯如下:
如果ThreadLocal中有sessionHolder,說(shuō)明前面已經(jīng)在ThreadLocal中綁定過(guò)了session,直接從sessionHolder中取出sesision即可,不需要重新開(kāi)啟一個(gè)新的session,而是沿用之前綁定到ThreadLocal中的sessoin。如果ThreadLocal中沒(méi)有sessionHolder,說(shuō)明沒(méi)有綁定sesison,就通過(guò)
SessionFactoryUtils開(kāi)啟一個(gè)新的session,得到這個(gè)session之后再判斷當(dāng)前是否存在spring事務(wù),如果有就綁定到ThreadLocal中,之后在這個(gè)事務(wù)范圍內(nèi)的dao操作都共用這個(gè)session,如果沒(méi)有事務(wù),就直接返回這個(gè)session。
回過(guò)頭去看OpenSessionInViewFilter 在單session的情況下,通過(guò)getSession方法獲取到session之后,就執(zhí)行bindResource方法將session綁定到ThreadLocal中了:
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
到此就可以得出幾個(gè)小結(jié)論:
單sessoin情況下,請(qǐng)求一開(kāi)始就會(huì)創(chuàng)建session并綁定到ThreadLocal變量中,請(qǐng)求過(guò)程中需要用到hibernate的sesison進(jìn)行數(shù)據(jù)庫(kù)操作的時(shí)候,就直接從ThreadLocal變量中取出復(fù)用,請(qǐng)求結(jié)束了就關(guān)閉這個(gè)唯一的session,整個(gè)請(qǐng)求過(guò)程也只會(huì)占用一個(gè)數(shù)據(jù)庫(kù)鏈接。
多session的情況下,請(qǐng)求剛開(kāi)始并不創(chuàng)建session,而是等到請(qǐng)求過(guò)程中需要session的時(shí)候就open一個(gè)session,執(zhí)行多個(gè)HibernateTemplate的增刪改查方法就會(huì)創(chuàng)建多個(gè)session,請(qǐng)求過(guò)程中將會(huì)占用多個(gè)數(shù)據(jù)庫(kù)鏈接,并直到請(qǐng)求結(jié)束鏈接才會(huì)被釋放。
然后接著看HibernateTemplate的增刪改查代碼對(duì)session的處理情況,舉個(gè)save方法的例子:
save
public Serializable save(final Object entity) throws DataAccessException {
return executeWithNativeSession(new HibernateCallback<Serializable>() {
public Serializable doInHibernate(Session session) throws HibernateException {
checkWriteOperationAllowed(session);
return session.save(entity);
}
});
}
增刪改查都會(huì)調(diào)用executeWithNativeSession方法,并傳入一個(gè)HibernateCallback實(shí)例,執(zhí)行相應(yīng)的增刪改查操作。executeWithNativeSession方法屬于設(shè)計(jì)模式中的命令模式,簡(jiǎn)單來(lái)說(shuō)就是往這個(gè)方法傳入什么命令,hibernate就幫你執(zhí)行什么操作。繼續(xù)往里面跟蹤代碼可以看到:
doExecute
public <T> T executeWithNativeSession(HibernateCallback<T> action) {
return doExecute(action, false, true);
}
protected <T> T doExecute(HibernateCallback<T> action, boolean enforceNewSession, boolean enforceNativeSession)
throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Session session = (enforceNewSession ?
SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());
boolean existingTransaction = (!enforceNewSession &&
(!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateTemplate");
}
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction);
enableFilters(session);
Session sessionToExpose =
(enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));
T result = action.doInHibernate(sessionToExpose);
flushIfNecessary(session, existingTransaction);
return result;
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
catch (SQLException ex) {
throw convertJdbcAccessException(ex);
}
catch (RuntimeException ex) {
// Callback code threw application exception...
throw ex;
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
disableFilters(session);
if (previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
}
else {
// Never use deferred close for an explicitly new Session.
if (isAlwaysUseNewSession()) {
SessionFactoryUtils.closeSession(session);
}
else {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
}
}
}
這個(gè)方法就比較明了了:
首先獲取hibernate的session,就是上面分析過(guò)的那個(gè)方法。然后判斷existingTransaction,也就是當(dāng)前是否存在spring事務(wù)??梢钥吹絧reviousFlushMode = applyFlushMode(session, existingTransaction),這行代碼,如果當(dāng)前存在事務(wù),就會(huì)修改session的flushMode,并取得修改之前的previousFlushMode,執(zhí)行增刪改查操作之后再把這個(gè)previousFlushMode,也就是舊的flushMode設(shè)置回session中,這也就是為什么在單session情況下,有spring事務(wù)的可以執(zhí)行增刪改操作,而沒(méi)有spring事務(wù)執(zhí)行增刪改就會(huì)報(bào)錯(cuò)的原因。
中間的:
T result = action.doInHibernate(sessionToExpose);
此處就是執(zhí)行的增刪改操作,以及返回操作結(jié)果。hinernate創(chuàng)建session的時(shí)候并不會(huì)占用數(shù)據(jù)庫(kù)鏈接,只有hibernate真正執(zhí)行與數(shù)據(jù)庫(kù)交互的時(shí)候才會(huì)申請(qǐng)并占用數(shù)據(jù)庫(kù)鏈接。在執(zhí)行action.doInHibernate的時(shí)候hibernate其實(shí)還沒(méi)有開(kāi)始真正的進(jìn)行數(shù)據(jù)庫(kù)操作,只是在內(nèi)存中操作,只有執(zhí)行session.flush()的時(shí)候才會(huì)把內(nèi)存中的這些數(shù)據(jù)真正的同步到數(shù)據(jù)庫(kù)中,也就是執(zhí)行session.flush()的時(shí)候才會(huì)開(kāi)始占用數(shù)據(jù)庫(kù)鏈接。當(dāng)然查詢方法是個(gè)例外,因?yàn)楸仨氁獜臄?shù)據(jù)庫(kù)中取出數(shù)據(jù),所以如果是查詢方法,在查詢的時(shí)候就會(huì)與數(shù)據(jù)庫(kù)有交互操作,也就是進(jìn)行查詢的時(shí)候就會(huì)開(kāi)始占用數(shù)據(jù)庫(kù)鏈接。接下來(lái)的代碼就是判斷是否要進(jìn)行flush操作,繼續(xù)跟蹤到flushIfNecessary這個(gè)方法中:
flushIfNecessary
protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
logger.debug("Eagerly flushing Hibernate session");
session.flush();
}
}
可以看到,當(dāng)flushMode為FLUSH_EAGER的時(shí)候,或者不存在spring事務(wù)且flushMode不為FLUSH_NEVER的時(shí)候就會(huì)進(jìn)行session.flush()操作,同步數(shù)據(jù)。綜上可見(jiàn)在存在spring事務(wù)的情況下,所有dao的增刪改操作都不會(huì)馬上被同步到數(shù)據(jù)庫(kù)中,而要等到事務(wù)提交才會(huì)被flush到數(shù)據(jù)庫(kù)。所有如果你在某個(gè)帶有spring事務(wù)的service方法內(nèi)部對(duì)dao執(zhí)行try catch會(huì)出現(xiàn)捕獲不到數(shù)據(jù)庫(kù)異常的情況:
public calss Service{
public void saveObj(){
try{
dao.save(dto);
}catch(Exception e){
//這里catch不到數(shù)據(jù)庫(kù)錯(cuò)誤
}
}
}
public calss Action{
public void saveSomeThing(){
try{
service.saveObj(dto);
}catch(Exception e){
//這里才能catch到數(shù)據(jù)庫(kù)錯(cuò)誤
}
}
}
執(zhí)行完增刪改查之后,如果當(dāng)前有spring事務(wù)就把flushMode設(shè)置為原來(lái)的狀態(tài):session.setFlushMode(previousFlushMode)。如果當(dāng)前沒(méi)有spring事務(wù),會(huì)看到以下方法:
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
closeSessionOrRegisterDeferredClose
static void closeSessionOrRegisterDeferredClose(Session session, SessionFactory sessionFactory) {
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) {
logger.debug("Registering Hibernate Session for deferred close");
// Switch Session to FlushMode.MANUAL for remaining lifetime.
session.setFlushMode(FlushMode.MANUAL);
Set<Session> sessions = holderMap.get(sessionFactory);
sessions.add(session);
}
else {
closeSession(session);
}
}
從這個(gè)方法就可以很清晰的看出在OpenSessionInViewFilter多session的情況下,只要deferredCloseHolder有holderMap,session就不會(huì)被關(guān)閉,而是被保存到Set<Session>集合中,也就是多session情況下數(shù)據(jù)庫(kù)鏈接在請(qǐng)求結(jié)束之前都不會(huì)被釋放的原因。
最后是Transactioninterceptor攔截器,入口方法如下:
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(invocation.getMethod(), targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
retVal = invocation.proceed();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
//此處省略n行代碼......
}
retVal = invocation.proceed()
這行代碼是執(zhí)行攔截器鏈中的其他攔截器以及被攔截的目標(biāo)方法。這個(gè)方法前面的內(nèi)容就是開(kāi)啟事務(wù),后面的內(nèi)容就是提交和回滾事務(wù)。也就是spring聲明式事務(wù)可以不用手動(dòng)開(kāi)啟和提交事務(wù)的原理。createTransactionIfNecessary這個(gè)方法就是根據(jù)applicationcontext.xml的配置來(lái)生成不同類型的事務(wù),而commitTransactionAfterReturning就是提交事務(wù)。
跟蹤createTransactionIfNecessary進(jìn)入到getTransaction方法:
getTransaction
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// Use defaults if no transaction definition given.
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
由于spring提供了多種事務(wù)嵌套的傳播機(jī)制,所以這段代碼里面有許多if else的判斷,開(kāi)啟事務(wù)在doBegin這個(gè)方法里面。這里需要注意最后那個(gè)else分支:創(chuàng)建一個(gè)空的事務(wù),并非真正的數(shù)據(jù)庫(kù)事務(wù),也就是不執(zhí)行session.begintransaction()這個(gè)開(kāi)啟事務(wù)的方法。spring事務(wù)傳播有個(gè)配置叫PROPAGATION_NOT_SUPPORTED:意思是以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起。PROPAGATION_NOT_SUPPORTED這個(gè)配置就會(huì)走到最后的這個(gè)else分支。雖然以非事務(wù)方式運(yùn)行,但是在前面分析的許多用來(lái)判斷當(dāng)前是否存在spring事務(wù)的existingTransaction這個(gè)變量的值依舊會(huì)是ture。也就是說(shuō),spring認(rèn)為當(dāng)前存在事務(wù),雖然是一個(gè)空事務(wù)。這就導(dǎo)致可能出現(xiàn)下面的情況:
- 在某個(gè)service中配置為PROPAGATION_NOT_SUPPORTED的方法中開(kāi)始執(zhí)行了查數(shù)據(jù)庫(kù)的操作,占用了一個(gè)數(shù)據(jù)庫(kù)鏈接,然后又把查詢出來(lái)的數(shù)據(jù)作為參數(shù)調(diào)用了一個(gè)耗時(shí)很長(zhǎng)的外圍系統(tǒng)的接口,這樣就會(huì)導(dǎo)致這個(gè)空事務(wù)一直不被提交,這個(gè)數(shù)據(jù)庫(kù)鏈接也就會(huì)被一直被占用而無(wú)法釋放。
繼續(xù)看開(kāi)啟事務(wù)的doBegin方法:
doBegin
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
//此處省略n行代碼......
hibTx = session.beginTransaction();
//此處省略n行代碼......
}
執(zhí)行完目標(biāo)方法,中間有一個(gè)準(zhǔn)備提交事務(wù)的方法:
prepareForCommit
protected void prepareForCommit(DefaultTransactionStatus status) {
if (this.earlyFlushBeforeCommit && status.isNewTransaction()) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
Session session = txObject.getSessionHolder().getSession();
if (!session.getFlushMode().lessThan(FlushMode.COMMIT)) {
logger.debug("Performing an early flush for Hibernate transaction");
try {
session.flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
finally {
session.setFlushMode(FlushMode.MANUAL);
}
}
}
}
這里可以看到 session.flush()方法,也就是前面說(shuō)的事務(wù)開(kāi)始提交的時(shí)候才會(huì)把hibernate內(nèi)存中的數(shù)據(jù)同步到數(shù)據(jù)庫(kù)之中。
最后就是事務(wù)提交完成之后的處理session.disconnect()或者session.close()釋放數(shù)據(jù)庫(kù)鏈接,如下:
doCleanupAfterCompletion
protected void doCleanupAfterCompletion(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
// Remove the session holder from the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
// Remove the JDBC connection holder from the thread, if exposed.
if (getDataSource() != null) {
TransactionSynchronizationManager.unbindResource(getDataSource());
}
Session session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) {
// We're running with connection release mode "on_close": We're able to reset
// the isolation level and/or read-only flag of the JDBC Connection here.
// Else, we need to rely on the connection pool to perform proper cleanup.
try {
Connection con = session.connection();
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (HibernateException ex) {
logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
}
}
if (txObject.isNewSession()) {
if (logger.isDebugEnabled()) {
logger.debug("Closing Hibernate Session [" + SessionFactoryUtils.toString(session) +
"] after transaction");
}
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not closing pre-bound Hibernate Session [" +
SessionFactoryUtils.toString(session) + "] after transaction");
}
if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
}
if (!this.hibernateManagedSession) {
session.disconnect();
}
}
txObject.getSessionHolder().clear();
}
- 多session的情況下,如果沒(méi)有嵌套事務(wù),txObject.isNewSessionHolder()和txObject.isNewSession()的返回值為true,會(huì)執(zhí)行unbindResource操作和closeSessionOrRegisterDeferredClose操作。
- 單session情況下不解除綁定,后面依然可以從ThreadLocal中獲取到session,但是會(huì)執(zhí)行session.disconnect()來(lái)關(guān)閉數(shù)據(jù)庫(kù)鏈接。
下面給出各種情況下的結(jié)論:
在單session情況下,session會(huì)被綁定至當(dāng)前線程(ThreadLocal),整個(gè)請(qǐng)求過(guò)程中共用一個(gè)sesssion,整個(gè)請(qǐng)求過(guò)程至多一個(gè)數(shù)據(jù)庫(kù)連接。如果執(zhí)行帶有spring事務(wù)的service方法結(jié)束之后會(huì)觸發(fā)session.disconnect()釋放連接。雖然只有一個(gè)連接,但如果在service內(nèi)部方法結(jié)束之前調(diào)用了外圍耗時(shí)的操作會(huì)導(dǎo)致連接長(zhǎng)時(shí)間不被釋放。
多session情況下,每執(zhí)行一個(gè)dao或者service方法都將創(chuàng)建一個(gè)session,每個(gè)session都將占用一個(gè)數(shù)據(jù)庫(kù)連接,dao和service方法結(jié)束都不會(huì)關(guān)閉session,直到請(qǐng)求結(jié)束才會(huì)釋放連接。service內(nèi)部的所有dao操作共用一個(gè)sesison。如果在for循環(huán)中調(diào)用dao或者service,將產(chǎn)生n個(gè)連接。所以,這種模式下一次請(qǐng)求將占用n個(gè)連接,將會(huì)導(dǎo)致數(shù)據(jù)庫(kù)連接瞬間飆升的情況。
后臺(tái)自啟線程,如quartz定時(shí)調(diào)度等不經(jīng)過(guò)OpenSessionInViewFilter的情況下,每執(zhí)行一個(gè)dao或者service方法都將創(chuàng)建一個(gè)session,執(zhí)行結(jié)束之后都會(huì)關(guān)閉sessoin。service內(nèi)部的所有dao操作共用一個(gè)sesison。所以即使在for循環(huán)里面調(diào)用dao或者service,同一時(shí)刻也只會(huì)占用一個(gè)數(shù)據(jù)庫(kù)連接,不會(huì)導(dǎo)致數(shù)據(jù)庫(kù)連接飆升的情況。
創(chuàng)建hibernate的sessoin的時(shí)候并不占用數(shù)據(jù)庫(kù)連接,執(zhí)行查詢方法或者session的flush方法才會(huì)開(kāi)始占用數(shù)據(jù)庫(kù)連接。spring事務(wù)開(kāi)啟的時(shí)候,由于需要調(diào)用jdbc底層的Connection.setAutoCommit方法,所以spring事務(wù)開(kāi)啟的時(shí)候,即使中間沒(méi)有執(zhí)行任何數(shù)據(jù)庫(kù)操作,也會(huì)占用數(shù)據(jù)庫(kù)連接。如果service方法的propagation為PROPAGATION_NOT_SUPPORTED,雖有spring事務(wù)(空事務(wù)),但是由于不會(huì)創(chuàng)建JDBC底層事務(wù),所以這類的service方法開(kāi)始的時(shí)候不會(huì)占用數(shù)據(jù)庫(kù)連接。