博客 / 詳情

返回

spring-事務管理

事務支持

什麼是事務?

在一個業務流程中,需要多條DML(insert、delete、update)語句聯合才能完成。這些語句必須同時成功或者同時失敗。這樣才能保證數據安全。

多條DML同時成功或者同時失敗,叫做事務。

事務處理的四個過程

  1. 開啓事務
  2. 執行業務代碼
  3. 提交事務(沒出現異常,提交成功。commit transaction)
  4. 回滾事務(出現異常。執行回滾事務. rollback transaction)

事務的四個特性(ACID)

  1. A原子性:事務是最小的工作單元,不可分
  2. C一致性:事務要麼同時成功,要麼同時失敗
  3. I隔離性:事務與事務之間保證和互不干擾
  4. D持久性:持久性是事務結束的標誌。

spring對事務的支持

spring實現事務的2種方式:

  1. 編程式事務:通過編寫代碼的方式來實現事務管理
  2. 聲明式事務:基於註解方式和基於xml方式(推薦使用)

spring事務管理api

spring對事務的管理是基於aop實現的。所以spring專門針對事務開發了一套api,其核心接口如下:PlatformTransactionManager 接口。

聲明式事務基於註解方式實現

需要配置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"
       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.ali" />

<!--    配置數據源-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/bank"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    </bean>
<!--    配置jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>

<!--    配置事務管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
<!--    開啓事務註解驅動器,開啓事務註解,需要加上tx的命名空間-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

可以在類和方法上加@Transactional 開啓事務

  • 加在類上表示這個類上的所有方法都開啓事務
  • 加在方法方法上表示只有這個方法開啓事務

事務的傳播行為

什麼是事務的傳播行為?

在service種有a()和b()2個方法,a()上有事務,b()上也有事務,當a()在執行過程中調用了b(),事務是如何傳遞的?是合併到一個事務?還是開啓一個新事務?這就是事務的傳播行為。

一共有7種傳播行為:

  1. REQUIRD:支持當前事務,如果不存在就新建一個事務(默認)【沒有就新建,有就加入】
  2. SUPPORTS:支持當前事務,如果當前沒有事務,就以非事務方式執行【有就加入,沒有就不管了】
  3. MANDATORY:必須運行在一個事務中,如果當前沒有事務發生,將拋出異常【有就加入,沒有就拋異常】
  4. REQUIRES_NEW:開啓一個新事務,如果一個事務已經存在,則將這個存在的事務掛起【不管有沒有。直接開啓一個新事務。新事務和舊事務不存在嵌套關係,舊事務被掛起了】
  5. NOT_SUPPORTED:以非事務方式運行。如果有事務。則掛起當前事務【不支持事務,存在就掛起】
  6. NEVER:以非事務方式運行。如果有事務。則拋異常【不支持事務,存在就拋異常】
  7. NESTED:如果當前有一個事務在進行中,則該方法應當運行在一個嵌套事務中。被嵌套的事務可以獨立於外層事務進行提交或回滾。如果外層事務不存在。行為就像REQUIRD一樣【有事務的話,就在這個事務裏嵌套一個完全獨立的事務,嵌套的事務可以獨立的提交和回滾。沒有事務就和REQUIRD一樣】

在代碼中設置事務的傳播行為:

@Transactional(propagation = Propagation.MANDATORY)

事務隔離級別

數據庫中讀取數據存在三大問題:

  1. 髒讀:讀取到沒有提交到數據庫的數據
  2. 不可重複讀:在同一個事務中,第一次和第二次讀取的數據不一樣
  3. 幻讀:讀到的數據是假的()

事務的隔離級別有4個:

  1. 讀未提交READ_UNCOMMITTED: 存在髒讀、不可重複讀、幻讀問題
  2. 讀提交READ_COMMITTED:事務提交之後才讀到。存在不可重複讀、幻讀問題
  3. 可重複讀REPEATABLE_READ:解決不可重複讀的問題,存在幻讀問題
  4. 序列化SERIALIZABLE:解決幻讀問題,事務排隊執行。不支持併發。

MySQL默認可重複讀,Oracle默認讀提交

僅在讀的事務中設置隔離級別就行,寫的事務沒必要設置

代碼中設置事務的隔離級別:

@Transactional(isolation = Isolation.DEFAULT)

事務超時

@Transactional(timeout = 10)

以上代碼設置事務超時時間為10s

表示超過10s,如果事務中所有的DML語句還沒有執行完畢的話,最終結果會回滾。

默認值-1,表示沒有時間限制。

事務的超時時間值得是哪段時間?

在當前事務中,最後一條DML語句執行之前的時間,如果最後一條DML語句後面有很多業務邏輯,這些業務代碼執行的時間不被計入超時時間。

只讀事務

@Transactional(readOnly = true)

將當前事務設為只讀事務,在該事務中只允許執行select 語句。

該特性的作用是:啓動spring的優化策略。提高select語句的執行效率。

設置哪些異常回滾事務

@Transactional(rollbackFor = RuntimeException.class)

表示發生RuntimeException異常或該異常的子類異常才回滾

設置哪些異常不回滾事務

@Transactional(noRollbackFor = NullPointerException.class)

表示發生NullPointerException異常或該異常的子類不回滾,其他異常才回滾

事務的全註解式開發

編寫配置類

@Configuration // 代替xml配置文件
@ComponentScan("com.ali") // 掃描com.ali包下的所有類
@EnableTransactionManagement // 開啓事務管理
public class Spring6Config {

    // @Bean註解用於將方法的返回值註冊為Spring容器中的一個Bean
    @Bean(name = "druidDataSource")
    public DruidDataSource druidDataSource() {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/spring6?useSSL=false&serverTimezone=UTC");
        druidDataSource.setUsername("root");
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setPassword("123456");
        return druidDataSource;
    }

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

    @Bean(name = "jdbcTemplate")
    // 該方法的參數DataSource dataSource會自動從Spring容器中找到類型為DataSource的Bean並注入
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
}

使用時和其他方式一樣。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.