2016-06-20

Strategies for loading object graphs with JPA

In this article, I will describe and discuss the different strategies for loading object graphs with JPA 2.1. This should help you to choose the right strategy for your application. Firstly, I will explain how you can load your object graphs with JPA. Secondly, I will describe the pros and cons of the different approaches and when to use them.

As I've already mentioned, this article is all about object graphs, but before we delve into the topic I will briefly introduce JPA, explain what an object graph actually is and why it is important to choose the right strategy to load it. 

Java Persistence API (JPA)

Whenever you use such things as Java Persistence API with its most popular implementations Hibernate, EclipseLink or OpenJPA you probably try to solve the following problem. You have written an object-oriented application with Java and you want to store your data into a database. In Java, data is always represented by objects and their attributes, in contrast, databases (at least the relational ones) define concepts such as tables with rows and columns. The problem that must be solved is to map the objects to database tables and vice versa. This includes questions such as how to map inheritance, how to map primitive Java types to SQL data types and how to deal with identity of objects. This problem is exactly what an Object-Relational-Mapping (ORM) tool is solving for you and JPA provides a standardized API to access it. 

Object graphs 

As I've already mentioned, in Java, data will be represented by objects. Most probably your application will have something like a domain model, representing all the domain objects of the application. Domain object define attributes, getter- and setter methods to access these attributes and probably other more high-level methods to operate on the data stored in the attributes. Attributes may be of different data types. Primitive types such as int, long or double (of course also the wrapper types of the same) or more complex types such as address objects or other domain object types.

Example:

@Entity
@Table(name = "usr")
public class User {

   @Id
   private Long id;

   @OneToMany
   private Set<Car> cars;

   @OneToMany
   private Set<UserLogins> logins;
}

The class User has been mapped to the table named usr. It defines an attribute id of primitive (wrapper) type Long and two attributes of complex type Car as well as UserLogins. One User may have several Cars and several UserLogins. Both target types are domain objects mapped to the database (defining an identifier and at least the annotation @Entity). The classes UserLogins and Car will probably define their own attributes and some of them are pointing to other domain objects, too. As a consequence, when you have several domain objects / entities, you might be faced with a huge object graph and each object of a particular type is stored in a separate table: User objects are stored in the table usr, Car objects in the table car and so on. You can see this in the following UML class diagram:


In this example you can navigate from a User to a Car to its Axis and it Wheels. This is a navigation path of length 3. You can see that most of the UML associations between classes have multiplicity * and thus a User could have several Cars a Car could have several Axis and an Axis several Wheels. In this example the * will probably represent small numbers between 2-4. But imagine you had objects such as a calculation with several positions and a much longer navigation path. The number of objects will explode. Thus it is very important to load only the relevant parts of the object graph from database.

Static loading strategies

Static loading strategies define the to be loaded object graphs already at compile time via mappings (XML or annotations), specific queries or so called NamedEntityGraphs. I will distinguish application-wide strategies such as lazy loading or eager loading and operation-specific strategies such as explicit joins.

Lazy loading

JPA's default behaviour is that only the root object which is explicitly requested from database will be loaded. In contrast, all other objects in the graph will be lazy. They will be represented by a proxy and loaded whenever you access them. However, the prerequisite is that the entity manager session is still open. When is this session open? By default the session is bound to the transaction life cycle. That means if you don't have any transaction, the session will be closed after you've performed your database operation. The following test will exemplify this behaviour. It expects a LazyInitializationException.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { Config.class })
public class StaticFetchTest {

   @PersistenceContext
   private EntityManager entityManager;

   @Test(expected = LazyInitializationException.class)
   public void testFailLazyAccess() {
      User user = entityManager.find(User.class, 1L);
      // Lazy init exception cars is lazy but session closed
      user.getCars().stream().forEach(System.out::println);
   }
}

The reason for raising the exception is that the @PersistenceContext is by default set to type =  PersistenceContextType.TRANSACTION. In order to extend the lifetime of the session, you could put all the database operations which shall operate in this very same session into one common transaction. Of course you will hesitate to use a transaction although there is no write to the database which might cause performance penalties. And you are right. If you just use the transaction as session demarcation, you should set the attribute read-only to true. The next test method demonstrates this:

