0

I know there are already several questions on this.

I started a personal project on Spring Boot version 2.7.9 and wanted to use field level validation using @NotNull, @NotBlank, etc. After coding accordingly, it turned out the code is not able to detect the error. I came across this question and found that starter-validation is required to be explicitly mentioned. I added and restarted, but still it did not work. I scrolled down the same question and found one answer that IDE needs to be restarted as well. After doing so, it started working.

A couple of days later, I started working on it again. I added some business logic and some config like request logging. However, this time the annotation stopped working again.

I tried:

  • Removing starter-validation dependency and re-added, restarted IDE
  • Removing whole requestlogging component (using OncePerRequestFilter) just for peace of mind.
  • Checked a few more questions here. All talk about missing dependency only. From here, I added the two properties as well.

Nothing has worked so far. Did someone also face a similar issue?

Observation: I don't know if it helps or not, although code fails to detect the error, I do get ConstraintViolationException when it tries to persist the entity.

My Pom.xml: (dependencies section only)

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-envers</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-ui</artifactId>
        <version>1.6.15</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

Controller:

@PostMapping(value = "/create")
public ResponseEntity<MyDetailedResponse> createClient(
        @Valid @RequestBody MyRequest request) {
    //business logic
}

EDIT 1: Entity class as requested:

@Entity
@Audited
@Table(name = "client_details")
@JsonIgnoreProperties(value = { "optlock", "createdDate", "createdBy", "lastModifiedDate",
        "lastModifiedBy" }, ignoreUnknown = true)
public class AppClientDetails extends EntityVersionedMetaData {

    /**
     * 
     */
    private static final long serialVersionUID = 756964595629774899L;

    @NotBlank(message = "Name can not be blank")
    private String fullName;

    @NotBlank(message = "Please choose a short code for the client")
    private String shortCode;

    @NotBlank(message = "Address is mandatory.")
    @Size(min = 1, max = 255)
 // @Pattern(regexp = "^([a-zA-Z0-9.&:-\\S+]{1,255})$", message = "Invalid address. Only alphabets, number and . & : - are allowed")
    private String address;

    @NotBlank(message = "Contact person name is mandatory")
    private String contactPerson;

    private long phoneNumber;

    private String emailId;

    private String logoUrl;

    @NotNull(message = "Strength is required")
    private Short strength;

    @Temporal(TemporalType.DATE)
    private Date establishedDate;

    @Temporal(TemporalType.DATE)
    private Date onboardingDate;

    @NotBlank(message = "Plan must be selected")
    private String currentPlan;

    @Enumerated(EnumType.ORDINAL)
    private AppUserStatus clientStatus = AppUserStatus.ACTIVE;

   //getters setters
}

Just to add, I had commented @Pattern as it was throwing illegal character exception due to the \s in regex. That is also one issue.

EDIT 2: As last resort, I deleted whole .m2 directory and workspace both. Freshly created the whole project. Still it did not work.

Nayan J
  • 27
  • 9

2 Answers2

0

After trying so many things, I am finally able to resolve the issue. Along with the fields in entity, @Valid annotation at field name in main request pojo was also required.

My Main request POJO:

class MyPojo{
   // Other fields;
   @Valid
   private AppClientDetails clientDetails;
   // getters setters
}

This @Valid annotation here resolved the issue.

Nayan J
  • 27
  • 9
0

First of all, please let us know whether you are validating your request body(i.e MyRequst) or Entity(i.e AppClientDetails) or both. Not clear to me completely so providing you the answers for both

  1. Validating request body- In POST and PUT requests, it’s common to pass a JSON payload within the request body. Spring automatically maps the incoming JSON to a Java object. Now, we want to check if the incoming Java object meets our requirements. We should use spring-boot-starter-validation dependency that brings bean validation api. In request body(DTO), we use constraint annotations such as @NotNull etc. and In controller method, we use @Valid. Now Spring does validation and throws MethodArgumentNotValidException if validation fails.

  2. Validating JPA entity- Again, we have to use spring-boot-starter-validation. By default, Spring Data uses Hibernate underneath, which supports Bean Validation out of the box. Any time we use the repository to store the entity object whose constraint annotations are violated, we’ll get a ConstraintViolationException. Note that Bean Validation is only triggered by Hibernate once the EntityManager is flushed. Hibernate flushes the EntityManager automatically under certain circumstances. If we want to disable Bean Validation in our Spring Data repositories, we can set the Spring Boot property spring.jpa.properties.javax.persistence.validation.mode to none.

Note that the persistence layer should definitely not be the only location for validation. If validation fails here, it usually means that some sort of validation is missing in other application components. Persistence layer validation should be seen as the last line of defense. In addition to that, the persistence layer is usually too late for business related validation.

  • I am validating both MyRequest POJO and AppClientDetails. Thing is that AppClientDetails is a field of MyRequest class and in Controller class I am taking MyRequest as request body. While I added ```@NotNull``` , ```@NotBlank``` or ```@Pattern``` etc in applicable fields of MyRequest as well AppClientDetails, it turns out that ```@Valid``` annotation was also required on the AppClientDetails field in my POJO class. Adding that resolved the issue. – Nayan J Apr 07 '23 at 18:30
  • 1
    yes agreed, you have to use @Valid on nested objects as well if you want to validate them. Thanks – Akshay Tyagi Apr 07 '23 at 19:00