developer tip

Lombok @Builder 및 JPA 기본 생성자

optionbox 2020. 12. 15. 19:02
반응형

Lombok @Builder 및 JPA 기본 생성자


SpringData 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


업데이트 됨

피드백와 요한을 바탕으로 대답 나는 더 이상 사용에 대한 답변을 업데이트 한 @Tolerate@Data대신 우리는을 통해 접근 및 뮤 테이터를 작성 @Getter하고 @Setter,를 통해 기본 생성자를 생성 @NoArgsConstructor하고, 마지막으로 우리는 빌더를 통해 필요로하는 모든 인수 생성자를 만듭니다 @AllArgsConstructor.

빌더 패턴을 사용하고 싶기 때문에 생성자와 뮤 테이터 메소드의 가시성을 제한하고 싶다고 생각합니다. 이를 달성하기 위해 주석의 속성 주석 package privateaccess속성 통해 가시성을로 설정합니다 .@NoArgsConstructor@AllArgsConstructorvalue@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:

사용 @Toleratenoarg 생성자를 추가 할 수 있도록했다.

빌더 패턴을 사용하고 싶기 때문에 setter 메소드의 가시성을 제어하고 싶다고 생각합니다.

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

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.


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.

If the project uses SpringData with JPA, it can be solved using org.springframework.data.annotation.PersistenceConstructor (Spring annotation, not JPA!)

Then, in combination with Lombok, annotations will be like this:

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

For Lombok builder you also need to add:

@Builder
@AllArgsConstructor

Using @NoArgsConstructor and @AllArgsContructor will help solve the issue of having a default constructor with @Builder.

e.g

@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.

Here is nore explaination: https://github.com/rzwitserloot/lombok/issues/1389#issuecomment-369404719

ReferenceURL : https://stackoverflow.com/questions/34241718/lombok-builder-and-jpa-default-constructor

반응형