@Test
@Transactional(readOnly = true)
public void testLoadInTx() {
   User user = entityManager.find(User.class, 1L);
   // Cars are loaded on demand because entity manager is still open
   // because of the transaction
   user.getCars().stream().forEach(System.out::println);

}

The session is open as long as you don't leave the test method and thus JPA can load the objects you access on demand (proxied objects will get initialized). 

You are able to control the lifetime of the session. Let's assume you had an application with a controller layer, a service layer and a repository layer. You decide where to put your transaction: to the controller? to the service ? or to the repository? What strategy you choose depends on your application, but in general I think it is advantageous to put the transactions to the controller level. There is one exception from this rule: If your application runs controllers and services on separate machines and the service are consumed remotely via REST or SOAP, for example, then it makes no sense to put the transaction annotations to the controller because from there it won't be possible to initialise the proxies, because they run in different JVMs. In this case I would advise you to put the @Transaction annotation only to your services. Btw. you could also add transactions to controllers AND services (for example if the controllers are not the only consumer of the services). In this case, the transaction will be started in the controller and all subsequent service calls will join the ongoing transaction. If you call the service from a non-transactional application part the service will automatically start a new transaction.

A completely different strategy is to use extended sessions. With this strategy, sessions are open as long as possible and thus independent from the transaction's life time. What does as long as possible mean? To manage an open session you need to maintain state and how long this state can be maintained depends on the lifecycle of the object instance with the reference to the EntityManager instance. In a JEE application with Session Beans, for instance, you could use a Stateful Session Bean to manage this state. Let's have a look on how our test would look like with extended session:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { Config.class })
public class StaticFetchTest {

   @PersistenceContext(type = PersistenceContextType.EXTENDED)
   private EntityManager entityManagerExtended;

   @Test
   public void testLoadExtended() {
      User user = entityManagerExtended.find(User.class, 1L);
      // Cars are loaded on demand because entity manager is extended
      user.getCars().stream().forEach(System.out::println);
}

In this example the attribute entityManagerExtended is annotated with @PersistenceContext and its attribute type set to PersistenceContextType.EXTENDED. This enables extended session and although there is no transaction this test will run successfully.

Eager loading

The opposite to lazy loading is loading parts of the object graph instantly.  This is called eager loading and can be specified via  the object mapping. For each attribute of an entity pointing to another entity (and thus @OneToOne, @ManyToOne, @OneToMany or @ManyToMany) you can decide to load the target entities together with the source entity. If you want to load UserLogins whenever you load a User object, you would specify the following mapping:

@Entity
@Table(name = "usr")
public class User {

