天天看點

spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/w1lgy/article/details/81625848

一、事務的相關概念概念

1、事務:事務指的是邏輯上的一組操作,這組操作要麼全部成功,要麼全部失敗。這一組操作需要滿足事務的四大特效(ACID)。

2、事務的傳播行為:事務的傳播行為是指在開始目前事務前,如果一個事務上下文已經存在,此時采取的事務執行行為。Spring 的 TransactionDefinition 接口中定義了如下的事務傳播行為:

  • PROPAGATION_REQUIRED(預設) :如果目前已存在事務,則加入該事務;如果目前不存在事務,則建立一個新的事務。
  • PROPAGATION_REQUIRES_NEW :無論如何建立一個新的事務;如果目前存在事務,将目前事務挂起。
  • PROPAGATION_SUPPORTS :如果目前已存在事務,則加入事務;如果目前不存在事務,則以非事務方式執行。
  • PROPAGATION_NOT_SUPPORTED :以非事務方式執行;如果目前已存在事務,則将目前事務挂起。
  • PROPAGATION_NEVER :以非事務方式執行;如果目前已存在事務,則抛出異常。
  • PROPAGATION_MANDATORY :如果目前已存在事務,則加入該事務;如果目前不存在事務,則抛出異常。
  • PROPAGATION_NESTED:如果目前已存在事務,則建立一個事務作為目前事務的嵌套事務執行;如果目前不存在事務,則建立一個新的事務。
    spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

    注:上述事務傳播行為大同小異,一般記住預設的事務傳播行為(PROPAGATION_REQUIRED)即可。值得注意的是,以 PROPAGATION_NESTED 啟動的事務如果内嵌于外部事務中,此時内嵌事務并不是一個獨立的事務。隻有外部事務送出,内嵌事務才能送出,外部事務復原也會導緻内嵌事務復原。内嵌事務依賴于外部事務而存在,内嵌事務相當于安全點的作用。

    3、事務的逾時:事務逾時是指一個事務所允許執行的最長時間。如果超過該時間事務還沒執行完畢,則自動復原事務。

    4、事務的隻讀屬性:事務的隻讀屬性是指對事務性資源(如資料庫資源等)執行隻讀操作。比如事務隻讀屬性設定為 true ,那麼隻能對資料庫執行讀操作(如 select ),不能執行寫操作(如 insert、update 等)。當我們确定隻對資料庫進行隻讀操作時,開啟該屬性有利于提高事務處理的性能。

    5、事務的復原規則:事務的復原規則是指在抛出某些異常時事務的處理方式。預設情況下,如果事務抛出未檢查異常(也稱運作時異常,指所有繼承自 RuntimeException 的異常),則復原事務;如果沒有抛出異常或抛出已檢查異常,則送出事務。在實際應用中,我們可以自定義復原規則,比如在抛出某些未檢查異常時送出事務,抛出某些已檢查異常時復原事務。

二、事務的四大特性(ACID)

1、原子性(Atomicity):一個事務中的SQL,要麼全部執行,要麼全部不執行;

  原子性是指事務包含的所有操作要麼全部成功,要麼全部失敗復原,是以事務的操作如果成功就必須要完全應用到資料庫,如果操作失敗則不能對資料庫有任何影響。

2、一緻性(Consistency):一個事務執行前後,資料庫中的所有限制依然然滿足;

  一緻性是指事務必須使資料庫從一個一緻性狀态變換到另一個一緻性狀态,也就是說一個事務執行之前和執行之後都必須處于一緻性狀态。

拿轉賬來說,假設使用者A和使用者B兩者的錢加起來一共是5000,那麼不管A和B之間如何轉賬,轉幾次賬,事務結束後兩個使用者的錢相加起來應該還得是5000,這就是事務的一緻性。

3、持久性(Durability):一個事務執行完成後,事務對資料的修修改必須持久化到資料庫中。

  持久性是指一個事務一旦被送出了,那麼對資料庫中的資料的改變就是永久性的,即便是在資料庫系統遇到故障的情況下也不會丢失送出事務的操作。

例如我們在使用JDBC操作資料庫時,在送出事務方法後,提示使用者事務操作完成,當我們程式執行完成直到看到提示後,就可以認定事務以及正确送出,即使這時候資料庫出現了問題,也必須要将我們的事務完全執行完成,否則就會造成我們看到提示事務處理完畢,但是資料庫因為故障而沒有執行事務的重大錯誤。

4、隔離性(Isolation):一個事務執行過程中,資料不受另一個事務的影響;

  隔離性是當多個使用者并發通路資料庫時,比如操作同一張表時,資料庫為每一個使用者開啟的事務,不能被其他事務的操作所幹擾,多個并發事務之間要互相隔離。

即要達到這麼一種效果:對于任意兩個并發的事務T1和T2,在事務T1看來,T2要麼在T1開始之前就已經結束,要麼在T1結束之後才開始,這樣每個事務都感覺不到有其他事務在并發地執行。

當多個線程都開啟事務操作資料庫中的資料時,資料庫系統要能進行隔離操作,以保證各個線程擷取資料的準确性,在介紹資料庫提供的各種隔離級别之前,我們先看看如果不考慮事務的隔離性,會發生的幾種問題:

a、髒讀:髒讀是指在一個事務處理過程裡讀取了另一個未送出的事務中的資料。

當一個事務正在多次修改某個資料,而在這個事務中這多次的修改都還未送出,這時一個并發的事務來通路該資料,就會造成兩個事務得到的資料不一緻。例如:使用者A向使用者B轉賬100元,對應SQL指令如下

update account set money=money+100 where name=’B’;  (此時A通知B)
update account set money=money - 100 where name=’A’;           

當隻執行第一條SQL時,A通知B檢視賬戶,B發現确實錢已到賬(此時即發生了髒讀),而之後無論第二條SQL是否執行,隻要該事務不送出,則所有操作都将復原,那麼當B以後再次檢視賬戶時就會發現錢其實并沒有轉。

b、不可重複讀:不可重複讀是指在對于資料庫中的某個資料,一個事務範圍内多次查詢卻傳回了不同的資料值,這是由于在查詢間隔,被另一個事務修改并送出了。

  例如事務T1在讀取某一資料,而事務T2立馬修改了這個資料并且送出事務給資料庫,事務T1再次讀取該資料就得到了不同的結果,發送了不可重複讀。

不可重複讀和髒讀的差別是,髒讀是某一事務讀取了另一個事務未送出的髒資料,而不可重複讀則是讀取了前一事務送出的資料。

在某些情況下,不可重複讀并不是問題,比如我們多次查詢某個資料當然以最後查詢得到的結果為主。但在另一些情況下就有可能發生問題,例如對于同一個資料A和B依次查詢就可能不同,A和B就可能打起來了……

c、虛讀(幻讀)

  幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中所有的行的某個資料項做了從“1”修改為“2”的操作,這時事務T2又對這個表中插入了一行資料項,而這個資料項的數值還是為“1”并且送出給資料庫。而操作事務T1的使用者如果再檢視剛剛修改的資料,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像産生幻覺一樣,這就是發生了幻讀。

幻讀和不可重複讀都是讀取了另一條已經送出的事務(這點就髒讀不同),所不同的是不可重複讀查詢的都是同一個資料項,而幻讀針對的是一批資料整體(比如資料的個數)。

三、MySQL資料庫的四種隔離級别:

① Serializable (序列化):可避免髒讀、不可重複讀、幻讀的發生;性能最低,事務隻能一個個執行,解決所有問題。

② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生,可能出現幻讀現象(MySQL預設);。

③ Read committed (讀已送出):可避免髒讀的發生(大多數資料庫預設);。

④ Read uncommitted (讀未送出):最低級别,性能最高;事務中三種問題都可能産生;

