Tengo un error usando Hibernate con SparkJava en el modo lazy-loading.
Se está trabajando correctamente sin SparkJava, pero cuando se utiliza SparkJava que está tratando de forzar con ganas de carga para una relación OneToMany.
- Modelo
@Entity
@Table(name = "KU_SUPPLIER")
public class Supplier {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@NotEmpty(message = "Please provide a name")
private String name;
@OneToMany(mappedBy = "supplier")
private List<Item> items; // Should be lazy-loaded
// Constructor / Getters / Setters
}
- DAO
public class SupplierDao implements Dao<Supplier> {
private final SessionFactory sessionFactory;
public SupplierDao(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
@SuppressWarnings("unchecked")
public List<Supplier> findAll() {
try (Session session = sessionFactory.openSession()) {
return session.createQuery("FROM com.seafrigousa.model.Supplier").getResultList();
}
}
}
- Principal
// Working perfectly and lazy-load Items as desired
supplierDao.findAll();
// The method will be called when a web browser goes to "localhost/suppliers"
// It throws org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: model.Supplier.items, could not initialize proxy - no Session
get("/suppliers", "application/json", supplierDao::findAll);
Revisé al no cerrar la sesión desde el DAO y vi que Hibernate se ejecuta la consulta como si estuviera en modo de carga Eager, por lo que estaba ejecutando dos casillas, una para el Proveedor y uno para el artículo.
¿Hay alguna razón para este comportamiento?
¡Gracias!
Supongo que aquí: get("/suppliers", "application/json", supplierDao::findAll);
objeto Proveedor está serializando en JSON. Items
campo no está marcado como excluidos de la serialización, así que conseguir su valor de inicialización perezosa causa de sesión (o redundand segunda consulta para los artículos, si la sesión no se cierra).
Si mi suposición es correcta, hacer su serializador ignorar campo de artículos o buscarlos en su consulta
session.createQuery("FROM com.seafrigousa.model.Supplier s join fetch s.items").getResultList();
Usando GSON como serializador dispone de los siguientes opciones:
@Expose
anotación en los campos que desee para serializar.@Entity @Table(name = "KU_SUPPLIER") public class Supplier { @Expose @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Expose @NotEmpty(message = "Please provide a name") private String name; @OneToMany(mappedBy = "supplier") private List<Item> items; // Should be lazy-loaded // Constructor / Getters / Setters }
Con después de la iniciación GSON
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
ExclusionStrategy con la anotación personalizada Fe
public class IgnoreFieldExclusionStrategy implements ExclusionStrategy { @Override public boolean shouldSkipField(FieldAttributes fieldAttributes) { return fieldAttributes.getAnnotation(GsonIgnore.class) != null; } @Override public boolean shouldSkipClass(Class<?> aClass) { return false; } }
con la anotación personalizada
@GsonIgnore
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface GsonIgnore {}
y GSON iniciación
Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new IgnoreFieldExclusionStrategy()).create();
su clase sería el siguiente
@Entity @Table(name = "KU_SUPPLIER") public class Supplier { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @NotEmpty(message = "Please provide a name") private String name; @GsonIgnore @OneToMany(mappedBy = "supplier") private List<Item> items; // Should be lazy-loaded // Constructor / Getters / Setters }
Si usted tendría necesidad serializar Supplier
con items
en diferentes API, puede crear el objeto DTO para Supplier
y asignarla a resultados como este:
package com.seafrigousa.dto
public class SupplierDTO {
private int id;
private String name;
public SupplierDTO(int id, String name) {
this.id = id;
this.name = name;
}
// Getters / Setters
}
y consulta:
session.createQuery("select new com.seafrigousa.dto.SupplierDTO(s.id, s.name) FROM com.seafrigousa.model.Supplier s").getResultList();