私はSpring Data JPAと一緒にプロジェクトLombokを使用しています。 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
更新済み
フィードバックとジョンの answer に基づいて、@Tolerate
または@Data
を使用しないように回答を更新し、代わりに@Getter
および@Setter
を介してアクセサーとミューテーターを作成し、デフォルトのコンストラクターを作成します@NoArgsConstructor
、最後に、ビルダーが@AllArgsConstructor
を介して必要とするすべての引数コンストラクターを作成します。
ビルダーパターンを使用したいので、コンストラクターとミューテーターメソッドの可視性を制限したいと思います。これを実現するために、package private
および@NoArgsConstructor
アノテーションのaccess
属性と、@AllArgsConstructor
annotationのvalue
属性を介して、可視性を@Setter
に設定します。
重要
toString
、equals
、およびhashCode
を適切にオーバーライドすることを忘れないでください。詳細については、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());
}
}
@Tolerate
および@Data
を使用した古いバージョン:
@Tolerate
を使用すると、noargコンストラクターを追加できるようになりました。
ビルダーパターンを使用したいので、セッターメソッドの可視性を制御したいと思います。
@Data
アノテーションは、生成されたセッターをpublic
にし、フィールドに@Setter(value = AccessLevel.PROTECTED)
を適用すると、それらがprotected
になります。
toString
、equals
、およびhashCode
を適切にオーバーライドすることを忘れないでください。詳細については、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
を組み合わせて明示的に解決することもできます。
ここでは、注釈の順序が重要であるように思われます。同じ注釈を使用しますが、順序は異なり、コードを機能させることも、機能させないこともできます。
動作しない例を次に示します。
@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アノテーションを必ず最上部に配置してください。私の場合、アノテーションをアルファベット順にソートしたいため、このエラーが発生しました。
コンストラクターの注釈lombok.Tolerateおよびjavax.validation.constraints.NotNullいくつかのプロパティで同時に使用される場合、sonarqubeはそれを重大なエラーとしてマークします。PROPERTYは「javax.validation.constraints.NotNull」とマークされますが、このコンストラクタでは初期化されません。
プロジェクトがJPAでSpringDataを使用している場合、org.springframework.data.annotation.PersistenceConstructor(JPAではなく、Springアノテーション!)を使用して解決できます。
次に、Lombokと組み合わせて、注釈は次のようになります。
@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
Lombokビルダーの場合、以下も追加する必要があります。
@Builder
@AllArgsConstructor
@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