Constraints can be associated with groups.
Each constraint must have an element group=Class<?>[] where group has to be an empty interface, it's a way to specify a strong typed group.
Groups allow us to restrict the set of constraints applied during validation. A group defines a subset of constraints. Instead of validating all constraints for a given bean, only a subset is validated.
When kicking off validation process we can specify which group should be validated by using Validator#validate(T object, Class<?>... groups) and other variants of ExecutableValidator#validate(..)
If no group is explicitly declared, a constraint belongs to the javax.validation.groups.Default group.
Let's see with an example how it works.
Examples
Creating groups
package com.logicbig.example;
public interface GroupUserName {
}
package com.logicbig.example;
public interface GroupAddress {
}
Example bean
package com.logicbig.example;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class User {
@NotNull(groups = GroupUserName.class)
String firstName;
@NotNull(groups = GroupUserName.class)
String lastName;
@NotNull(groups = GroupAddress.class)
String streetAddress;
@NotNull(groups = GroupAddress.class)
String country;
@NotNull(groups = GroupAddress.class)
@Size(min = 5, groups = GroupAddress.class)
String zipCode;
@NotNull
String userId;
.............
}
Validating for group GroupUserName
package com.logicbig.example;
import javax.validation.*;
import java.util.Set;
public class ConstraintGroupExample {
private static final Validator validator;
static {
Configuration<?> config = Validation.byDefaultProvider().configure();
ValidatorFactory factory = config.buildValidatorFactory();
validator = factory.getValidator();
factory.close();
}
public static void main (String[] args) {
User user = new User();
user.setFirstName("Jennifer");
// user.setLastName("Wilson");
Set<ConstraintViolation<User>> constraintViolations =
validator.validate(user, GroupUserName.class);
if (constraintViolations.size() > 0) {
constraintViolations.stream().forEach(
ConstraintGroupExample::printError);
} else {
//proceed using user object
System.out.println(user);
}
}
private static void printError (ConstraintViolation<User> violation) {
System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
}
} OutputlastName must not be null
Validating for both groups along with the Default one
package com.logicbig.example;
import javax.validation.*;
import javax.validation.groups.Default;
import java.util.*;
import java.util.stream.Collectors;
public class ConstraintGroupExample2 {
private static final Validator validator;
static {
Configuration<?> config = Validation.byDefaultProvider().configure();
ValidatorFactory factory = config.buildValidatorFactory();
validator = factory.getValidator();
factory.close();
}
public static void main(String[] args) {
User user = new User();
user.setFirstName("Jennifer");
// user.setLastName("Wilson");
Set<ConstraintViolation<User>> constraintViolations =
validator.validate(user, GroupUserName.class,
GroupAddress.class, Default.class);
if (constraintViolations.size() > 0) {
Map<String, List<ConstraintViolation<User>>> groupViolationsMap =
constraintViolations.stream().collect(
Collectors.groupingBy(v ->
v.getConstraintDescriptor().getGroups().iterator()
.next().getSimpleName(), TreeMap::new, Collectors.toList()));
groupViolationsMap.forEach((k, v) -> {
System.out.printf("%n-- Group: %s --%n", k);
v.stream().sorted(Comparator.comparing(o -> o.getPropertyPath().toString()))
.forEach(ConstraintGroupExample2::printError);
});
} else {
//proceed using user object
System.out.println(user);
}
}
private static void printError(ConstraintViolation<User> violation) {
System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
}
}
Output -- Group: Default -- userId must not be null
-- Group: GroupAddress -- country must not be null streetAddress must not be null zipCode must not be null
-- Group: GroupUserName -- lastName must not be null
Validating without any group
When not specifying any group, the default group javax.validation.groups.Default is assumed. We have only one such constraint in our User class which is on userId field.
package com.logicbig.example;
import javax.validation.*;
import java.util.Set;
public class ConstraintGroupExample3 {
private static final Validator validator;
static {
Configuration<?> config = Validation.byDefaultProvider().configure();
ValidatorFactory factory = config.buildValidatorFactory();
validator = factory.getValidator();
factory.close();
}
public static void main (String[] args) {
User user = new User();
user.setFirstName("Jennifer");
// user.setLastName("Wilson");
Set<ConstraintViolation<User>> constraintViolations =
validator.validate(user);
if (constraintViolations.size() > 0) {
constraintViolations.stream().forEach(
ConstraintGroupExample3::printError);
} else {
//proceed using user object
System.out.println(user);
}
}
private static void printError (ConstraintViolation<User> violation) {
System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
}
} OutputuserId must not be null
Group Sequences
By default, constraint groups are validated in no particular order. But we can specify a particular order by defining a new interface which should be annotated with GroupSequence.
This annotation does one more thing, that is, next group will not be validated if current one has validation errors. This is different than previous examples where all groups specified in Validator#validate will be validated together.
Let's continue with our last example and define a new group sequence:
package com.logicbig.example;
import javax.validation.GroupSequence;
import javax.validation.groups.Default;
@GroupSequence({Default.class, GroupUserName.class, GroupAddress.class})
public interface GroupSequenceForUser {
}
Now groups will be validate in the provided order i.e. first Default group, if there's no error then GroupUserName will be validated, if still no error then GroupAddress will be validated.
To make it work, we have to pass the sequence interface as a group to Validate#validate method:
package com.logicbig.example;
import javax.validation.*;
import java.util.Set;
public class ConstraintGroupSequenceExample {
private static final Validator validator;
static {
Configuration<?> config = Validation.byDefaultProvider().configure();
ValidatorFactory factory = config.buildValidatorFactory();
validator = factory.getValidator();
factory.close();
}
public static void main (String[] args) {
User user = new User();
Set<ConstraintViolation<User>> constraintViolations =
validator.validate(user, GroupSequenceForUser.class);
if (constraintViolations.size() > 0) {
constraintViolations.stream().forEach(
ConstraintGroupSequenceExample::printError);
} else {
//proceed using user object
System.out.println(user);
}
}
private static void printError (ConstraintViolation<User> violation) {
System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
}
} OutputuserId must not be null
Redefining the default group sequence
We have one more interesting feature related to the group sequence: Instead of creating a new interface annotated with @GroupSequence and defining the sequence, we can use the same @GroupSequence annotation on our bean. In that case the order of the provided groups becomes the 'Default' group which is tied to the target bean class only:
package com.logicbig.example;
import javax.validation.GroupSequence;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.groups.Default;
@GroupSequence({User2.class, GroupUserName.class, GroupAddress.class})
public class User2 {
@NotNull(groups = GroupUserName.class)
String firstName;
@NotNull(groups = GroupUserName.class)
String lastName;
@NotNull(groups = GroupAddress.class)
String streetAddress;
@NotNull(groups = GroupAddress.class)
String country;
@NotNull(groups = GroupAddress.class)
@Size(min = 5, groups = GroupAddress.class)
String zipCode;
@NotNull
String userId;
.............
}
Since there must not be cyclic dependency in the group, we cannot add 'Default' group while redefining the default group. Instead the class itself can be added to the sequence. In above example User2.class is added in @GroupSequence itself to include the default.
Performing validation:
package com.logicbig.example;
import javax.validation.*;
import javax.validation.groups.Default;
import java.util.Set;
public class RedefiningDefaultGroupExample {
private static final Validator validator;
static {
Configuration<?> config = Validation.byDefaultProvider().configure();
ValidatorFactory factory = config.buildValidatorFactory();
validator = factory.getValidator();
factory.close();
}
public static void main (String[] args) {
User2 user = new User2();
Set<ConstraintViolation<User2>> constraintViolations =
validator.validate(user);
if (constraintViolations.size() > 0) {
constraintViolations.stream().forEach(
RedefiningDefaultGroupExample::printError);
} else {
//proceed using user object
System.out.println(user);
}
}
private static void printError (ConstraintViolation<User2> violation) {
System.out.println(violation.getPropertyPath() + " " + violation.getMessage());
}
} OutputuserId must not be null
Example ProjectDependencies and Technologies Used: - hibernate-validator 6.2.0.Final (Hibernate's Jakarta Bean Validation reference implementation)
Version Compatibility: 5.0.0.Final - 6.2.0.Final Version compatibilities of hibernate-validator with this example: groupId: org.hibernate artifactId: hibernate-validator Reference implementation for Bean Validation 1.1
- 5.0.0.Final
- 5.0.1.Final
- 5.0.2.Final
- 5.0.3.Final
- 5.1.0.Final
- 5.1.1.Final
- 5.1.2.Final
- 5.1.3.Final
- 5.2.0.Final
- 5.2.1.Final
- 5.2.2.Final
- 5.2.3.Final
- 5.2.4.Final
- 5.2.5.Final
- 5.3.0.Final
- 5.3.1.Final
- 5.3.2.Final
- 5.3.3.Final
- 5.3.4.Final
- 5.3.5.Final
- 5.3.6.Final
- 5.4.0.Final
- 5.4.1.Final
- 5.4.2.Final
- 5.4.3.Final
groupId: org.hibernate.validator artifactId: hibernate-validator Reference implementation for Bean Validation 2.0
- 6.0.0.Final
- 6.0.1.Final
- 6.0.2.Final
- 6.0.3.Final
- 6.0.4.Final
- 6.0.5.Final
- 6.0.6.Final
- 6.0.7.Final
- 6.0.8.Final
- 6.0.9.Final
- 6.0.10.Final
- 6.0.11.Final
- 6.0.12.Final
- 6.0.13.Final
- 6.0.14.Final
- 6.0.15.Final
- 6.0.16.Final
- 6.0.17.Final
- 6.0.18.Final
- 6.0.19.Final
- 6.0.20.Final
- 6.0.21.Final
- 6.0.22.Final
- 6.1.0.Final
- 6.1.1.Final
- 6.1.2.Final
- 6.1.3.Final
- 6.1.4.Final
- 6.1.5.Final
- 6.1.6.Final
- 6.1.7.Final
- 6.2.0.Final
Version 7 and later: Jakarta Bean Validation 3.0 jakarta.* packages
Versions in green have been tested.
- javax.el-api 3.0.0 (Expression Language 3.0 API)
- javax.el 2.2.6 (Expression Language 2.2 Implementation)
- JDK 8
- Maven 3.8.1
|