簡體   English   中英

Lombok @Builder 和 JPA 默認構造函數

[英]Lombok @Builder and JPA Default constructor

我正在將 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

更新

根據反饋和約翰的回答,我更新了不再使用@Tolerate@Data的答案,而是通過@Getter@Setter創建訪問器和@Setter器,通過@Getter創建默認構造@NoArgsConstructor ,最后我們創建所有參數構建器通過@AllArgsConstructor需要的構造@AllArgsConstructor

由於您想使用構建器模式,我想您想限制構造函數和修改器方法的可見性。 為了實現這一目標,我們將能見度設為package private通過access該屬性@NoArgsConstructor@AllArgsConstructor注釋和value的屬性@Setter注解。

重要的

請記住正確覆蓋toStringequalshashCode 有關詳細信息,請參閱 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 構造函數。

由於您想使用構建器模式,我想您想控制 setter 方法的可見性。

@Data注釋使生成的 setter public ,將@Setter(value = AccessLevel.PROTECTED)應用於字段使它們protected

請記住正確覆蓋toStringequalshashCode 有關詳細信息,請參閱 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解決(Spring注解,不是JPA!)

然后,結合 Lombok,注解會是這樣的:

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

對於 Lombok builder,您還需要添加:

@Builder
@AllArgsConstructor

要使用以下組合

  • 龍目島
  • 日本特許經營協會
    • 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;
}

使用@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

我使用所有這些注釋解決了這個問題:

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

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

檢查此問題以獲取更多詳細信息:

JPA @OnetoOne 自引用關系與兩列非空

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM