1. 程式人生 > 其它 >Spring——事務管理

Spring——事務管理

事務

(1)事務是資料庫操作最基本單元,邏輯上一組操作,要麼都成功,如果有一個失敗所有操作都失敗
(2)典型場景:銀行轉賬

事務四個特性(ACID

原子性(atomicity) 一個事務是一個不可分割的工作單位,事務中包括的操作要麼都做,要麼都不做。
一致性(consistency) 事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation) 一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的資料對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
永續性(durability) 永續性也稱永久性(permanence),指一個事務一旦提交,它對資料庫中資料的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

環境搭建

模擬銀行轉賬

資料庫表

Dao層

public interface AccountDao {

    /**
     * 增加金額
     * @param username 轉賬人
     * @param money 金額
     */
    void addMoney(String username, BigDecimal money);

    /**
     * 減少金額
     * @param username 收賬人
     * @param money 金額
     */
    void reduceMoney(String username, BigDecimal money);
}
@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public void addMoney(String username, BigDecimal money) {
        String sql = "update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql, money, username);
    }

    @Override
    public void reduceMoney(String username, BigDecimal money) {
        String sql = "update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql, money, username);
    }
}

service層

@Service
public class AccountService {

    @Autowired
    AccountDao accountDao;

    /**
     * 轉賬
     * @param fromName 轉賬人
     * @param toName 收賬人
     * @param money 金額
     */
    public void transfer(String fromName, String toName, BigDecimal money){

        accountDao.reduceMoney(fromName, money);
        accountDao.addMoney(toName, money);

    }

}

配置檔案

資料庫配置

jdbc.username=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/wcy
jdbc.driver=com.mysql.cj.jdbc.Driver

spring配置

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="com.wcy"/>

    <!--配置檔案-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!--引用外部配置檔案 配置資料來源-->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
    </bean>

    <!--配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>


</beans>

測試

@Test
public void transfer() {
	ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
	AccountService accountService = context.getBean("accountService", AccountService.class);
	accountService.transfer("wcy", "wp", new BigDecimal(100));
}

在不發生異常的情況下,執行轉賬的方法沒有問題
但是如果在轉賬圖中發生了異常,就會造成不對應的情況,所以需要用到事務管理

Spring事務管理

事務新增到 JavaEE 三層結構裡面 Service 層(業務邏輯層)

Spring 進行事務管理操作有兩種方式:程式設計式事務管理和宣告式事務管理(使用)

宣告式事務管理
(1)基於註解方式(使用)
(2)基於 xml 配置檔案方式

Spring 進行宣告式事務管理,底層使用 AOP 原理

Spring 事務管理 API
提供一個介面,代表事務管理器,這個介面針對不同的框架提供不同的實現類

基於註解的事務管理

在配置檔案中配置事務管理器,並開啟註解事務管理

需要匯入tx名稱空間

<!--開啟基於註解的事務管理-->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!--配置事務管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSource"/>
</bean>

在service層添加註解

@Service
@Transactional
public class AccountService {
}
  • @Transactional 可以新增到類上面,也可以新增方法上面
  • 如果把這個註解新增類上面,這個類裡面所有的方法都新增事務
  • 如果把這個註解新增方法上面,為這個方法新增事務

這樣就完成了基本的事務管理

事務的操作

@Transactional中可以配置許多的引數

// 事務的傳播行為
Propagation propagation() default Propagation.REQUIRED;

// 事務的隔離級別
Isolation isolation() default Isolation.DEFAULT;

// 超時時間
int timeout() default -1;

//是否只讀
boolean readOnly() default false;

// 回滾配置
Class<? extends Throwable>[] rollbackFor() default {};

// 回滾配置
String[] rollbackForClassName() default {};

// 不回滾
Class<? extends Throwable>[] noRollbackFor() default {};

// 不回滾
String[] noRollbackForClassName() default {};

propagation

作用:配置事務的傳播特性

