Lombok @Builder et JPA Default constructor

j'utilise le projet Lombok avec Spring Data JPA. Est-il possible de se connecter Lombok @Builder avec JPA default constructor?

Code:

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

autant que je sache, JPA a besoin du constructeur par défaut qui est remplacé par @Builder annotation. Est-il une solution de contournement pour qui?

ce code me donne une erreur: org.hibernate.InstantiationException: No default constructor for entity: : app.domain.model.Person

36
demandé sur krzakov 2015-12-12 18:44:09

4 réponses

mise à Jour

sur la Base de la rétroaction et de Jean réponse j'ai mis à jour la réponse à ne plus utiliser @Tolerate ou @Data et à la place Nous créons des accesseurs et des mutants via @Getter et @Setter, créez le constructeur par défaut à l'aide de @NoArgsConstructor, et finalement nous créons le constructeur all args dont le constructeur a besoin via @AllArgsConstructor.

puisque vous voulez utiliser le modèle builder j'imagine que vous voulez restreindre la visibilité du constructeur et des mutateurs méthodes. Pour ce faire, nous définissons la visibilité à package private via le access attribut @NoArgsConstructor et @AllArgsConstructor annotations et le value attribut @Setter annotation.

Important

N'oubliez pas d'annuler correctement toString,equals et hashCode. Voir les articles suivants de Vlad Mihalcea pour détails:

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

ancienne Version utilisant @Tolerate et @Data:

en utilisant @Tolerate travaillé pour permettre l'ajout d'un noarg constructeur.

Puisque vous voulez utiliser le générateur de modèle j'imagine que vous voulez contrôler la visibilité des méthodes de définition.

@Data annotation rend les setters générés public, de l'application @Setter(value = AccessLevel.PROTECTED) pour les champs rend protected.

N'oubliez pas d'annuler correctement toString,equals et hashCode. Voir les articles suivants de Vlad Mihalcea pour détails:

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());
    }
}
37
répondu Jeff 2018-01-04 11:43:03

Vous pouvez aussi le résoudre explicitement avec @Builder @NoArgsConstructor @AllArgsConstructor combinés sur la définition de la classe.

47
répondu John John Pichler 2016-02-24 12:28:59

Si les annotations lombok.Tolérer sur le constructeur et l' javax.validation.contraintes.NotNull sur certaines propriétés sont utilisés en même temps, sonarqube le marquera comme une erreur critique: la propriété est marquée " javax.validation.contraintes.NotNull " mais n'est pas initialisé dans ce constructeur.

si le projet utilise SpringData avec JPA, il peut être résolu en utilisant org.springframework.données.annotation.PersistenceConstructor (Annotation de printemps, pas JPA!)

puis, en combinaison avec Lombok, les annotations seront comme ceci:

@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
@Builder
@AllArgsConstructor
1
répondu Pavel V 2017-05-31 22:23:47

il semble que l'ordre des annotations soit important ici, en utilisant les mêmes annotations, mais des ordres différents, vous pouvez faire fonctionner le code, ou pas.

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

Et c'est un exemple:

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

alors assurez-vous d'avoir l'annotation @Builder à la position très haut, dans mon cas j'ai rencontré cette erreur parce que je voulais trier les annotations par ordre alphabétique.

1
répondu Karl.S 2018-08-27 23:45:07