Javax Validation and groups

Mary Reni
Javarevisited
Published in
2 min readMar 16, 2021
Photo by Michiel Leunens on Unsplash

Simply put, Javax validation works with two main group of annotations

  • Annotations to ensure that validation happens like @Valid or @Validated
  • Annotations to denote the kind of constraints to be enforced on a value like @NotNull, @Pattern

If we are not using the Spring framework in our project, hibernate validator would be the most common implementation for JSR validation specifications.

How @Valid and @Validated works?

When a parameter is annotated with @Valid, Java will know that this object needs to be validated, all the constraints declared inside the class will be evaluated.

Let us use a simple model with few constraints like @NotEmpty, @Min defined :

package com.mimr.model;import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.*;public class Word { @NotEmpty
private String id;
private Language language;
@NotEmpty
@Size(min = 1)
private String originalWord;
private String translatedWord;
@Email
private String addedBy;
@JsonCreator
public Word(@JsonProperty("id") String id,
@JsonProperty("language") Language language,
@JsonProperty("originalWord") String originalWord,
@JsonProperty("translatedWord") String translatedWord,
@JsonProperty("addedBy") String addedBy) {
this.id = id;
this.language = language;
this.originalWord = originalWord;
this.translatedWord = translatedWord;
this.addedBy = addedBy;
}
// Getters
}

To enable the validation when the deserialization happens, for example when an endpoint is receiving a request with a request body, we will annotate the RequestBody with @Valid annotation

package com.mimr.resource;import com.mimr.model.Word;import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/api/word/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class WordResource {
@POST
public Response createWord( @Valid Word word){
return Response.ok().build();
}

When we make the below request to the endpoint, we will receive an error as the “originalWord” in the request is empty.

POST /api/word/ HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"id": "id1",
"language": "DEUTSCH",
"originalWord": "",
"translatedWord": "Hello",
"addedBy": "xyz@gmail.com"
}

The below response is received:

{"errors":["originalWord must not be empty","originalWord size must be between 1 and 2147483647"]}

Validation group:

When we are using the same model for multiple operations like Create or Update, we may want to validate same model differently based on need.

For Example, if we want to use Word.java for Creating and updating a word object, we might not want to have the id field as mandatory while creating it.

The solution here would be creating a validation group and use it to enforce validation only for update operation.

  1. Creating validation group:

A validation group is nothing but a simple marker interface.

package com.mimr.validation;public interface Update {
}

2. Updating the model by marking the constraint with the validation group

public class Word {    @NotEmpty(groups = {Update.class})
private String id;
private Language language;

//
..
//

3. Use @Validated annotation to enforce validation selectively

public class WordResource {    @POST
@Path("create")
public Response createWord( @Valid Word word){
return Response.ok().build();
}
@POST
@Path("update")
public Response updateWord( @Validated(Update.class) Word word){
return Response.ok().build();
}

Now the validation on the id will only happen for the Update operation.

--

--