簡體   English   中英

在@Transactional 服務方法中捕獲 DataIntegrityViolationException

[英]Catch DataIntegrityViolationException in @Transactional service method

我有一個帶有 Hibernate 的 REST Spring 啟動應用程序。 為簡單起見,我們假設此工作流程:

  1. 控制器處理傳入的請求,調用服務方法
  2. 服務方法是@Transactional ,做一些業務邏輯並調用 Persistence 方法
  3. 持久性方法由 DAO 對象處理,將內容保存到數據庫中。

數據庫對username的用戶usernameunique約束。 我現在的工作方式是這樣的:

  1. 客戶端向控制器發送請求
  2. 控制器調用服務
  3. 服務嘗試通過 DAO 保存對象。 如果發生DataViolationException ,Service 返回自定義異常
  4. 控制器捕獲自定義異常並發回適當的響應

偽代碼是這樣的:

public class UserController {
    @RequestMapping("/user")
    public User createUser(...){
        try{
            return userService.createUser(...);
        } catch (UserAlreadyExistsException e){
            // Do some processing and return error message to client
        }
    }
}

public class UserService {
    @Transactional
    public User createUser(...){
        (...)
        try{
            userDAO.save(newUserObject);
        } catch(DataIntegrityViolationException e){
            throw new UserAlreadyExistsException(username);
        }
    }
}

但是,這樣我在嘗試創建重復用戶時會收到錯誤消息。

javax.persistence.RollbackException: Transaction marked as rollbackOnly

解決此問題的一種方法似乎是讓DataIntegrityViolationException從事務中“冒泡”(而不是在服務中捕獲它)。 但這意味着控制器必須處理持久性異常,我不喜歡那樣。

我更喜歡服務拋出“可理解”的異常以供控制器處理。 該服務知道預期什么持久性異常以及何時發生,並且能夠將廣泛的DataIntegrityViolationException “轉換”為有意義的DataIntegrityViolationException

有沒有辦法以這種方式處理異常? 我不是特別喜歡使用“2 層服務層”來實現這一點的想法。


編輯:我想拋出自定義異常的另一個原因是編譯器需要捕獲它。 我想強制控制器處理所有可能發生的異常。

您的存儲庫需要擴展 JpaRepository,當您這樣做時。 您可以使用該存儲庫中的 saveAndFlush 方法。 這意味着,您的代碼將立即在數據庫上執行,並在完成事務之前拋出異常,您將能夠在 Catch 塊中捕獲它。 我還添加了用於刪除操作的示例。

存儲庫:

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserDAO extends JpaRepository<User, Long> {
}

服務:

 public class UserService {
        private UserDAO userDAO;

        (...)

        @Transactional
        public User createUser(...){
            (...)
            try{
                userDAO.saveAndFlush(newUserObject);
            } catch(DataIntegrityViolationException e){
                throw new UserAlreadyExistsException(username);
            }
        }

        @Transactional
        public void deleteUser(...){
            (...)
            try{
                userDAO.delete(deletingUserObject);
                userDAO.flush();
            } catch(DataIntegrityViolationException e){
                throw new UserException(username);
            }
        }
    }

注釋您的服務方法

@Transactional(rollbackFor = UserAlreadyExistsException.class)

如果拋出異常,它會告訴 spring 不要提交事務,這樣你就可以在控制器中捕獲它

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM