Ich verwende das Projekt Lombok zusammen mit Spring Data JPA. Gibt es eine Möglichkeit, Lombok @Builder
mit dem JPA-Standardkonstruktor zu verbinden ?
Code:
@Entity
@Builder
class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
Soweit ich weiß, benötigt JPA einen Standardkonstruktor, der durch @Builder
Anmerkungen überschrieben wird . Gibt es dafür eine Problemumgehung?
Dieser Code gibt mir Fehler:
org.hibernate.InstantiationException: No default constructor for entity: : app.domain.model.Person
java
spring
spring-data-jpa
lombok
krzakov
quelle
quelle
@NoArgsConstructor
projectlombok.org/api/lombok/NoArgsConstructor.html@Builder
überschreibt Ihren Konstruktor ohne Argumente nichtAntworten:
Aktualisiert
Basierend auf dem Feedback und John Antwort habe ich die Antwort auf nicht mehr zu verwenden aktualisiert
@Tolerate
oder@Data
stattdessen wir schaffen Zugriffs- und Mutatoren über@Getter
und@Setter
erstellen Sie den Standard - Konstruktor über@NoArgsConstructor
, und schließlich schaffen wir die alle args Konstruktor , dass der Builder erfordert über@AllArgsConstructor
.Da Sie das Builder-Muster verwenden möchten, stellen Sie sich vor, Sie möchten die Sichtbarkeit der Konstruktor- und Mutator-Methoden einschränken. Um dies zu erreichen setzen wir die Sichtbarkeit
package private
über dasaccess
Attribut auf die@NoArgsConstructor
und@AllArgsConstructor
Anmerkungen und dasvalue
Attribut auf der@Setter
Anmerkung.Wichtig
Denken Sie daran, richtig außer Kraft setzen
toString
,equals
undhashCode
. Siehe die folgenden Beiträge von Vlad Mihalcea für Details: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()); } }
Alte Version mit
@Tolerate
und@Data
:Verwenden von
@Tolerate
funktioniert, um das Hinzufügen eines Noarg-Konstruktors zu ermöglichen.Da Sie das Builder-Muster verwenden möchten, stellen Sie sich vor, Sie möchten die Sichtbarkeit der Setter-Methoden steuern.
Die
@Data
Annotation macht die generierten Setterpublic
, die Anwendung@Setter(value = AccessLevel.PROTECTED)
auf die Felder macht sieprotected
.Denken Sie daran, richtig außer Kraft setzen
toString
,equals
undhashCode
. Siehe die folgenden Beiträge von Vlad Mihalcea für Details: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()); } }
quelle
@Tolerate
. Danke dafür, Jeff. Aber gibt es einen Grund, warum Sie die@Data
Anmerkung hinzufügen ? Setter sind in diesem Fall nicht erforderlich und@Data
überschreiben gleich / hash / toString mit dem Standardverhalten, was zu Problemen führen kann.@Data
mit Entitäten verwenden.@Data
eine schlechte Idee mit Entitäten?@Data
verwendet standardmäßig alle Felder zum Generierenequals
undhashCode
Methoden, einschließlichid
. Einfaches Beispiel: Möglicherweise haben Sie vor und nach dem Speichern dieselbe Entitätsdarstellung, bei der es sich aus Java-Sicht um unterschiedliche Instanzen handelt (mit und ohne ID). Dies kann zu Verwirrung und Konsistenzproblemen führen. Sie können verwenden,@Data
wenn Sie diese Methoden überschreiben. Es gibt ein Kapitel von Hibernate-Dokumenten darüber: docs.jboss.org/hibernate/orm/5.3/userguide/html_single/…Sie können es auch explizit mit
@Data @Builder @NoArgsConstructor @AllArgsConstructor
kombiniert in der Klassendefinition lösen .quelle
@Data
Es scheint, dass die Reihenfolge der Anmerkungen hier wichtig ist, wenn dieselben Anmerkungen verwendet werden, aber unterschiedliche Reihenfolge. Sie können den Code verwenden oder nicht.
Hier ist ein nicht funktionierendes Beispiel:
@AllArgsConstructor @Builder @Data @Entity @EqualsAndHashCode @NoArgsConstructor @RequiredArgsConstructor @Table @ToString public class Person implements Serializable { private String name; }
Und das ist ein funktionierendes Beispiel:
@Builder @Data @Entity @EqualsAndHashCode @AllArgsConstructor @NoArgsConstructor @RequiredArgsConstructor @Table @ToString public class Person implements Serializable { private String name; }
Stellen Sie also sicher, dass sich die @ Builder-Annotation ganz oben befindet. In meinem Fall ist dieser Fehler aufgetreten, weil ich Annotationen alphabetisch sortieren wollte.
quelle
Wenn die Anmerkungen lombok.Tolerate on constructor und javax.validation.constraints.NotNull für einige Eigenschaften gleichzeitig verwendet werden, markiert sonarqube dies als kritischen Fehler: PROPERTY ist mit "javax.validation.constraints.NotNull" gekennzeichnet, jedoch nicht in diesem Konstruktor initialisiert.
Wenn das Projekt SpringData mit JPA verwendet, kann es mit org.springframework.data.annotation.PersistenceConstructor (Spring-Annotation, nicht JPA!) Gelöst werden.
In Kombination mit Lombok sehen die Anmerkungen dann folgendermaßen aus:
@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
Für Lombok Builder müssen Sie außerdem Folgendes hinzufügen:
@Builder @AllArgsConstructor
quelle
Um die folgende Kombination zu verwenden
@EqualsAndHashCode
@Builder
und@With
Ich benutzte:
//Lombok & JPA ///programming/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; }
quelle
Die Verwendung von
@NoArgsConstructor
und@AllArgsContructor
hilft bei der Lösung des Problems, einen Standardkonstruktor mit zu haben@Builder
.z.B
@Entity @Builder @NoArgsConstructor @AllArgsContructor class Person { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; }
Dies liegt daran,
@Builder
dass alle Argumentkonstruktoren erforderlich sind und die Angabe nur eines Standardkonstruktors ein Problem verursacht.Hier ist keine Erklärung: https://github.com/rzwitserloot/lombok/issues/1389#issuecomment-369404719
quelle
Ich habe dies mit all diesen Anmerkungen gelöst:
@Data @Builder @AllArgsConstructor(access = AccessLevel.PACKAGE) @NoArgsConstructor(access = AccessLevel.PACKAGE)
quelle
Jeffs Antwort funktioniert gut, @Builder unterstützt jedoch noch keine Selbstreferenzbeziehungen.
Überprüfen Sie diese Frage für weitere Details:
JPA @OnetoOne Selbstreferenzbeziehung mit beiden Spalten ungleich Null
quelle