3

Lombok's @NonNull annotation interferes with javax.validation.constraints.NotNull annotation during validation in a spring boot app.

I am using Lombok in my JPA entities to drastically shorten the code in them (eliminate getters and setters, hashcode and equals, constructors, etc). I use the @NonNull annotation on each required field and then the @RequiredArgsConstructor annotation on top of the class to generate a constructor accepting only those fields (eliminates the need to send the entity's id, UUID and one-to-many Sets as null when creating the object).

The issue i have is that, since adding Lombok not too long ago, my original @NotNull annotation's message is being replaced by a generic Lombok message for the @NonNull annotation. Take a look at this field for example :

@Digits(integer = 5, fraction = 0, message = "The orders port must be a number from 1 to 65 535!")
@NotNull(message = "The orders port is required!")
@Min(value = 1, message = "The orders port must be a number from 1 to 65 535!")
@Max(value = 65535, message = "The orders port must be a number from 1 to 65 535!")
@Column(nullable = false)
@NonNull
private Integer ordersPort;

When i put nothing in this field, get this message :

Property ordersPort threw exception; nested exception is java.lang.NullPointerException: ordersPort is marked @NonNull but is null

Prior to adding Lombok, this was working fine and i was getting the messages i put above. Is there a way to keep using Lombok but somehow disable it from being considered during validation?

Thanks!

Martin
  • 1,977
  • 5
  • 30
  • 67
  • 2
    Can you show the imports section of the class containing `ordersPort` field? –  Jan 07 '19 at 14:18
  • The 2 relevant ones are import javax.validation.constraints.NotNull; and import lombok.NonNull; – Martin Jan 07 '19 at 14:22
  • @EugenCovaci `Not` vs `Non` it works as-is. – Karol Dowbecki Jan 07 '19 at 14:29
  • I never said the application didn't compile, it still works perfectly fine. Again, the validations produced my custom error message before adding Lombok but now i'm getting the Lombok generic NonNull message instead. I want to know if there is a way for the Valid mechanism to skip the Lombok NonNull annotation – Martin Jan 07 '19 at 14:31
  • Actually you get the error on bean instantiation, not on validation. See @KarolDowbecki answer. –  Jan 07 '19 at 14:35
  • Correct and it prevents the actual message i want from the NotNull annotation to display. I will try lowering the flagUsage property to warning – Martin Jan 07 '19 at 14:38

2 Answers2

0

Since lombok.NonNull will emit an if statement directly in the code, which later throws NullPointerException, there is not much you can do. javax.validation requires the bean to be constructed before it's being validated and Lombok will prevent it.

You could remove lombok.NonNull or decrease it to warning with lombok.nonNull.flagUsage=warning but this would allow you to create invalid objects that you would have to manually validate with javax.validation.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • 1
    Well, to be honest, the ONLY reason i use NonNull is to use RequiredArgsConstructor with it. I do not use the built in exception throwing mechanism and use purely javax.validation for the actual validating so this may be what i'm looking for! – Martin Jan 07 '19 at 14:36
  • 1
    Try marking field with `final` modifier instead of `lombok.NonNull`. The constructor still should have it but Lombok won't prevent `null` value. – Karol Dowbecki Jan 07 '19 at 14:39
  • won't that interfere with the getters and setters? – Martin Jan 07 '19 at 14:41
  • It will e.g. setter won't be possible since the field is `final`. – Karol Dowbecki Jan 07 '19 at 14:42
  • unfortunately marking one of the fields final makes the NoArgsConstructor annotation complain that that field may not have been initialized. I need that constructor in my entities because i do instantiate a blank entity to pass to my view when the user creates an entity. – Martin Jan 07 '19 at 14:47
  • furthermore, i think JPA itself needs a no arg constructor to work in the first place. – Martin Jan 07 '19 at 14:48
  • Question, my application does not have an application.properties file and i do every configuration programmatically (tomcat port, protocol, etc). Is there a way to set this flag programmatically via a bean in a configuration class? – Martin Jan 07 '19 at 14:51
  • `lombok.nonNull.flagUsage` goes into it's own `lombok.config` and is used only during Lombok code generation, see [docs](https://projectlombok.org/features/configuration) – Karol Dowbecki Jan 07 '19 at 14:54
  • Unfortunately, all that did was make Eclipse flag each annotation with a warning that says : Use of NonNull is flagged according to lombok configuration. I'm still having the same problem where it's somehow overriding NotNull during validation. – Martin Jan 07 '19 at 15:06
  • 1
    IMHO you're wrong concerning `flagUsage`. AFAIK it's mean for people wanting to use *only a subset of* Lombok features. All it does is to produce a warning for every usage of the given feature. The behavior does not change. – maaartinus Jan 07 '19 at 23:02
  • @Martin There's [`@NoArgsConstructor(force=true)`](https://projectlombok.org/api/lombok/NoArgsConstructor.html#force--). I'm afraid, you can use neither `@NonNull` nor `final` because of JPA. This makes the `@RequiredArgsConstructor` works just like the `@AllArgsConstructor`. `+++` What you need may be covered by [third paragraph of this proposal](https://github.com/rzwitserloot/lombok/wiki/FEATURE-IDEA:-Annotation-based-Include-Exclude-for-ToString,-EqualsAHC,-etc#more-ideas), but it may take long till it gets implemented, if ever. – maaartinus Jan 07 '19 at 23:08
  • realistically i think i might have to drop Lombok's constructor generation and make my own. I will still use it for getters and setters as well as equals and hashcode though. Shame. – Martin Jan 08 '19 at 12:19
0

So, I had a similar issue with Jackson and Lombok.

I needed javax validations to work when deserializing an input for my Controller but I also wanted to be able to create instances of my POJOs "by hand", using Lombok's validations. Since Lombok's validations were executed while building the objects, javax never had a chance to run validations since lombok's failed first.

My solution was the following:

import com.fasterxml.jackson.annotation.JsonCreator;
import javax.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

@Value
//Make immutable
public class MyObject {

    //javax annotations go here
    @NotBlank
    String fieldOne;

    //javax annotations go here
    @NotBlank
    String fieldTwo;


    //Lombok's validations go here. Lombok will create builder methods
    //for this parameters only and will rewrite this constructor to add 
    //validations
    @Builder
    private MyObject(@NonNull String fieldOne,
                     @NonNull String fieldTwo) {
        this.fieldOne = fieldOne;
        this.fieldTwo = fieldTwo;
    }

    //Constructor for jackson. No lombok validations here
    @JsonCreator
    private MyObject(String fieldOne,
                     String fieldTwo,
                     boolean jacksonHack) {
        this.fieldOne = fieldOne;
        this.fieldTwo = fieldTwo;
    }

}

Here, you have to make sure that javax validations are always processed after deserialization. Otherwise you will end up with an invalid object.

But it looks like it works.

user3013172
  • 1,637
  • 3
  • 15
  • 26