douglasrlee
Tengo un modelo que tiene un gráfico bastante grande de subentidades e hibernación termina haciendo alrededor de 9 declaraciones para obtener de forma perezosa todos los datos necesarios, pero aproximadamente 4 niveles de profundidad aparece el error “no se pudo inicializar el proxy – no hay sesión” y estoy No estoy seguro de por qué.
Controlador
@Transactional(readOnly = true)
@RequestMapping(value = "/v2/plans", method = RequestMethod.GET)
public @ResponseBody List<PlanPresenter> show(HttpServletRequest request) throws Exception {
List<PlanPresenter> planPresenters = new ArrayList<>();
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Plan> planQuery = criteriaBuilder.createQuery(Plan.class);
Root<Plan> root = planQuery.from(Plan.class);
if (request.getParameter("region") != null || request.getParameter("group") != null) {
List<Predicate> criteria = new ArrayList<Predicate>();
if (request.getParameter("region") != null) {
criteria.add(criteriaBuilder.equal(root.get(Plan_.region), request.getParameter("region")));
}
if (request.getParameter("group") != null) {
criteria.add(criteriaBuilder.equal(root.get(Plan_.groupCode), request.getParameter("group")));
criteria.add(root.get(Plan_.planSetId).in(groupPlanSetIds));
} else {
criteria.add(root.get(Plan_.planSetId).in(currentPlanSetIds));
}
Query query = entityManager.createQuery(planQuery.where(criteriaBuilder.and(criteria.toArray(new Predicate[]{}))));
for (Plan plan : (List<Plan>)query.getResultList()) {
planPresenters.add(new PlanPresenter(plan));
}
}
return planPresenters;
}
Presentador
public class PlanPresenter {
public String id;
public String plan_set_id;
public String region;
public String name;
public String description;
public HashMap<String, Object> details = new HashMap<String, Object>();
public PlanPresenter(Plan plan) throws Exception {
this.id = String.valueOf(plan.id);
this.plan_set_id = String.valueOf(plan.planSetId);
this.region = plan.region.trim();
this.name = plan.getName();
this.description = plan.getDescription();
this.details.put("spanish_plan", plan.isSpanishPlan());
this.details.put("mutually_exclusive", plan.isMutuallyExclusive());
this.details.put("group_plan", plan.isGroupPlan());
this.details.put("group_code", plan.groupCode.trim());
this.details.put("family_plan", plan.isFamilyPlan());
this.details.put("price", plan.getPrice());
this.details.put("enrollment_fee", plan.getEnrollmentFee());
this.details.put("riders", plan.getRiders());
}
}
Plan
@Entity
public class Plan implements Serializable {
private static final long serialVersionUID = 7639611964474770505L;
private static List<String> familyPlanShortNames = Arrays.asList("ABCD");
@Transient
private String description = "";
(Column definitions)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "XXXX", insertable = false, updatable = false, nullable = true)
@NotFound(action = NotFoundAction.IGNORE)
public PlanDetail planDetail;
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "XXXX", insertable = false, updatable = false, nullable = true)
@OrderBy("XXXX")
@NotFound(action = NotFoundAction.IGNORE)
public List<Rider> riders;
public String getName() {
return this.planDetail != null ? this.planDetail.longName.trim() : null;
}
public Boolean isSpanishPlan() {
return this.language.trim().equals("ES");
}
public Boolean isMutuallyExclusive() {
return this.mutuallyExclusive.trim().equals("Y");
}
public Boolean isGroupPlan() {
return this.groupCode != null && !this.groupCode.trim().equals("");
}
public Boolean isFamilyPlan() {
return familyPlanShortNames.contains(this.planDetail.shortName.trim());
}
public BigDecimal getPrice() {
return this.planDetail != null ? this.planDetail.price.setScale(2) : null;
}
public BigDecimal getEnrollmentFee() {
return this.planDetail != null ? this.planDetail.enrollmentFee.setScale(2) : null;
}
public String getDescription() {
if (this.planDetail != null && this.planDetail.brochureSections != null) {
this.planDetail.brochureSections.forEach((brochureSection) -> {
if (brochureSection.type.trim().equals("P1") && brochureSection.order == 1) {
this.description = this.description + " " + brochureSection.text.trim();
}
});
}
return this.description.trim();
}
public List<HashMap<String, Object>> getRiders() {
List<HashMap<String, Object>> riders = new ArrayList<HashMap<String, Object>>();
if (this.riders != null && this.riders.size() > 0) {
this.riders.forEach((rider) -> {
HashMap<String, Object> planRider = new HashMap<String, Object>();
planRider.put("name", rider.getName());
planRider.put("price", rider.getPrice());
planRider.put("description", rider.getDescription());
riders.add(planRider);
});
}
return riders;
}
}
Detalle del plano
@Entity
public class PlanDetail implements Serializable {
private static final long serialVersionUID = 2256881691562712018L;
(Column definitions)
@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "XXXX", referencedColumnName = "XXXX", insertable = false, updatable = false, nullable = true)
@OrderBy("XXXX")
@NotFound(action = NotFoundAction.IGNORE)
public List<BrochureSection> brochureSections;
}
Sección del folleto
@Entity
public class BrochureSection implements Serializable {
private static final long serialVersionUID = 1856191232387921427L;
(Column definitions)
}
Excepción
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.models.PlanDetail.brochureSections, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:576) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:215) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:555) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:143) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]
at java.lang.Iterable.forEach(Iterable.java:74) ~[?:1.8.0_66]
at com.models.Plan.getDescription(Plan.java:100) ~[classes/:?]
at com.presenters.v2.PlanPresenter.<init>(PlanPresenter.java:20) ~[classes/:?]
at com.controllers.v2.PlansController.show(PlansController.java:64) ~[classes/:?]
Cualquier ayuda sería apreciada.
Si desea mantener Carga lenta y está utilizando Spring Boot, simplemente agregue la configuración a continuación en su aplicación.propiedades:
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
-
Solo porque tengo curiosidad. ¿Dónde encontraste esto?
– douglasrlee
30 de agosto de 2016 a las 19:14
-
en realidad es un anti-patrón, para una explicación más detallada, eche un vistazo a vladmihalcea.com/2016/09/05/…
– oscurecido
2 mayo 2017 a las 13:50
-
@Jules estuvo de acuerdo, no todas las aplicaciones necesitan el mayor rendimiento, pero es un ejemplo de una solución que generalmente es ineficaz y corre el riesgo de ser altamente contraproducente (que es la definición de antipatrones en wikipedia en.wikipedia.org/wiki/Anti-patrón).
– oscurecido
7 de agosto de 2017 a las 13:22
-
seguramente hay otra forma de solucionar el problema? tal vez escriba directamente en jpql la consulta?
– robert trudel
18/10/2017 a las 20:52
-
Esta es información interesante, pero la pregunta es más sobre por qué “@Transactional” no es suficiente para mantener la transacción abierta en mi opinión.
– Tristán
2 de julio de 2018 a las 15:27
La carga diferida se puede mantener, sin configurar el parámetro enable_lazy_load_no_trans. La solución más simple que encontré fue @NamedEntityGraph mientras usaba Spring Data JPA. https://www.baeldung.com/spring-data-jpa-named-entity-graphs
La desventaja era que no podía tener más de una colección en @NamedEntityGraph. Agregar una segunda colección resultó en una excepción org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags:
Por lo tanto, si no desea utilizar el antipatrón y solo intenta cargar una colección, @NamedEntityGraph y @EntityGrpah funcionan con Spring Data JPA.
-
Bueno, de hecho puede evitar la excepción multipleBagException usando Conjuntos en lugar de Colecciones o Listas…
– usuario1006641
26 de noviembre de 2020 a las 13:47
-
Usar conjuntos en lugar de listas es una mala idea según vladmihalcea.com/hibernate-multiplebagfetchexception
– Wim Deblauwe
22 oct 2021 a las 9:45
chamán jainista
agregando @Transactional
me funciona el metodo over
Tuve la misma excepción y el problema era que tenía una entidad en la que se perdió el mapeo
@Entity
Class A {
}
@Entity
Class B {
private A a;
}
Solución
@Entity
Class B {
@OneToOne
@MapsId
private A a;
}
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
es un anti-patrón y recomendamos encarecidamente evitar su uso.
could not initialize proxy
la excepción ocurrirá a menudo cuando la clase secundaria contiene
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
en tu relación
recomiendo usar fetch = FetchType.EAGER
en su relación en lugar de usar LAZY
. No es la mejor manera, pero es mucho mejor que usar antipatrones.
-
FetchType.EAGER funcionó para mí. Creo que su respuesta debería obtener más votos, es fácil hablar sobre el antipatrón, pero en realidad proporcionó una alternativa, ¡gracias!
– chriszichrisz
29 de junio de 2022 a las 21:07
-
FetchType.EAGER funcionó para mí. Creo que su respuesta debería obtener más votos, es fácil hablar sobre el antipatrón, pero en realidad proporcionó una alternativa, ¡gracias!
– chriszichrisz
29 de junio de 2022 a las 21:07
La paz del código ayudaría. Enfoque simple que lo ayudaría a devolver el objeto VO del servicio o cambiar el FetchType.
– Antón N.
12/04/2016 a las 20:26
Entonces, si cambio el tipo de recuperación a EAGER, puedo hacer que funcione, PERO realmente no quiero hacerlo por razones de rendimiento.
– douglasrlee
12 abr 2016 a las 20:44
Según el error y el código si falla en la sección del folleto. ¿Puedes agregar hibernate.instance(this.plandetail.getbrochure()) en tu método getdescription(). Como este atributo está cargado de forma diferida, su código en obtener descripción no puede encontrarlo, por lo que para usarlo primero debe cargarlo y hacer lo que quiera con él. Por favor, avíseme si esto ayuda. Solo para señalar que también soy nuevo en hibernación y encontré este error personalmente y pude resolverlo de esta manera
– Fase de aprendizaje
12/04/2016 a las 21:29
¿Cómo obtengo la variable de hibernación en ese modelo?
– douglasrlee
12/04/2016 a las 21:31
Solución rápida:
myEntity.getListOfThings().size();
Forzar a JPA a inicializar la colección.– Antón N.
13 de abril de 2016 a las 11:56