   @OneToMany(fetch = FetchType.EAGER)
   private Set<UserLogins> logins;
}

You set the attribute fetch to FetchType.EAGER which tells JPA to load the UserLogins instantly together with the User. So you can run the following test now:

@Test
public void testLoadEager() {
   User user = entityManager.find(User.class, 1L);
   // Logins are loaded because attribute is mapped eager
   user.getLogins().stream().forEach(System.out::println);
}

The advantage of this approach is that it is easy to configure and its impact is application-wide. However, this is also a drawback because you might not always want to load User objects with UserLogins. Still, if the probability is high you can define it as the default strategy for your application. Candidates for eager loading are in general objects which can be represented by UML composite associations. They are parent-child connections where the child lifecycle depends on the parent lifecycle.

In contrast to the application-wide configuration of the loading behaviour, JPA allows also to define the loading strategy on a per-operation-basis.

Explicit join

If you are using queries written in JPAQL (an object-oriented extension to SQL) you can join parts of the object graph via JPAQL's join keyword. But be careful, there are two join variants in JPAQL. The standard join keyword should be used when you would like to constrain the results by attributes of other objects from the graph than your root object. In this example we load only users who own a car with a particular license plate:

@Test(expected = LazyInitializationException.class)
public void testFailLazyAccessByExplicitJoinInJPAQL() {
   TypedQuery<User> loadUserQuery = entityManager
      .createQuery("select usr from User usr left outer join usr.cars cars where    
         cars.licensePlate = :licensePlate", User.class);
   loadUserQuery.setParameter("licensePlate", "HIBERNATE");
   User user = loadUserQuery.getSingleResult();
   user.getCars().stream().forEach(System.out::println);
}

As you can see this test will result in a LazyInitializationException although we have joined cars. The right keyword for using the explicit join loading strategy is to use fetch join keyword instead.

@Test
public void testInitByExplicitFetchJoinInJPAQL() {
   TypedQuery<User> loadUserQuery = entityManager
      .createQuery("select usr from User usr left outer join fetch usr.cars where usr.id =   
         :id", User.class);
   loadUserQuery.setParameter("id", 1L);
   User user = loadUserQuery.getSingleResult();
   user.getCars().stream().forEach(System.out::println);
}

Dynamic loading strategies

Dynamic loading strategies vary in the way how dynamic they actually are. In general, all those strategies allow you to decide at runtime which parts of the graph you want to load, whereas the aforementioned strategies do not support this because they are configured via mappings at compile time or via constant query strings.

Named Entity Graphs

One way to define several dynamic loading strategies is to use a NamedEntityGraph which can be configured as an annotation on top of an entity as follows:

@NamedEntityGraph(name = "user.cars", attributeNodes = @NamedAttributeNode("cars")
@Entity
@Table(name = "usr")
public class User {
}

An entity graph has two important attributes: a uniquely identifiable name and the attributeNodes which described the parts of the object graph to be loaded. The given graph defines that whenever a user is loaded its cars shall be fetched eagerly. But why is this dynamic? I have to define the graph as annotation and it cannot be changed at runtime. You should see how to use the graph. You can use it on a per-operation basis:

@Test
public void testInitCarsByEntityGraph() {
   User user = entityManager.find(User.class, 1L,    
      Collections.singletonMap("javax.persistence.fetchgraph",
         entityManager.getEntityGraph("user.cars")));
   user.getCars().stream().forEach(System.out::println);

}

There is a property called javax.persistence.fetchgraph allowing you to pass in a given entity graph to be used for the find operation. In this example we have used the find operation of the EntityManager. You can also set an entity graph when you are using a JPAQL query:

@Test
public void testInitByNamedEntityGraphInJPAQL() {
   TypedQuery<User> loadUserQuery = entityManager.createQuery("select usr from User usr 
      where usr.id = :id"User.class);
   loadUserQuery.setParameter("id", 1L);
   loadUserQuery.setHint("javax.persistence.fetchgraph",    
      entityManager.getEntityGraph("user.cars"));
   User user = loadUserQuery.getSingleResult();
   user.getCars().stream().forEach(System.out::println);

}

The entity graph in this example is passed in via the setHint method which is defined on TypedQuery.

You could also have defined several different entity graphs and choose the entity graph depending on some condition, or you could even allow to pass the name of the entity graph as a parameter for your repository or data access object methods. That is why I categorize entity graphs as dynamic.

However, named entity graphs are still limited because I can only use one entity graph per operation at the same time. It is not allowed to mix them. Hence, you could potentially face a huge number of different graphs representing all combinations of object paths to be loaded. If that is the case you should rather use dynamic entity graphs.

Dynamic Entity Graphs

Dynamic entity graphs can be instantiated at runtime. If your application must be very flexible in loading different graphs you could choose this concept. The next test case shows how you use it:

@Test
public void testInitDynamicEntityGraph() {
   EntityGraph<User> graph = entityManager.createEntityGraph(User.class);
   graph.addAttributeNodes("cars");
   User user = entityManager.find(User.class, 1L,     
      Collections.singletonMap("javax.persistence.fetchgraph"graph));
   user.getCars().stream().forEach(System.out::println);

}

In this example we create a new EntityGraph<User> via EntityManager.createEntityGraph. To this entity graph we add the attributes nodes cars. The instance is then passed in as javax.persistence.fetchgraph property again, similar to the named entity graph example.

Criteria API

The most powerful and flexible way to define queries with JPA is to use the JPA Criteria API. The criteria API allows you to define the whole query dynamically at runtime. A typical use case would be to define a search filter. Imagine you had different filter attributes and whenever you put a value into the filter form you want to add a test whether this attribute equals the given value to the where clause conjunction. The most obvious approach would be to heavily rely on String concatenation. But this is not type safe and most probably inefficient. Hence, you would rather make use of the JPA Criteria API as follows:


@Test
public void testInitByExplicitFetchJoinInJPACriteria() {
   CriteriaBuilder builder = entityManager.getCriteriaBuilder();
   CriteriaQuery<User> query = builder.createQuery(User.class);
   Root<User> root = query.from(User.class);
   root.fetch("cars", JoinType.LEFT);
   CriteriaQuery<User> criteriaQuery =  
      query.select(root).where(builder.and(builder.equal(root.get("id"), 1L)));
   User user = entityManager.createQuery(criteriaQuery).getSingleResult();
   user.getCars().stream().forEach(System.out::println);

}

In this example, you first create a CriteriaBuilder object. This object allows you to create a new CriteriaQuery object. Next, you create the query root. On this root object you call fetch on cars. Then you define what to select and how the where clause looks like. To make the query type safe you could also use a canonical meta model. This would allow you to replace the Strings "cars" and "id" with type safe expression such as root.get(User_.id) or root.get(User_.cars). In order to dynamically load certain objects from the graph you can add further parts of the graph for example root.fetch("cars", JoinType.LEFT).fetch("axis", JoinType.LEFT) by concatenating the fetch method calls.

Alternatively you can even combine named entity graphs or dynamic entity graphs with criteria API as follows:

@Test
public void testInitByNamedEntityGraphInJPACriteria() {
   CriteriaBuilder builder = entityManager.getCriteriaBuilder();
   CriteriaQuery<User> query = builder.createQuery(User.class);
   Root<User> root = query.from(User.class);
   CriteriaQuery<User> criteriaQuery
      query.select(root).where(builder.and(builder.equal(root.get("id"), 1L)));
   User user = entityManager.createQuery(criteriaQuery)
      .setHint("javax.persistence.fetchgraph"
         entityManager.getEntityGraph("user.cars")).getSingleResult();
   user.getCars().stream().forEach(System.out::println);

}

When to use which strategy

To sum up, I will briefly discuss the pros, cons of the presented strategies and a guideline to choose the appropriate strategy.

Lazy Loading

Pros:
  • You will only load objects that you access
  • You don't have to think about a concrete loading strategy
  • Your root object loads fast
Cons:
  • Might produce higher delay when you access objects which a long navigation path
  • You can't use SQL joins to load parts of your object graph, you will always produce several select statements 
  • You are not flexible because your strategy is global on mapping level
You should use it if
  • your frontend is running in the same JVM as your Hibernate backend
  • you can't anticipate which parts of your object graphs are needed in which situation
  • you want your application start quickly and distribute load time among subsequent user interactions
You should not use it if
  • your frontend calls your Hibernate backend remotely
  • it is clear that certain parts of the object graph must always be loaded
  • you can neglect application start up time and preload most of your data

Eager Loading

Pros: 
  • You load all data that you need instantly
  • You allow JPA to use the optimal fetching strategy (batch, joins, select)
  • You don't have to deal with closed sessions
Cons:
  • You need to think about which parts should be eager loaded and which not
  • You will have higher delay to load the root object from the graph because also other parts of the graph must be loaded
  • You will probably load unnecessary parts of the object graph
  • You are not flexible because your strategy is global on mapping level
You should use it if
  • it is clear which objects in the graph always belong together
  • you want to preload data to have quick access later on
You should not use it if
  • it is not clear which parts of the object graph are required 
  • your fetch graph is getting too big

Explicit join

Pros: 
  • You decide on operation level what to load, you probably won't load unnecessary objects
  • You can offer different variants of your operation (e.g. a lazy and an eager one) by using different join strategies for the same operation
Cons:
  • It is more complex to define what to load on query level
  • No separation of the loading strategy from the query making it less reusable because you have to define what to load for each query again and again, although you might have the same strategy you need to redefine it for the next query
You should use it if
  • for a certain operation, always the same data must be loaded
You should not use it if
  • the objects to be loaded vary strongly by the context from where your operation is called

Named Entity Graphs

Pros: 
  • You decide on operation level what to load, you probably won't load unnecessary objects
  • You can offer different variants of your operation by allowing to use different named entity graphs
  • Increases reusability because you can use the same graph for different operations
Cons:
  • Syntax is verbose if the graph gets complex
  • Named entity graphs cannot be combined, number of defined graphs may explode 
You should use it if
  • you have a few different loading strategies to be supported which can be reused for different operations in your repository/dao
You should not use it if
  • there are too many strategies or combinations of strategies

Dynamic Entity Graphs 

Pros: 
  • You decide on operation level what to load, you probably won't load unnecessary objects
  • The consumer of your repository/dao operation may decide completely on his/her own what to load, this will probably reduce the number of operations you need to offer in your repository/dao
Cons:
  • Complex to define
  • Difficult to reuse because they are only valid for a particular use case
You should use it if
  • there are many different loading strategies or combinations of strategies
You should not use it if
  • you only need a few strategies to offer in your dao/repositories

Criteria API

Pros: 
  • Allows you to write completely dynamic queries including dynamic loading strategies
  • Can be combined with entity graphs
Cons:
  • More verbose, difficult to read and complex to define
You should use it if
  • you have many variants of the same query
  • you start to build your queries using String concatenations
You should not use it if
  • you can implement the same with one static query

All examples used in this article can be found on GitHub https://github.com/Javatar81/code-examples/tree/master/jpa-object-graphs

2016-06-03

How to SpEL Validation - Class-Level & Cross-Parameter Constraints with Spring Expression Language

In this article I will describe how to leverage Spring's Expression Language (SpEL) in order to write powerful class-level and cross-parameter constraints using the JSR 349 - (Bean Validation 1.1) reference implementation Hibernate Validator.

First of all, I will briefly introduce Hibernate Validator which is a great implementation of the JSR 349 standard. It can be used to validate your Java classes against certain constraints. Annotations such as @NotNull, @Size, @AssertTrue, @Past or @Future are typical examples for such constraints. These annotations allow you to separate the validation logic from your code using a declarative mechanism. However, once you are getting used to this approach you will soon realise that the standard annotations will not suffice to validate all your domain classes and services. But don't worry, Hibernate Validator allows you the writing of your own custom annotations which I will briefly explain in Part 1 of this article. After introducing the state-of-the art in validation, I will show you the limitations of the approach in Part 2  and present a flexible, powerful and still easy-to-implement strategy based on the Spring Expression Language to support class-level & cross-parameter constraints.

Part 1. Implementing your own validation annotations

It is really desirable to write your own validation annotations because you will be able to express different types of constraints in a declarative way and separate validation logic from business logic. I distinguish the following types of constraints as described in the Hibernate Validation documentation: 
  1. Bean constraints: field-level, property-level, class-level and type argument (since Java 8) constraints
  2. Method constraints: (cross-) parameter and return value constraints
Field-level annotations constrain the value of particular field. Property-level annotations are similar but are annotated on the getter method of a particular field. Class-level annotations constrain the state of an object. This may involve several fields whose values may depend on each other.  Type argument annotations ca be used for validation on Java generic types. This allows you to check for null values in a collection, for example. Parameter annotations can be used to check individual parameters of a method. A more advanced mechanism is to validate against cross-parameter constraints which involve the state of a (sub) set of the method's parameters. Return value annotations can be used to validate the returned value of a non-void method. In the following I will elaborate on field-level, class and cross-parameter constraints.

1.1 Field-level constraints

Lets say we want to implement a new annotation which verifies the minimum age of a user to register for a social network. The validation standard defines two date-related annotations @Past and @Future. They allow you to check whether the date of the annotated attribute lies in the past or respectively in the future. In our case we want to check whether the user has an age of at least x years. To achieve this, we have to implement our own annotation first which we call @ValidAge shown in the following code snippet:

import java.lang.annotation.*;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {AgeValidator.class})
@Documented
public @interface ValidAge {
   String message() default "{age.validation.message}";

   Class<?>[] groups() default { };
    
   Class<? extends Payload>[] payload() default { };
    
   int min();

}

This annotation is targeted at fields or other annotations and the @Constraint annotation defines by which class the validation will be implemented. Furthermore, we define methods groups() for group-based validation (for further information on this topic, please refer to Chapter 5, Grouping constraints of the Hibernate Validator documentation) and message() / payload() for printing messages when validation fails. These are the standard methods supported by Hibernate Validator. Additionally, we define the min() method to allow the user to define the minimum age to be checked. Next, I will show how to implement age validation class containing the validation logic:

import java.util.Calendar;
import java.util.Date;

import javax.validation.ConstraintValidatorContext;

public class AgeValidator implements ConstraintValidator<ValidAge, Date> {
private ValidAge constraintAnnotation;
public void initialize(ValidAge constraintAnnotation) {
this.constraintAnnotation = constraintAnnotation;
}

public boolean isValid(Date value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.YEAR, -constraintAnnotation.min());
return !cal.getTime().before(value);
}
}

}

The class responsible for the validation must implement the ConstraintValidator interface with type parameters of the annotation and the type of the annotated field (here java.util.Date). It is also possible to have multiple validation implementation classes to support several types of fields. In this example we could also write a validator for attributes of the new java.time.LocalDateTime class. The isValid() method returns true if the validation is successful. We are now ready to use our new annotation in our domain class called User:

public class User {
    @ValidAge(min = 13)
private Date birthday;
}

Spring enables the validation by annotating the bean class with @Validated. You can then annotate method parameters of those beans with @Valid to validate the User object passed as parameter:

@Validated
@Service
public class UserService {
   @Transactional
public User registerUser(@NotNull @Valid User newUser) {
           // Implement register logic here 
   }
}

1.2 Class-level constraints

In some situations field-level annotations are not sufficient, for instance, if you want to validate the state of an object. This often involves more than one attribute and the attribute values may interfere. An example would be the following User class:

public class User {
   @NotNull
private String login;
   @NotNull
   @Email
private String email;

       // Getters and setters for login and email not shown here
}

Let's imagine we don't want to permit null values, neither for login nor for email. However, if we want to check that either login or email must not be null because one of both shall serve as the username (one can be null but not both), we cannot rely on the given set of standard annotations. We need to write our own annotation but this time it must operate on class not on field level.

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { UsernameValidator.class })
@Documented
public @interface ValidUsername {
   String message() default "{username.validation.message}";

