Java EE學習筆記(五)

Spring事務管理

一、Spring事務管理概述

1)、在實際開發中,操做數據庫時都會涉及到事務管理問題,爲此Spring提供了專門用於事務處理的API(事務特性:ACID,原子性,一致性,隔離性,持久性)java

2)、事務管理的核心接口:在Spring的全部JAR包中,包含一個名爲spring-tx-4.3.6.RELEASE的JAR包,該包就是Spring提供的用於事務管理的依賴包。在該JAR包的org.springframework.transaction包中,有3個接口文件PlatformTransactionManager、TransactionDefinition和TransactionStatus,以下圖所示:mysql

3)、Platform TransactionManager接口spring

a)、PlatformTransactionManager接口是Spring提供的平臺事務管理器,主要用於管理事務。該接口中提供了三個事務操做的方法,具體以下:sql

用於獲取事務狀態信息:TransactionStatus getTransaction(TransactionDefinition definition);數據庫

用於提交事務:void commit(TransactionStatus status);express

用於回滾事務:void rollback(TransactionStatus status);編程

b)、PlatformTransactionManager接口只是表明事務管理的接口並不知道底層是如何管理事務的,具體如何管理事務則由它的實現類來完成。該接口常見的幾個實現類以下:app

一、用於配置JDBC數據源的事務管理器:
org.springframework.jdbc.datasource.DataSourceTransactionManager
二、用於配置Hibernate的事務管理器:
org.springframework.orm.hibernate4.HibernateTransactionManager
三、用於配置全局事務管理器:
org.springframework.transaction.jta.JtaTransactionManager測試

當底層採用不一樣的持久層技術時,系統只需使用不一樣的PlatformTransactionManager實現類便可。ui

4)、TransactionDefinition接口

a)、TransactionDefinition接口事務定義(描述)的對象,該對象中定義了事務規則(事物詳情),並提供了獲取事務相關信息的方法,具體以下:

一、獲取事務對象名稱:String getName( );
二、獲取事務的隔離級別:int getIsolationLevel( );
三、獲取事務的傳播行爲:int getPropagationBehavior( );
四、獲取事務的超時時間:int getTimeout( );
五、獲取事務是否只讀:boolean isReadOnly( );

 上述方法中,事務的傳播行爲是指在同一個方法中,不一樣操做先後所使用的事務。傳播行爲有不少種,具體以下表所示:

 

 在事務管理過程當中,傳播行爲能夠控制是否須要建立事務以及如何建立事務,一般狀況下,數據的查詢不會影響原數據的改變,因此不須要進行事務管理,而對於數據的插入、更新和刪除操做,必須進行事務管理。若是沒有指定事務的傳播行爲,Spring默認傳播行爲是REQUIRED(required)

5)、TransactionStatus接口

a)、TransactionStatus接口事務的狀態,它描述了某一時間點上事務的狀態信息。該接口中包含6個方法,具體以下:

一、刷新事務:void flush();
二、獲取是否存在保存點:boolean hasSavepoint();
三、獲取事務是否完成:boolean isCompleted();
四、獲取是否爲新事務:boolean isNewTransaction();
五、獲取事務是否回滾:boolean isRollbackOnly();
六、設置事務回滾:void setRollbackOnly();

 6)、事務管理的方式:

 

 聲明式事務管理最大的優勢在於開發者無需經過編程的方式來管理事務只需在配置文件中進行相關的事務規則聲明,就能夠將事務應用到業務邏輯中

二、聲明式事務管理

1)、Spring的聲明式事務管理能夠經過兩種方式來實現,一種是基於XML的方式,另外一種是基於Annotation的方式

2)、基於XML方式的聲明式事務:在配置文件中經過<tx:advice>元素配置事務規則來實現的。當配置了事務的加強處理後,就能夠經過編寫的AOP配置,讓Spring自動對目標生成代理。<tx:advice>元素及其子元素以下圖所示:

3)、配置<tx:advice>元素的重點是配置<tx:method>子元素,上圖中使用灰色標註的幾個屬性是<tx:method>元素中的經常使用屬性。其屬性描述具體以下:

4)、src->com.itheima.jdbc

①普通用戶類:Account.java

 1 package com.itheima.jdbc;
 2 public class Account {
 3     
 4     private Integer id;       // 帳戶id
 5     private String username; // 用戶名
 6     private Double balance;  // 帳戶餘額
 7     
 8     public Integer getId() {
 9         return id;
10     }
11     
12     public void setId(Integer id) {
13         this.id = id;
14     }
15     
16     public String getUsername() {
17         return username;
18     }
19     
20     public void setUsername(String username) {
21         this.username = username;
22     }
23     
24     public Double getBalance() {
25         return balance;
26     }
27     
28     public void setBalance(Double balance) {
29         this.balance = balance;
30     }
31     
32     public String toString() {
33         return "Account [id=" + id + ", "+ "username=" + username + ", balance=" + balance + "]";
34     }
35 }

