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
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:
- le-meilleur-de façon à mettre en œuvre-est égal à-hashcode-et-tostring-avec-jpa-hibernate
- comment implémenter equals et hashcode-utilisant-le-jpa-entité-identificateur
- mise en veille prolongée-faits-equals et hashcode
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:
- le-meilleur-de façon à mettre en œuvre-est égal à-hashcode-et-tostring-avec-jpa-hibernate
- comment implémenter equals et hashcode-utilisant-le-jpa-entité-identificateur
- mise en veille prolongée-faits-equals et hashcode
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());
}
}
Vous pouvez aussi le résoudre explicitement avec @Builder @NoArgsConstructor @AllArgsConstructor
combinés sur la définition de la classe.
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
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.