在現代軟件開發中,數據驗證是確保應用程序健壯性和可靠性的關鍵環節。Java Bean Validation (JSR 380) 作為一個功能強大的規範,為我們提供了一套全面的註解工具集,這些註解能夠幫助開發者在Java應用程序中以一種聲明式的方式執行數據驗證。從基本的非空驗證到複雜的正則表達式匹配,JSR 380規範及其實現,如Hibernate Validator,都為我們提供了豐富的選項來滿足各種驗證需求。
肖哥彈架構 跟大家“彈彈” 常用框架註解應用
@NotNull
1.1 註解作用介紹
@NotNull 註解用於確保被註解的字段不為 null。這在需要強制字段必須有值的情況下非常有用,例如,用户實體的用户名或電子郵件字段。
1.2 註解屬性介紹
- message: 指定驗證失敗時返回的自定義錯誤消息。
- groups: 指定此約束所屬的驗證組,用於在不同的場景下應用不同的驗證規則。
-
payload: 允許註解攜帶額外的元數據,這些元數據可以在驗證失敗時由驗證器使用。
1.3 註解業務案例
public class User { @NotNull(message = "用户名不能為空") private String username; // 其他用户屬性... } public class User { @NotNull(message = "用户名不能為空", groups = RegistrationChecks.class, payload = ErrorPayloads.class) private String username; // 用户名是用户身份的唯一標識,註冊時必須填寫。 // groups屬性定義了此驗證屬於RegistrationChecks組,可用於部分驗證場景。 // payload屬性可用於攜帶額外的元數據,供驗證器使用。 }@NotBlank
2.1 註解作用介紹
@NotBlank註解除了確保字符串不為null外,還檢查字符串至少有一個非空白字符。這適用於需要文本輸入的字段,如用户評論或表單提交。2.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
- payload: 額外數據。
-
trim: 布爾值,默認為
true,指示是否在驗證前去除字符串兩端的空白字符。2.3 註解業務案例
public class Comment { @NotBlank(message = "評論內容不能為空", trim = true) private String content; // 其他評論屬性... } public class Comment { @NotBlank(message = "評論內容不能為空", trim = true, groups = CommentChecks.class, payload = ErrorPayloads.class) private String content; // 評論內容必須填寫,trim=true表示在驗證前去除兩端空白字符。 // groups屬性定義了此驗證屬於CommentChecks組。 }@NotEmpty
3.1 註解作用介紹
@NotEmpty註解用於驗證字符串、集合或數組不為null且至少有一個元素(對於集合和數組)或至少有一個非空白字符(對於字符串)。3.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
-
payload: 額外數據。
3.3 註解業務案例
public class Message { @NotEmpty(message = "消息內容不能為空") private String text; // 其他消息屬性... } public class Message { @NotEmpty(message = "消息內容不能為空", groups = MessageChecks.class, payload = ErrorPayloads.class) private String text; // 消息文本不能為空,適用於消息發送場景。 }@Size
4.1 註解作用介紹
@Size註解用於驗證字符串、集合、數組或Map的大小是否在指定的範圍內。4.2 註解屬性介紹
- min: 指定最小大小。
- max: 指定最大大小。
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
-
payload: 額外數據。
4.3 註解業務案例
public class Book { @Size(min = 1, max = 100, message = "書名長度必須在1到100個字符之間") private String title; // 其他書籍屬性... } public class Book { @Size(min = 1, max = 100, message = "書名長度必須在1到100個字符之間", groups = BookChecks.class, payload = ErrorPayloads.class) private String title; // 書名長度限制,適用於圖書管理系統。 }@Past
5.1 註解作用介紹
@Past註解用於驗證日期類型的字段是否表示過去的日期。5.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
5.3 註解業務案例
import java.time.LocalDate; public class Event { @Past(message = "事件日期必須是過去的日期") private LocalDate eventDate; // 其他事件屬性... } import java.time.LocalDate; public class Event { @Past(message = "事件日期必須是過去的日期", groups = EventChecks.class, payload = ErrorPayloads.class) private LocalDate eventDate; // 事件日期應為過去日期,適用於歷史事件記錄。 }@Future
6.1 註解作用介紹
@Future註解用於驗證日期類型的字段是否表示未來的日期。6.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
6.3 註解業務案例
public class Delivery { @Future(message = "預計送達日期必須是未來的日期") private LocalDate expectedDeliveryDate; // 其他送達屬性... } public class Delivery { @Future(message = "預計送達日期必須是未來的日期", groups = DeliveryChecks.class, payload = ErrorPayloads.class) private LocalDate expectedDeliveryDate; // 預計送達日期應為未來日期,適用於訂單處理系統。 }@Pattern
7.1 註解作用介紹
@Pattern註解用於驗證字符串是否與指定的正則表達式匹配。7.2 註解屬性介紹
- regexp: 指定的正則表達式。
- message: 自定義驗證失敗時的錯誤消息。
-
flags: 正則表達式的匹配標誌。
7.3 註解業務案例
public class UserRegistration { @Pattern(regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$", message = "電子郵箱格式不正確" flags = {Pattern.Flag.CASE_INSENSITIVE}) private String email; // 其他註冊屬性... } public class UserRegistration { @Pattern(regexp = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", message = "電子郵箱格式不正確", flags = {Pattern.Flag.CASE_INSENSITIVE}, groups = RegistrationChecks.class, payload = ErrorPayloads.class) private String email; // 電子郵箱格式驗證,適用於用户註冊表單。 }@Min
8.1 註解作用介紹
@Min註解用於驗證數值類型的字段是否大於或等於指定的最小值。8.2 註解屬性介紹
- value: 指定的最小值。
-
message: 自定義驗證失敗時的錯誤消息。
8.3 註解業務案例
public class Product { @Min(value = 1, message = "購買數量不能少於1") private int quantity; // 其他產品屬性... } public class Product { @Min(value = 1, message = "購買數量不能少於1", groups = ProductChecks.class, payload = ErrorPayloads.class) private int quantity; // 購買數量驗證,適用於購物車系統。 }@Max
9.1 註解作用介紹
@Max註解用於驗證數值類型的字段是否小於或等於指定的最大值。9.2 註解屬性介紹
- value: 指定的最大值。
-
message: 自定義驗證失敗時的錯誤消息。
9.3 註解業務案例
public class Product { @Max(value = 100, message = "庫存數量不能超過100") private int stock; // 其他產品屬性... } public class Product { @Max(value = 100, message = "庫存數量不能超過100", groups = StockChecks.class, payload = ErrorPayloads.class) private int stock; // 庫存數量驗證,適用於庫存管理系統。 }@DecimalMin
10.1 註解作用介紹
@DecimalMin註解用於驗證BigDecimal或包裝的數值類型字段是否大於或等於指定的十進制最小值。10.2 註解屬性介紹
- value: 指定的十進制最小值。
- inclusive: 布爾值,指示是否包含指定的邊界值(默認為
true)。 -
message: 自定義驗證失敗時的錯誤消息。
10.3 註解業務案例
public class Price { @DecimalMin(value = "0.00", inclusive = true, message = "價格必須非負") private BigDecimal value; // 其他價格屬性... } public class Price { @DecimalMin(value = "0.00", inclusive = true, message = "價格必須非負", groups = PriceChecks.class, payload = ErrorPayloads.class) private BigDecimal value; // 價格必須為非負值,適用於財務管理系統。 }@DecimalMax
11.1 註解作用介紹
@DecimalMax註解用於驗證BigDecimal或包裝的數值類型字段是否小於或等於指定的十進制最大值。11.2 註解屬性介紹
- value: 指定的十進制最小值。
- inclusive: 布爾值,指示是否包含指定的邊界值(默認為
true)。 -
message: 自定義驗證失敗時的錯誤消息。
11.3 註解業務案例
public class Price { @DecimalMax(value = "1000.00", inclusive = true, message = "價格不能超過1000") private BigDecimal value; // 其他價格屬性... } public class Price { @DecimalMax(value = "1000.00", inclusive = true, message = "價格不能超過1000", groups = PriceChecks.class, payload = ErrorPayloads.class) private BigDecimal value; // 價格上限驗證,適用於定價策略。 }@Email
12.1 註解作用介紹
@Email註解用於驗證字符串字段是否為有效的電子郵件地址。12.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
-
regexp: 用於驗證電子郵件地址的正則表達式(可選)。
12.3 註解業務案例
public class User { @Email(message = "電子郵件地址無效") private String email; // 其他用户屬性... } public class User { @Email(message = "電子郵件地址無效", regexp = "^.+@.+\\..+$", groups = UserChecks.class, payload = ErrorPayloads.class) private String email; // 電子郵箱地址驗證,適用於用户信息管理。 }@Valid
13.1 註解作用介紹
@Valid註解用於遞歸地對關聯的對象或集合進行驗證。13.2 註解屬性介紹
- groups: 指定嵌套對象應使用的驗證組。
-
payload: 額外數據。
13.3 註解業務案例
public class Order { @Valid private User user; @Valid private List<OrderItem> items; // 其他訂單屬性... } public class Order { @Valid(groups = OrderChecks.class, payload = ErrorPayloads.class) private User customer; // 遞歸驗證用户信息,適用於訂單處理。 }## @Positive
14.1 註解作用介紹
@Positive註解用於驗證數值類型的字段是否嚴格大於 0。14.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
-
payload: 額外數據。
14.3 註解業務案例
public class Account {
@Positive(message = "賬户餘額必須為正數")
private BigDecimal balance;
// 其他賬户屬性...
}
public class Account {
@Positive(message = "賬户餘額必須為正數", groups = AccountChecks.class, payload = ErrorPayloads.class)
private BigDecimal balance;
// 賬户餘額驗證,適用於銀行賬户管理。
}
@PositiveOrZero
15.1 註解作用介紹
@PositiveOrZero 註解用於驗證數值類型的字段是否大於或等於 0。
15.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
-
payload: 額外數據。
15.3 註解業務案例
public class Account {
@PositiveOrZero(message = "賬户餘額不能為負數")
private BigDecimal balance;
// 其他賬户屬性...
}
public class Account {
@PositiveOrZero(message = "賬户餘額不能為負數", groups = AccountChecks.class, payload = ErrorPayloads.class)
private BigDecimal balance;
// 賬户餘額驗證,確保餘額非負。
}
@Negative
16.1 註解作用介紹
@Negative 註解用於驗證數值類型的字段是否嚴格小於 0。
16.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
-
payload: 額外數據。
16.3 註解業務案例
public class Debt { @Negative(message = "債務金額必須為負數") private BigDecimal amount; // 其他債務屬性... } public class Debt { @Negative(message = "債務金額必須為負數", groups = DebtChecks.class, payload = ErrorPayloads.class) private BigDecimal amount; // 債務金額驗證,適用於債務管理。 }@NegativeOrZero
17.1 註解作用介紹
@NegativeOrZero註解用於驗證數值類型的字段是否小於或等於 0。17.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- groups: 指定驗證組。
-
payload: 額外數據。
17.3 註解業務案例
public class Debt { @NegativeOrZero(message = "債務金額不能為正數") private BigDecimal amount; // 其他債務屬性... } public class Debt { @NegativeOrZero(message = "債務金額不能為正數", groups = DebtChecks.class, payload = ErrorPayloads.class) private BigDecimal amount; // 債務金額驗證,確保債務非正。 }@Digits
18.1 註解作用介紹
@Digits註解用於驗證數值類型的字段是否符合指定的整數和小數位數。18.2 註解屬性介紹
- integer: 指定整數部分的最大位數。
- fraction: 指定小數部分的最大位數。
-
message: 自定義驗證失敗時的錯誤消息。
18.3 註解業務案例
public class FinancialTransaction { @Digits(integer = 10, fraction = 2, message = "交易金額格式不正確") private BigDecimal amount; // 其他交易屬性... } public class FinancialTransaction { @Digits(integer = 10, fraction = 2, message = "交易金額格式不正確", groups = TransactionChecks.class, payload = ErrorPayloads.class) private BigDecimal amount; // 交易金額必須是最多10位整數和2位小數。 }@PastOrPresent
19.1 註解作用介紹
@PastOrPresent註解用於驗證日期類型的字段是否表示現在或過去的日期。19.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
19.3 註解業務案例
import java.time.LocalDate; public class InsurancePolicy { @PastOrPresent(message = "保險生效日期必須為當前或過去的日期") private LocalDate effectiveDate; // 其他保險屬性... } import java.time.LocalDate; public class InsurancePolicy { @PastOrPresent(message = "保險生效日期必須為當前或過去的日期", groups = InsuranceChecks.class, payload = ErrorPayloads.class) private LocalDate effectiveDate; // 保險生效日期驗證,適用於保險單管理。 }@FutureOrPresent
20.1 註解作用介紹
@FutureOrPresent註解用於驗證日期類型的字段是否表示現在或未來的日期。20.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
20.3 註解業務案例
public class Appointment { @FutureOrPresent(message = "預約日期必須是當前或未來的日期") private LocalDate appointmentDate; // 其他預約屬性... } public class Appointment { @FutureOrPresent(message = "預約日期必須是當前或未來的日期", groups = AppointmentChecks.class, payload = ErrorPayloads.class) private LocalDate appointmentDate; // 預約日期驗證,適用於預約系統。 }@Valid
21.1 註解作用介
@Valid註解用於遞歸地驗證關聯的對象或集合中的元素。21.2 註解屬性介紹
- groups: 指定應應用於驗證的組。
-
payload: 指定應應用於驗證的額外數據。
21.3 註解業務案例
public class Order { @Valid private User customer; @Valid private List<OrderItem> items; // 其他訂單屬性... } public class Order { @Valid(groups = {OrderChecks.class, ItemChecks.class}, payload = {ErrorPayloads.class}) private List<OrderItem> items; // 遞歸驗證訂單項列表,適用於訂單詳細處理。 }@Validated
22.1 註解作用介紹
@Validated註解用於指定驗證時使用的驗證組。22.2 註解屬性介紹
-
groups: 指定應應用於驗證的組。
22.3 註解業務案例
public class PaymentService { @Validated(PaymentChecks.class) public void processPayment(PaymentDetails details) { // 使用特定的驗證組來處理支付詳情 } } public class PaymentService { @Validated(PaymentChecks.class) public void processPayment(@Validated(PaymentDetailsChecks.class) PaymentDetails details) { // 使用特定的驗證組來處理支付詳情。 } }@ConvertGroup
23.1 註解作用介紹
@ConvertGroup註解用於在驗證過程中將一個分組轉換為另一個分組。23.2 註解屬性介紹
- from: 指定原始分組。
-
to: 指定目標分組。
23.3 註解業務案例
public class DataMigrationService { @ConvertGroup(from = LegacyDataChecks.class, to = CurrentDataChecks.class) public void migrateData(LegacyData data) { // 從舊數據驗證轉換為新數據驗證 } } public class DataMigrationService { @ConvertGroup(from = LegacyDataChecks.class, to = CurrentDataChecks.class) public void migrateData(@Validated(LegacyDataChecks.class) LegacyData data) { // 從舊數據驗證轉換為新數據驗證。 } }@ReportAsSingleViolation
24.1 註解作用介紹
@ReportAsSingleViolation註解用於將驗證過程中的所有違反約束的情況報告為單個驗證錯誤。24.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
24.3 註解業務案例
public class CriticalField { @ReportAsSingleViolation(message = "關鍵字段驗證失敗") private String field; // 其他屬性... } public class CriticalField { @ReportAsSingleViolation(message = "關鍵字段驗證失敗") private String field; // 將所有違反約束的情況報告為單個驗證錯誤。 }@Groups
25.1 註解作用介紹
@Groups註解用於指定驗證的分組,允許在不同場景下應用不同的驗證規則。25.2 註解屬性介紹
-
無特定屬性,通常與
@Valid或@Validated註解一起使用。25.3 註解業務案例
public class User { @Valid private Profile profile; // 其他用户屬性... } public class Profile { @NotNull(groups = RegistrationChecks.class) private String biography; // 其他個人資料屬性... }@SafeHtml
26.1 註解作用介紹
@SafeHtml註解用於驗證HTML內容是否是安全的,防止跨站腳本(XSS)攻擊。26.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
26.3 註解業務案例
public class HtmlContent { @SafeHtml(message = "內容包含不安全的HTML") private String content; // 其他HTML內容屬性... }@EAN
27.1 註解作用介紹
@EAN註解用於驗證國際標準書號(ISBN)或歐洲商品編號(EAN-13)。27.2 註解屬性介紹
-
message: 自定義驗證失敗時的錯誤消息。
27.3 註解業務案例
public class Product { @EAN(message = "無效的EAN或ISBN編號") private String ean; // 驗證國際標準書號或歐洲商品編號 }@URL
28.1 註解作用介紹
雖然
@URL註解在JSR 380規範中沒有定義,但Hibernate Validator提供了類似的功能,用於驗證字符串是否是有效的URL格式。28.2 註解屬性介紹
- message: 自定義驗證失敗時的錯誤消息。
- protocol: 指定必須的協議(如http, https)。
- host: 指定必須的主機名。
- port: 指定端口號。
-
regexp: 自定義的URL匹配正則表達式。
28.3 註解業務案例
public class Website { @URL(message = "無效的URL") private String url; // 其他網站屬性... } public class Website { @URL(message = "無效的URL", protocol = "https", host = "example.com", port = 443) private String url; // 驗證字符串是否是有效的URL格式
註解驗證綜合性案例
場景描述
電商平台,用户可以瀏覽商品、下單購買,並進行訂單管理。該平台需要驗證用户信息、商品詳情、訂單數據以及支付信息的準確性和有效性。
業務實體和驗證需求
- 用户信息(User):必須包含有效的電子郵件和非空的用户名。
- 商品詳情(Product):需要有有效的庫存數量和價格範圍。
- 訂單(Order):必須包含用户信息、商品列表,並且總金額必須為正數。
- 訂單項(OrderItem):每個訂單項需要驗證購買數量和商品ID。
- 支付信息(PaymentInfo):需要驗證支付金額是否正確,並且支付方式是否被接受。
import javax.validation.constraints.*;
import javax.validation.Valid;
import java.util.List;
public class ECommercePlatform {
// 用户信息
public class User {
@Email(message = "電子郵件地址無效", groups = UserChecks.class)
private String email;
@NotBlank(message = "用户名不能為空", groups = UserChecks.class)
private String username;
// 用户相關方法...
}
// 商品詳情
public class Product {
@NotNull(message = "商品ID不能為空", groups = ProductChecks.class)
private String productId;
@PositiveOrZero(message = "庫存數量不能為負數", groups = ProductChecks.class)
private int stock;
@DecimalMin(value = "0.01", message = "商品價格必須至少為0.01", groups = ProductChecks.class)
private BigDecimal price;
// 商品相關方法...
}
// 訂單
public class Order {
@Valid
private User user;
@Valid
@Size(min = 1, message = "訂單至少需要一個商品項", groups = OrderChecks.class)
private List<OrderItem> items;
@Positive(message = "訂單總金額必須為正數", groups = OrderChecks.class)
private BigDecimal totalAmount;
// 訂單相關方法...
}
// 訂單項
public class OrderItem {
@NotNull(message = "商品ID不能為空", groups = ItemChecks.class)
private String productId;
@Positive(message = "購買數量必須為正數", groups = ItemChecks.class)
private int quantity;
// 訂單項相關方法...
}
// 支付信息
public class PaymentInfo {
@Positive(message = "支付金額必須為正數", groups = PaymentChecks.class)
private BigDecimal amount;
@NotBlank(message = "支付方式不能為空", groups = PaymentChecks.class)
private String method;
// 支付信息相關方法...
}
}
//驗證組定義
public interface UserChecks {}
public interface ProductChecks {}
public interface OrderChecks {}
public interface ItemChecks {}
public interface PaymentChecks {}
//錯誤元數據載荷
public class ErrorPayloads implements Payload {}
在這個綜合性業務場景中,我們使用了@NotNull、@NotBlank、@PositiveOrZero、@DecimalMin、@Positive、@Email等註解來確保數據的有效性。@Valid註解用於遞歸驗證嵌套對象,而自定義驗證組(如UserChecks、ProductChecks等)允許我們在不同的場景下應用不同的驗證規則。ErrorPayloads類用於攜帶額外的元數據,這些元數據可以在驗證失敗時由驗證器使用。
在線預訂系統分組驗證
場景描述
在線預訂系統允許用户根據其類型(個人或公司)進行預訂。系統需要對不同類型的預訂應用不同的驗證規則。
import javax.validation.*;
import javax.validation.constraints.*;
import java.time.LocalDate;
import java.util.Set;
// 驗證組接口
public interface PersonalBookingChecks {}
public interface CorporateBookingChecks {}
// 預訂類型枚舉
public enum BookingType {
PERSONAL, CORPORATE;
}
// 預訂實體類
public class Booking {
private BookingType type;
// 個人預訂字段
@NotBlank(message = "個人預訂的姓名必須填寫", groups = PersonalBookingChecks.class)
private String personalName;
@Email(message = "個人預訂的電子郵箱格式必須正確", groups = PersonalBookingChecks.class)
private String personalEmail;
// 公司預訂字段
@NotBlank(message = "公司預訂的公司名稱必須填寫", groups = CorporateBookingChecks.class)
private String corporateName;
@PositiveOrZero(message = "公司預訂的税務編號必須是非負數", groups = CorporateBookingChecks.class)
private Long corporateTaxId;
// 所有預訂類型共有的字段
@NotNull(message = "入住日期不能為空")
private LocalDate checkInDate;
@NotNull(message = "退房日期不能為空")
private LocalDate checkOutDate;
// 用於設置預訂類型的setter方法
public void setBookingType(BookingType type) {
this.type = type;
}
}
// 服務層驗證邏輯
public class BookingService {
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
public void processBooking(Booking booking) {
Set<ConstraintViolation<Booking>> violations;
switch (booking.getType()) {
case PERSONAL:
violations = validator.validate(booking, PersonalBookingChecks.class);
break;
case CORPORATE:
violations = validator.validate(booking, CorporateBookingChecks.class);
break;
default:
throw new IllegalStateException("未知的預訂類型");
}
if (!violations.isEmpty()) {
// 將驗證錯誤轉換為用户友好的字符串信息
throw new IllegalArgumentException("預訂驗證失敗: " + formatValidationErrors(violations));
}
// 如果驗證通過,繼續處理預訂邏輯,例如保存到數據庫
// 省略處理預訂的代碼...
}
private String formatValidationErrors(Set<ConstraintViolation<Booking>> violations) {
StringBuilder sb = new StringBuilder();
for (ConstraintViolation<Booking> violation : violations) {
sb.append(violation.getPropertyPath()).append(": ").append(violation.getMessage()).append("\n");
}
return sb.toString();
}
// 省略其他服務方法...
}
// 測試類
public class BookingApplication {
public static void main(String[] args) {
BookingService service = new BookingService();
Booking booking = new Booking();
// 這裏設置了booking的屬性和類型...
// 處理預訂
service.processBooking(booking);
}
}
上述代碼,Booking類包含了根據不同預訂類型(個人或公司)所需的不同字段,並使用相應的JSR 380註解進行了驗證。BookingService類中的processBooking方法根據Booking對象的類型使用分組驗證,並處理驗證結果。如果存在驗證錯誤,它將拋出一個包含所有錯誤的IllegalArgumentException異常。