②操做用戶接口類:AccountDao.java

 1 package com.itheima.jdbc;
 2 
 3 import java.util.List;
 4 
 5 public interface AccountDao {
 6     
 7     // 添加
 8     public int addAccount(Account account);
 9     
10     // 更新
11     public int updateAccount(Account account);
12     
13     // 刪除
14     public int deleteAccount(int id);
15     
16     // 經過id查詢
17     public Account findAccountById(int id);
18     
19     // 查詢全部帳戶
20     public List<Account> findAllAccount();
21     
22     // 添加一個轉帳方法
23     public void transfer(String outUser,String inUser,Double money);
24 }

③在AccountDaoImpl.java實現transfer()方法:

 1 /**
 2      *  轉帳
 3      *  inUser:收款人
 4      *  outUser:匯款人
 5      *  money:收款金額
 6     */
 7     public void transfer(String outUser, String inUser, Double money) {
 8         // 收款時,收款用戶的餘額=現有餘額+所匯金額
 9         this.jdbcTemplate.update("update account set balance = balance +? "
10                 + "where username = ?",money, inUser);
11         // 模擬系統運行時的突發性問題
12         int i = 1/0;
13         // 匯款時,匯款用戶的餘額=現有餘額-所匯金額
14         this.jdbcTemplate.update("update account set balance = balance-? "
15                 + "where username = ?",money, outUser);
16     }

③配置文件applicationContext.xml(注意數據庫登錄密碼須要本身的數據庫密碼)

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4 xmlns:aop="http://www.springframework.org/schema/aop"
 5 xmlns:tx="http://www.springframework.org/schema/tx" 
 6 xmlns:context="http://www.springframework.org/schema/context"
 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8                 http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 9                 http://www.springframework.org/schema/tx 
10                 http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
11                 http://www.springframework.org/schema/context 
12                 http://www.springframework.org/schema/context/spring-context-4.3.xsd
13                 http://www.springframework.org/schema/aop 
14                 http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
15     
16     <!-- 一、配置數據源 -->
17     <bean id="dataSourceID" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
18     
19         <!--1.一、數據庫驅動 -->
20         <property name="driverClassName" value="com.mysql.jdbc.Driver" />
21         
22         <!--1.二、鏈接數據庫的url(即數據庫所在地址) -->
23         <property name="url" value="jdbc:mysql://localhost:3306/spring" />
24         
25         <!--1.三、鏈接數據庫的用戶名 -->
26         <property name="username" value="root" />
27         
28         <!--1.四、鏈接數據庫的密碼 -->
29         <property name="password" value="******" />
30     </bean>
31     
32     <!-- 二、配置JDBC模板,而且注入數據源 -->
33     <bean id="jdbcTemplateID" class="org.springframework.jdbc.core.JdbcTemplate">
34     
35         <!-- 2.一、默認必須使用數據源 -->
36         <property name="dataSource" ref="dataSourceID" />
37     </bean>
38     
39     <!--三、定義id爲accountDaoID的Bean,即配置DAO層-->
40     <bean id="accountDaoID" class="com.itheima.jdbc.AccountDaoImpl">
41     
42         <!-- 3.一、將jdbcTemplate注入到accountDao類實例中 -->
43         <property name="jdbcTemplate" ref="jdbcTemplateID" />
44     </bean>
45     
46    <!-- 四、事務管理器,依賴於數據源(注入DataSource),事務從Connection得到,鏈接從鏈接池DataSource得到 -->
47    <bean id="transactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
48         <property name="dataSource" ref="dataSourceID" />
49    </bean>    
50    
51    <!-- 五、編寫通知:對事務進行加強(通知),須要編寫對切入點和具體執行事務細節
52        <tx:attributes>用於配置事務的屬性
53        <tx:method name=""/>詳情的具體配置
54        propagation:指定事務的傳播行爲,REQUIRED是必須的
55        isolation:指定事務的隔離級別    
56     -->
57    <tx:advice id="txAdviceID" transaction-manager="transactionManagerID">
58    
59         <tx:attributes>
60             <!-- 5.一、name:*表示任意方法名稱 -->
61             <tx:method name="*" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
62         </tx:attributes>
63     </tx:advice>
64     
65     <!-- 6.編寫aop,讓spring自動對目標生成代理,須要使用AspectJ的表達式 -->
66     <aop:config>
67     
68         <!-- 6.一、切入點 -->
69         <aop:pointcut expression="execution(* com.itheima.jdbc.*.*(..))" id="txPointCutID" />
70         
71         <!-- 6.二、切面:將切入點與通知整合 -->
72         <aop:advisor advice-ref="txAdviceID" pointcut-ref="txPointCutID" />
73     </aop:config>
74 </beans>

④交易測試類:TransactionTest.java

 1 package com.itheima.jdbc;
 2 import org.junit.Test;
 3 import org.springframework.context.ApplicationContext;
 4 import org.springframework.context.support.ClassPathXmlApplicationContext;
 5 //測試類
 6 public class TransactionTest {
 7     
 8     @Test
 9     public void xmlTest(){
10         
11         ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
12         
13         // 一、獲取AccountDao的Bean實例accountDaoID
14         AccountDao accountDao = (AccountDao)applicationContext.getBean("accountDaoID");
15         
16         // 二、調用實例中的轉帳方法
17         accountDao.transfer("Jack", "Rose", 100.0);
18         
19         // 三、輸出提示信息
20         System.out.println("轉帳成功!");
21     }
22 }

⑤運行結果:(異常/by zero報錯,說明Spring中的事務管理配置已經生效)

5)、基於Annotation方式的聲明式事務

a)、在Spring容器中註冊事務註解驅動

<tx:annotation-driven transaction-manager="transactionManager"/>

b)、在須要事務管理的類或方法上使用@Transactional註解
若是將註解添加在Bean類上,則表示事務的設置對整個Bean類的全部方法都起做用;若是將註解添加在Bean類中的某個方法上,則表示事務的設置只對該方法有效

c)、使用@Transactional註解時,能夠經過參數配置事務詳情:

d)src->applicationContext-annotation.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4     xmlns:aop="http://www.springframework.org/schema/aop"
 5     xmlns:tx="http://www.springframework.org/schema/tx" 
 6     xmlns:context="http://www.springframework.org/schema/context"
 7     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8     http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
 9     http://www.springframework.org/schema/tx 
10     http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
11     http://www.springframework.org/schema/context 
12     http://www.springframework.org/schema/context/spring-context-4.3.xsd
13     http://www.springframework.org/schema/aop 
14     http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
15     <!-- 1.配置數據源 -->
16     <bean id="dataSourceID" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
17     
18         <!--數據庫驅動 -->
19         <property name="driverClassName" value="com.mysql.jdbc.Driver" />
20         
21         <!--鏈接數據庫的url -->
22         <property name="url" value="jdbc:mysql://localhost/spring" />
23         
24         <!--鏈接數據庫的用戶名 -->
25         <property name="username" value="root" />
26         
27         <!--鏈接數據庫的密碼 -->
28         <property name="password" value="******" />
29         
30     </bean>
31     <!-- 2.配置JDBC模板 -->
32     <bean id="jdbcTemplateID" class="org.springframework.jdbc.core.JdbcTemplate">
33     
34         <!-- 默認必須使用數據源 -->
35         <property name="dataSource" ref="dataSourceID" />
36         
37     </bean>
38     
39     <!--3.定義id爲accountDao的Bean實例 -->
40     <bean id="accountDaoID" class="com.itheima.jdbc.AccountDaoImpl">
41     
42         <!-- 將jdbcTemplate注入到AccountDao實例中 -->
43         <property name="jdbcTemplate" ref="jdbcTemplateID" />
44     </bean>
45     
46     <!-- 4.事務管理器,依賴於數據源 -->
47     <bean id="transactionManagerID" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
48         <property name="dataSource" ref="dataSourceID" />
49     </bean>    
50     
51     <!-- 5.註冊事務管理器驅動,替換編寫aop和編寫通知 -->
52     <tx:annotation-driven transaction-manager="transactionManagerID"/>
53 </beans>

e)、在AccountDaoImpl.java的transfer()方法上添加事務註解(定義方法接口)

 1     @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
 2     public void transfer(String outUser, String inUser, Double money) {
 3         
 4         // 收款時,收款用戶的餘額=現有餘額+所匯金額
 5         this.jdbcTemplate.update("update account set balance = balance + ? " +
 6                  "where username = ?",money, inUser);
 7         
 8         // 模擬系統運行時的突發性問題
 9         int i = 1/0;
10         
11         // 匯款時,匯款用戶的餘額=現有餘額-所匯金額
12         this.jdbcTemplate.update("update account set balance = balance-? " +
13                                  "where username = ?",money, outUser);
14     }

f)、運行結果:(異常/by zero報錯,說明Spring中的事務管理配置已經生效)

我的總結:

事務處理這方面比較陌生,對某些參數還有些不熟悉,sql很重要!!!其實操做的方式和前面的知識點大同小異。

相關文章
相關標籤/搜索