web-dev-qa-db-ja.com

Lombok @BuilderおよびJPA Defaultコンストラクター

私は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

57
krzakov

更新済み

フィードバックとジョンの answer に基づいて、@Tolerateまたは@Dataを使用しないように回答を更新し、代わりに@Getterおよび@Setterを介してアクセサーとミューテーターを作成し、デフォルトのコンストラクターを作成します@NoArgsConstructor、最後に、ビルダーが@AllArgsConstructorを介して必要とするすべての引数コンストラクターを作成します。

ビルダーパターンを使用したいので、コンストラクターとミューテーターメソッドの可視性を制限したいと思います。これを実現するために、package privateおよび@NoArgsConstructorアノテーションのaccess属性と、@AllArgsConstructorannotationのvalue属性を介して、可視性を@Setterに設定します。

重要

toStringequals、および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になります。

toStringequals、および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());
    }
}
62
Jeff

クラス定義で@Data @Builder @NoArgsConstructor @AllArgsConstructorを組み合わせて明示的に解決することもできます。

59

ここでは、注釈の順序が重要であるように思われます。同じ注釈を使用しますが、順序は異なり、コードを機能させることも、機能させないこともできます。

動作しない例を次に示します。

@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アノテーションを必ず最上部に配置してください。私の場合、アノテーションをアルファベット順にソートしたいため、このエラーが発生しました。

4
Karl.S

コンストラクターの注釈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
1
Pavel V

@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

0
Amrut Prabhu