Spring+Hibernate中OpenSessionInView模式運(yùn)用
?中會在Update Domain Object時遇到
org.springframework.dao.InvalidDataAccessApiUsageException:?Write?operations?are?not?allowed?in?read-only?mode?(FlushMode.NEVER)?-?turn?your?Session?into?FlushMode.AUTO?or?remove?'readOnly'?marker?from?transaction?definition異常問題,這個Exception在尚未進(jìn)入DAO時就會遇到,是一個會發(fā)生在Modify Domain Object時的問題。
可能的解決方式有:
1、將singleSession設(shè)為false,這樣只要改web.xml,缺點(diǎn)是Hibernate Session的Instance可能會大增,使用的JDBC Connection量也會大增,如果Connection Pool的maxPoolSize設(shè)得太小,很容易就出問題。
2、在控制器中自行管理Session的FlushMode,麻煩的是每個有Modify的Method都要多幾行程式。
session.setFlushMode(FlushMode.AUTO);?
??session.update(user);?
??session.flush();?
3、Extend OpenSessionInViewFilter,Override protected Session getSession(SessionFactory sessionFactory),將FlushMode直接改為Auto。
4、讓方法受Spring的事務(wù)控制。
下面著重解說第4種方式:
OpenSessionInViewFilter里的幾個方法:
protected?void?doFilterInternal(HttpServletRequest?request,?

HttpServletResponse?response,FilterChain?filterChain)?throws?ServletException,?IOException?
{?
SessionFactory?sessionFactory?=?lookupSessionFactory();?
logger.debug("Opening?Hibernate?Session?in?OpenSessionInViewFilter");?
Session?session?=?getSession(sessionFactory); TransactionSynchronizationManager.bindResource(?
sessionFactory,?new?SessionHolder(session));?

try?
{?
filterChain.doFilter(request,?response); ?

??} finally?
{?
TransactionSynchronizationManager.unbindResource(sessionFactory);?
logger.debug("Closing?Hibernate?Session?in?OpenSessionInViewFilter"); ?
??closeSession(session,?sessionFactory); ?
??}?
}?

protected?Session?getSession(SessionFactory?sessionFactory)?

throws?DataAccessResourceFailureException?
{?
Session?session?=?SessionFactoryUtils.getSession(sessionFactory,?true);?
session.setFlushMode(FlushMode.NEVER);?
return?session;?
}?
protected?void?closeSession(Session?session,?SessionFactory?sessionFactory)?

throws?CleanupFailureDataAccessException?
{ ??
??SessionFactoryUtils.closeSessionIfNecessary(session,?sessionFactory);?
}?
可以看到OpenSessionInViewFilter在getSession的時候,會把獲取回來的session的flush mode 設(shè)為FlushMode.NEVER。然后把該sessionFactory綁定到TransactionSynchronizationManager,使request的整個過程都使用同一個session,在請求過后再解除該sessionFactory的綁定,最后closeSessionIfNecessary根據(jù)該session是否已和transaction綁定來決定是否關(guān)閉session。在這個過程中,若HibernateTemplate 發(fā)現(xiàn)自當(dāng)前session有不是readOnly的transaction,就會獲取到FlushMode.AUTO Session,使方法擁有寫權(quán)限:

public?static?void?closeSessionIfNecessary(Session?session,?SessionFactory?sessionFactory)??????throws?CleanupFailureDataAccessException?
{?

????if?(session?==?null?||?TransactionSynchronizationManager.hasResource(sessionFactory))
{?
??????return;????
}?
????logger.debug("Closing?Hibernate?session");?

????try?
{?
??????session.close();?
????}?

????catch?(JDBCException?ex)?
{?
??????
?????throw?new?CleanupFailureDataAccessException("Could?not?close?Hibernate?session",?ex.getSQLException());?

????}????catch?(HibernateException?ex)?
{?
??????throw?new?CleanupFailureDataAccessException("Could?not?close?Hibernate?session",?ex);?
????}?
}?
如果有不是readOnly的transaction就可以由Flush.NEVER轉(zhuǎn)為Flush.AUTO,擁有insert,update,delete操作權(quán)限,如果沒有transaction,并且沒有另外人為地設(shè)flush model的話,則doFilter的整個過程都是Flush.NEVER。所以受transaction保護(hù)的方法有寫權(quán)限,沒受保護(hù)的則沒有。
采用spring的事務(wù)聲明,使方法受transaction控制:
<bean?id="manager"?class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
????????<property?name="proxyInterfaces">
????????????<list>
????????????????<value>com.zhupan.service.IManager</value>
????????????</list>
????????</property>
????????<property?name="transactionManager">
????????????<ref?bean="transactionManager"?/>
????????</property>
????????<property?name="target">
????????????<ref?local="managerTarget"?/>
????????</property>
????????<property?name="transactionAttributes">
????????????<props>????????????????????????????
????????????????<prop?key="*Insert">PROPAGATION_REQUIRED</prop>
????????????????<prop?key="*Update">PROPAGATION_REQUIRED</prop>
????????????????<prop?key="*Get*">PROPAGATION_REQUIRED,readOnly</prop>????
????????????????<prop?key="*List*">PROPAGATION_REQUIRED,readOnly</prop>????????????
????????????</props>
????????</property>
????</bean>
對于上面的,以Insert,Update結(jié)尾的方法擁有可寫的事務(wù),如果某個方法,如方法名為savaSort(),則沒有寫權(quán)限,這時若此方法內(nèi)有insert,update,delete操作的話,則需要手動設(shè)置flush model為Flush.AUTO,如:
session.setFlushMode(FlushMode.AUTO);?
??session.update(user);?
??session.flush();?


可能的解決方式有:
1、將singleSession設(shè)為false,這樣只要改web.xml,缺點(diǎn)是Hibernate Session的Instance可能會大增,使用的JDBC Connection量也會大增,如果Connection Pool的maxPoolSize設(shè)得太小,很容易就出問題。
2、在控制器中自行管理Session的FlushMode,麻煩的是每個有Modify的Method都要多幾行程式。




4、讓方法受Spring的事務(wù)控制。
下面著重解說第4種方式:
OpenSessionInViewFilter里的幾個方法:



































可以看到OpenSessionInViewFilter在getSession的時候,會把獲取回來的session的flush mode 設(shè)為FlushMode.NEVER。然后把該sessionFactory綁定到TransactionSynchronizationManager,使request的整個過程都使用同一個session,在請求過后再解除該sessionFactory的綁定,最后closeSessionIfNecessary根據(jù)該session是否已和transaction綁定來決定是否關(guān)閉session。在這個過程中,若HibernateTemplate 發(fā)現(xiàn)自當(dāng)前session有不是readOnly的transaction,就會獲取到FlushMode.AUTO Session,使方法擁有寫權(quán)限:

























如果有不是readOnly的transaction就可以由Flush.NEVER轉(zhuǎn)為Flush.AUTO,擁有insert,update,delete操作權(quán)限,如果沒有transaction,并且沒有另外人為地設(shè)flush model的話,則doFilter的整個過程都是Flush.NEVER。所以受transaction保護(hù)的方法有寫權(quán)限,沒受保護(hù)的則沒有。
采用spring的事務(wù)聲明,使方法受transaction控制:





















對于上面的,以Insert,Update結(jié)尾的方法擁有可寫的事務(wù),如果某個方法,如方法名為savaSort(),則沒有寫權(quán)限,這時若此方法內(nèi)有insert,update,delete操作的話,則需要手動設(shè)置flush model為Flush.AUTO,如:



