Lombok @Builder和JPA Default构造函数

时间:2015-12-12 15:44:09

标签: java spring spring-data-jpa lombok

我将项目Lombok与Spring Data JPA一起使用。 有没有办法将Lombok @Builder与JPA默认构造函数连接?

代码:

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

据我所知,JPA需要默认构造函数,该构造函数被@Builder注释覆盖。有没有解决办法?

这段代码给了我错误: org.hibernate.InstantiationException: No default constructor for entity: : app.domain.model.Person

8 个答案:

答案 0 :(得分:58)

您也可以在类定义上结合使用@Builder @NoArgsConstructor @AllArgsConstructor来明确解决它。

答案 1 :(得分:56)

<强>更新

根据反馈和John的answer我已更新答案,不再使用@Tolerate@Data而是通过{创建访问者和变更器{1}}和@Getter,通过@Setter创建默认构造函数,最后我们通过@NoArgsConstructor创建构建器所需的all args构造函数。

由于你想使用构建器模式,我想你想要限制构造函数和mutators方法的可见性。 为实现此目的,我们通过@AllArgsConstructorpackage private注释的access属性以及@NoArgsConstructor上的@AllArgsConstructor属性将可见性设置为value注释

重要

请务必正确覆盖@SettertoStringequals。 有关详细信息,请参阅Vlad Mihalcea的以下帖子:

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()); } } @Tolerate

使用@Data可以添加noarg构造函数。

由于你想使用构建器模式,我想你想控制setter方法的可见性。

@Tolerate注释使生成的设置者@Datapublic应用于字段,使其成为@Setter(value = AccessLevel.PROTECTED)

请务必正确覆盖protectedtoStringequals。 有关详细信息,请参阅Vlad Mihalcea的以下帖子:

hashCode

答案 2 :(得分:4)

这里的注释顺序似乎很重要,使用相同的注释,但是使用不同的顺序,可以使代码正常工作。

这是一个无效的示例:

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

这是一个有效的示例:

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

因此,请务必将@Builder批注放在最高位置,在我的情况下,我遇到此错误,因为我想按字母顺序对批注进行排序。

答案 3 :(得分:2)

使用以下组合

  • 龙目岛
  • JPA
    • CRUD
    • 正确的@EqualsAndHashCode
  • 不变性-公共最终字段
  • 没有吸气剂
  • 没有二传手
  • 通过@Builder@With
  • 进行更改

我用过:

//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;
}

答案 4 :(得分:1)

如果构造函数上的注释 lombok.Tolerate 和某些属性上的 javax.validation.constraints.NotNull 同时使用,则sonarqube会将其标记为关键错误: PROPERTY标记为“javax.validation.constraints.NotNull”,但未在此构造函数中初始化。

如果项目使用SpringData和JPA,可以使用 org.springframework.data.annotation.PersistenceConstructor (Spring注释,而不是JPA!)来解决它。

然后,结合Lombok,注释将是这样的:

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

对于Lombok构建器,您还需要添加:

@Builder
@AllArgsConstructor

答案 5 :(得分:1)

我使用所有这些注释解决了这个问题:

@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@NoArgsConstructor(access = AccessLevel.PACKAGE)

答案 6 :(得分:1)

Jeff的回答很好用,但是@Builder还不支持自引用关系。

请查看此问题以获取更多详细信息:

JPA @OnetoOne self reference relationship with both columns non null

答案 7 :(得分:0)

使用@NoArgsConstructor@AllArgsContructor将有助于解决在@Builder中使用默认构造函数的问题。

例如

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

这是因为@Builder需要所有参数构造函数,而仅指定默认构造函数将导致问题。

这里没有解释:https://github.com/rzwitserloot/lombok/issues/1389#issuecomment-369404719