實際就是多事務方法直接進行呼叫,在這個過程中事務是如何進行管理的
spring中定義了7種傳播行為

ioslation

作用:配置事務隔離級別

事務有特性成為隔離性,多事務操作之間不會產生影響。不考慮隔離性產生很多問題

有三個讀問題:髒讀、不可重複讀、虛(幻)讀

  • 髒讀:即為事務1第二次讀取時,讀到了事務2未提交的資料。若事務2回滾,則事務1第二次讀取時,讀到了髒資料。
    舉例:學生表中張三的成績是59,事務一修改了學生表張三的成績69,但是事務還沒有提交,這個時候事務二來讀取張三的成績,讀到了69,但是事務一出問題回滾了,資料庫學生表中張三還是59,明顯事務二讀的資料不對,“髒讀”了。

  • 不可重複讀:事務一第二次查詢某條資料時,讀到了事務二提交的修改的該資料
    舉例:事務一第一次讀取張三的成績是59(事務沒結束),這個時候,事務二改了張三的成績為69並且commit,這時候事務一又去讀取張三的成績,哎?變成69了,就是重複讀資料不一致,不可重複讀。

  • 事務2在事務1第二次讀取時,提交了資料。導致事務1前後兩次讀取的資料量不一致
    舉例:事務一讀取學生表總共有45人(事務沒結束),這個時候事務二突然插入一條學生資料,事務一再次讀取總數,發現變成了46條資料,就是兩次讀取的資料數量不一致。

通過配置ioslation來解決

timeout:超時時間

(1)事務需要在一定時間內進行提交,如果不提交進行回滾
(2)預設值是 -1 ,設定時間以秒單位進行計算

readOnly:是否只讀

(1)讀:查詢操作,寫:新增修改刪除操作
(2)readOnly 預設值 false,表示可以查詢,可以新增修改刪除操作
(3)設定 readOnly 值是 true,設定成 true 之後,只能查詢

rollbackFor:回滾

(1)設定出現哪些異常進行事務回滾,通過Class物件來設定

noRollbackFor:不回滾

(1)設定出現哪些異常不進行事務回滾,通過Class物件來設定

rollbackForClassName

(1)設定出現哪些異常進行事務回滾,通過類名來設定

純註解方式的事務管理

使用配置類來替代配置檔案

@Configuration
@ComponentScan(basePackages = {"com.wcy"})
@EnableTransactionManagement  //開啟事務管理
public class TransactionConfig {

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        dataSource.setUrl("jdbc:mysql://localhost:3306/wcy");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        return dataSource;
    }

    /**
     * 配置jdbcTemplate 和配置檔案中的bean標籤作用一樣
     * DataSource引數通過byType的方式自動注入 也可以使用byName
     * Autowired可以寫可以不寫
     */
    @Bean
    public JdbcTemplate jdbcTemplate(@Autowired DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public TransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }

}
  • @EnableTransactionManagement的作用和<tx:annotation-driven transaction-manager="transactionManager"/>的作用一樣
  • @Bean和bean標籤的作用一樣

新增@Transactional註解

@Service
@Transactional(
        propagation = Propagation.REQUIRED,
        rollbackFor = {Exception.class}
)
public class AccountService {
}

基於xml配置的事務管理

不需要開啟註解事務,使用aop來切入

<!--配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--事務通知-->
    <tx:advice transaction-manager="transactionManager" id="transactionAdvice">
        <tx:attributes>
            <!--配置那些方法需要新增事務 可以使用萬用字元*來配置-->
            <!--可以配置事務的引數 和註解中的一樣-->
            <tx:method name="transfer" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!--aop事務切入-->
    <aop:config>
        <aop:pointcut id="point" expression="execution(* com.wcy.service.AccountService.*(..))"/>
        <aop:advisor advice-ref="transactionAdvice" pointcut-ref="point"/>
    </aop:config>

Service中不需要@Transactionl註解

事務的基本配置~