以上四種隔離級别最高的是Serializable級别,最低的是Read uncommitted級别,當然級别越高,執行效率就越低。像Serializable這樣的級别,就是以鎖表的方式(類似于Java多線程中的鎖)使得其他的線程隻能在鎖外等待,是以平時選用何種隔離級别應該根據實際情況。在MySQL資料庫中預設的隔離級别為Repeatable read (可重複讀)。

在MySQL資料庫中,支援上面四種隔離級别,預設的為Repeatable read (可重複讀);而在Oracle資料庫中,隻支援Serializable (串行化)級别和Read committed (讀已送出)這兩種級别,其中預設的為Read committed級别。

在MySQL資料庫中檢視目前事務的隔離級别:

select @@tx_isolation;           
spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

在MySQL資料庫中設定事務的隔離 級别:

set  [glogal | session]  transaction isolation level 隔離級别名稱;
set tx_isolation=’隔離級别名稱;’           
spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

或者:

spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

注意:設定資料庫的隔離級别一定要是在開啟事務之前!

如果是使用JDBC對資料庫的事務設定隔離級别的話,也應該是在調用Connection對象的setAutoCommit(false)方法之前。調用Connection對象的setTransactionIsolation(level)即可設定目前連結的隔離級别,至于參數level,可以使用Connection對象的字段:

spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

在JDBC中設定隔離級别的部分代碼:

spring事務管理一、事務的相關概念概念二、事務的四大特性(ACID)

補充:隔離級别的設定隻對目前連結有效。對于使用MySQL指令視窗而言,一個視窗就相當于一個連結,目前視窗設定的隔離級别隻對目前視窗中的事務有效;對于JDBC操作資料庫來說,一個Connection對象相當于一個連結,而對于Connection對象設定的隔離級别隻對該Connection對象有效,與其他連結Connection對象無關。

Spring 的 TransactionDefinition 接口中定義了五個表示隔離級别的常量,分别是:

ISOLATION_DEFAULT(預設) :表示采用底層資料庫的隔離級别,比如底層資料庫采用的是 MySQL ,則采用的隔離級别就是 REPEATABLE_READ 。

ISOLATION_READ_UNCOMMITTED :對應隔離級别 READ_UNCOMMITTED 。

ISOLATION_READ_COMMITTED :對應隔離級别 READ_COMMITTED 。

ISOLATION_REPEATABLE_READ :對應隔離級别 REPEATABLE_READ 。

ISOLATION_SERIALIZABLE :對應隔離級别 SERIALIZABLE 。

四、Spring提供了的兩種事務管理方式:

1、程式設計式事務管理

PlatformTransactionManager實作程式設計式事務管理

步驟1、配置Spring事務管理器

springConfig.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 配置Spring自動掃描的目錄 -->
    <context:component-scan base-package="com.prosay.transaction"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/java?useSSL=true"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置Spring事務管理器(無論哪種方式都要這個事務管理器) -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
</beans>           

步驟2、擷取PlatformTransactionManager執行個體

步驟3、擷取TransactionStatus對象,打開事務

步驟4、送出事務/復原事務

@Component("orderService4Platform")
public class OrderService4Platform implements IOrderService {

    @Autowired
    private IOrderDao orderDao;

    @Autowired
    @Qualifier("productDaoImpl4Platform")
    private IProductDao productDao;

    @Autowired
    private PlatformTransactionManager transaction;

