Home > Net >  Annotation @NotNull not working on value objects
Annotation @NotNull not working on value objects

Time:09-11

In one of my spring boot's @RestController methods I'm getting a json object in @RequestBody.

I want the fields in this object to be not null, that is they should always have non-blank value.

Based on many answers like these, I tried to use @NotNull, @NotEmpty, @JsonProperty(required = true) on fields, but it is not working.

Even if the attribute in JSON is not present, the field is receiving a default value.

My code:

RestController:

import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/api")
public class MyController {

    @PostMapping("/save-data")
    public void saveData( @RequestHeader("my-request-header") String myHeader, @RequestBody MyValueObject myValueObject) {
        service.saveData(myValueObject);
    }

}

POJO:

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import javax.validation.constraints.NotNull;


@Builder
@Getter
@Setter
@Data
@AllArgsConstructor
public class MyValueObject {

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    @NotNull
    private String id;

    @JsonProperty(value = "my_name")
    @NotNull
    private String name;

    @NotNull
    private int age;
}

So if in json request I'm passing only id & name, I'm still getting age as 0(zero).

CodePudding user response:

Primitive types do have default value which is what is getting set for age since it is int. Can you change it to Integer class.

CodePudding user response:

javax.validation.constraints.NotNull;

The notion of validation is generally what you apply to outside input. You have no control over that input directly; hence, the need to validate it before continuing.

Crucially, then, it should be possible for an object that models this input to represent invalid state. After all, invalid input can happen, and your code is supposed to perfectly represent such input. This input should then be deemed as invalid and rejected once you try to use it.

That means it should be possible to mark something as @NotNull (this particular NotNull annotation, which is specifically about validation, there are others that are not!), and still create an object where the value is null anyway. Such an object is not valid, and a validation framework will know it isn't, based on that annotation.

There are other takes on not null annotations. When you are using a class as a 'model' for defining an SQL database table, annotations exist that guide the creation of the CREATE TABLE statement, and one can 'guide': Please add an SQL NOT NULL constraint. That's a completely different concept entirely and yet those annotations are also called @NotNull.

A third concept is requesting code injection to inject a null check. This is what lombok's @NonNull annotation does. If you write:

void foo(@lombok.NonNull String hello) {
  System.out.println(hello);
}

then lombok turns that into:

void foo(@lombok.NonNull String hello) {
  if (hello == null) throw new NullPointerException("hello");
  System.out.println(hello);
}

And a fourth, yet again completely different take, is as a type system modifier. The notion 'the parameter to the foo method is a string reference that CANNOT be null. This take sounds identical to lombok's take, but it is in fact essentially the opposite. This code, is obviously silly and doesn't even compile, it's that silly:

void foo(String arg) {
  if (arg instanceof Integer) thow new IllegalArgumentException("Please give me a string and not an integer");
}

for the exact same reason, the lombok generated code is 'silly' if you deem that @NonNull to be a type modifier. It CANNOT be null as per the type analysis, so why are you checking for an impossibility?

Thus, this is a 4th 'take' and all 4 takes are completely different in what they mean and do. This last one doesn't "work" with vanilla java (which doesn't enforce non-nullity onto callers, the way java does enforce that you e.g. don't pass an Integer instance to a method like this, and you can't get around this enforcement in any way), but there are compiler plugins and the like that add it, and if you live in a world where all compilation uses these plugins, then the null check is, indeed, warning-to-error-level unneccessary.

To add fuel to this already considerable fire, java supports annotating parameters, fields, and methods. Java also supports annotating types. Some null annotations operate on p/f/ms, others operate on types, and yet again this causes significant differences in how to use them and what they actually end up causing to happen.

Given all that, hopefully you now understand why it is 'hard' to make a @NonNull or @NotNull annotation do what you want it to do. They are all different, and there are like 20 commonly used variants.

I noticed in your question you didn't actually properly describe what you want. Probably because you're not quite aware of all the various takes on what 'not null' can mean. You can use this answer as a guide so you know precisely what you do want and then read the documentation of the various null-annotation framework(s) you have available to you, and pick the correct one. Then apply that annotation, and it'll then do what you want it to do.

I want the fields in this object to be not null, that is they should always have non-blank value.

an int cannot be 'blank'. If you consider '0' to be 'blank', okay, you can of course add a constraint that they must not be 0, but @NonNull or @NotNull, any variant and any of the 20 takes out there on the concept, they'd all NOT do that. Because null and 0 are very different things. Various validation frameworks do have a 'not this specific int value' concept, you can use one of those.

  • Related