若使用 MyBatis[JDBC] 与hibernate[ORM] 框架结合,且 使用 相同的事务:
解决方案三种:
1: 将JDBC事务打开,且Hibernate事务关闭,再修改 hibernate sessionFactory的 useTransactionAwareDataSource 属性为true
<!-- 事务管理器配置, 使用jdbc事务 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 使用annotation定义事务 --> <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<!-- Hibernate配置 --> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="namingStrategy"> <bean class="org.hibernate.cfg.ImprovedNamingStrategy" /> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <prop key="hibernate.format_sql">${hibernate.format_sql}</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> <prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache/ehcache-hibernate-local.xml</prop> </props> </property> <property name="packagesToScan" value="org.springtest.entity" /> <property name="useTransactionAwareDataSource" value="true"/> </bean>
参加Spring源码:
if (dataSource != null) { Class providerClass = LocalDataSourceConnectionProvider.class; if (isUseTransactionAwareDataSource() || dataSource instanceof TransactionAwareDataSourceProxy) { providerClass = TransactionAwareDataSourceConnectionProvider.class; } else if (config.getProperty(Environment.TRANSACTION_MANAGER_STRATEGY) != null) { providerClass = LocalJtaDataSourceConnectionProvider.class; } // Set Spring-provided DataSource as Hibernate ConnectionProvider. config.setProperty(Environment.CONNECTION_PROVIDER, providerClass.getName()); }第二种: 使用JTA事务:
<beans> <bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName value="java:comp/env/jdbc/myds1"/> </bean> <bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="java:comp/env/jdbc/myds2"/> </bean> <bean id="mySessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource1"/> <property name="mappingResources"> <list> <value>product.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.MySQLDialect hibernate.show_sql=true </value> </property> </bean> <bean id="mySessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="myDataSource2"/> <property name="mappingResources"> <list> <value>inventory.hbm.xml</value> </list> </property> <property name="hibernateProperties"> <value> hibernate.dialect=org.hibernate.dialect.OracleDialect </value> </property> </bean> <bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/> <bean id="myProductDao" class="product.ProductDaoImpl"> <property name="sessionFactory" ref="mySessionFactory1"/> </bean> <bean id="myInventoryDao" class="product.InventoryDaoImpl"> <property name="sessionFactory" ref="mySessionFactory2"/> </bean> <!-- this shows the Spring 1.x style of declarative transaction configuration --> <!-- it is totally supported, 100% legal in Spring 2.x, but see also above for the sleeker, Spring 2.0 style --> <bean id="myProductService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <property name="transactionManager" ref="myTxManager"/> <property name="target"> <bean class="product.ProductServiceImpl"> <property name="productDao" ref="myProductDao"/> <property name="inventoryDao" ref="myInventoryDao"/> </bean> </property> <property name="transactionAttributes"> <props> <prop key="increasePrice*">PROPAGATION_REQUIRED</prop> <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop> <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop> </props> </property> </bean> </beans>
第三种:使用被代理的数据源:
使用 TransactionAwareDataSourceProxy
如果不得已要显式获取数据连接,除了使用 DataSourceUtils 获取事务上下文绑定的连接外,还可以通过 TransactionAwareDataSourceProxy 对数据源进行代理。数据源对象被代理后就具有了事务上下文感知的能力,通过代理数据源的 getConnection() 方法获取的连接和使用 DataSourceUtils.getConnection() 获取连接的效果是一样的。
下面是使用 TransactionAwareDataSourceProxy 对数据源进行代理的配置:
清单 11.applicationContext.xml:对数据源进行代理
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="oracle.jdbc.driver.OracleDriver" p:url="jdbc:oracle:thin:@localhost:1521:orcl" p:username="test" p:password="test" p:defaultAutoCommit="false"/> <!-- ①对数据源进行代理--> <bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy" p:targetDataSource-ref="dataSource"/> <!-- ②直接使用数据源的代理对象--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" p:dataSource-ref="dataSourceProxy"/> <!-- ③直接使用数据源的代理对象--> <bean id="jdbcManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSourceProxy"/> |
对数据源进行代理后,我们就可以通过数据源代理对象的 getConnection() 获取事务上下文中绑定的数据连接了。
因此,如果数据源已经进行了 TransactionAwareDataSourceProxy 的代理,而且方法存在事务上下文,那么清单 1 的代码也不会生产连接泄漏的问题。
参加 void org.mybatis.spring.SqlSessionFactoryBean.setDataSource(DataSource dataSource)
public void setDataSource(DataSource dataSource) { if (dataSource instanceof TransactionAwareDataSourceProxy) { this.dataSource = ((TransactionAwareDataSourceProxy) dataSource) .getTargetDataSource(); } else this.dataSource = dataSource; }
以及:void org.springframework.jdbc.datasource.DataSourceTransactionManager.setDataSource(DataSource dataSource)
public void setDataSource(DataSource dataSource) { if(dataSource instanceof TransactionAwareDataSourceProxy) this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource(); else this.dataSource = dataSource; }
及: void org.springframework.orm.jpa.JpaTransactionManager.setDataSource(DataSource dataSource)
对于 Hibernate 的 sessionFactory 取得的所有的实际数据库连接皆取自于
Connection org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourcedataSource) throws SQLException
而就是这个方法保证了取得的数据库连接 是 绑定到当前线程的真正数据库连接.
对于MyBatis官方文档中就是 使用 JDBC事务管理器便能够管理事务,则
只要 数据库被代理了,且使用的是 JDBC事务管理器则能够保证事务的一致性,
另参见Hibernate配置:void org.springframework.orm.hibernate3.AbstractSessionFactoryBean.setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource)
和 http://www.blogjava.net/i369/articles/194855.html
但JPA事务管理器取得的数据库连接也是 DataSourceUtils.doGetConnection 取得的连接