Spring 事務傳播詳解

一、事務概念

事務是一組由一個或多個操作組成的不可分割的工作單元,這些操作要麼全部成功,要麼全部失敗。在關係型數據庫中,事務是指一組SQL語句組成的操作序列,具有四個特性:原子性、一致性、隔離性和持久性。

二、Spring 事務處理

Spring 通過TransactionManager來統一管理事務,支持聲明式事務和編程式事務兩種方式。其中,聲明式事務可以通過AOP實現,而編程式事務可以通過編寫代碼來實現。

三、Spring 事務傳播行為

Spring 定義了七種事務傳播行為,用於配置事務的傳播屬性,確保多個事務之間的正確性和一致性。

1、PROPAGATION_REQUIRED(默認值)

如果當前存在事務,則加入該事務;如果不存在事務,則創建一個新事務。

@Transactional(propagation = Propagation.REQUIRED)
public void method(){
   // 事務代碼
}

2、PROPAGATION_SUPPORTS

如果當前存在事務,則加入該事務;如果不存在事務,則以非事務的方式繼續執行。

@Transactional(propagation = Propagation.SUPPORTS)
public void method(){
   // 事務代碼
}

3、PROPAGATION_MANDATORY

如果當前存在事務,則加入該事務;如果不存在事務,則拋出異常。

@Transactional(propagation = Propagation.MANDATORY)
public void method(){
   // 事務代碼
}

4、PROPAGATION_REQUIRES_NEW

創建一個新事務,並且暫停當前事務(如果存在)。

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method(){
   // 事務代碼
}

5、PROPAGATION_NOT_SUPPORTED

以非事務方式執行操作,如果當前存在事務,則掛起該事務。

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void method(){
   // 事務代碼
}

6、PROPAGATION_NEVER

以非事務方式執行操作,如果當前存在事務,則拋出異常。

@Transactional(propagation = Propagation.NEVER)
public void method(){
   // 事務代碼
}

7、PROPAGATION_NESTED

如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則相當於PROPAGATION_REQUIRED。

@Transactional(propagation = Propagation.NESTED)
public void method(){
   // 事務代碼
}

四、Spring 事務傳播案例

下面我們通過一個簡單的案例來說明 Spring 事務傳播的使用。

1、創建數據庫表

創建一個名為 user 的表,用於演示 Spring 事務傳播功能的使用。

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

2、添加依賴

Pom.xml 文件中添加 Spring Boot 相關依賴。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3、配置數據源

在 application.properties 文件中添加數據源相關配置。

spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

4、創建User實體類

創建 User 實體類。

public class User {
    private int id;
    private String name;
    private int age;
    // 省略 getter 和 setter 方法
}

5、添加數據訪問對象

創建 UserDAO 接口和實現類,用於操作數據庫。

public interface UserDAO {
    void addUser(User user);
    void addUserWithRequired(User user);
    void addUserWithRequiresNew(User user);
}
@Repository
public class UserDAOImpl implements UserDAO {
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Override
    public void addUser(User user) {
        String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, new Object[]{user.getName(), user.getAge()});
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public void addUserWithRequired(User user) {
        String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, new Object[]{user.getName(), user.getAge()});
        throw new RuntimeException();
    }
    
    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addUserWithRequiresNew(User user) {
        String sql = "INSERT INTO user(name, age) VALUES(?, ?)";
        jdbcTemplate.update(sql, new Object[]{user.getName(), user.getAge()});
        throw new RuntimeException();
    }
}

6、添加服務層

創建 UserService 接口和實現類,用於操作數據庫。

public interface UserService {
    void addUser(User user);
    void addUserWithRequired(User user);
    void addUserWithRequiresNew(User user);
}
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;
 
    @Override
    @Transactional
    public void addUser(User user) {
        userDAO.addUser(user);
        throw new RuntimeException();
    }
 
    @Override
    @Transactional
    public void addUserWithRequired(User user) {
        userDAO.addUserWithRequired(user);
    }
 
    @Override
    @Transactional
    public void addUserWithRequiresNew(User user) {
        userDAO.addUserWithRequiresNew(user);
    }
}

7、寫測試類

創建一個測試類,測試 Spring 事務傳播的效果。

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class UserServiceImplTest {
 
    @Autowired
    private UserService userService;
    @Autowired
    private UserDAO userDAO;
    
    @Test(expected = RuntimeException.class)
    public void testTransactionRequired() {
        User user = new User();
        user.setName("John");
        user.setAge(20);
        userService.addUserWithRequired(user);
    }
    
    @Test(expected = RuntimeException.class)
    public void testTransactionRequiresNew() {
        User user = new User();
        user.setName("John");
        user.setAge(20);
        userService.addUserWithRequiresNew(user);
    }
}

總結

本文詳細介紹了 Spring 事務傳播的概念、配置方式和七種傳播行為,並提供了一個簡單的案例,演示了 Spring 事務傳播機制的實際使用。掌握了事務傳播的知識,能夠在必要的時候進行正確的配置,確保多個事務之間的正確性和一致性。

原創文章,作者:AAZAW,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/372133.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
AAZAW的頭像AAZAW
上一篇 2025-04-24 06:40
下一篇 2025-04-24 06:40

相關推薦

發表回復

登錄後才能評論