Reputation: 3145
I have an entity like this:
@Entity
@Table(name = "transaction_receiver")
public class TransactionReceiver implements Serializable, Addressable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull
@Column(name = "contact_id", nullable = false)
private String contactId;
@Column(name = "datev_number")
private String datevNumber;
@NotNull
@Column(name = "receiver", nullable = false)
private String receiver;
@NotNull
@Size(min = 22, max = 34)
@Column(name = "iban", length = 34, nullable = false)
private String iban;
@Size(min = 8, max = 11)
@Column(name = "bic", length = 11, nullable = false)
private String bic;
@NotNull
@Column(name = "institute")
private String institute;
@Column(name = "company")
private String company;
I need to write a custom validation "you can provide an empty iban, bic and institute, that's ok. But if any of the fields is not empty, the above constraint have to hold"
I am looking for the most elegant way to accomplish this.
My current solution is - what I think somehow dirty, but working - to use a @PrePersist
statement and throw exceptions from there
@PrePersist
public void checkBankData() {
boolean ibanEmpty = iban == null || iban.isEmpty();
boolean ibanValid = !ibanEmpty && iban.length() >= 22 && iban.length() <= 34;
boolean bicEmpty = bic == null || bic.isEmpty();
boolean bicValid = !bicEmpty && bic.length() >= 8 && bic.length() <= 11;
boolean instituteEmpty = institute == null || institute.isEmpty();
boolean validState = (ibanEmpty && bicEmpty && instituteEmpty) || ibanValid && bicValid;
if (!validState) {
throw new IllegalStateException(
String.format(
"bank data is not empty and %s%s%s%s%s",
!ibanValid ? "iban has to be from 22 to 34 chars long" : "",
!ibanValid && !bicValid ? "and" : "",
!bicValid ? "bic has to be from 8 to 11 chars long" : "",
!ibanValid && !bicValid && instituteEmpty ? "and" : "",
instituteEmpty ? "institue must not be empty" : ""
)
);
}
}
Which isn't subject to @Valid
annotations. A different approach would be defining a custom validator like described here: http://docs.jboss.org/hibernate/validator/4.1/reference/en-US/html/validator-customconstraints.html
But this really looks like an overkill for my constraint.
Isn't there any other elegant way?
Upvotes: 6
Views: 12688
Reputation: 1632
Using Hibernate Validation API is not as complex as it seems, and for your constraint is a nice solution. However you can get a easier way to define constrains using Hibernate Validator as we have done in one project adding a few classes. Your constraints will look like this:
@Validate(method = "checkBankData", message = "{BankData.invalid.message}")
@Entity
@Table(name = "transaction_receiver")
public class TransactionReceiver implements Serializable, Addressable {
To get this you need to define @Validate annotation and a CustomValidator class.
@Target({ ElementType.TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = CustomValidator.class)
@Documented
/**
* Annotation to allow custom validation against model classes
*/
public @interface Validate {
/**
* Validation message
*/
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Validation method name
*/
String method() default "";
}
public class CustomValidator implements ConstraintValidator<Validate, BusinessObject> {
private static Log log = LogFactory.getLog(CustomValidator.class);
private String validator;
@Override
public void initialize(Validate constraintAnnotation) {
validator = constraintAnnotation.method();
}
@Override
public boolean isValid(BusinessObject bo, ConstraintValidatorContext constraintContext) {
try {
return isValidForMethod(bo);
} catch (Exception e) {
/* Error durante la ejecución de la condición o del validador */
log.error("Error validating "+bo, e);
return false;
}
}
private boolean isValidForMethod(BusinessObject bo) throws Exception {
Method validatorMethod = ReflectionUtils.findMethod(bo.getClass(), validator, new Class[] {});
if (validatorMethod != null) {
/* Validator call */
Boolean valid = (Boolean) validatorMethod.invoke(bo);
return valid != null && valid;
} else {
/* Method not found */
log.error("Validator method not found.");
return false;
}
}
}
This aproach will be nice if you plan to define more constraints. And you can extend it with more features like conditions for validation or adding multiple validations, etc.
Off-topic:
Validation has nothing to do with Spring Boot so there is no need to mention it in your question.
serialVersionUID = 1L; Is a very bad idea. Use your IDE serialVersionUID generator to fill this field with a value different for 1L.
Upvotes: 9
Reputation: 3870
To get around the default JPA validation not having the equivalent of the Hibernate validator's @NotEmpty you could try using the @Size annotation to specify it must be greater than or less than a certain number.
@NotNull
@Size(min=1)
private String description;
This is the cleanest way to go about what you are looking to do that I can think of.
Upvotes: 0