Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I programmatically call the same validator that runs on a @RequestMethod method with a @Valid parameter in Spring?

I have a class with hibernate's validation annotation on some fields (such as @NotNull and @Size(min = 4, max = 50), etc...)

public class MyClass {

    Long id;

    @NotEmpty
    @Size(min = 4, max = 50)
    String machineName;

    @NotEmpty
    @Size(min = 4, max = 50)
    String humanName;

    // Getters, setters, etc…
}

I also have a custom controller that acts as a JSON API, and a JSON deserializer that creates MyClass objects when API methods are called. In my custom controller I have a method to create a new object of that type:

@RequestMapping(method = RequestMethod.POST)
public long createMyObject(@RequestBody @Valid MyClass newObj) {
    // Create the object in the database
    return newObj.getId();
}

and another method that updates an existing object

@RequestMapping(method = RequestMethod.PUT)
public void updateMyObject(@RequestBody MyClass updatedObj) {
    MyClass existingObj = // Get existing obj from DB by updatedObj.getId();

    // Do some secondary validation, such as making sure that a specific
    // field remains unchanged compared to the existing instance
    if (existingObj.getMachineName() != null && 
            !existingObj.getMachineName().equals(updatedObj.getMachineName())) {
        throw new CannotChangeMachineNameException();
    }
    else {
        updatedObj.setMachineName(existingObj.getMachineName());
    }

    // [HERE IS WHERE I WANT THE MAGIC TO HAPPEN]

    // Save updatedObj to the database
}

While I can use @Valid in createMyObject, I cannot use it in updateMyObject because our API implementation requires that machineName remains unchanged - users can call the API with a JSON object that either excludes machineName entirely or populate it with the same value that exists in the database.*

Before saving the updated object to the database I want to call the same validator that having the @Valid annotation would cause to be called. How can I find this validator and use it?

like image 637
Daniel Avatar asked Jul 04 '13 00:07

Daniel


People also ask

Which annotation is used for cascading validation from one bean to another bean?

The Bean Validation API does not only allow to validate single class instances but also complete object graphs (cascaded validation). To do so, just annotate a field or property representing a reference to another object with @Valid as demonstrated in Example 2.5, “Cascaded validation”.

What is @valid annotation in spring boot?

The @Valid annotation ensures the validation of the whole object. Importantly, it performs the validation of the whole object graph. However, this creates issues for scenarios needing only partial validation. On the other hand, we can use @Validated for group validation, including the above partial validation.


1 Answers

Nothing says you need to use @Valid in your controller methods only. Why not make a validation method that accepts a parameter you annotate as @Valid, then just return that same parameter.

Like this:

public Book validateBook(@Valid Book book) {
   return book;
}

Looks like an alternative would be to use Hibernate's validation package. Here's it's documentation.

Basically, you get a Validator from a ValidationFactory, and then use the validator like this:

 @Test
    public void manufacturerIsNull() {
        Car car = new Car(null, "DD-AB-123", 4);

        Set<ConstraintViolation<Car>> constraintViolations =
            validator.validate(car);

        assertEquals(1, constraintViolations.size());
        assertEquals("may not be null", constraintViolations.iterator().next().getMessage());
}
like image 76
CorayThan Avatar answered Oct 21 '22 08:10

CorayThan