How to use different validation rules on same entity in Hibernate? - java

Problem:
How to save object Account as nested object when only ID is needed without getting ConstraintValidator exception?
Problem is because i have set validation rules to class, but when i want to save sem entity as nested object i get exception that some property values are missing. So i would liek to have different validation rules when i want to persist object as a whole and when i want to use it only sa nested object (when only ID is needed).
public class Account {
private int id;
#NotNull
private String name;
#NotNull
private String lastName;
#NotNull
private String userName;
//getters&setters
If I include Account as nested object i just need ID to be able to use it as FK (account entity is already in DB), but because of #NotNull annotation i get Exception.
Is there a way to ignore those annotations from Account when trying to save object Shop or how to create different validation rules for Account to validate just soem other properties and not all?
public class Shop {
private int id;
private Account owner; // only ID is needed
Do you have any basic example? I dont understand those in documentation. I have already read documentation before posting here.

You want to look at Bean Validation groups where you can classify specific validations so they are only activated when that group is validated and ignored otherwise.
You can refer to the documentation here for details.
Taking an example from the documentation:
// This is just a stub interface used for tagging validation criteria
public interface DriverChecks {
}
// The model
public class Driver {
#Min(value = 18, message = "You must be 18", groups = DriverChecks.class)
private int age;
// other stuffs
}
A group is nothing more than a tag that allows you to enable/disable validations based on specific use cases at run-time. By not specifying the groups attribute on a bean validation annotation, it defaults to the Default group, which is what Bean Validation uses if a group-tag isn't specified at the time of validation.
That means the following holds true:
// Age won't be validated since we didn't specify DriverChecks.class
validator.validate( driver );
// Age will be validated here because we specify DriverChecks.class
validator.validate( driver, DriverChecks.class );
This works great when you're triggering the validation yourself inside your service methods because you can manually control which group checks are applicable based on that method's use case.
When it comes to integrating directly with Hibernate ORM's event listeners that can also trigger bean validation, group specifications become a bit harder as they must be specified based on the event-type raised by hibernate.
javax.persistence.validation.group.pre-persist
javax.persistence.validation.group.pre-update
javax.persistence.validation.group.pre-remove
For each of the above properties you can specify in the JPA properties supplied to Hibernate, you can list a comma delimited list of groups that are to be validated for each of those event types. This allows you to have varying checks during insert versus update versus removal.
If that isn't sufficient, there is always the fact that you can create your own constraint validator implementation and annotation to plug into Bean Validation and specify that at the class or property level.
I have often found this useful in cases where values from multiple fields must be validated as a cohesive unit to imply their validity as the normal field-by-field validations didn't suffice.

Related

Can Java Validator method validateProperty validate object with multiple fields inside?

As stated in the title, I'm using the Validator to validate fields based on their names like this:
mandatoryInputs.stream()
.map(x -> v.validateProperty(accountBenefitForm, x, AccountBenefitFormAdditionalInfo.class))
it works fine, but only for the simple fields like Strings that have their constraints in the accountBenefitForm for example:
#NotBlank(message = "Username can not be null.", groups = {AccountBenefitFormBasicInfo.class})
#Size(max = 255, message = "Username is too long (max size 255).")
private String username;
But it won't work for objects that have multiple fields inside them, like this one:
#Valid
private ContactData contactData;
where ContactData implementation looks like this:
#NotBlank(message = "You have to add e-mail address.", groups = {AccountBenefitFormAdditionalInfo.class})
#Email(message = "E-mail is not valid.", groups = {AccountBenefitFormAdditionalInfo.class})
#Size(max = 255, message = "E-mail is too long (max size 255).", groups = {AccountBenefitFormAdditionalInfo.class})
private String email;
#NotBlank(groups = {AccountBenefitFormAdditionalInfo.class})
private String phoneNumber;
Is there a way I can make this work or do I need to validate those more complex objects on their own?
You have basically two kinds of annotations that can be used for validations here: Spring annotations (#Validated) as well as the javax annotation (#Valid, #NotBlank) etc.
For Spring, you can luckily often skip the manual validation unless you have some custom constraints (e.g. if person lives in country ABC, they need to provide additional info). Annotating just the field is not enough if you don't cascade the validation from the outer class. This cascade can be done conveniently on method-level by annotating the method param with #Valid e.g.
void doSomething(#Valid ContactDataHolder contactDataHolder) { ... }
If you'd like to use validation in Spring, I would recommend to use the Spring Validator interface instead of the one from javax as it should give you the expected behavior for nesting. You might also decide to apply #Validated on the class level to save you from writing #Valid(ated) on the method level each time.
So I've managed to somewhat resolve my problem by using the Apache BVal. Heres the code to create a validator to use the validateProperty method with cascading validation enabled:
ValidatorFactory factory = Validation.byProvider(ApacheValidationProvider.class).configure().buildValidatorFactory();
CascadingPropertyValidator validator = factory.getValidator().unwrap(CascadingPropertyValidator.class);
validator.validateProperty(accountBenefitForm, x, true, Default.class, AccountBenefitFormAdditionalInfo.class))
where x is the string of field to validate, and if that field is annotated with #Valid, it will then validate the inside fields according to their own constraints.
Along the way I've also found out that you can just use the "normal" javax Validator and pass the field to validate as contactData.email which means validate email field of the contactData field of the object that u pass as first argument to the validateProperty method.
Edit:
BVal supports Bean Validation 1.1 (JSR 349) and not the 2.0 version(JSR 380), and since #NotBlank or #NotEmpty constrains are part of 2.0, it won't validate a field annotated with them. Here are the docs for the 1.1 , and 2.0

using javax.validation to validate bean with choices

I'm implementing an in-memory API gateway to a SOAP service utilizing JAXB. One of the schema elements is a "choice", and there are several elements in the choice block.
I'm attempting to mirror the generated JAXB classes in the client namespace, so for this "choice" scenario I have a bean class with several properties, only one of which will be non-null.
I'm attempting to use the #NotNull annotation from javax.validation, along with the ValidatorFactory and Validator. However, a "choice" scenario makes this a little more complicated. I'm guessing this would call for a custom ConstraintValidator, along with a custom annotation to refer to the custom ConstraintValidator.
For instance, here's some fake code that resembles a part of my structure:
public class InquireRandomInformationRequest {
#NotNull(message ="subscriberSelector cannot be null")
#Valid
private SubscriberSelector subscriberSelector; // required
private SelectorMode mode; // optional
...
}
public class SubscriberSelector {
// Choice 1
private String billingAccountNumber; // \d{8,9,12,13}; required
private MarketInfo billingMarket; // optional
// Choice 2
private String subscriberNumber; // \d{10}; required
private ValidationCriteria validationCriteria; // optional
private BillingAccountInformation billingAccountInformation; // optional
private MemoProductType memoProductType; // optional
// Choice 3
private String unifiedBillingAccountNumber; // [0-9A-Za-z]{13}; required
...
}
I understand that I need the #Valid annotation on the "subscriberSelector" property for the validator to validate the sub-object. Past that, I'm not quite sure what I need to do to handle the choices problem.
To fit my example, I will need exactly one of "billingAccountNumber", "subscriberNumber", or "unifiedBillingAccountNumber" to be non-null (although I could compromise on simply taking the first non-null one in a particular sequence). In each "choice group", the other properties are optional, but it's possible that another property could be "required" if that particular choice group is selected (the selector property is non-null, in other words).
I've looked through the Hibernate Validator documentation, but I'm not sure exactly how to apply that for this situation.
If I define a custom annotation and a custom ConstraintValidator, where is the annotation referenced? On the class (like "SubscriberSelector") or on the "subscriberSelector" property (the former seems more logical to me).
You can define constraints on the class or on the property depending on your requirements.
Usually, the constraints are placed on the property but, in the case you mention, as multiple properties are concerned and interdependent, you should place the constraint at the class level.
See https://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-declaring-bean-constraints in our documentation.

Hibernate validations on save (insert) only

We encountered a problem with legacy code. There is a validation set for a "username" field, validating its length and making sure it contains at least one letter:
#Column(name = "username")
#Size(min = 4, max = 40)
#Pattern(regexp = "^.*[a-zA-Z]+.*$")
private String username;
The problem we have is that some existing legacy data do not fit these validations, and I'm trying to find a way to make these validations to be ignored for legacy data (old users), while still be applied to newly created users.
I was thinking about moving the validations to setUsername(...) method (so value will be validated on an actual change only), but this caused an exception:
javax.validation.ValidationException: Annotated methods must follow the JavaBeans naming convention. setUsername() does not.
I also made sure the entity is set to dynamicUpdate=true, but this doesn't help since hibernate is validating all properties, even if no change occurred.
How can I prevent these validations to be performed on existing entities during update?
I do not want the fix to impact other properties validations on the same entity and I can't change hibernate configuration.
After two days of research I found out how to make this work.
Apparently, specifying validations that would be validated on INSERT only is not that difficult. The only changes required are to set these validations to a specific validation group and to validate this group during INSERT/pre-persist events.
First of all I created an interface called platform.persistence.InsertOnlyValidations to be used as a group which will be validated during pre-persist only.
Than, I added the group to the username field validations:
#Column(name = "username")
#Size(min = 4, max = 40, groups = {InsertOnlyValidations.class})
#Pattern(regexp = "^.*[a-zA-Z]+.*$", groups = {InsertOnlyValidations.class})
private String username;
This instructs hibernate not to use these validations as part of the default group. Now, I needed to instruct hibernate to validate these validation rules during insert only.
The way to do that is very simple, I needed to pass the property javax.persistence.validation.group.pre-persist, while indicating which groups will be validated during a pre-persist event:
javax.persistence.validation.group.pre-persist=javax.validation.groups.Default,platform.persistence.InsertOnlyValidations
This instructs hibernate that during a pre-persist event all default validations will be validated (javax.validation.groups.Default) in addition to all the validations included in the InsertOnlyValidations group.

Exclude field in JPA Entity Listener

I have an entity class in my Enterprise Java application that has an entity listener attached to it:
#Entity
#EntityListeners(ChangeListener.class)
public class MyEntity {
#Id
private long id;
private String name;
private Integer result;
private Boolean dirty;
...
}
However, I would like it so that the entity listener got triggered for all fields except the boolean one. Is there any way exclude a field from triggering the entity listener without making it transient?
I'm using Java EE 5 with Hibernate.
However, it is possible if you implement your own solution. I've had the same need for audit log business requirement, so designed my own AuditField annotation, and applied to the fields to be audit-logged.
Here's the example in one entity bean - Site.
#AuditField(exclude={EntityActionType.DELETE})
#Column(name = "site_code", nullable = false)
private String siteCode;
So, the example indicates the 'siteCode' is a field to audit log, except DELETE action. (EntityActionType is an enum and it contains CRUD operations.)
Also, the EntityListenerhas this part of code.
#PostPersist
public void created(Site pEntity) {
log(pEntity, EntityActionType.CREATE);
}
#PreUpdate
public void updated(Site pEntity) {
log(pEntity, EntityActionType.UPDATE);
}
#PreRemove
public void deleted(Site pEntity) {
log(pEntity, EntityActionType.DELETE);
}
Now what it has to do in log() is, to figure what fields are to audit log and what custom actions are involved optionally.
However, there's another to consider.
If you put the annotation at another entity variable, what fields of the entity have to be logged? (i.e. chained logging)
It's your choice whether what are annotated with #AuditField only in the entity or some other ways. For my case, we decided to log only the entity ID, which is a PK of a DB table. However, I wanted to make it flexible assuming the business can change. So, all the entites must implement auditValue() method, which is coming from a base entity class, and the default implementation (that's overridable) is to return its ID.
There is some kind of mixing of concepts here. EntityListeners are not notified about changes in attribute values - not for single attribute, neither for all attributes.
For reason they are called lifecycle callbacks. They are triggered by following lifecycle events of entity:
persist (pre/post)
load (post)
update(pre/post)
remove (pre/post)
For each one of them there is matching annotation. So answer is that it is not possible to limit this functionality by type of persistent attributes.

How to access Session information on service layer?

Is there a way I can share Http/Wicket Session information to the service layer without introducing servlet api/Wicket dependency?
I'll provide some context to why am I asking this question, just in case I'm missing something and asking the wrong question.
I've got several entities that have groups of attributes that can be validatable.
Being validatable means there are fields indicating the validation value, the user who made the validation and the date it was validated in.
This is how these entities are modelled:
#Embeddable
public class ValidationBean<T> implements Serializable {
private T validated;
private String user;
private Date date;
// Constructors, getters, setters ahead.
// ...
}
#Entity
#Table(name="SOME_TABLE")
public class SomeEntity implements Serializable, SomeInterface {
// Some attributes which conform validation group 1
public String attribute11;
public String attribute12;
public String attribute13;
private ValidationBean<Integer> validationBean1 = new ValidationBean<Integer>();
// Some attributes which conform validation group 2
public String attribute21;
private ValidationBean<String> validationBean2 = new ValidationBean<Integer>();
// Constructors, various attribute getters with JPA annotations
// ...
#Embedded
#AttributeOverrides(/*various overrides, each entity/validation group has its own validation column names...*/)
public ValidationBean<Integer> getValidationBean1() { return validationBean1; }
#Embedded
#AttributeOverrides(/*various overrides, each entity/validation group has its own validation column names...*/)
public ValidationBean<Integer> getValidationBean2() { return validationBean2; }
}
ValidationBean's user and date fields are automatically modified in the presentation layer when a change in the validated field is detected.
All of this is working correctly. Now, I'm trying to find an elegant & general solution that integrates with the current modelling to the following requirement: When any of the attributes in a validation group gets its value changed, and the related ValidationBean.validated doesn't change, user and date must also be modified with the current user's id and the current date.
There are, as I see it, two alternatives; putting that logic in the presentation layer, or in business/service layer
Putting it in the presentation layer would have an efficieny advantage. Entities are stored in session so that the DB doesn't have to be queried again to check for field changes. But unfortunately, some entities have some of their fields ajax-updated and it would be hard to tell if the entity really changed. Apart from not being the presentation layer's responsability to fulfill this requirement.
Putting it in the service layer seems the best alternative, and I've already found a possible way to handle this properly. I've come up with #PreUpdate. It would be easy to implement a #PreUpdate method on the #Entities to compare the values in DB with the values about to be updated, and modify the related ValidationBeans accordingly. The problem here, and I suppose it's a common problem, is that in the business layer, I don't have where to get the user id from. The current user Id is stored in the Session, which belongs to the presentation layer.
So, any tips, comments, recommendations on how can I share http session information to the service layer (not necessarily Wicket-specific), or even alternatives to fulfill this requirement will be welcome.
UDPATE : Following gkamal's suggestion, I'll try to integrate spring-security in the less intrusive way I can, just to take advantage of SecurityContext. I'd also appreciate tips on this matter.
The common approach used to solve this is to introduce a SecurityContext class that holds the details of the current user as a static thread local variable. The variable is initialized (from the httpsession) by the security filter or some other filter and cleared after the request processing is complete. The SecurityContext class will itself be part of the business layer which provides a set / get methods and hence doesn't have any web layer dependency.

Categories

Resources