getters - lombok java example @data
Lombok @Builder y JPA Constructor predeterminado (5)
Estoy usando el proyecto Lombok junto con Spring Data JPA.
¿Hay alguna forma de conectar Lombok
@Builder
con el constructor predeterminado JPA?
Código:
@Entity
@Builder
class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
Hasta donde sé, JPA necesita un constructor predeterminado que se anula con la anotación
@Builder
.
¿Hay alguna solución para eso?
Este código me da el error:
org.hibernate.InstantiationException: No default constructor for entity: : app.domain.model.Person
El uso de
@NoArgsConstructor
y
@AllArgsContructor
ayudará a resolver el problema de tener un constructor predeterminado con
@Builder
.
p.ej
@Entity
@Builder
@NoArgsConstructor
@AllArgsContructor
class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
Esto se debe a que
@Builder
requiere todo constructor de argumentos y especificar solo un constructor predeterminado causará un problema.
Aquí hay más explicaciones: https://github.com/rzwitserloot/lombok/issues/1389#issuecomment-369404719
Parece que el orden de las anotaciones es importante aquí, usando las mismas anotaciones, pero diferentes órdenes, puede hacer que el código funcione o no.
Aquí hay un ejemplo que no funciona:
@AllArgsConstructor
@Builder
@Data
@Entity
@EqualsAndHashCode
@NoArgsConstructor
@RequiredArgsConstructor
@Table
@ToString
public class Person implements Serializable {
private String name;
}
Y este es un ejemplo de trabajo:
@Builder
@Data
@Entity
@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Table
@ToString
public class Person implements Serializable {
private String name;
}
Así que asegúrese de tener la anotación @Builder en la posición más alta, en mi caso encontré este error porque quería ordenar las anotaciones alfabéticamente.
Si las anotaciones lombok.Tolerate en constructor y javax.validation.constraints.NotNull en alguna propiedad se usan al mismo tiempo, sonarqube lo marcará como un error crítico: PROPIEDAD se marca "javax.validation.constraints.NotNull" pero no se inicializado en este constructor.
Si el proyecto usa SpringData con JPA, se puede resolver usando org.springframework.data.annotation.PersistenceConstructor (¡Anotación Spring, no JPA!)
Luego, en combinación con Lombok, las anotaciones serán así:
@RequiredArgsConstructor(onConstructor = @__(@PersistenceConstructor))
Para el constructor de Lombok también necesita agregar:
@Builder
@AllArgsConstructor
También puede resolverlo explícitamente con
@Data @Builder @NoArgsConstructor @AllArgsConstructor
combinados en la definición de clase.
Actualizado
Según los comentarios y la
answer
de John, actualicé la respuesta para que ya no use
@Tolerate
o
@Data
y, en su lugar, creamos
@Getter
y mutadores a través de
@Getter
y
@Setter
, creamos el constructor predeterminado a través de
@NoArgsConstructor
, y finalmente creamos todos los argumentos constructor que el constructor requiere a través de
@AllArgsConstructor
.
Como desea utilizar el patrón de construcción, imagino que desea restringir la visibilidad de los métodos de constructor y mutadores.
Para lograr esto, establecemos la visibilidad del
package private
través del atributo de
access
en las anotaciones
@NoArgsConstructor
y
@AllArgsConstructor
y el atributo de
value
en la anotación
@Setter
.
Importante
Recuerde anular correctamente
toString
,
equals
y
hashCode
.
Vea las siguientes publicaciones de Vlad Mihalcea para más detalles:
- the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate
- how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier
- hibernate-facts-equals-and-hashcode
package com..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());
}
}
Versión anterior usando
@Tolerate
y
@Data
:
Usar
@Tolerate
funcionó para permitir agregar un constructor noarg.
Como desea utilizar el patrón de creación, imagino que desea controlar la visibilidad de los métodos de establecimiento.
La anotación
@Data
hace
public
setters generados, aplicando
@Setter(value = AccessLevel.PROTECTED)
a los campos los
protected
.
Recuerde anular correctamente
toString
,
equals
y
hashCode
.
Vea las siguientes publicaciones de Vlad Mihalcea para más detalles:
- the-best-way-to-implement-equals-hashcode-and-tostring-with-jpa-and-hibernate
- how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier
- hibernate-facts-equals-and-hashcode
package lombok.javac.handlers.;
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());
}
}