Annotations from javax.validation.constraints not working
Asked Answered
K

22

84

What configuration is needed to use annotations from javax.validation.constraints like @Size, @NotNull, etc.? Here's my code:

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

public class Person {
      @NotNull
      private String id;

      @Size(max = 3)
      private String name;

      private int age;

      public Person(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
      }
}

When I try to use it in another class, validation doesn't work (i.e. the object is created without error):

Person P = new Person(null, "Richard3", 8229));

Why doesn't this apply constraints for id and name? What else do I need to do?

Koroseal answered 6/1, 2012 at 10:56 Comment(2)
It doesn't work by magic, you need to configure a validator. What context is this code running in?Renata
Also refer this question and it's answer which avoids manually checking BindingResult for validation errors: #61993096Selfheal
H
74

For JSR-303 bean validation to work in Spring, you need several things:

  1. MVC namespace configuration for annotations: <mvc:annotation-driven />
  2. The JSR-303 spec JAR: validation-api-1.0.0.GA.jar (looks like you already have that)
  3. An implementation of the spec, such as Hibernate Validation, which appears to be the most commonly used example: hibernate-validator-4.1.0.Final.jar
  4. In the bean to be validated, validation annotations, either from the spec JAR or from the implementation JAR (which you have already done)
  5. In the handler you want to validate, annotate the object you want to validate with @Valid, and then include a BindingResult in the method signature to capture errors.

Example:

@RequestMapping("handler.do")
public String myHandler(@Valid @ModelAttribute("form") SomeFormBean myForm, BindingResult result, Model model) {
    if(result.hasErrors()) {
      ...your error handling...
    } else {
      ...your non-error handling....
    }
}
Highline answered 6/1, 2012 at 13:31 Comment(4)
This provides us with the default configuration of the validator. Now, I'd like to configure the validator before it being used. Where can I configure it ? Any overwrite to implement or setter to call ?Hump
The problem I experienced was I didn't realise that I needed to add @Valid to each member variable, which was also an object that contained validation constraints.Ingle
I have added @Valid to my RequestBody but still the constraint doesn't get invoked. I have checked the dependencies if there is any conflict. Resolved it as well. Not sure what they problem is. I have also checked of there is annotation applied to the the field. It is applied.Selfstyled
Strangely enough I also have all the points mentioned above fulfilled. Still the validation does not work :(Hoskinson
R
38

You should use Validator to check whether you class is valid.

Person person = ....;
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Set<ConstraintViolation<Person>> violations = validator.validate(person);

Then, iterating violations set, you can find violations.

Ruffian answered 6/1, 2012 at 11:2 Comment(2)
I think, this one is perfectly answered irrespective of framework being used. thanks.. but I have one doubt, how can we validate a parameter being passed to some method in context of without any framework. e.g. test(@NotNull String str), can we write validator for the same ?Gujarat
you can try @NonNull from lombook projectlombok.org/features/NonNullLeoine
O
14

I come here some years after, and I could fix it thanks to atrain's comment above. In my case, I was missing @Valid in the API that receives the Object (a POJO in my case) that was annotated with @Size. It solved the issue.

I did not need to add any extra annotation, such as @Valid or @NotBlank to the variable annotated with @Size, just that constraint in the variable and what I mentioned in the API...

Pojo Class:

...
@Size(min = MIN_LENGTH, max = MAX_LENGTH);
private String exampleVar;
...

API Class:

...
public void exampleApiCall(@RequestBody @Valid PojoObject pojoObject){
  ...
}

Thanks and cheers

Oomph answered 22/10, 2018 at 16:8 Comment(0)
C
11

In my case, I was using spring boot version 2.3.0. When I changed my maven dependency to use 2.1.3 it worked.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.3.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
    </dependency>
</dependencies>
Chkalov answered 12/5, 2020 at 19:25 Comment(4)
Starting from version 2.3.0.RELEASE Spring Boot Web and WebFlux starters no longer depend on the validation starter, so you have to add spring-boot-starter-validation to your pom.xml. For details check 2.3.0 Release notesMclean
@Mclean It didn't work for me. Do I need to download it or something?Tamathatamaulipas
@gabopushups No, all specified dependencies are downloaded automatically. If you still have the problem better post a new question with all the detailsMclean
@Mclean Thanks for your answer, you were right. I found the solution in another site: I only had to reload my code editor.Tamathatamaulipas
T
8

You would have to call a Validator on the Entity if you want to validate it. Then you will get a set of ConstraintViolationException, which basically show for which field/s of your Entity there is a constraint violation and what exactly was it. Maybe you can also share some of the code you expect to validate your entity.

An often used technique is to do validation in @PrePersist and rollback transaction if using multiple data modifications during transaction or do other actions when you get a validation exception.

Your code should go like this:

@PrePersist
public void prePersist(SomeEntity someEntity){
    Validator validator = Validation.buildDefaultValidatorFactory.getValidator();
    Set<ConstraintViolation<SomeEntity>> = validator.validate(someEntity);
    //do stuff with them, like notify client what was the wrong field, log them, or, if empty, be happy
}
Towhaired answered 6/1, 2012 at 11:8 Comment(0)
D
6

In my case the reason was the hibernate-validator version. Probably something is not supported in the newer version any more.

I changed:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>${hibernate-validator.version}</version>
</dependency>

I downgraded the version from 7.0.1.Final to 6.0.2.Final and this helped me.

Dehumidify answered 6/10, 2021 at 16:13 Comment(2)
After trying everything this was the only thing that worked! ThanksWeathers
Love you man, saved my day.Musky
L
5

You can also simply use @NonNull with the lombok library instead, at least for the @NotNull scenario. More details: https://projectlombok.org/api/lombok/NonNull.html

Lumbard answered 17/8, 2015 at 10:33 Comment(0)
S
4

After the Version 2.3.0 the "spring-boot-strarter-test" (that included the NotNull/NotBlank/etc) is now "sprnig boot-strarter-validation"

Just change it from ....-test to ...-validation and it should work.

If not downgrading the version that you are using to 2.1.3 also will solve it.

Saddlecloth answered 21/8, 2020 at 14:25 Comment(0)
J
4

I also faced the same problem. Javax annotations ( @NotNull, @Valid) were not performing any validation. Their presence was not making any difference.

I have to use 'springboot-starter-validation' dependency to make the javax validations effective. Here is the related dependencies configuration. Also don't miss to add @Valid annotation on the Object you want to validate.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.2</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
.....
.....
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>

    <dependency>
        <groupId>javax.validation</groupId>
        <artifactId>validation-api</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
 <dependencies>
Jablonski answered 26/7, 2021 at 5:39 Comment(0)
A
3

You need to add @Valid to each member variable, which was also an object that contained validation constraints.

Amling answered 25/5, 2017 at 10:46 Comment(0)
R
3

Recently I faced the same. I upgraded hibernate-validator to ver 7.x but later I noticed this release note

Hibernate Validator 7.0 is the reference implementation for Jakarta Bean Validation 3.0.
The main change is that all the dependencies using javax. packages are now using jakarta.* packages.
Upgrade to Hibernate Validator 7 is only recommended if you are moving to Jakarta EE 9.

My project should target java 8, so keeping javax.validation instead of switiching to jakarta.validation, I've had to downgrade to

<dependency>
  <groupId>org.hibernate.validator</groupId>
  <artifactId>hibernate-validator</artifactId>
  <version>6.0.2.Final</version>
</dependency>
Radioactive answered 8/11, 2021 at 23:0 Comment(0)
G
3

Just add the following in your pom.xml under tag if you are using Maven.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

For Gradle you can do the following.

dependencies {
  ...
  implementation 'org.springframework.boot:spring-boot-starter-validation'
}
Godhead answered 30/12, 2022 at 4:20 Comment(1)
Welcome to SO! Please take a look at the other answers that were given before. Your approach is mentioned there already. In order to keep the site clear and make it easy to find answers, we try to avoid double answers.Wellfound
P
2

in my case i had a custom class-level constraint that was not being called.

@CustomValidation // not called
public class MyClass {
    @Lob
    @Column(nullable = false)
    private String name;
}

as soon as i added a field-level constraint to my class, either custom or standard, the class-level constraint started working.

@CustomValidation // now it works. super.
public class MyClass {
    @Lob
    @Column(nullable = false)
    @NotBlank // adding this made @CustomValidation start working
    private String name;
}

seems like buggy behavior to me but easy enough to work around i guess

Panayiotis answered 17/7, 2016 at 18:44 Comment(0)
S
2

By default javax validation in spring works for Rest controller method input variables. But for other places to use the same we have to annotate class containing @Valid annotation with @Validated class level annotation.

I was facing same issue with kafka listener and after that I annotated it with @Validated it started working.

@Component
@Log4j2
@Validated
public class KafkaMessageListeners {

    @KafkaListener(topics = "message_reprocessor", errorHandler = "validationErrorHandler")
    public void processMessage(@Payload @Valid CustomPojo payload,
                               @Header(KafkaHeaders.OFFSET) List<Long> offsets, Acknowledgment acknowledgment) {

    }

}
Sidonia answered 13/4, 2022 at 15:23 Comment(0)
U
1

So @Valid at service interface would work for only that object. If you have any more validations within the hierarchy of ServiceRequest object then you might to have explicitly trigger validations. So this is how I have done it:

public class ServiceRequestValidator {

      private static Validator validator;

      @PostConstruct
      public void init(){
         validator = Validation.buildDefaultValidatorFactory().getValidator();
      }

      public static <T> void validate(T t){
        Set<ConstraintViolation<T>> errors = validator.validate(t);
        if(CollectionUtils.isNotEmpty(errors)){
          throw new ConstraintViolationException(errors);
        }
     }

}

You need to have following annotations at the object level if you want to trigger validation for that object.

@Valid
@NotNull
Unsettled answered 18/9, 2017 at 18:49 Comment(0)
S
0

for method parameters you can use Objects.requireNonNull() like this: test(String str) { Objects.requireNonNull(str); } But this is only checked at runtime and throws an NPE if null. It is like a preconditions check. But that might be what you are looking for.

Serles answered 10/10, 2018 at 19:23 Comment(0)
C
0

Great answer from atrain, but maybe better solution to catch exceptions is to utilize own HandlerExceptionResolver and catch

@Override
public ModelAndView resolveException(
    HttpServletRequest aReq, 
    HttpServletResponse aRes,
    Object aHandler, 
    Exception anExc
){
    // ....
    if(anExc instanceof MethodArgumentNotValidException) // do your handle     error here
}

Then you're able to keep your handler as clean as possible. You don't need BindingResult, Model and SomeFormBean in myHandlerMethod anymore.

Curator answered 17/1, 2019 at 12:21 Comment(0)
O
0

I came across this problem recently in a very similar situation: Met all requirements as the top-rated answer listed but still got the wrong result.

So I looked at my dependencies and found I was missing some of them. I corrected it by adding the missing dependencies.

I was using hibernate, the required dependencies were: Dependencies Snapshot

*Snapshot taken in class "Spring & Hibernate for Beginners" @ Udemy

Overcasting answered 1/10, 2019 at 5:10 Comment(0)
T
0

If you are using lombok then, you can use @NonNull annotation insted. or Just add the javax.validation dependency in pom.xml file.

Treatment answered 20/6, 2020 at 18:9 Comment(0)
B
0

For those who have not been able to perform server-side validation through Hibernate validation dependency. Just remove Hibernate validator +javax validation dependency and add spring-boot-starter validation. It provides Hibernate Validator Internally, and it worked just fine for me.

Credits:- a comment from youtube.

Bandoleer answered 19/7, 2021 at 7:51 Comment(0)
T
0

I fell into the same issue, but I got solution

your servlet configuration xml file i.e {servlet-name}-servlet.xml file

should be like

    <?xml version="1.0" encoding="UTF-8"?>
    
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">
    
       <context:component-scan base-package = "spring.tutorial.controller" />
       
->>>  Step 4: Add support for conversion, formatting and validation support
       
        <mvc:annotation-driven/>        
    
       <bean class = "org.springframework.web.servlet.view.InternalResourceViewResolver">
          <property name = "prefix" value = "/WEB-INF/views/" />
          <property name = "suffix" value = ".jsp" />
       </bean>
    
    </beans>

step 4 is important one

Tobolsk answered 31/8, 2022 at 13:15 Comment(0)
D
-5

In my case i removed these lines

1-import javax.validation.constraints.NotNull;

2-import javax.validation.constraints.Size;

3- @NotNull

4- @Size(max = 3)

Design answered 14/6, 2020 at 22:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.