Spring事務處理探究
開發環境:
OS:windows XP
Web Server: jakarta-tomcat-5.0.28
DataBase Server: MS SQL Server 2000 (打了SP3補丁)
IDE: MyEclipse 6.0.1
測試案例系統結構:
web層<---->Service 層<---->DAO 層
web層就是Struts2,DAO 使用hibernate -3.3.1.GA-dist.zip,spring 是spring -framework-2.5.5
資料庫表和它一樣吧:
student1和Student2,表結構相同:id,name,address.其中id為主鍵且為自增長型.
student1表中有一條記錄:
測試情形一:
web層捕獲異常並處理,DAO
Service 層介面:
- publicinterface Student1Service {
- void addStudent1(Student1 stu);
- }
- publicinterface StudentSService {
- void addStudent2(Student2 stu) throws SaveException;
- }
Service 實現
- publicvoid addStudent1(Student1 stu) {
-
stufDAO.insertStuF(stu);
- }
- publicvoid addStudent2(Student2 stu) throws SaveException {
- String[] aa={"ww","ww","ww"};
- for(int i=0;i<5;i++){ //出錯
- System.out.println(aa[i]);
- }
- stusDAO.insertStuS(stu);
- }
DAO 層介面
- publicinterface StudentFDAO {
-
void insertStuF(Student1 stu);
- }
- publicinterface StudentSDAO {
- void insertStuS(Student2 stu);
- }
DAO 實現
- publicvoid insertStuF(Student1 stu) {
- getHibernateTemplate().save (stu);
- }
- publicvoid insertStuS(Student2 stu) {
- getHibernateTemplate().save (stu);
- }
Action
- public String execute() throws Exception{
- Student1 sti=new Student1(stu1Name,stu1Address);
- Student2 stu=new Student2(stu1Name,stu1Address);
- try{
- studentfService.addStudent1(sti);
- studentsService.addStudent2(stu);
- }catch(DataAccessException e){
- System.out.println(“error”);
- return “failer”:
- }
- return SUCCESS;
- }
JSP
- <form action="testaction.action" method="POST">
- <table>
- <tr><td>名:</td><td><input type="text" value="stu1Name" name="stu1Name"></td></tr>
- <tr><td>地址:</td><td><input type="text" value="stu1Address" name="stu1Address"></td></tr>
- <Tr><td></td><td><input type="submit" value="提交" style="width:80px"></td></Tr>
- </table>
- </form>
配置檔案
- <beanid="transactionManager"class="org.springframework.orm.hibernate3.HibernateTransactionManager">
- <propertyname="sessionFactory"ref="sessionFactory"/>
- </bean>
- <beanid="transactionInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor">
- <propertyname="transactionManager"ref="transactionManager"/>
- <propertyname="transactionAttributes">
- <props>
- <propkey="*">PROPAGATION_REQUIRED</prop>
- <propkey="get*">PROPAGATION_REQUIRED,readOnly</prop>
- </props>
- </property>
- </bean>
- <!-- 定義BeanNameAutoProxyCreator-->
- <beanclass="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
- <propertyname="beanNames">
- <list>
- <value>st1Service</value>
- <value>stsService</value>
- </list>
- </property>
- <propertyname="interceptorNames">
- <list>
- <value>transactionInterceptor</value>
- </list>
- </property>
- </bean>
執行程式:啟動伺服器,並部署.進入index.jsp頁面,點選提交
檢視資料庫: 兩個表中都沒有新增資料。
小結:如果DAO 層和Service 不捕獲異常而在web層捕獲異常,web成功捕獲異常(拋不丟擲都行),spring 事務管理成功!
二、Service 層捕捉異常,DAO 不捕捉異常(下面就簡單一點了,直接給結論把)
1.如果,你在Service 捕捉異常而不丟擲異常,Action層捕捉不到異常,那麼事物處理失敗。
2.如果在Service 捕捉異常並且丟擲異常,那麼,如果丟擲的異常是Checked異常(自定義的),不會回滾。也正好驗證了Spring 事物裡的這段話:預設情況下,事物只會出現執行異常(runtime
exception)時回滾,而出現受阻異常(checked exception)時不回滾。
3.如果丟擲的異常是RuntimeChecked異常(自定義的),那麼事物處理成功。
所以如果你的丟擲的異常是CheckException,你配置事物的時候必須加上-Exception引數,指定出現Checked異常時回滾,也就是PROPAGATION_REQUIRED,-Exception。
三、DAO 捕獲異常
如果DAO 捕捉異常而不丟擲異常,Action層捕捉不到異常,Service 捕獲不到異常。
執行程式:啟動伺服器,並部署.進入index.jsp頁面,點選提交
檢視資料庫:student1表中新曾資料,Student2表中都沒有資料。事務處理失敗。
小結:
前面的文章中提到,如果DAO 的每一個方法不捕獲異常,Service 層捕獲DataAccessException異常並丟擲自己定義異常(自定義異常為DataAccessException的子類),Web層可以捕獲到異常,spring 事務管理成功!
然後在總結中說:
1.spring 在進行宣告時事務管理時,通過捕獲Service 層方法的DataAccessException來提交和回滾事務的,而Service 層方法的DataAccessException又是來自呼叫DAO 層方法所產生的異常.
2.我們一般在寫DAO 層程式碼時,如果繼承JdbcDaoSupport 類,並使用此類所實現的JdbcTemplate來執行資料庫操作,此類會自動把低層的SQLException轉化成DataAccessException以及DataAccessException的子類.
3.一般在Service 層我們可以自己捕獲DAO 方法所產成的DataAccessException,然後再丟擲一個業務方法有意義的異常(ps:此異常最好繼承DataAccessException),然後在Web層捕獲,這樣我們就可以手動編碼的靈活實現通過業務方法執行的成功和失敗來向用戶轉發不同的頁面
其實這到不是因為自定義異常為DataAccessException的子類,而是DataAccessException是 RuntimeException,那麼它的子類也是Runtime,所以丟擲異常是會回滾的。所以你的自定義異常繼承自 RuntimeException也可以。
再發表一些個人看發吧,在Spring 中所有資料庫操作異常都轉換為DataAccessException這個執行時異常,不過在業務層中也有可能丟擲一些其他的執行時異常,那麼這時候在一些簡單的專案中,我們直接用下面的方式也未嘗不可:
Java程式碼
- try{
- …….
- ………
- ……..(可能會有其他執行時異常,並不僅僅是DataAccessException)
- }catch(Exception e){
- log.error(“error”);
- thrownew CheckedException(“error”); //丟擲受檢查異常
- }
然後在Action層捕捉這個受檢查異常
Java程式碼
- try{
- service.method();
- }catch(CheckedException e){
- }
這個異常,它是可以被呼叫者正確處理的。返回相應的提示。當然,如果丟擲CheckException,那麼Spring 宣告式事物的時候就應該加上-Exception引數,前面已經提到。
當然在一些業務邏輯比較複雜,並且要根據複雜的邏輯返回不同的試圖的時候,這樣整個的try{}catch(){}就不合適了,這時候應該將不正常的事件流轉換為執行時異常,並且在方法宣告中詳細的說明該方法可能會丟擲的unChekced異常。由呼叫者自己去決定是否捕獲unChecked異常 ,返回相應的檢視。
總結:
1.一般DAO 中不捕捉異常也不丟擲異常
2.至於Service 層和Action層,是在service 層丟擲CheckException然後由action捕捉,還是在service 中全部採用unCheckException,然後在action中有選擇的捕捉或者都不捕捉就要看你自己的選擇了,
個人認為邏輯比較簡單的可以選擇前者,比較複雜就選擇後者。
3.禁忌捕捉異常而不做任何處理(如果是dao 層或者service 層,最好丟擲),不然很有可能造成事務處理失敗。