Spring AOP在Hibernate事務管理


事務管理是用來以確保資料庫中資料的完整性和一致性。Spring AOP技術允許開發者管理事務的宣告。
這裡有一個例子來說明如何使用Spring AOP 來管理 Hibernate 事務。整個工程的檔案結構如下所示:

P.S 這裡很多Hibernate和Spring組態檔案是隱藏的,只有一些重要的檔案顯示,如果你想看全部檔案,請在文章的結尾下載完整的專案程式碼。

1.建立表

MySQL表的指令碼,一個「product」表
CREATE TABLE  `yiibai`.`product` (
  `PRODUCT_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `PRODUCT_CODE` varchar(20) NOT NULL,
  `PRODUCT_DESC` varchar(255) NOT NULL,
  PRIMARY KEY (`PRODUCT_ID`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE  `yiibai`.`product_qoh` (
  `QOH_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `PRODUCT_ID` bigint(20) unsigned NOT NULL,
  `QTY` int(10) unsigned NOT NULL,
  PRIMARY KEY (`QOH_ID`),
  KEY `FK_product_qoh_product_id` (`PRODUCT_ID`),
  CONSTRAINT `FK_product_qoh_product_id` FOREIGN KEY (`PRODUCT_ID`) 
  REFERENCES `product` (`PRODUCT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2.產品業務物件

在這個「productBo」實現save()方法將插入記錄到「product「通過「ProductDao」,並通過「productQohBo」類庫存量到「productQoh'表。
package com.yiibai.product.bo.impl;

import com.yiibai.product.bo.ProductBo;
import com.yiibai.product.bo.ProductQohBo;
import com.yiibai.product.dao.ProductDao;
import com.yiibai.product.model.Product;
import com.yiibai.product.model.ProductQoh;

public class ProductBoImpl implements ProductBo{
	
	ProductDao productDao;
	ProductQohBo productQohBo;
	
	public void setProductDao(ProductDao productDao) {
		this.productDao = productDao;
	}
	
	public void setProductQohBo(ProductQohBo productQohBo) {
		this.productQohBo = productQohBo;
	}

	//this method need to be transactional
	public void save(Product product, int qoh){
		
		productDao.save(product);
		System.out.println("Product Inserted");
		
		ProductQoh productQoh = new ProductQoh();
		productQoh.setProductId(product.getProductId());
		productQoh.setQty(qoh);
		
		productQohBo.save(productQoh);
		System.out.println("ProductQoh Inserted");
	}
}
Spring 的 bean 組態檔案。
<!-- Product business object -->
   <bean id="productBo" class="com.yiibai.product.bo.impl.ProductBoImpl" >
   	<property name="productDao" ref="productDao" />
   	<property name="productQohBo" ref="productQohBo" />
   </bean>
 
   <!-- Product Data Access Object -->
   <bean id="productDao" class="com.yiibai.product.dao.impl.ProductDaoImpl" >
   	<property name="sessionFactory" ref="sessionFactory"></property>
   </bean>

執行它

Product product = new Product();
    product.setProductCode("ABC");
    product.setProductDesc("This is product ABC");
    	
    ProductBo productBo = (ProductBo)appContext.getBean("productBo");
    productBo.save(product, 100); 

假設save() 不具有事務功能,如果異常丟擲由productQohBo.save(),鈄只插入一條記錄到「product」表,沒有記錄將被插入到「productQoh'表。這是一個嚴重的問題,在資料庫中打破資料一致性。

3.事務管理

宣告「TransactionInterceptor' bean,以及」HibernateTransactionManager' Hibernate事務,並且通過必要的屬性。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="transactionInterceptor" 
       class="org.springframework.transaction.interceptor.TransactionInterceptor">
	<property name="transactionManager" ref="transactionManager" />
	<property name="transactionAttributes">
	   <props>
		<prop key="save">PROPAGATION_REQUIRED</prop>
	   </props>
	</property>
    </bean>
   
    <bean id="transactionManager" 
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
	  <property name="dataSource" ref="dataSource" />
	  <property name="sessionFactory" ref="sessionFactory" />
    </bean>

</beans>

事務屬性

在事務攔截器,必須定義的事務的屬性「傳播行為」應使用。這意味著,如果一個事務「ProductBoImpl.save()方法被呼叫另外的」productQohBo.save()'方法,事務應該怎麼傳播?它能繼續在現有的事務中執行?或者為自己開始一個新的事務。

由Spring支援傳播的 7種型別:
  • PROPAGATION_REQUIRED – 支援當前事務;如果不存在則建立一個新的。
  • PROPAGATION_SUPPORTS – 支援當前事務;如果不存在執行非事務。
  • PROPAGATION_MANDATORY – 支援當前事務;如果當前不存在事務丟擲異常。
  • PROPAGATION_REQUIRES_NEW – 建立一個新的事務,如果當前事務暫停。
  • PROPAGATION_NOT_SUPPORTED – 不支援當前的事務;而始終執行非事務。
  • PROPAGATION_NEVER – 不支援當前的事務;如果當前事務存在則丟擲異常。
  • PROPAGATION_NESTED – 如果當前存在事務巢狀事務中執行,表現與 PROPAGATION_REQUIRED 一樣。
在大多數情況下,可能只需要使用PROPAGATION_REQUIRED。
此外,必須定義方法來支援這個事務屬性。方法名支援萬用字元格式,save*會匹配所有的方法名 以save(...)開始的方法 。

事務管理器

在Hibernate事務,需要使用 HibernateTransactionManager 。 如果只對付純JDBC,useDataSourceTransactionManager; 而如果是 JTA,需要使用 JtaTransactionManager 。

4.代理工廠bean

建立一個新的代理工廠bean的ProductBo,並設定「interceptorNames」屬性。
<!-- Product business object -->
   <bean id="productBo" class="com.yiibai.product.bo.impl.ProductBoImpl" >
   	<property name="productDao" ref="productDao" />
   	<property name="productQohBo" ref="productQohBo" />
   </bean>
 
   <!-- Product Data Access Object -->
   <bean id="productDao" class="com.yiibai.product.dao.impl.ProductDaoImpl" >
   	<property name="sessionFactory" ref="sessionFactory"></property>
   </bean>
   
   <bean id="productBoProxy"
	class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="target" ref="productBo" />
	<property name="interceptorNames">
		<list>
			<value>transactionInterceptor</value>
		</list>
	</property>
  </bean>

執行它

Product product = new Product();
    product.setProductCode("ABC");
    product.setProductDesc("This is product ABC");
    	
    ProductBo productBo = (ProductBo)appContext.getBean("productBoProxy");
    productBo.save(product, 100);
代理 bean' productBoProxy'和 save()方法是支援事務,現在,裡面productBo.save()方法任何異常會導致整個事務回滾,沒有資料會被插入到資料庫中。

下載程式碼 – http://pan.baidu.com/s/1qXynbo4

參考

  1. http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/transaction/TransactionDefinition.html
  2. http://static.springsource.org/spring/docs/2.5.x/reference/transaction.html