網頁

2020/8/27

Spring Boot TransactionTemplate programmatically rollback

本篇介紹Spring TransactionTemplate如何在程式中手動回滾交易。

範例環境:

  • Spring Boot 2.3.2

Spring進行資料庫交易時,除了在交易的方法前加上@Transactional來管理交易外,Spring的TransactionTemplate提供在程式中"手動"管理交易的方法。

下面DepartmentServicedeleteDepartment(Long id)依傳入的部門ID來刪除部門資料及所屬員工資料,資料異動操作共兩次。這邊用TransactionTemplate以程式的方式手動管理交易。

建構TransactionTemplate物件需傳入PlatformTransactionManager,此為實際負責commit及rollback的介面。Spring預設已註冊PlatformTransactionManager的bean,因此以建構式注入DepartmentService()並建立TransactionTemplate物件。

DepartmentService

package com.abc.demo.service;

import com.abc.demo.entity.Employee;
import com.abc.demo.repository.DepartmentRepository;
import com.abc.demo.repository.EmployeeRepository;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.sql.SQLException;
import java.util.List;

@Log4j2
@Service
public class DepartmentService {

    @Autowired
    private DepartmentRepository departmentRepository;

    @Autowired
    private EmployeeRepository employeeRepository;

    private final TransactionTemplate transactionTemplate;

    public DepartmentService(PlatformTransactionManager platformTransactionManager) {
        transactionTemplate = new TransactionTemplate(platformTransactionManager);
    }

//    @Transactional
    public void deleteDepartment(Long id) {
        List<Employee> employeeList = employeeRepository.findByDepartmentId(id);

        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                try {
                    employeeRepository.deleteInBatch(employeeList); // 刪除部門所屬員工資料
                    departmentRepository.deleteById(id); // 刪除部門資料
                    if (id == 1L) {
                        throw new SQLException("Database transaction error!!"); // 模擬交易發生錯誤
                    }
                } catch (SQLException e) {
                    log.error(e.getMessage());
                    transactionStatus.setRollbackOnly(); // rollback
                }
            }
        });
    }
}

TransactionTemplate.execute(TransactionCallback<T> action)方法用來執行資料庫操作,傳入一個TransactionCallback實例作為參數,並在TransactionCallback的實作中執行資料庫操作程式並捕捉錯誤。若操作不回傳值則改用TransactionCallbackWithoutResult

當錯誤發生時呼叫TransactionStatus.setRollbackOnly()進行rollback,因此前面刪除員工及部門資料的動作會回復到交易前的狀態。

參考github


沒有留言:

張貼留言