1. 概述
在 Spring Boot 項目中使用 Hibernate 時,實體驗證通常會在持久化過程中自動應用。
雖然 Hibernate 內置的驗證功能很有用,但如果我們的控制器已經處理了所有必要的驗證檢查,則可能會變得冗餘。 這種雙重驗證設置會導致驗證重複,從而產生不必要的開銷。 此外,它還會導致使用自定義驗證器出現依賴注入問題,這些驗證器依賴於 Spring Bean。
通過針對 JPA 實體禁用 Hibernate 驗證,我們可以防止冗餘檢查,並在控制器層集中驗證邏輯,如果控制器已經有效地處理了它。 這提供了改進的性能和簡化代碼庫,因為驗證變得集中。
本教程將介紹如何禁用 Hibernate 的 JPA 實體驗證,並突出顯示這種方法的優勢及其對應用程序效率的積極影響。
2. 為什麼禁用 Hibernate 驗證?
讓我們探討在 Spring Boot 應用程序中禁用 Hibernate 驗證的主要原因。雖然 Hibernate 的自動驗證很方便,但如果我們的控制器已經有效地處理驗證,則可能是不必要的。
- 避免冗餘檢查: 當我們在控制器中完全處理驗證時,Hibernate 的實體級別驗證會重複此過程,導致資源浪費。每次持久化一個實體時,Hibernate 都會執行控制器已經處理過的驗證檢查,從而產生不必要的性能成本。
- 防止依賴注入問題: Hibernate 驗證可能會導致依賴注入問題,尤其是在使用自定義驗證器時。例如,如果一個自定義驗證器注入一個 Spring 組件(例如一個倉庫來檢查唯一性約束),Hibernate 驗證將無法識別這些注入的依賴項。 禁用 Hibernate 驗證使這些驗證器能夠在 Spring 框架中順利運行。
- 提高性能: 禁用冗餘的 Hibernate 檢查可以提高應用程序性能。僅依賴於基於控制器的驗證可以減少持久化期間的處理負載,這對處理高事務量或大型數據集的應用程序至關重要。
簡而言之,如果我們在控制器中已經全面管理驗證,那麼禁用 Hibernate 的驗證使我們的應用程序更加簡潔高效。
3. 在控制器層僅設置驗證
通過在控制器層配置驗證,我們可以確保數據在到達持久化層之前得到正確驗證。 這種方法使我們的驗證保持集中,併為應用程序中數據的流控制提供更好的控制。
讓我們在控制器中實現驗證:
@PostMapping
public ResponseEntity<String> addAppUser(@Valid @RequestBody AppUser appUser, BindingResult result) {
if (result.hasErrors()) {
return new ResponseEntity<>(result.getFieldError().getDefaultMessage(), HttpStatus.BAD_REQUEST);
}
appUserRepository.save(appUser);
return new ResponseEntity<>("AppUser created successfully", HttpStatus.CREATED);
}在本示例中,我們使用 @Valid 註解以及 BindingResult 參數,以便直接在控制器中捕獲驗證錯誤。 讓我們分解一下這個配置:
- @Valid 註解: 觸發基於 AppUser 實體上定義的約束(例如 @NotNull、@Size 或自定義註解)進行的驗證。 這種配置確保驗證在控制器級別發生,而不是在持久化過程中發生。
- BindingResult: 此參數捕獲任何驗證錯誤,從而允許我們在實體持久化之前管理這些錯誤。 這樣,如果 AppUser 實體未通過驗證,BindingResult 將包含錯誤詳細信息,並且我們可以相應地處理它們。
通過在控制器中設置驗證,我們可以將所有數據檢查集中到應用程序的入口點,從而降低無效數據到達數據庫的風險。
4. 在 application.properties 中禁用 Hibernate 實體驗證
要禁用 Hibernate 對 JPA 實體進行驗證,我們修改 Spring Boot 項目中的 application.properties 文件。通過設置一個屬性,我們完全禁用 Hibernate 的實體驗證機制,完全依賴於基於控制器的驗證:
spring.jpa.properties.jakarta.persistence.validation.mode=none此設置指示 Hibernate 在持久化期間跳過實體驗證。 jakarta.persistence.validation.mode 屬性提供了三種選項:
- auto:如果 Hibernate 檢測到驗證配置,則驗證會自動運行。
- callback:驗證僅在通過回調顯式觸發時才會發生。
- none:此選項完全禁用 Hibernate 實體驗證,將驗證委託給其他應用程序層(在本例中為我們的控制器層)。
將此屬性設置為 none 指示 Hibernate 忽略實體上的任何驗證註釋,從而允許我們僅在控制器中管理驗證。
5. 自定義驗證器和依賴注入
當我們禁用Hibernate驗證時,我們可以自信地使用依賴於Spring依賴注入的自定義驗證器。自定義驗證器提供了一種強大的方式來強制執行特定的業務規則或驗證邏輯,尤其適用於唯一約束。
讓我們來看一個自定義驗證器的示例:
public class UserUniqueValidator implements ConstraintValidator<UserUnique, String> {
@Autowired
private AppUserRepository appUserRepository;
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
return appUserRepository.findByUsername(username) == null;
}
}此示例展示了一個自定義驗證器,它通過查詢倉庫來檢查用户名是否唯一。如果啓用了 Hibernate 驗證,由於依賴注入問題(例如,AppUserRepository 可能未正確注入),此驗證器可能會失敗。
但是,在 Hibernate 驗證已禁用時,此驗證器在控制器中獨立運行,利用 Spring 的依賴注入,確保 AppUserRepository 可用。
6. 理解實體和模式驗證的區別
區分實體驗證(檢查數據約束,如@NotNull或@Size)和模式驗證(將實體結構與數據庫模式同步)至關重要。 禁用 Hibernate 的驗證模式只會影響實體級別的驗證,而不會影響模式驗證。
Hibernate 中的模式驗證會檢查數據庫模式是否與應用程序中定義的實體映射相符。 例如,如果我們更改實體類(例如重命名字段或更改列長度),模式驗證會檢測實體定義和數據庫模式之間的差異。
7. 在 application.properties 中禁用模式驗證
如果希望 Hibernate 跳過模式驗證(即不會自動驗證或修改數據庫模式),則我們在 application.properties 文件中調整相應的屬性:
spring.jpa.hibernate.ddl-auto=none使用此設置後,Hibernate 不再執行模式驗證或數據庫創建/修改操作。 此設置特別適用於我們使用像 Flyway 或 Liquibase 這樣的專用遷移工具獨立管理數據庫模式的情況。
8. 在禁用 Hibernates 驗證後測試設置
在配置 Hibernates 忽略實體驗證後,至關重要的是驗證驗證功能在控制器層是否正常工作。 此測試步驟確保我們的驗證在 Hibernates 的參與下仍然有效。
讓我們回顧一下我們為測試設置需要採取的步驟:
- 功能測試: 將各種有效和無效的數據輸入發送到控制器端點,以確認驗證錯誤已正確捕獲。 此步驟驗證控制器級別的驗證設置是否按預期工作。
- 日誌驗證: 檢查應用程序日誌,以確保 Hibernates 驗證錯誤不存在。 驗證錯誤消息現在應全部來自控制器,表明我們已成功禁用 Hibernates 驗證。
此測試階段確認我們的應用程序通過控制器層處理驗證,並且 Hibernates 在持久化期間不會重新引入驗證檢查。
9. 全局處理驗證異常
使用 @ControllerAdvice 提供了一種有效的方法,可以在 Spring Boot 中全局處理驗證錯誤,從而確保應用程序中響應的一致性。
以下是一個全局異常處理器的示例:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
return new ResponseEntity<>("Validation error: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
}
}通過這種配置,我們可以更有效地管理驗證錯誤,為無效數據輸入提供乾淨且一致的錯誤響應。 這種方法通過清晰地傳達驗證錯誤,從而提升用户體驗。
10. 禁用實體驗證的關鍵優勢
通過禁用實體驗證,我們獲得了一系列優勢,從而改善了應用程序的性能、可維護性和整體效率:
- 集中式驗證邏輯: 將驗證處理僅限於控制器層,所有驗證規則都集中管理在一個地方,簡化代碼庫並提高可維護性。
- 減少冗餘: 刪除重複檢查可以消除不一致的驗證結果並防止不必要的處理。
- 增強性能: 減少驗證檢查可帶來更快的處理時間,尤其是在高流量應用程序中,性能至關重要。
11. 結論
禁用 Spring Boot 中的實體驗證是一個實用的優化,可以簡化驗證管理,提高應用程序性能並減少複雜性。 通過在控制器層集中驗證邏輯,我們可以在不受到依賴注入和冗餘檢查潛在問題的影響下,保持強大的數據完整性。
這種方法為具有高性能需求或複雜驗證規則的應用程序提供了一種更簡潔、更高效的解決方案,讓我們更好地控制驗證過程,而不會影響功能。