    public void createOrder(){
        //打開預設事務   
        TransactionStatus status = transaction.getTransaction(new DefaultTransactionDefinition());
        try {
            Order order = new Order();
            order.setOrder_id(104);
            order.setProduct_id(1004);
            order.setCustomer("Kevin");
            order.setNumber(100);
            int row = orderDao.insertOrder(order);

            System.out.println("添加訂單影響行數:"+row);
            if(row>0){
                //下單成功,修改庫存
                boolean falg = productDao.updateProduct(order);
                if(falg){
                    System.out.println("庫存修改成功。");
                }else{
                    System.out.println("庫存修改失敗。");
                }
            }
            //通過事務管理器送出目前事務
            transaction.commit(status);
        } catch (Exception e) {
            //通過事務管理器復原目前事務
            transaction.rollback(status);
            e.printStackTrace();
        }
    }
}           
@Component("productDaoImpl4Platform")
public class ProductDaoImpl4Platform implements IProductDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean updateProduct(Order order) {
        String sql = "update t_product set count = count-? where product_id=?";
        int row = jdbcTemplate.update(sql, order.getNumber(), order.getProduct_id());
//      MessageUtil.sendMessage();
        if(row>0){
            return true;
        }
        return false;
    }

}           
@Component
public class OrderDaoImpl implements IOrderDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public int insertOrder(Order order) {
        String sql = "insert into t_order(order_id, product_id, customer, number) values(?,?,?,?)";
        int row = jdbcTemplate.update(sql, order.getOrder_id(), order.getProduct_id(), order.getCustomer(), order.getNumber());
        System.out.println("execute row:"+row);
        return row;
    }

}           
public class Client {

    public static void main(String[] args) {

        AbstractApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");
        IOrderService orderService = (IOrderService) context.getBean("orderService4Platform");
        orderService.createOrder();
    }
}
           

TransactionTemplate實作程式設計式事務管理

springConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 配置Spring自動掃描的目錄 -->
    <context:component-scan base-package="com.prosay.transaction"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/java?useSSL=true"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置Spring事務管理器(無論哪種方式都要這個事務管理器)  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 配置TransactionTemplate -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"></property>
        <!-- 定義事務隔離級别 -->
        <property name="isolationLevelName" value="ISOLATION_DEFAULT"></property>
        <!-- 定義事務傳播屬性 -->
        <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>
    </bean>
</beans>           

步驟2、擷取TransactionTemplate執行個體

步驟3、通過TransactionTemplate.execute(),打開事務環境

public class Client {

    public static void main(String[] args) {

        AbstractApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");
        IOrderService orderService = (IOrderService) context.getBean("orderService4Template");
        orderService.createOrder();
    }
}
           
@Component("orderService4Template")
public class OrderService4Template implements IOrderService {

    @Autowired
    private IOrderDao orderDao;

    @Autowired
    @Qualifier("productDaoImpl4Template")
    private IProductDao productDao;

    @Autowired
    private TransactionTemplate transactionTemplate;

    public void createOrder(){
        Integer result = transactionTemplate.execute(new TransactionCallback<Integer>() {
            public Integer doInTransaction(TransactionStatus status) {
                Order order = new Order();
                order.setOrder_id(104);
                order.setProduct_id(1004);
                order.setCustomer("Kevin");
                order.setNumber(100);
                int row = orderDao.insertOrder(order);

                System.out.println("添加訂單影響行數:"+row);
                if(row>0){
                    //下單成功,修改庫存
                    boolean falg = productDao.updateProduct(order);
                    if(falg){
                        System.out.println("庫存修改成功。");
                    }else{
                        System.out.println("庫存修改失敗。");
                    }
                }
                return row;
            }
        });

    }
}           
@Component("productDaoImpl4Template")
public class ProductDaoImpl4Template implements IProductDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean updateProduct(Order order) {

        String sql = "update t_product set count = count-? where product_id=?";
        int row = jdbcTemplate.update(sql, 100, 1004);
//      MessageUtil.sendMessage();
        if(row>0){
            return true;
        }
        return false;
    }

}           

2、聲明式事務管理

注解實作聲明式事務管理

步驟1、開啟注解式事務管理

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 配置Spring自動掃描的目錄 -->
    <context:component-scan base-package="com.prosay.transaction"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/java?useSSL=true"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置Spring事務管理器(無論哪種方式都要這個事務管理器)  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置啟動注解方式使用事務 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>           

步驟2、使用@Transactional注解使用事務

public class Client {

    public static void main(String[] args) {

        AbstractApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");
        IOrderService orderService = (IOrderService) context.getBean("orderService4Annotation");
        orderService.createOrder();
    }
}
           
@Component("orderService4Annotation")
public class OrderService4Annotation implements IOrderService {

    @Autowired
    private IOrderDao orderDao;

    @Autowired
    @Qualifier("productDaoImpl4Annotation")
    private IProductDao productDao;

    @Transactional(propagation=Propagation.REQUIRED)
    public void createOrder(){
        Order order = new Order();
        order.setOrder_id(104);
        order.setProduct_id(1004);
        order.setCustomer("Kevin");
        order.setNumber(100);
        int row = orderDao.insertOrder(order);

        System.out.println("添加訂單影響行數:"+row);
        if(row>0){
            try {
                //下單成功,修改庫存
                boolean falg = productDao.updateProduct(order);
                if(falg){
                    System.out.println("庫存修改成功。");
                }else{
                    System.out.println("庫存修改失敗。");
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}
           
@Component("productDaoImpl4Annotation")
public class ProductDaoImpl4Annotation implements IProductDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public boolean updateProduct(Order order) {

        String sql = "update t_product set count = count-? where product_id=?";
        int row = jdbcTemplate.update(sql, 100, 1004);
//      MessageUtil.sendMessage();
        if(row>0){
            return true;
        }
        return false;
    }

}           

補充:

在上例上OrderService4Annotation添加事務

@Transactional(propagation=Propagation.REQUIRED)

和ProductDaoImpl4Annotation添加的事務

@Transactional(propagation=Propagation.REQUIRES_NEW)

注解參數是事務的傳播屬性,上文已經提到REQUIRES_NEW表示當目前有事務時,挂起目前事務,建立事務,這就把這裡的下單和修改庫存變成兩個事務了,由事務的隔離性,當修改庫存時出現異常時,庫存修改不會成功,事務會復原;在createOrder方法中處理了修改庫存的異常,是以createOrder不會抛出異常,下單的事務沒有問題,事務不會復原。這就會有下單成功,但是庫存不會修改的情況。

步驟1、配置事務屬性

步驟2、配置事務切點

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context  
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
    <!-- 配置Spring自動掃描的目錄 -->
    <context:component-scan base-package="com.prosay.transaction"/>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/java?useSSL=true"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 配置Spring事務管理器(無論哪種方式都要這個事務管理器)  -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean> 
    <!-- AOP配置聲明式事務管理 -->
    <tx:advice id="advice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 方法名可以直接指定也可以用正規表達式去比對,propagation是隔離級别屬性,還可以加入其它屬性 -->
            <tx:method name="createOrder" propagation="REQUIRED"  />
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <aop:pointcut expression="execution(** com.prosay.transaction.service.*.*(..))" id="pointCut"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointCut"/>
    </aop:config>
</beans>           
public class Client {

    public static void main(String[] args) {

        AbstractApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");
        IOrderService orderService = (IOrderService) context.getBean("orderService4Advice");
        orderService.createOrder();
    }
}
           
@Component("orderService4Advice")
public class OrderService4Advice implements IOrderService {

    @Autowired
    private IOrderDao orderDao;

    @Autowired
    @Qualifier("productDaoImpl4Advice")
    private IProductDao productDao;

    public void createOrder(){
        Order order = new Order();
        order.setOrder_id(104);
        order.setProduct_id(1004);
        order.setCustomer("Kevin");
        order.setNumber(100);
        int row = orderDao.insertOrder(order);

        System.out.println("添加訂單影響行數:"+row);
        if(row>0){
            //下單成功,修改庫存
            boolean falg = productDao.updateProduct(order);
            if(falg){
                System.out.println("庫存修改成功。");
            }else{
                System.out.println("庫存修改失敗。");
            }
        }
    }
}
           
@Component("productDaoImpl4Advice")
public class ProductDaoImpl4Advice implements IProductDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean updateProduct(Order order) {

        String sql = "update t_product set count = count-? where product_id=?";
        int row = jdbcTemplate.update(sql, 100, 1004);
//      MessageUtil.sendMessage();
        if(row>0){
            return true;
        }
        return false;
    }
}