   Class<?>[] groups() default { };
    
   Class<? extends Payload>[] payload() default { };

}

Once again a new validation implementation is needed:

public class UsernameValidator implements ConstraintValidator<ValidUsername, User> {

public void initialize(ValidUsername constraintAnnotation) {
}

public boolean isValid(User user, ConstraintValidatorContext context) {
     return !(user.getLogin() == null && user.getEmail() == null);
}

}

We can use our new annotation to add it to the User class:

@ValidUsername
public class User {

  private String login;
  @Email
private String email;

     // Getters and setters for login and email not shown here
}

1.3 Cross-parameter constraints

Imagine you have a service method which has two parameters from and until --both of type LocalTimeDate-- and you want to validate that the first parameter from is before until

@Validated
@Service
public class UserService {
   @ValidDates
public Collection<User> findUser(LocalDateTime from, LocalDateTime until) {
           // Implement findUser logic here 
   }
}

In order to validate the parameters, you need to define so called cross-parameter constraints. How do you do that? The answer is: just as in the approach above! Start writing a new annotation:

@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { DatesValidator.class })
@Documented
public @interface ValidDates {
   String message() default "{dates.validation.message}";

   Class<?>[] groups() default { };
    
   Class<? extends Payload>[] payload() default { };

}

Then implement the DatesValidator class as follows:

