How to impose common constraint on class with javax.validation library? - java

I am using Bean Validation constraints to validate class instances at run time. I have many DTOs where each has multiple fields often with a common constraint. I want to add a constraint to the class so that it applies to all the properties of the class. (As lombok #NotNull constraint).
e.g
class Person {
#NotNull
private String name;
#NotNull
private String address;
#NotNULL
private String contact;
}
I want to make it something like this.
#NotNull
class Person {
private String name;
private String address;
private String contact
}

You cannot do this with plain Bean Validation. So just adding #NotNull to the class won't work.
One potential approach could be to utilize XML configuration. The idea would be to have your own annotation MyNotNull. Your framework would need to scan for these annotations and build the programmatic configuration for the class in question. This could for example be done via a annotation processor during compile time. Once you have the constraint mapping XML files add them to your jar and reference them in validation.xml. That's just a basic idea. Personally, I am not sure whether it is worth the effort.

Related

How to avoid adding #Valid on each and every inner-class fields during the hibernate-validator?

I am currently developing an application within that I am adding a few validations on an inner class such as #NotNull, #Min, #Max, etc.
To make the validations work I need to add the #Valid on each and every field which is making use of the inner class. Is there a way to avoid adding the #Valid on each and every object rather add some annotations on the Class so it can be applicable to all the fields within that class?
I am currently using the following library to achieve the validations:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
I tried to add the #Validated on the class but seems like this annotation is not available in this maven dependency. Can someone please let me know what I need to change?
Following is a simple example that is working but I would like to remove #Valid that I have to add on each field. If I do not add #Valid then those inner classes won't be validated.
public class Book {
#NotNull(message="Book ID cannot be NULL")
private int bookId;
#Valid
private List<Author> author;
#Valid
private List<Publication> author;
}
public class Author {
#NotNull(message="Author ID cannot be NULL")
private int authorID;
#NotNull(message="Author Name cannot be NULL")
private String name;
}
public class Publication {
#NotNull(message="Publication ID cannot be NULL")
private int authorID;
#NotNull(message="Publication Name cannot be NULL")
private String name;
}
There is no way to do what you want to do, except if you write your own Quarkus extension that will add the annotations at build time.
It will be some rather involved work, though, as you will need to add some bytecode transformation to add the annotations where you want them.
Also, you should add the #Valid annotations inside the List e.g. List<#Valid Publication> rather than at the field level. It's more optimized this way.

Using Data annotation on Java DTO class

I have confusion in using Data annotation to DTO class. Here is my sample class.
#Data
public class MyClass {
private Long id;
private String name;
}
I've read online that using Data annotation specifically on DTO class will allow the values of the fields to be changed by use of the generated setters.
Should I remove the lombok Data annotation? And implement the getters and setters manually.
Thanks :)
I would avoid #Data for DTOs since it has too many pitfalls. First of all as you mentioned it is mutable which you don't really want for a DTO. And despite it being mutable, it implements equals() and hashCode() which is only asking for trouble.
You can use #Value for an immutable DTO. For an incoming DTO you may need to add lombok.anyConstructor.addConstructorProperties to your lombok.config, which will allow libraries like jackson to deserialize to your POJO without a default constructor.
The annotation #Data comes from the Project Lombok which is designed to use reflection via annotations mostly. This annotation assures generation of all the setters, getters, a constructor with all the required arguments and overridden Object::toString, Object::equals and Object::hashCode methods.
Briefly said, this annotation "completes" a simple POJO object and generates all the boilerplate without a need to use IDE.
They named the annotation #Data because they support the idea of the understanding objects as data containers only.
As far as I understand, the generation happens only for the missing getters/setters (let's speak about them for brevity). The generated getters/setters are in their pure form as you know:
public int getId() { return this.id; }
public void setId(int id) { this.id = id; }
You can use more verbose setter/getter performing validation or anything similar which override the generated ones. You can both use #Data annotation and write your ones manually.
DTO is used to transmit data information, some information is actually we do not want users to be able to change or access, such as the user password, we do not want to pass to the front end when the user can see the encrypted password, or we do not want users to modify the password while changing their information, and what works in this serialization process is setter and getter, and data annotations that automatically generate getters and setters for all fields.
For example
#Data
class User{
private String userName;
private String pwd;
}
This class, will have all setter and getter. When you trans to web, you will see
{userName: "123", pwd: "xxx"}
This is terrible.
But if you use DTO
class User{
private String userName;
private String pwd;
public String getUserName(){
return userName;
}
}
They only see
{userName: "123"}
By default the #Data lombok annotation will generate setters and getters for all fields in the class.
If you want an immutable data transfer object, annotate it as #Value instead.
If you want a mixure of some immmutable values and some mutable values in your MyClass type, for instance you might want the id field to be immutable and the rest mutable, you would use the #Setter annotation on the field you want to be immutable, specifying an AccessLevel of NONE. For instance:
#Data
public class MyClass {
#Setter(AccessLevel.NONE)
private Long id;
private String name;
}
This will generate a getter but no setter for the id, and a getter and setter for the name.

Exclude field while reading in child entity using jpa oracle db

I want to exclude some of the fields in child entity using jpa.
Ex :
Class Person extends Serializable
{
private String firstName;
private String lastName;
private String id;
private PersonalInformation personalInformation;
}
Class PersonalInformation extends Serializable
{
private Date dob;
private List<PersonalDocument> documents;
}
Class PersonalDocument extends Serializable
{
private String fileName;
private int fileSize;
private byte[] fileData;
}
When I tried to get the details of a person, I could get all the information about a person, including PersonalInformation and PersonalDocument list, but since PersonalDocument.fileData can be huge, every time getting this field from DB is impacting the performance.
So I wanted to ignore/exclude PersonalDocument.fileData field while reading only, I wanted to know how to write the jpa query for the same.
One solution then would be to have the file data lazily fetched i.e. fetched on demand from the database when you access the field. Note that JPA specification does not require that provider implementations actually support the lazy loading of individual fields (rather than associations): any directives in this area can only be considered as a hint to the persistence provider.
I know that Hibernate does support the lazy loading of fields and there are lots of similar questions in this area but I have been unable to find a definitive answer about exactly what is required.
Firstly, however you need to mark the field with the #Lob annotation (http://docs.oracle.com/javaee/6/api/javax/persistence/Lob.html)
import javax.persistence.Lob;
public class PersonalDocument implements Serializable
{
private String fileName;
private int fileSize;
#Basic(fetch=LAZY) //optional??
#Lob
private byte[] fileData;
}
The following suggests that #Lob is lazy by default and so we may not need the additional #Basic(fetch=LAZY) however no harm in adding it anyway.
https://hibernate.atlassian.net/browse/ANN-418
While some similar Stack Overflow questions appear to report that adding #Lob is all that is required to have such fields lazily loaded, the Hibernate docs themselves note that lazy field loading requires byte code enhancement.
https://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
To enable property level lazy fetching, your classes have to be
instrumented: bytecode is added to the original class to enable such
feature, please refer to the Hibernate reference documentation. If
your classes are not instrumented, property level lazy loading is
silently ignored.
So, in summary:
Add #Lob and see if it works.
If not, add #Basic(fetch=LAZY) and see if it works.
if not, either add byte code enhancement to your build.
https://docs.jboss.org/hibernate/orm/5.0/topical/html/bytecode/BytecodeEnhancement.html

Spring Hibernate Bean Validation #valid

I know it is not new question in this forum but I am very confused what should i do.
Problem: I am developing one application with spring mvc + hibernate. For server side validation I am using #valid annotation in controller and #null and #notNull annotation in my bean.
e.g
public class User implements Serializable {
private static final long serialVersionUID = 2158419746939747203L;
#Id
#Column(name="USER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long userId;
#Column(name="USERNAME", unique = true)
#NotEmpty #NotNull #Size(min=6, max=20)
private String username;
#Column(name="PASSWORD")
#NotEmpty #NotNull #Size(min=6, max=20)
private String password;
This validation is happening fine and data is also saving in DB.
But I want to validate unique constraint,referential integrity and other constraint using annotation without any validator class.
Is it possible? if no, then what is best and easiest way to to do it(less coding)? I will appreciate if framework will do it for me.
Saurabh,
For unique constraint in table,
#Id
You will be able to enforce referential integrity via hibernate annotations as well
eg.
#OneToOne(mappedBy = "foo")
Here is an example post
Referential integrity with One to One using hibernate
Here is a very detailed tutorial also exploring the same:
http://www.journaldev.com/2882/hibernate-tutorial-for-beginners-using-xml-annotations-and-property-configurations
You could write a "CustomUniqueConstraintValidator" kinda like mentioned in
http://www.journaldev.com/2668/spring-mvc-form-validation-example-using-annotation-and-custom-validator-implementation
You can also pass in paramters from the annotation to the custom validator.
eg.
#CustomValidDate("columnName")
To make a generic class that applies for any field /column
1. YOu can write a generic custom validator
2. use annotaiton parameters (on each class attribute) to pass in the table name and column name.
3. Then in the validator you can use the table name, column name to apply your validation logic (unique etc).
Thanks,
Paul

Play framework, Different constraints for different requests

How to implement different constraints for different requests? For example, there is User class:
public class User extends Model{
#Required
#Email
#Id
public String email;
#Required
#Column(length = 50)
public String firstname;
#Required
#Column(length = 50)
public String lastname;
#Required
public String password;
}
When I create a new user, all constraints are required. But when I update user information, I don't need the password constraint.
Should I create separate classes for createUser() and updateUser() actions?
Is there any way I can use just one class?
It is bad practise to mix "back-end entity" annotations with "front-end entity" annotations. Create separate class for inserting user and updating user with #Required annotations accordingly. Remove front-end annotations from User entity and leave only JPA annotations like #Id #Column etc.
As Play's validation framework conforms to the Java bean validation specification (JSR-303), you can use the validation groups feature that is part of the spec. This is exactly what you are looking for - a neat way of enforcing different validation rules for different actions. You can see an example of how to use it in code in this answer I gave to a similar question.

Categories