在 Spring Boot 專案中停用 Hibernate 實體驗證
1. 概述
在使用 Hibernate 的 Spring Boot 專案中,實體驗證通常在持久化期間會自動套用。
雖然 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);
}
在此範例中,我們使用帶有BindingResult
參數的@Valid
註解來直接在控制器中擷取驗證錯誤。讓我們分解一下這個設定:
-
@Valid Annotation:
根據AppUser
實體上定義的限制觸發驗證(例如@NotNull
、@Size
或自訂註釋)。此設定可確保驗證發生在控制器級別,而不是在持久性期間。 -
BindingResult
:此參數會擷取任何驗證錯誤,使我們能夠在實體持久化之前管理這些錯誤。這樣,如果AppUser
實體驗證失敗,BindingResult
將包含錯誤詳細訊息,我們可以進行相應的處理。
透過在控制器中設定驗證,我們將所有資料檢查集中在應用程式的入口點,從而降低了無效資料到達資料庫的風險。
4. 在application.properties
中停用 Hibernate 實體驗證
要停用 JPA 實體的 Hibernate 驗證,我們修改 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. 停用 Hibernate 驗證後測試設置
將 Hibernate 設定為忽略實體驗證後,必須驗證控制器層中的驗證功能是否正確。此測試步驟可確保我們的驗證在沒有 Hibernate 參與的情況下仍然有效。
讓我們回顧一下測試我們的設定所需採取的步驟:
- 功能測試:將各種有效且無效的資料輸入傳送到控制器端點,以確認正確擷取驗證錯誤。此步驟驗證控制器級驗證設定是否如預期運作。
- 日誌驗證:檢查應用程式日誌以確保不存在 Hibernate 驗證錯誤。所有驗證錯誤訊息現在都應來自控制器,表示我們已成功停用 Hibernate 驗證。
此測試階段確認我們的應用程式僅透過控制器層處理驗證,且 Hibernate 在持久性期間不會重新引入驗證檢查。
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 中停用實體驗證是一種實用的最佳化,可簡化驗證管理、提高應用程式效能並最大程度地降低複雜性。透過將驗證邏輯集中在控制器層中,我們可以保持強大的資料完整性,同時避免與依賴項注入和冗餘檢查相關的潛在陷阱。
這種方法為具有高效能需求或複雜驗證規則的應用程式提供了更精簡、更有效率的解決方案,使我們能夠在不影響功能的情況下更好地控制驗證。
本教學的完整原始碼可在 GitHub 上取得。