Spring中的事物管理,用 @Transactional 注解聲明式地管理事務


事物:

  事務管理是企業級應用程序開發中必不可少的技術,  用來確保數據的 完整性和 一致性.

  事務就是一系列的動作, 它們被當做一個單獨的工作單元. 這些動作要么全部完成, 要么全部不起作用

事務的四個關鍵屬性:

  原子性:事務是一個原子操作, 由一系列動作組成. 事務的原子性確保動作要么全部完成要么完全不起作用.

  一致性:一旦所有事務動作完成, 事務就被提交. 數據和資源就處於一種滿足業務規則的一致性狀態中.

  隔離性:可能有許多事務會同時處理相同的數據, 因此每個事物都應該與其他事務隔離開來, 防止數據損壞.

  持久性:一旦事務完成, 無論發生什么系統錯誤, 它的結果都不應該受到影響. 通常情況下, 事務的結果被寫到持久化存儲器中.

-------------------------------------------------------------------

用 @Transactional 注解聲明式地管理事務的例子:

在mysql中建立三個數據表分別為:

account:屬性:用戶名:username(varchar),該用戶的賬戶余額:balance(int);

book:書的編號:isbn(int),書名:book_name(varchar),價錢:price(int);

book_stock:書的編號:isbn(int),庫存:stock(int);

導包。。。。

建立存放連接數據庫的file文件:jdbc.properties;

jdbc.user=root jdbc.password=lxn123 jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql:///spring2
 jdbc.initPoolSize=5 jdbc.maxPoolSize=10

 

建立spring bean configuration file的xml文件,導入資源文件和配置c3p0數據源,這是基於注解的聲明管理事物,所以也將加特定注解的包進行掃描;

    <!-- 基於注解的bean配置,掃描這個包及其自包 -->
    <context:component-scan base-package="com.atguigu.spring.jdbc"></context:component-scan>
    
    <!-- 導入資源文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 配置c3p0數據源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        
        <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
    </bean>
    
    <!-- 配置spring 的 JdbcTemplate ,里面有一些jdbc的方法,實現對數據庫數據的增刪改查-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

 

建立一個接口:讓繼承他的子類去實例化一些方法

package com.atguigu.spring.jdbc; public interface BookShopDao { //根據書號獲取書的單價,isbn為書名的編號
    public int findBookPriceByIsbn(int isbn); //更新數的庫存. 使書號對應的庫存 - 1
    public void updateBookStock(int isbn); //更新用戶的賬戶余額: 使 username 的 balance - price
    public void updateUserAccount(String username,int price); }

 

建立BookShopDaoImpl 類並繼承接口: BookShopDao,實例化其方法,並且在類名前 加注解:@Repository("bookShopDao"),和在屬性private JdbcTemplate jdbcTemplae;的上邊加注解:@Autowired

package com.atguigu.spring.jdbc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; @Repository("bookShopDao") public class BookShopDaoImpl implements BookShopDao { @Autowired private JdbcTemplate jdbcTemplae; //通過書名獲取書的價格
 @Override public int findBookPriceByIsbn(int isbn) { String sql="select price from book where isbn=?"; return jdbcTemplae.queryForObject(sql, Integer.class, isbn); } @Override public void updateBookStock(int isbn) { String sql="select stock from book_stock where isbn=?"; //stock,指數的庫存 //通過書名檢查書的庫存
        int stock=jdbcTemplae.queryForObject(sql, Integer.class, isbn); if(stock==0){ System.out.println("庫存不足!!!"); throw new BookStockException("庫存不足!!!"); } String sql2="update book_stock set stock=stock-1 where isbn=?"; jdbcTemplae.update(sql2, isbn); } //用戶的余額減去要購買書的價錢
 @Override public void updateUserAccount(String username, int price) { //驗證余額是否足夠, 若不足, 則拋出異常
        String sql2="select balance from account where username=?"; int balance=jdbcTemplae.queryForObject(sql2, Integer.class, username); if(balance<price){ System.out.println("余額不足!!!"); throw new BookStockException("余額不足!!!"); } String sql="update account set balance=balance-? where username=?"; jdbcTemplae.update(sql, price, username); } }

 

建立一個接口:BookShopService,有一個沒有實例化的方法;

package com.atguigu.spring.jdbc; public interface BookShopService { public void purchase(String username,int isbn); }

 

