Spring: Same object, different validation - validation

I have an object called User where I save all the data of the User. I have some annotations to perform validation and it works fine.
public class User{
#NotEmpty
#Email
#Size(max=100)
#Column(name="username", length=100, nullable=false, unique=true)
private String username;
#NotEmpty
#Size(min=5, max=40)
#Column(name="password", length=40, nullable=false)
private String password;
#Size(min=5, max=40)
#Transient
private String newPassword;
// other attributes ,getters and setters
}
I have two different forms (each one in a different page). In the first one I ask for username and password to create the user, so both of them are compulsory.
In the second form I show the information about the user: username, other data (which will be validated as well) andthe password and newPassword. If the newPassword is set and the password is the same as the user has I'll change the user's password, but if they are left empty it means I shouldn't change the password.
The problem is that I have two forms relating to the same object where there is a different validation for the field password. In the first one it must be not empty, but in the second one it can be empty.
In the controller I validate the object in this way:
public String getUserDetails(#Valid #ModelAttribute("User") User user, BindingResult result, Model model){
if(result.hasErrors()){
//There have been errors
}
...
}
but is password is empty there will be an error.
Is there any way to perform a validation only in some fields of the object?
Can I, at least, remove any validation error after the validation?
What is the best practice in this case?
Thanks

You can use JSR-303 constraint groups to achieve this.
public class User {
public interface GroupNewUser {};
#NotEmpty
private String username;
#NotEmpty(groups = {GroupNewUser.class});
private String password;
// ...
}
Then in the controller, instead of using #Valid, use Spring's #Validated annotation which allows specifying a constraint group to apply.
See this blog post and this one also for more information.
There is also this superb post for Spring usage.

This is the problem with declarative validation, it's not very easy to do this sort of thing.
The easiest solution is to remove the validation annotations from the password field, and validate that field manually in the controller. The BindingResult class has methods on it for you to explicitly mark fields as invalid.
Another alternative would be to create two subclasses of the form class, each with its own password field, one with validation annotations, and one without, and use the appropriate one in the appropriate place.

A few pointers. I'm not sure they will be useful, though:
Spring docs say that:
you may call binder.setValidator(Validator) within a #Controller's #InitBinder callback. This allows you to configure a Validator instance per #Controller class
javax.validation has two *Context interfaces. Get into more details with them to see whether different validation can be achieved in different contexts.

instead of using hasErrors() function, use hasFieldErrors(fieldname) and only validate particular fields required by the form.

Related

Spring MVC: how to avoid duplicating validation at controller and entity

Let's say I have a "Person" #Entity managed via JPA, which has a series of validations applied at entity-level (#NotBlank, #NotNull etc).
#Entity
public class Person {
#NotBlank
private String name;
#Email
private String email;
...
}
For various reasons, we shouldn't directly use an entity as the controller method argument, but rather create a custom "form" (taking the example from https://spring.io/guides/gs/validating-form-input/)
#PostMapping("/person/save")
public String savePerson(#Valid PersonForm personForm, BindingResult bindingResult) {
// map fields to Person entity individually and save
...
}
But now, to make use of Spring's built-in form validation / BindingResult, it appears I have to duplicate all my validation logic on both the Person and PersonForm classes. I don't just want to define them on PersonForm, because there might be other routes in the application to update a Person.
Ideally there would be some way that Spring could lift up the validation constraints on the #Entity and apply them to the form (e.g. if the properties had the same name).
Am I missing something here with validation?

Marking a field as mandatory in the request body for a specific request

