19

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?

Daniel
  • 2,728
  • 3
  • 21
  • 33
  • 1
    I think you can use validation groups. Have all the validations other than the `@NotNull` on `machineName` (or a custom validator that compares the old and new name) be in the default group, and have the remaining validator be in the `Update` group. Use both groups in the `updateMyObject` method. See http://docs.jboss.org/hibernate/validator/5.0/reference/en-US/html/chapter-groups.html#d0e2595 – Eric Jablow Jul 04 '13 at 01:22

1 Answers1

14

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());
}
beat
  • 1,857
  • 1
  • 22
  • 36
CorayThan
  • 17,174
  • 28
  • 113
  • 161
  • 3
    Thanks! This lead me close enough to the answer - the first method didn't work, I did something similar to the second method: Set> errors = Validation.buildDefaultValidatorFactory().getValidator().validate(updatedObj); // I got to this from the documentation you pointed – Daniel Jul 04 '13 at 18:24
  • 1
    does the validateBook method throw any exceptions? what if validation fails? – Alexandros Kourtis Aug 30 '20 at 04:16
  • the first method doesn't initiate any validation. or am I missing something? – Happy Sep 30 '20 at 06:10
  • 1
    @Happy most probably you invoke the method from the same class so the validation method doesn't get proxied – Dmitry Senkovich Jan 29 '21 at 13:18