Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Data JPA - "could not initialize proxy - no Session" - With Methods marked as transactional

I have a model that has a pretty large graph of sub entities and hibernate ends up making around 9 statements to lazily fetch all of the data needed but about 4 levels deep I get a "could not initialize proxy - no Session" error and I am not sure why.

Controller

@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; } 

Presenter

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;   } } 

Plan Detail

@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; } 

Brochure Section

@Entity public class BrochureSection implements Serializable {   private static final long serialVersionUID = 1856191232387921427L;    (Column definitions) } 

Exception

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/:?] 

Any help would be appreciated.

like image 629
douglasrlee Avatar asked Apr 12 '16 20:04

douglasrlee


2 Answers

If you would like to keep Lazy Load and you are using Spring Boot, just add the config below in your application.properties:

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true 
like image 175
Joao Luiz Cadore Avatar answered Sep 22 '22 16:09

Joao Luiz Cadore


The lazy loading can be kept, without the setting the enable_lazy_load_no_trans parameter. The simplest solution I found was @NamedEntityGraph while using Spring Data JPA. https://www.baeldung.com/spring-data-jpa-named-entity-graphs

The downside was I could not have more than one collection in the @NamedEntityGraph. Adding a second collection resulted in an exception org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags:

Therefore, if you don't want to use the anti-pattern, and only have one collection you are trying to load, the @NamedEntityGraph and @EntityGrpah works with Spring Data JPA.

like image 41
Bill Snee Avatar answered Sep 19 '22 16:09

Bill Snee