Spring-JDBC/ORM 统一事务管理解决方案

若使用 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源码:

 

SessionFactory org.springframework.orm.hibernate3.LocalSessionFactoryBean.buildSessionFactory() throws Exception
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 取得的连接

你可能感兴趣的:(spring)