Close

JavaBean Validation - Using @ConvertGroup to use a different group with @Valid annotation (during cascaded validation)

[Last Updated: Sep 12, 2021]

According to Bean validation specification, when performing cascading validation (via @Valid annotation), it is possible to use a different group than the one originally specified using the group conversion feature.

Group conversions are declared by using the @ConvertGroup annotation. Following is this annotation's definition snippet:

Definition of ConvertGroup

(Version: java-bean-validation 6.2.0.Final)
package javax.validation.groups;
   ........
@Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
public @interface ConvertGroup {
    Class<?> from() default Default.class; 1
    Class<?> to(); 2
    @Target({METHOD, FIELD, CONSTRUCTOR, PARAMETER, TYPE_USE})
    @Retention(RUNTIME)
    @Documented
    public @interface List {
        ConvertGroup[] value();
    }
}
1 The source group of this conversion.
Returns the source group of this conversion
2 The target group of this conversion.
Returns the target group of this conversion
  • @ConvertGroup can be used everywhere @valid can be used.
  • The conversion is declared by specifying 'to' and 'from' elements.
  • ConvertGroup.List can be used to define several group conversions.

Example

package com.logicbig.example;

import javax.validation.Valid;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import javax.validation.groups.ConvertGroup;

public class DriverLicense {
    @NotNull
    @Valid
    @ConvertGroup(from = LicenceNumberCheck.class,
            to = DriverPhysicalRequirement.class)
    private Driver driver;
    @Digits(integer = 7, fraction = 0, groups = LicenceNumberCheck.class)
    private int number;
    .............
}
package com.logicbig.example;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import java.util.Date;

public class Driver {
    @NotNull
    private String fullName;
    @Min(value = 100, groups = DriverPhysicalRequirement.class)
    private int height;
    @Past(groups = DriverPhysicalRequirement.class)
    @NotNull
    private Date dateOfBirth;
    .............
}
package com.logicbig.example;

public interface LicenceNumberCheck {
}
package com.logicbig.example;

public interface DriverPhysicalRequirement {
}

The main class

package com.logicbig.example;

import javax.validation.*;
import javax.validation.groups.Default;
import java.text.ParseException;
import java.util.Comparator;
import java.util.Date;
import java.util.Set;

public class ConvertGroupExample {

    public static void main(String[] args) throws ParseException {
        Driver driver = new Driver(null, 60,
                new Date(System.currentTimeMillis() + 100000));
        DriverLicense dl = new DriverLicense(driver, 3454343);

        Validator validator = createValidator();
        Set<ConstraintViolation<DriverLicense>> violations =
                validator.validate(dl, LicenceNumberCheck.class, Default.class);
        if (violations.size() == 0) {
            System.out.println("No violations.");
        } else {
            System.out.printf("%s violations:%n", violations.size());
            violations.stream()
                      .sorted(Comparator.comparing(o -> o.getPropertyPath().toString()))
                      .forEach(ConvertGroupExample::printError);
        }
    }

    public static void printError(ConstraintViolation<?> violation) {
        System.out.println(violation.getPropertyPath()
                + " " + violation.getMessage());
    }

    public static Validator createValidator() {
        Configuration<?> config = Validation.byDefaultProvider().configure();
        ValidatorFactory factory = config.buildValidatorFactory();
        Validator validator = factory.getValidator();
        factory.close();
        return validator;
    }
}

Output

3 violations:
driver.dateOfBirth must be a past date
driver.fullName must not be null
driver.height must be greater than or equal to 100

In above example, if we don't use @ConvertGroup then no validation will be performed in the Driver class.

Using default group:

package com.logicbig.example;

import javax.validation.*;
import java.text.ParseException;
import java.util.Comparator;
import java.util.Date;
import java.util.Set;

public class ConvertGroupDefaultExample {

    public static void main(String[] args) throws ParseException {
        Driver driver = new Driver(null, 60,
                new Date(System.currentTimeMillis() + 100000));
        DriverLicense dl = new DriverLicense(driver, 3454343);

        Validator validator = createValidator();
        Set<ConstraintViolation<DriverLicense>> violations = validator.validate(dl);
        if (violations.size() == 0) {
            System.out.println("No violations.");
        } else {
            System.out.printf("%s violations:%n", violations.size());
            violations.stream()
                      .sorted(Comparator.comparing(o -> o.getPropertyPath().toString()))
                      .forEach(ConvertGroupDefaultExample::printError);
        }
    }

    public static void printError(ConstraintViolation<?> violation) {
        System.out.println(violation.getPropertyPath()
                + " " + violation.getMessage());
    }

    public static Validator createValidator() {
        Configuration<?> config = Validation.byDefaultProvider().configure();
        ValidatorFactory factory = config.buildValidatorFactory();
        Validator validator = factory.getValidator();
        factory.close();
        return validator;
    }
}

Output

1 violations:
driver.fullName must not be null

Example Project

Dependencies 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 List
    ×

    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

@ConvertGroup Example Select All Download
  • convert-group-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • DriverLicense.java

    See Also