I intend to use the same bean as request body for creating/updating/deleting profile (depending on request methods). There are two scenarios for an update. First is updating profile where any parameter like firstName, lastName etc. can be updated except the emailAddress and the second is updating only the emailAddress. For the first case newEmailAddress is optional but for the second case, newEmailAddress field is mandatory.
Below is a sample bean:
class ProfileModel {
#NotEmpty
#Email
private String emailAddress;
#Size(max=30)
private String firstName;
#Email
private String newEmailAddress;
.
.
.
}
I created different API endpoints for updating profile and updating the email address. I tried to find a way to make newEmailAddress field as optional for the first request and required for the second request but couldn't find anything but to manually check in the controller method for the second request and throw Exception.
Is there any other way through which this can be achieved?
The validation groups should solve your problem. See the examples either on beanvalidation.org or at hibernate-validator documentation page.
Basically you will need to add a group attribute value to your annotation constraints. Something like:
class ProfileModel {
#NotEmpty
#Email
private String emailAddress;
#Size(max=30)
private String firstName;
#Email(groups= UpdateEmail.class)
private String newEmailAddress;
and then on your controllers make use of Springs #Validated annotation which allows you to pass a group for which you want to validate.
One endpoint could use then this UpdateEmail group and the other the Default one.
This can be achieved using validation groups. That needs an identifier class or interface.
So you can do something like this:
=> Define a validation group identifier. It can be a class or interface.
public interface MyValidationGroup{}
=> Specify the validation group on request body.
class ProfileModel {
#NotEmpty
#Email
private String emailAddress;
#Size(max=30)
private String firstName;
#Email
#NotBlank(groups={MyValidationGroup.class})
private String newEmailAddress;
.
.
.
}
=> Specify validation group on controller method.
public ProfileModel add(#Validated({MyValidationGroup.class})
#RequestBody ProfileModel profile){
...
}

Ignoring spring mvc JSR-303 validations for selective entity fields

I have spring4 mvc application to save an Address entity, code bit as follows.
My Controller
#RequestMapping(value = "addAddress", method = POST)
public String registerComplaint(#Valid #ModelAttribute final Address address, final BindingResult resultBinder) {
if (resultBinder.hasErrors())
return "addAddress";
addressService.addAddress(address);
return "redirect:myAddress";
}
My Entity
#Entity
#Table(name = "address")
public class Address {
#NotNull
private String street;
#NotNull
private String pin;
#NotNull
private String createdBy;
........
}
My form conatins only street and pin as field, where as createdBy should be set by me after validating the other form values.
Here the problem is spring JSR303 validation support is validating a field ie createdBy which i don't want to validate by spring mvc.
How can i instruct spring mvc not to validate these kind of optional fields while using #Valid annotation.
Is there any way i can skip fields like this using spring mvc ?
Validation is mainly for user input. Since you will be setting createdBy yourself, just do so before saving it (e.g #PrePersist), or have a new Date as a default value. If you need to enforce a constraint for createBy, you can do so at the schema level.
#Column(nullable=false, ...)
private String createdBy = new Date();
You need to read up on Validation Groups. This lets you use different validators depending on the "scenario"
Use Spring's #Validated annotation to use groups
If you don't protect the createdBy field, a user can change it by altering the POST variables. See DataBinder.setDisallowedFields()
Conceptually, how is a pin related to an address?
It sounds like you want to use a Form Backing Object here (a regular non-JPA POJO made just for a form), and copy values to your real entities.

How do I add an interceptor to save/fetch methods of Spring's JPARepository to update transient values?

First let me say I'm a complete novice with Spring AOP, and I apologize if this is a duplicate question.
Here's my scenario:
Let's say I have the following domain class:
#Entity(name="MyTable")
#Table(name="MY_TABLE")
public class MyTable {
private static final long serialVersionUID = 1234567890123456L;
#Id
#Column(name = "USER_ID")
private Long userID;
#Transient
private String key;
#Column(name = "KEY")
private String secureKey;
/* Other columns */
/* Getters and Setters */
}
and I have the following JPARepository class to manage it:
#Repository
public interface MyTableRepository extends JpaRepository<MyTable, Long> {
/* findBy methods */
}
As you can see, I have a secureKey field and a transient key field. In this case secureKey is an encrypted version of key.
What I need is for the secureKey value to be populated before a domain object is saved, and for the key value to be populated after a domain object is fetched. (This is a trivial example but in the real case I have multiple transient and encrypted values.) The idea is for the secure values to be persisted to the DB, but users of the domain class will only need to work with the "insecure" values.
Currently I'm handling this in my service layer. After I call a fetch method I'm populating the transient values, and before calling a save method I'm populating the "secure" values. This is working as expected but ideally I'd like this to be managed transparently, because now the burden is on each developer to remember to update those values after fetching or before saving.
I'm assuming the best way to handle this would be through some AOP class, but I confess I have little to no idea where to begin there. Is this a common scenario, and if so, would someone be willing to point me in the right direction? Also, if you have a suggestion for a better way to implement this decrypted/encrypted field pair scenario, please let me know.
Ideally I'd like to be able to add an annotation to both the secure and insecure fields, maybe pointing to each other, maybe something like:
#Insecure(secureValue = "secureKey")
#Transient
private String key;
#Secure(insecureValue = "key")
#Column(name = "KEY")
private String secureKey;
Any assistance you could provide is most appreciated.
Thanks,
B.J.
I think Spring AOP isn't the correct technology in your use case, i would recommend to use EntityListeners.
Hibernate: https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/listeners.html
Eclipselink: https://wiki.eclipse.org/EclipseLink/Release/2.5/JPA21#CDI_Entity_Listeners

Entity Objects vs Value Objects - Hibernate and Spring

Okay. I am getting a little confused here...
Lets say I have a class called User.
class User {
// all variables
// all getters and setters
}
Now, I use JSR 303 validation and put #NotNull, #Range, etc here for the variables.
I use this as Form / Command object. Meaning, when a form a submitted, the values are validated and BindingResult gives me errors.
Should this be used as in Entity Object for Hibernate as well? (If so, I need to add other Hibernate annotations like #Entity, #Id, #Column, etc on top of Validation annotations)
When we load the data from Database, do these validations kick in as well? (If yes, what if the data is already existing, and do not confirm to the validations?)
Where do we normally write business validations, like for example, country exists or not in the database, xyz value exists in a different table, etc?
Questions arise here as well:
- User form may not have all the fields that exist in the User class
- Database table User may have more fields or less fields than User class
- Form may have fields from different objects as well, say User and and Order.
How do we handle these?
Trying to wrap my mind around it
No you shouldn't mix entities objects and values objects. Entities objects are for DB mapping and values objects are used in the presentation layer.
To validate an object annoted, you need to use a Validator (commonly used with a DataBinder. Spring validation)
Is it DB constraints or backend validation?
For your last question, that's one of the reason to have 2 differentes objects for your presentation layer and your persistence layer. This way values objects can match what is displayed or input by the user (Form).
Entity object is an object of our plain old java class(POJO) Model/Business class, which needs to be persisted in a database using Hibernate, while Value Type object is an object of another class but it is stored as a part of Entity object within a database table.
#Embeddable
#Data
#AllArgsConstructor
public class Address{
private String country;
private String city;
private String street1;
private String street2;
private postalCode;
}
#Entity
#Data
#AllArgsConstructor
public class Person{
#Id
private Long id;
private String firstName;
private String lastName;
private ing age;
private Address address;
}
}
So after running this code in spring and hibernate project you will see Person table in database is created with it's attributes and Address class attributes.
For more information, I suggest reading this:
[https://www.decodejava.com/hibernate-value-type-object.htm][1]

Resources