import java.time.LocalDateTime;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class DatesValidator implements ConstraintValidator<ValidDates, Object[]> {

  public void initialize(ValidDates constraintAnnotation) {
 
   }

       public boolean isValid(Object[] value, ConstraintValidatorContext context) {
      if (value.length != 2) {
throw new IllegalArgumentException("Only methods with 2 arguments supported!");
      }
      if (value[0] == null || value[1] == null) {
         return true;
      }
      if (!(value[0] instanceof LocalDateTime) || !(value[1] instanceof LocalDateTime)) {
         throw new IllegalArgumentException("Parameters must be of type LocalDateTime!");
      }
      return ((LocalDateTime) value[0]).isBefore(((LocalDateTime) value[1]));
   }

}

You can extend this class to support other types of dates, such as java.util.Date or java.time.ZonedDateTime as well. 

Part 2. Implementing powerful validation strategies with the Spring Expression Language

In summary, Hibernate Validation enables you to write your own annotations and to define your own class-level constraints quite easily. However, the code tends to get verbose really quickly and thus it does not scale very well. For instance, you have to write new annotations and validation classes for each specific scenario. Whatever new class-level annotation you defined it will be difficult to reuse for different target classes and scenarios. If we would like to support the annotation of another class besides User in our example, we had to write a new UsernameValidator class which would be specific to that newly supported class. One solution would be to define an interface UsernameProvider defining getLogin() and getEmail(). Our validation class would then use this interface as type parameter:

public class UsernameValidator implements ConstraintValidator<UsernameProvider, User>

All classes you are planning to use our @ValidUsername annotation for must then implement the new UsernameProvider interface.

But what if we want to generalize our username validation strategy into a generic @NotAllAttributesNull constraint which checks whether at least one attribute of the annotated class is not null? We could use Object as the type parameter in the validation class and lookup all getter methods via reflection. However, the code based on reflection tends to get complex and difficult to read. Isn't there any better approach? Yes, for sure! Use Spring Expression Language to implement class-level and cross parameters constraints.

2.1 Class-level constraints

Wouldn't it be nice if we could just define one powerful annotation for all class-level constraints and only one validator class? Yes it is possible and it is not very difficult! In the following I will show you how to accomplish that.

Let's start again with a new annotation called @ValidateClassExpression:

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {SpELClassValidator.class})
@Documented
public @interface ValidateClassExpression {

   String message() default "{expression.validation.message}";
   Class<?>[] groups() default { };

   Class<? extends Payload>[] payload() default { };
   String value();

}

As you can see I've specified an extra attribute called value which allows us to define our concrete (Spring expression language, SpEL) expression. Now have a look on how simple it is to write the validator class based on SpEL:

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SpELClassValidator implements ConstraintValidator<ValidateClassExpression,   
  Object> {
   private ValidateClassExpression annotation;
   private ExpressionParser parser = new SpelExpressionParser();
   public void initialize(ValidateClassExpression constraintAnnotation) {
      annotation = constraintAnnotation;
      parser.parseExpression(constraintAnnotation.value());
   }

   public boolean isValid(Object value, ConstraintValidatorContext context) {
      StandardEvaluationContext spelContext = new StandardEvaluationContext(value); 
      return (Boolean) parser.parseExpression(annotation.value()).getValue(spelContext);
   }

}

This class is able to evaluate the SpEL expression given by the expression attribute of your annotation and returns the result as the result for your validation! Please make yourself a bit familiar with SpEL to understand how powerful and flexible the application of this approach is (please refer to the Spring docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html)

The starting point for your evaluation SpEL's #this variable pointing to the current evaluation object which is the object to be validated in our case. You can access all properties and invoke all methods of the object using SpEL.

Lets come back to our User class and apply the new validator for checking whether login and email are null.

@ValidateClassExpression(value = "!(#this.login == null && #this.email == null)", message = "Login or email must be defined.")
public class User {

private String login;
 @Email
private String email;

}

Great :-). And imagine user has another attribute which is collection of cars. How could we check that the user has at least one car? It is so easy:

@ValidateClassExpression(value = "!#this.cars.isEmpty()", message = "User must have at least one car.")
public class User {

 private List<Car> cars new ArrayList<>();
 // Getters and setters for login and email not shown here
}

We could also combine several expressions by a simple conjunction of both expressions but the downside of this approach is that you could only use one validation message instead of a specific message for each constraint.

@ValidateClassExpression(value = "!(#this.login == null && #this.email == null) && !#this.cars.isEmpty()", message = "Validation failed")

No problem! Since Java 8 it is possible to make annotations repeatable. Thus, we can add the following annotation to the @ValidateClassExpression annotation:

@Repeatable(ValidateClassExpressions.class)

Furthermore, we have to write a new container annotation:

@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateClassExpressions {
ValidateClassExpression[] value();
}

This enables us to put multiple ValidateClassExpression annotations to the User class with individual error messages:

@ValidateClassExpression(value = "!(#this.login == null && #this.email == null)", message = "Login or email must be defined.")
@ValidateClassExpression(value = "!#this.cars.isEmpty()", message = "User must have at least one car.")
public class User {

private String login;
 @Email
private String email;

 private List<Car> cars new ArrayList<>();
 // Getters and setters for login, email and cars not shown here
}

Now you have a great toolset to write really flexible and powerful constraints. Last but not least let's have a look at how to apply this concept to cross parameter constraints as well. 

2.2 Cross-parameter constraints

For the validation of cross-parameters we first define a new annotation:

@Constraint(validatedBy = SpELParameterValidator.class)
@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ValidateParametersExpression {
String message() default "{parameters.validation.message}";
Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };
String value();
}

Then, we implement the validation class as follows:

@SupportedValidationTarget(ValidationTarget.PARAMETERS)
public class SpELParameterValidator implements ConstraintValidator< ValidateParametersExpression,
   Object[]> {

   private ValidateParametersExpression validParameters;
private ExpressionParser parser = new SpelExpressionParser();
public void initialize(ValidateParametersExpression
 constraintAnnotation) {
this.validParameters = constraintAnnotation;
parser.parseExpression(constraintAnnotation.value());
  }

public boolean isValid(Object[] values, ConstraintValidatorContext context) {
StandardEvaluationContext spelContext = new StandardEvaluationContext(values);
Map<String, Object> spelVars = IntStream.range(0,  
         values.length).boxed().collect(Collectors.toMap(
     i -> "arg" + i,
       i -> values[i]
     ));
spelContext.setVariables(spelVars);
Boolean evaluationValue = (Boolean)  
     parser.parseExpression(validParameters.value()).getValue(spelContext);
return evaluationValue;
  }

}

In contrast to the class-level validation where we accessed only a single object (the one to be validated) we need to access the array of arguments from the method to be validated. For convenience we register the arguments as variables named arg0 - argN. We can now redefine our DatesValidator as follows:

@Validated
@Service
public class UserService {
 @ValidateParametersExpression("#arg0.isBefore(#arg1)")
public Collection<User> findUser(LocalTimeDate from, LocalTimeDate until) {
           // Implement findUser logic here 
 }
}

How powerful and simple! In my opinion simplicity and productivity for writing constraints is inherently important because otherwise you might skip certain validation constraint because of the high implementation effort and this could lead to illegal states in the future.

I hope you found this article helpful and it inspired you to combine the Spring Expression language with Hibernate Validator in the future. The two annotations that we've implemented using SpEL @ValidClassExpression and @ValidParametersExpression can be used for all kinds of use cases related to class-level or cross-parameter validation. This allows you to apply validation to all your classes and methods which will definitely increase your code quality in the long run and helps you to separate your validation from your business code.

I am looking forward to any comments, improvements or extensions to the presented approach. Thanks for reading.  You can checkout the code examples presented in this article from my Github repository:  https://github.com/Javatar81/code-examples/tree/master/spelvalidation