How to use @ConvertGroup with @GroupSequenceProvider

I'm trying to use @ConvertGroup for some cascading validation with my spring boot project but it does not seem to work. Can anyone tell me what i am doing wrong?

I've created a trimmed down project for this question.

You can check it out here:

I have the following DTOs for the form:

The Parent Dto

@GroupSequenceProvider(ParentGroupSequenceProvider.class) public class ParentDto { @Valid @ConvertGroup(from= CreateChild.class , to = Creation.class) private ChildDto childDto; private boolean createChild; public ChildDto getChildDto() { return childDto; } public void setChildDto(ChildDto childDto) { this.childDto = childDto; } public boolean isCreateChild() { return createChild; } public void setCreateChild(boolean createChild) { this.createChild = createChild; } } 

From my understanding the ConvertGroup annotation should pass The CreationGroup in the child validation if the CreateGroup Group is present while validating the parent. (this group will be provided by the ParentGroupSequenceProvider.

And the child object:

public class ChildDto { @NotEmpty(groups = Creation.class) private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } 

If the Creation group is present the name cannot be null. I've tested this by adding @GroupSequence({ChildDto.class,Creation.class}) to the top of this class this resulted in a Validation error.

The Parent DTO has the folowing Group sequence provider:

public class ParentGroupSequenceProvider implements DefaultGroupSequenceProvider<ParentDto> { static Logger log = Logger.getLogger(ParentGroupSequenceProvider.class.getName()); @Override public List<Class<?>> getValidationGroups(ParentDto parentDto) { List<Class<?>> sequence = new ArrayList<Class<?>>(); /* * must be added to the returned list so that the validator gets to know * the default validation rules, at the very least. */ sequence.add(ParentDto.class); if (parentDto == null) return sequence; /* * Here, we can implement a certain logic to determine what are the additional group of rules * that must be applied. */ if(parentDto.isCreateChild()){ sequence.add(CreateChild.class); log.info("Added CreateChild to groups"); } return sequence; } } 

This sequence provider will add the creatChild group if the creation boolean is true.

I did test the groupSequenceProvider by adding a string property to the parentDto with the @NotEmpty(groups = CreateChild.class). This threw a validation error so I know the group is provided.

The controller mehtod:

@RequestMapping(value = "/test",method = RequestMethod.POST) public String doPost(@Valid ParentDto parentDto, BindingResult bindingResult){ if(bindingResult.hasErrors()){ bindingResult.getAllErrors().forEach( error-> log.error(error)); return "redirect: /error"; }else{ return "redirect: /"; } } 

The problem is when the form submits and createChild is true the name property in the childDto is not validated.

What did i miss?

Pom file below:

<?xml version="1.0" encoding="UTF-8"?> <project xmlns="" xmlns:xsi="" xsi:schemaLocation=" "> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>valid-testing</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>valid-testing</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-el</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 
2

1 Answer

Documentation of bean validation @GroupSequence states:

The Default group sequence overriding is local to the class it is defined on and is not propagated to associated objects. For the example this means that adding DriverChecks to the default group sequence of RentalCar would not have any effects. Only the group Default will be propagated to the driver association.

Note that you can control the propagated group(s) by declaring a group conversion rule

The same goes for @GroupSequenceProvider too. In your example, @GroupSequenceProvider only affects its target, ParentDto class and not ChildDto. Therefore ChildDto only sees Default group. So group conversion rule must be:

@Valid @ConvertGroup(from= Default.class , to = Creation.class) private ChildDto childDto; 

This solves the problem with current scenario but creates another issue: in another scenrio, when you validate ParentDto with Default group (when createChild is false), it still gets converted to Creation group for ChildDto. As result only validations annotated with groups = Creation.class get validated which I don't think is what you intend to do (in that scenario).

Generally, I don't recommend the way you currently are trying to validate your classes. Either use a Validator and manually call validate with different groups according to value of createChild field or write two different methods in ParentController (one for when child should be created and one for the other case) and use @Validated with suitable groups.

The first way is as follows:

public class ParentController{ @Autowired Validator validator; ... @RequestMapping(value = "/test",method = RequestMethod.POST) public String doPost(ParentDto parentDto, BindingResult bindingResult){ if(parentDto.isCreateChild()) { ValidationUtils.invokeValidator(validator, parentDto, bindingResult, Creation.class); } else { ValidationUtils.invokeValidator(validator, parentDto, bindingResult); } if(bindingResult.hasErrors()){ ... } } } 

and in ParentDto:

// No GroupSequenceProvider here public class ParentDto { @Valid private ChildDto childDto; ... } 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like