[英]Lombok @Builder and JPA Default constructor
I'm using project Lombok together with Spring Data JPA.我正在将 Lombok 项目与 Spring Data JPA 一起使用。 Is there any way to connect Lombok
@Builder
with JPA default constructor?有没有办法将 Lombok
@Builder
与 JPA 默认构造函数连接起来?
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.据我所知,JPA 需要由
@Builder
注释覆盖的默认构造函数。 Is there any workaround for that?有什么解决方法吗?
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
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
创建访问器和@Setter
器,通过@Getter
创建默认构造@NoArgsConstructor
,最后我们创建所有参数构建器通过@AllArgsConstructor
需要的构造@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 @Setter
annotation.为了实现这一目标,我们将能见度设为
package private
通过access
该属性@NoArgsConstructor
和@AllArgsConstructor
注释和value
的属性@Setter
注解。
Important重要的
Remember to properly override toString
, equals
, and hashCode
.请记住正确覆盖
toString
、 equals
和hashCode
。 See the following posts by Vlad Mihalcea for details:有关详细信息,请参阅 Vlad Mihalcea 的以下帖子:
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
可以允许添加 noarg 构造函数。
Since you want to use the builder pattern I imagine you want to control visibility of the setter methods.由于您想使用构建器模式,我想您想控制 setter 方法的可见性。
The @Data
annotation makes the generated setters public
, applying @Setter(value = AccessLevel.PROTECTED)
to the fields makes them protected
. @Data
注释使生成的 setter public
,将@Setter(value = AccessLevel.PROTECTED)
应用于字段使它们protected
。
Remember to properly override toString
, equals
, and hashCode
.请记住正确覆盖
toString
、 equals
和hashCode
。 See the following posts by Vlad Mihalcea for details:有关详细信息,请参阅 Vlad Mihalcea 的以下帖子:
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());
}
}
您还可以使用@Data @Builder @NoArgsConstructor @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.所以一定要把@Builder 注释放在最上面的位置,在我的例子中我遇到了这个错误,因为我想按字母顺序对注释进行排序。
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.如果同时使用构造函数上的lombok.Tolerate和某些属性上的javax.validation.constraints.NotNull注释,sonarqube 会将其标记为严重错误: PROPERTY 标记为“javax.validation.constraints.NotNull”但不是在此构造函数中初始化。
If the project uses SpringData with JPA, it can be solved using org.springframework.data.annotation.PersistenceConstructor (Spring annotation, not JPA!)如果项目使用带有JPA的SpringData,可以使用org.springframework.data.annotation.PersistenceConstructor解决(Spring注解,不是JPA!)
Then, in combination with Lombok, annotations will be like this:然后,结合 Lombok,注解会是这样的:
@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
For Lombok builder you also need to add:对于 Lombok builder,您还需要添加:
@Builder
@AllArgsConstructor
To use the following combination要使用以下组合
@EqualsAndHashCode
@EqualsAndHashCode
@Builder
and @With
@Builder
和@With
更改I used:我用了:
//Lombok & JPA
//https://stackoverflow.com/questions/34241718/lombok-builder-and-jpa-default-constructor
//Mandatory in conjunction with JPA: an equal based on fields is not desired
@lombok.EqualsAndHashCode(onlyExplicitlyIncluded = true)
//Mandatory in conjunction with JPA: force is needed to generate default values for final fields, that will be overriden by JPA
@lombok.NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
//Hides the constructor to force usage of the Builder.
@lombok.AllArgsConstructor(access = AccessLevel.PRIVATE)
@lombok.ToString
//Good to just modify some values
@lombok.With
//Mandatory in conjunction with JPA: Some suggest that the Builder should be above Entity - https://stackoverflow.com/a/52048267/99248
//Good to be used to modify all values
@lombok.Builder(toBuilder = true)
//final fields needed for imutability, the default access to public - since are final is safe
@lombok.experimental.FieldDefaults(makeFinal = true, level = AccessLevel.PUBLIC)
//no getters and setters
@lombok.Getter(value = AccessLevel.NONE)
@lombok.Setter(value = AccessLevel.NONE)
//JPA
@javax.persistence.Entity
@javax.persistence.Table(name = "PERSON_WITH_MOTTO")
//jpa should use field access
@javax.persistence.Access(AccessType.FIELD)
public class Person {
@javax.persistence.Id
@javax.persistence.GeneratedValue
//Used also automatically as JPA
@lombok.EqualsAndHashCode.Include
Long id;
String name;
String motto;
}
Using @NoArgsConstructor
and @AllArgsContructor
will help solve the issue of having a default constructor with @Builder
.使用
@NoArgsConstructor
和@AllArgsContructor
将有助于解决带有@Builder
的默认构造函数的问题。
eg例如
@Entity
@Builder
@NoArgsConstructor
@AllArgsContructor
class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
This is because @Builder
requires all argument constructor and specifying only a default constructor will cause an issue.这是因为
@Builder
需要所有参数构造函数,并且仅指定默认构造函数会导致问题。
Here is nore explaination: https://github.com/rzwitserloot/lombok/issues/1389#issuecomment-369404719这里没有解释: https : //github.com/rzwitserloot/lombok/issues/1389#issuecomment-369404719
i solved this using all these annotations:我使用所有这些注释解决了这个问题:
@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@NoArgsConstructor(access = AccessLevel.PACKAGE)
Jeff's answer works fine however, @Builder does not support self-reference relationships yet. Jeff 的回答很好,但是,@Builder 还不支持自引用关系。
Check this question for more details:检查此问题以获取更多详细信息:
JPA @OnetoOne self reference relationship with both columns non null JPA @OnetoOne 自引用关系与两列非空
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.