spring 中 Hibernate 事务和JDBC事务嵌套问题
http://www.iteye.com/topic/11063?page=2
---mixed ORM and JDBC usage is a feature of Spring DAO
這是Rod Johnson在Spring官方上說的....以前我的理解是可以在同一個事務(wù)里
混合JDBC和ORM 編程(不考慮JTA).但我發(fā)現(xiàn)我錯了..
比如有這樣一個業(yè)務(wù)方法
void doBusiness(){
? doAction1(); 使用JdbcTemplete
? doAction2(); 使用JdoDaoSupport或HBDaoSupport
}
但這樣缺是行不通的,Spring只是能拿到HB和JDBC的同一個數(shù)據(jù)源(DataSource),卻拿不到Hibernate和Jdbc的同一個底層Connection的連接
導(dǎo)致這2個方法將分別完成,并不能處在同一個事務(wù),而且我運行的后還出現(xiàn)連接未釋放的情況.(他們單獨拿出來當(dāng)然是正確的)...
難道rod的話的意思只是可以在不同的方法里,使jdbc和ORM拿到同一個數(shù)據(jù)源,mixed JDBC and ORM ?????郁悶中...]
?
===============
樓主以及二樓的朋友的論斷錯誤倒也罷了,那種肯定的,結(jié)論性總結(jié)的態(tài)度很容易誤導(dǎo)初學(xué)者的學(xué)習(xí)和理解。
提個小小的建議:下結(jié)論前要進行充分的考證,我們技術(shù)工作者尤其需要嚴謹?shù)膽B(tài)度。需要用證據(jù)來說話。
jdo dao和jdbc dao能否在同一個事務(wù)里這我不太清楚。因為我沒用過jdo daosupport。
但是jdbc daosupport和hibernate daosupport卻能被wrap到同一個事務(wù)里。成立需要幾點條件:
1、使用同一個datasource
2、事務(wù)交由hibernateTransactionManager管理
3、相關(guān)dao以及service需要使用runtime exception體系,使用spring提供的exception可以,自己封裝設(shè)計的runtime exception體系也行。
與此相關(guān)的事務(wù)代碼片斷在HibernateTransactionManager類中。最好可以把DatasourceTransactionManager和HibernateTransactionManager對比來看。
在此貼上幾個源碼片斷,多余的我就不解釋了。相信大家一看自明。
HibernateTransactionManager#doGetTransaction
由此可以看出hibernateTransactionManager可以檢測到綁定在當(dāng)前線程上的connection
HibernateTransactionManager#doBegin
由此可以看出,在真正啟動一個事務(wù)時,hbTxnManager會先把connection綁定到當(dāng)前線程,再綁定session到當(dāng)前線程,由TransactionSynchronizationManager統(tǒng)一管理。并且上面提到的connection是從session中取得的,也就是說,無論是jdbc dao還是hibernate dao本質(zhì)上使用的是同一個database connection
因此得出結(jié)論:HibernateTransactionManager實際上是可以同時管理由JdbcTemplate或JdbcDaoSupport實現(xiàn)的dao以及HibernateTemplate或HibernateDaoSupport實現(xiàn)的事務(wù)的。
Rod Johnson的話:
The only issue to watch, of course, is that you may be invalidating your Hibernate cache by JDBC changes. Generally I find it best to use JDBC to update only tables that don't have Hibernate mappings.
Juergen Hoeller的話:
Note that you must specify the DataSource for Hibernate via LocalSessionFactoryBean's "dataSource" property to allow HibernateTransactionManager to auto-detect it. Alternatively, you can explicitly pass the DataSource to HibernateTransactionManager's "dataSource" property.
==================
http://forum.springsource.org/archive/index.php/t-24312.html
I am using Springs DataSourceTransactionManager and the TransactionProxyFactoryBean to provide declarative tx support over a number of service classes. Almost all methods proxied utilize the default isolation level. However, there are 3 methods that utilize SERIALIZABLE. In order to ensure that these methods utilize a new transaction their propogation behavior is set to PROPOGATION_NESTED.
Once in a blue moon I receive a "java.sql.SQLException: ORA-01453: SET TRANSACTION must be first statement of transaction" exception on our production systems when one of these serializable methods is invoked.
This exception occurs at:
org.springframework.jdbc.datasource.DataSourceUtil s.prepareConnectionForTransaction(DataSourceUtils. java:160)
when the DataSourceUtils attempts to set the isolation level.
Once this error occurs in production it occurs with great regularity - most likely as a result of pulling the same "crippled" connection out of the pool. We then have to bounce tomcat, we don't have other access to the DataSource, to stabilize the system.
We use DBCP as our connection pool. DBCP is configured to validate all connections when they are drawn from the pool. Neither the defaultAutoCommit or defaultTransactionIsolation parameters are set. So all connection should come out of the connection pool with autocommit set to true and the isolation level set to READ_COMMITTED.
The only way I think this could happen, is if a connection in the pool is set to autocommit false. When Spring requests the connection from the pool, DBCP would validate it thereby starting a JDBC transaction. When Spring then tries to set the isolation level to SERIALIZABLE the driver complains because a statement in a pre-existing JDBC tx has already been executed.
However, the DataSourceTransactionManager and the DataSourceUtils seem very particular about cleaning and resetting the connections before returning them to the pool. So no connection should ever be returned to the pool with its autoCommit set to false.
We never see this behavior in development. However, on those servers, tomcat is rarely up for more than a few hours between builds.
Can anyone think of another cause of this issue? How Spring would return a connection back to the pool without resetting its autoCommit back to true, perhaps when a checked exception is thrown?
thanks in advance,
carlos
Production is using:
Spring v1.2.1
Oracle DB v8.1.7
Oracle JDBC Driver v10.2.0.1.0
Apache DBCP v1.2.1
Tomcat v5.5.9
Sun JVM v1.5.0_05-b05
Windows Server 2003
TxManager and ProxyFactoryBean definitions:
<bean id="TransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTran sactionManager">
<property name="dataSource">
<ref local="EDISDataSource" />
</property>
</bean>
<bean id="TransactionProxyTemplate"
class="org.springframework.transaction.interceptor.Transa ctionProxyFactoryBean"
abstract="true">
<property name="transactionManager">
<ref local="TransactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="authenticate*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="next*">PROPAGATION_NESTED,ISOLATION_SERIALIZABLE</prop>
<prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
</props>
</property>
</bean>
STACK TRACE
WARN [http-80-Processor22][11 Apr 2006 13:08:22,124] org.springframework.remoting.support.RemoteInvocat ionTraceInterceptor(RemoteInvocationTraceIntercept or.java:78) - Processing of HessianServiceExporter remote call resulted in fatal exception: gov.usitc.eof.ws.EDISWebService.getNextImageID
org.springframework.transaction.CannotCreateTransa ctionException: Could not open JDBC connection for transaction; nested exception is java.sql.SQLException: ORA-01453: SET TRANSACTION must be first statement of transaction
java.sql.SQLException: ORA-01453: SET TRANSACTION must be first statement of transaction
at oracle.jdbc.driver.DatabaseError.throwSqlException (DatabaseError.java:112)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoe r.java:331)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoe r.java:288)
at oracle.jdbc.driver.T4C8Oall.receive(T4C8Oall.java: 743)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStateme nt.java:207)
at oracle.jdbc.driver.T4CStatement.executeForRows(T4C Statement.java:946)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTi meout(OracleStatement.java:1168)
at oracle.jdbc.driver.OracleStatement.executeInternal (OracleStatement.java:1687)
at oracle.jdbc.driver.OracleStatement.execute(OracleS tatement.java:1653)
at oracle.jdbc.driver.PhysicalConnection.setTransacti onIsolation(PhysicalConnection.java:1600)
at org.apache.commons.dbcp.DelegatingConnection.setTr ansactionIsolation(DelegatingConnection.java:277)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuar dConnectionWrapper.setTransactionIsolation(Pooling DataSource.java:308)
at org.springframework.jdbc.datasource.DataSourceUtil s.prepareConnectionForTransaction(DataSourceUtils. java:160)
at org.springframework.jdbc.datasource.DataSourceTran sactionManager.doBegin(DataSourceTransactionManage r.java:178)
at org.springframework.transaction.support.AbstractPl atformTransactionManager.getTransaction(AbstractPl atformTransactionManager.java:234)
at org.springframework.transaction.interceptor.Transa ctionAspectSupport.createTransactionIfNecessary(Tr ansactionAspectSupport.java:217)
at org.springframework.transaction.interceptor.Transa ctionInterceptor.invoke(TransactionInterceptor.jav a:50)
at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed(ReflectiveMethodInvocation.java :144)
at org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(JdkDynamicAopProxy.java:174)
at $Proxy1.nextId(Unknown Source)
at gov.usitc.eof.ws.EDISWebServiceImpl.getNextImageID (Unknown Source)
at sun.reflect.GeneratedMethodAccessor264.invoke(Unkn own Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Un known Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoi npointUsingReflection(AopUtils.java:288)
at org.springframework.aop.framework.ReflectiveMethod Invocation.invokeJoinpoint(ReflectiveMethodInvocat ion.java:155)
at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed(ReflectiveMethodInvocation.java :122)
at org.springframework.remoting.support.RemoteInvocat ionTraceInterceptor.invoke(RemoteInvocationTraceIn terceptor.java:68)
at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed(ReflectiveMethodInvocation.java :144)
at org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(JdkDynamicAopProxy.java:174)
at $Proxy7.getNextImageID(Unknown Source)
at sun.reflect.GeneratedMethodAccessor263.invoke(Unkn own Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Un known Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.caucho.hessian.server.HessianSkeleton.invoke(H essianSkeleton.java:188)
at org.springframework.remoting.caucho.HessianService Exporter.handleRequest(HessianServiceExporter.java :87)
at org.springframework.web.servlet.mvc.SimpleControll erHandlerAdapter.handle(SimpleControllerHandlerAda pter.java:44)
at org.springframework.web.servlet.DispatcherServlet. doDispatch(DispatcherServlet.java:684)
at org.springframework.web.servlet.DispatcherServlet. doService(DispatcherServlet.java:625)
at org.springframework.web.servlet.FrameworkServlet.s erviceWrapper(FrameworkServlet.java:386)
at org.springframework.web.servlet.FrameworkServlet.d oPost(FrameworkServlet.java:355)
at javax.servlet.http.HttpServlet.service(HttpServlet .java:709)
at javax.servlet.http.HttpServlet.service(HttpServlet .java:802)
at org.apache.catalina.core.ApplicationFilterChain.in ternalDoFilter(ApplicationFilterChain.java:252)
at org.apache.catalina.core.ApplicationFilterChain.do Filter(ApplicationFilterChain.java:173)
at org.apache.catalina.core.StandardWrapperValve.invo ke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invo ke(StandardContextValve.java:178)
at org.apache.catalina.core.StandardHostValve.invoke( StandardHostValve.java:126)
at org.apache.catalina.valves.ErrorReportValve.invoke (ErrorReportValve.java:105)
at org.apache.catalina.core.StandardEngineValve.invok e(StandardEngineValve.java:107)
at org.apache.catalina.connector.CoyoteAdapter.servic e(CoyoteAdapter.java:148)
at org.apache.coyote.http11.Http11Processor.process(H ttp11Processor.java:856)
at org.apache.coyote.http11.Http11Protocol$Http11Conn ectionHandler.processConnection(Http11Protocol.jav a:744)
at org.apache.tomcat.util.net.PoolTcpEndpoint.process Socket(PoolTcpEndpoint.java:527)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThr ead.runIt(LeaderFollowerWorkerThread.java:80)
at org.apache.tomcat.util.threads.ThreadPool$ControlR unnable.run(ThreadPool.java:684)
at java.lang.Thread.run(Unknown Source)
?
==============
http://www.blogjava.net/RongHao/archive/2007/10/09/151411.html
結(jié)合spring+hibernate與jdbc的事務(wù)問題背景:我們是一家工作流公司,客戶采購我們的產(chǎn)品后,將其嵌入其項目中。我們的工作流采用的是?? spring+hibernate的方式,客戶項目則是jdbc直接進行數(shù)據(jù)庫操作。
問題:客戶在其數(shù)據(jù)庫操作過程中需要調(diào)用我們的工作流接口,這樣就需要將我們的工作流操作與他們的業(yè)? 務(wù)操作置于同一個事務(wù)中。我們的服務(wù)采用的都是spring的聲明式事務(wù),而客戶采用的是對???????? connection進行事務(wù)處理。如何保證事務(wù)的一致性?
想到的解決方案一:使用jta事務(wù),用tomcat+jotm提供事務(wù)管理器。為什么一開始就想到要使用jta事務(wù)??實際上我們和客戶都是使用的同一個數(shù)據(jù)庫,為了方便,各自使用了不同的數(shù)據(jù)庫連接方式,使用jta的話確實有bt的意思在里面。但是事實上是我們的第一反應(yīng)都是jta。最后沒有采用該方法的原因也很簡單:我沒有將jotm配置成功!汗一個。
想到的解決方案二:將客戶的這些特定代碼用spring管理起來。因為要修改客戶部分代碼,這個方案遭到了客戶的強烈反對。于是放棄。
想到的解決方案三:客戶數(shù)據(jù)庫操作與我們的服務(wù)使用同一個數(shù)據(jù)庫連接。然后編程處理事務(wù)。存在兩種方式:一種是把客戶的連接傳給我們,另一種則是把我們的連接傳給客戶。第一種方式對我們的影響太大,所以最后決定采用后一種方式:從hibernate session中獲取connection然后傳遞給客戶。接下來查看一下HibernateTemplate的execute()方法,思路就很簡單了:獲取定義的sessionFactory-->創(chuàng)建一個新的session并打開-->將session與當(dāng)前線程綁定-->給客戶代碼返回connection-->打開事務(wù)-->客戶使用我們傳遞的connection進行數(shù)據(jù)庫操作-->我們不帶聲明事務(wù)的服務(wù)操作-->提交事務(wù)-->解除綁定。
實際要注意的地方是:1、將session與當(dāng)前線程綁定使用的TransactionSynchronizationManager.bindResource()方法,這樣在HibernateTemplate里才能找到session;
??????????????????? 2、我們的服務(wù)一定要把聲明式事務(wù)徹底干掉,否則會有commit;
??????????????????? 3、我們服務(wù)調(diào)用完畢后一定要flush session,否則客戶代碼不會感知數(shù)據(jù)庫里的數(shù)據(jù)變化。
最終解決:使用了spring里常用的模板和回調(diào)。代碼如下:
???? protected ? final ?Log?logger? = ?LogFactory.getLog(TransactionTemplate. class );
???? private ?FlushMode?flushMode? = ?FlushMode.ALWAYS;
???? public ?Object?execute(TransactionCallback?callback)?{
???????? // 首先獲取sessionFactory
????????SessionFactory?sessionFactory? = ?(SessionFactory)?Framework.getEngine()
????????????????.getContainer().getComponent( " sessionFactory " );
???????? // 創(chuàng)建一個新的session并打開
????????logger.debug( " Opening?single?Hibernate?Session?in?TransactionTemplate " );
????????Session?session? = ?getSession(sessionFactory);
???????? // 將session與當(dāng)前線程綁定
????????TransactionSynchronizationManager.bindResource(sessionFactory,? new ?SessionHolder(session));
???????? // 獲取數(shù)據(jù)庫連接
????????Connection?conn? = ?session.connection();
????????Object?result? = ? null ;
????????Transaction?transaction? = ? null ;
???????? try ?{
???????????? // 開始處理事務(wù)
????????????transaction? = ?session.beginTransaction();
???????????? try ?{
????????????????result? = ?callback.doInTransaction(conn);
????????????}
???????????? catch ?(RuntimeException?ex)?{
????????????????doRollback(session,?transaction);
???????????????? throw ?ex;
????????????}
???????????? catch ?(Error?err)?{
????????????????doRollback(session,?transaction);
???????????????? throw ?err;
????????????}
???????????? // 如果數(shù)據(jù)庫操作過程中沒有發(fā)生異常則提交事務(wù)
????????????transaction.commit();
????????}? catch ?(WorkflowException?e)?{
????????????logger.error( " 數(shù)據(jù)庫操作失敗,事務(wù)回滾也失敗! " );
???????????? throw ?e;
????????}? catch ?(RuntimeException?ex)?{
????????????logger.error( " 數(shù)據(jù)庫操作失敗,事務(wù)被回滾! " );
???????????? throw ?ex;
????????}? catch ?(Error?err)?{
????????????logger.error( " 數(shù)據(jù)庫操作失敗,事務(wù)被回滾! " );
???????????? throw ?err;
????????}? finally ?{
???????????? // ?將session與當(dāng)前線程解除綁定
????????????TransactionSynchronizationManager.unbindResource(sessionFactory);
????????????doClose(session);
????????}
???????? return ?result;
????}
???? protected ?Session?getSession(SessionFactory?sessionFactory)?{
????????Session?session? = ?SessionFactoryUtils.getSession(sessionFactory,? true );
????????FlushMode?flushMode? = ?getFlushMode();
???????? if ?(flushMode? != ? null )?{
????????????session.setFlushMode(flushMode);
????????}
???????? return ?session;
????}
???? private ? void ?doRollback(Session?session,?Transaction?transaction)?{
????????logger.debug( " 數(shù)據(jù)庫操作異常,開始回滾事務(wù) " );
???????? try ?{
????????????transaction.rollback();
????????????logger.debug( " 回滾事務(wù)成功! " );
????????}
???????? catch ?(Exception?e)?{
????????????logger.error( " 回滾事務(wù)失敗! " );
???????????? throw ? new ?WorkflowException( " 回滾事務(wù)失敗! " );
????????}? finally ?{
????????????session.clear();
????????}
????}
???? private ? void ?doClose(Session?session)?{
????????logger.debug( " 開始關(guān)閉連接 " );
???????? try ?{
????????????session.close();
????????}
???????? catch ?(Exception?e)?{
????????????logger.error( " 關(guān)閉連接失敗! " );
???????????? throw ? new ?WorkflowException( " 關(guān)閉連接失敗! " );
????????}
????}
???? public ?FlushMode?getFlushMode()?{
???????? return ?flushMode;
????}
???? public ? void ?setFlushMode(FlushMode?flushMode)?{
???????? this .flushMode? = ?flushMode;
????}
}
總結(jié)
以上是生活随笔為你收集整理的spring 中 Hibernate 事务和JDBC事务嵌套问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle插入回车换行符
- 下一篇: Excel中vlookup函数的使用方法