建立類: BookShopServiceImpl 繼承於接口 BookShopService,有一個方法,合並了BookShopDaoImpl類中的三個方法,建立了一個“事物”;在該類上邊加了注解@Service("bookShopService"),在屬性private BookShopDao bookShopDao;加了注解@Autowired,在其方法上加了注解@Transactional(propagation=Propagation.REQUIRES_NEW),都是便於在spring的xml文件中bean的配置掃描識別;

package com.atguigu.spring.jdbc; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; /** *添加事務注解 *1.使用 propagation 指定事務的傳播行為, 即當前的事務方法被另外一個事務方法調用時 *如何使用事務, 默認取值為 REQUIRED, 即使用調用方法的事務 *REQUIRES_NEW: 事務自己的事務, 調用的事務方法的事務被掛起. *2.使用 isolation 指定事務的隔離級別, 最常用的取值為 READ_COMMITTED *3.默認情況下 Spring 的聲明式事務對所有的運行時異常進行回滾. 也可以通過對應的 *屬性進行設置. 通常情況下去默認值即可. *4.使用 readOnly 指定事務是否為只讀. 表示這個事務只讀取數據但不更新數據, *這樣可以幫助數據庫引擎優化事務. 若真的事一個只讀取數據庫值的方法, 應設置 readOnly=true *5.使用 timeout 指定強制回滾之前事務可以占用的時間. *@Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, noRollbackFor={UserAccountException.class}, readOnly=false, timeout=3) * */ @Transactional(propagation=Propagation.REQUIRES_NEW) @Override public void purchase(String username, int isbn) { //1. 獲取書的單價
        int price=bookShopDao.findBookPriceByIsbn(isbn); //2. 更新書的庫存
 bookShopDao.updateBookStock(isbn); //3. 更新用戶余額
 bookShopDao.updateUserAccount(username, price); } }

 

建立一個接口:Cashier

package com.atguigu.spring.jdbc; import java.util.List; public interface Cashier { //購買多本書
    public void checkout(String username,List<Integer> isbns); }

 

建立類:CashierImpl 繼承於上邊的接口 Cashier,該中有private BookShopService bookShopService這個“事物”父類的屬性,類中僅有的一個方法,可以實現多個“原子事物",該類是調用事物的事物類,僅有的方法為調用事物的事物方法;在該類上邊加注解@Service("cashier"),在屬性private BookShopService bookShopService;上邊加注解@Autowired,在方法上邊加事物的注解@Transactional

package com.atguigu.spring.jdbc; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service("cashier") public class CashierImpl implements Cashier { @Autowired private BookShopService bookShopService; @Transactional @Override public void checkout(String username, List<Integer> isbns) { for(int isbn:isbns){ bookShopService.purchase(username, isbn); } } }

 

在spring的xml文件中 配置事物管理器和 啟用事物管理器的注解

    <!-- 配置事物管理器 -->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 啟用事物管理器的注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

 

最后建立測試類:JUnitTest

package com.atguigu.spring.jdbc; import java.sql.SQLException; import java.util.Arrays; import javax.sql.DataSource; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; public class JUnitTest { private ApplicationContext ctx=null; private BookShopService bookShopService; private Cashier cashier; { ctx=new ClassPathXmlApplicationContext("applicationContext.xml"); bookShopService=(BookShopService) ctx.getBean("bookShopService"); cashier=(Cashier) ctx.getBean("cashier"); } @Test //調用事物的事物方法的測試,即有多個事物
    public void testCashier(){ cashier.checkout("AA", Arrays.asList(1001,1002)); } @Test //事物的測試方法
    public void testPurchase(){ bookShopService.purchase("AA", 1001); } //測試數據庫連接池是否連接成功
    public void testDataSource() throws SQLException { DataSource dataSource=(DataSource) ctx.getBean("dataSource"); System.out.println(dataSource.getConnection()); } }

 

建立一個異常處理類:BookStockException 繼承於異常處理父類 RuntimeException

package com.atguigu.spring.jdbc; public class BookStockException extends RuntimeException { //異常類的建立:繼承於RuntimeException這個父類,點擊上邊的Source,設置構造器,即可
    public BookStockException() { super(); // TODO Auto-generated constructor stub
 } public BookStockException(String arg0, Throwable arg1, boolean arg2, boolean arg3) { super(arg0, arg1, arg2, arg3); // TODO Auto-generated constructor stub
 } public BookStockException(String arg0, Throwable arg1) { super(arg0, arg1); // TODO Auto-generated constructor stub
 } public BookStockException(String arg0) { super(arg0); // TODO Auto-generated constructor stub
 } public BookStockException(Throwable arg0) { super(arg0); // TODO Auto-generated constructor stub
 } }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM