Lombok @Builder and JPA Default constructor

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



Lombok @Builder and JPA Default constructor



I'm using project Lombok together with Spring Data JPA.
Is there any way to connect Lombok @Builder with JPA default constructor?


@Builder



Code:


@Entity
@Builder
class Person
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;



As far as I know JPA needs default constructor which is overriden by @Builder annotation. Is there any workaround for that?


@Builder



This code gives me error:
org.hibernate.InstantiationException: No default constructor for entity: : app.domain.model.Person


org.hibernate.InstantiationException: No default constructor for entity: : app.domain.model.Person





Try adding a @NoArgsConstructor projectlombok.org/api/lombok/NoArgsConstructor.html
– Robert Niestroj
Dec 12 '15 at 20:15


@NoArgsConstructor





try adding a no args constructor .AFAIK, @Builder will not override your no args constructor
– Ken Chan
Dec 12 '15 at 21:20


@Builder





Yeah but @Id is a required field. NoArgs doesnt gonna cut it
– krzakov
Dec 13 '15 at 10:50





I don't understand what you want. How can you have a noargs constructor that makes up values? @Id is either required or not. If it is, you need a constructor parameter, if not, you can use NoArgs. What am I missing here?
– Roel Spilker
Dec 14 '15 at 17:30




4 Answers
4



Updated



Based on the feedback and John's answer I have updated the answer to no longer use @Tolerate or @Data and instead we create accessors and mutators via @Getter and @Setter, create the default constructor via @NoArgsConstructor, and finally we create the all args constructor that the builder requires via @AllArgsConstructor.


@Tolerate


@Data


@Getter


@Setter


@NoArgsConstructor


@AllArgsConstructor



Since you want to use the builder pattern I imagine you want to restrict visibility of the constructor and mutators methods.
To achieve this we set the visibility to package private via the access attribute on the @NoArgsConstructor and @AllArgsConstructor annotations and the value attribute on the @Setterannotation.


package private


access


@NoArgsConstructor


@AllArgsConstructor


value


@Setter



Important



Remember to properly override toString, equals, and hashCode.
See the following posts by Vlad Mihalcea for details:


toString


equals


hashCode


package com.stackoverflow.SO34299054;

import static org.junit.Assert.*;

import java.util.Random;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import org.junit.Test;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@SuppressWarnings("javadoc")
public class Answer

@Entity
@Builder(toBuilder = true)
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@NoArgsConstructor(access = AccessLevel.PACKAGE)
@Setter(value = AccessLevel.PACKAGE)
@Getter
public static class Person

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

/*
* IMPORTANT:
* Set toString, equals, and hashCode as described in these
* documents:
* - https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
* - https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
* - https://vladmihalcea.com/hibernate-facts-equals-and-hashcode/
*/


/**
* Test person builder.
*/
@Test
public void testPersonBuilder()

final Long expectedId = new Random().nextLong();
final Person fromBuilder = Person.builder()
.id(expectedId)
.build();
assertEquals(expectedId, fromBuilder.getId());



/**
* Test person constructor.
*/
@Test
public void testPersonConstructor()

final Long expectedId = new Random().nextLong();
final Person fromNoArgConstructor = new Person();
fromNoArgConstructor.setId(expectedId);
assertEquals(expectedId, fromNoArgConstructor.getId());




Old Version using @Tolerate and @Data:


@Tolerate


@Data



Using @Tolerate worked to allow adding a noarg constructor.


@Tolerate



Since you want to use the builder pattern I imagine you want to control visibility of the setter methods.



The @Data annotation makes the generated setters public, applying @Setter(value = AccessLevel.PROTECTED) to the fields makes them protected.


@Data


public


@Setter(value = AccessLevel.PROTECTED)


protected



Remember to properly override toString, equals, and hashCode.
See the following posts by Vlad Mihalcea for details:


toString


equals


hashCode


package lombok.javac.handlers.stackoverflow;

import static org.junit.Assert.*;

import java.util.Random;

import javax.persistence.GenerationType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Data;
import lombok.Setter;
import lombok.experimental.Tolerate;

import org.junit.Test;

public class So34241718

@Builder
@Data
public static class Person

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Setter(value = AccessLevel.PROTECTED)
Long id;

@Tolerate
Person()

/* IMPORTANT:
Override toString, equals, and hashCode as described in these
documents:
- https://vladmihalcea.com/the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate/
- https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
- https://vladmihalcea.com/hibernate-facts-equals-and-hashcode/
*/


@Test
public void testPersonBuilder()

Long expectedId = new Random().nextLong();
final Person fromBuilder = Person.builder()
.id(expectedId)
.build();
assertEquals(expectedId, fromBuilder.getId());



@Test
public void testPersonConstructor()

Long expectedId = new Random().nextLong();
final Person fromNoArgConstructor = new Person();
fromNoArgConstructor .setId(expectedId);
assertEquals(expectedId, fromNoArgConstructor.getId());






I had the same question as krzakov and I solved it with your hint, using @Tolerate. Thanks for that Jeff. But is there any reason why you add the @Data annotation? Setters are not necessary in this case and @Data overwrites equal/hash/toString with the default behaviour, which can create problems.
– wollodev
Dec 29 '15 at 23:11



@Tolerate


@Data


@Data





Do not use @Data with entities.
– waste
Mar 15 '17 at 23:53


@Data





If this is concern is related to toString, equals and hashCode, I have added links to documents regarding proper implementation.
– Jeff
Sep 11 '17 at 15:29



You can also solve it explicit with @Builder @NoArgsConstructor @AllArgsConstructor combined on the class definition.


@Builder @NoArgsConstructor @AllArgsConstructor





This is better than the accepted answer. Avoid experimental features, kids.
– Michael
Jul 7 '17 at 15:23





Note, this does not automatically create accessor methods (getters).
– Jeff
Sep 11 '17 at 15:33




If the annotations lombok.Tolerate on constructor and javax.validation.constraints.NotNull on some property are used at the same time, sonarqube will mark it as a critical error: PROPERTY is marked "javax.validation.constraints.NotNull" but is not initialized in this constructor.



If the project uses SpringData with JPA, it can be solved using org.springframework.data.annotation.PersistenceConstructor (Spring annotation, not JPA!)



Then, in combination with Lombok, annotations will be like this:


@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))



For Lombok builder you also need to add:


@Builder
@AllArgsConstructor



It seems that the annotations order is important here, using the same annotations, but different orders, you can have the code working, or not.



Here is a non working example:


@AllArgsConstructor
@Builder
@Data
@Entity
@EqualsAndHashCode
@NoArgsConstructor
@RequiredArgsConstructor
@Table
@ToString
public class Person implements Serializable
private String name;



And this is a working example:


@Builder
@Data
@Entity
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Table
@ToString
public class Person implements Serializable
private String name;



So be sure to have the @Builder annotation at the very top position, in my case I encountered this error because I wanted to sort annotations alphabetically.






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

make 2 or more post in bootsrap

Store custom data using WC_Cart add_to_cart() method in Woocommerce 3

Firebase Auth - with Email and Password - Check user already registered