title: Spring Data JPA - "could not initialize proxy - no Session" - With Methods marked as transactional
body: <p>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. </p>
  
  <p>Controller</p>
  
  <pre><code>@Transactional(readOnly = true)
  @RequestMapping(value = "/v2/plans", method = RequestMethod.GET)
  public @ResponseBody List&lt;PlanPresenter&gt; show(HttpServletRequest request) throws Exception {
    List&lt;PlanPresenter&gt; planPresenters = new ArrayList&lt;&gt;();
  
    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery&lt;Plan&gt; planQuery = criteriaBuilder.createQuery(Plan.class);
    Root&lt;Plan&gt; root = planQuery.from(Plan.class);
  
    if (request.getParameter("region") != null || request.getParameter("group") != null) {
      List&lt;Predicate&gt; criteria = new ArrayList&lt;Predicate&gt;();
      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&lt;Plan&gt;)query.getResultList()) {
        planPresenters.add(new PlanPresenter(plan));
      }
    }
  
    return planPresenters;
  }
  </code></pre>
  
  <p>Presenter</p>
  
  <pre><code>public class PlanPresenter {
    public String id;
    public String plan_set_id;
    public String region;
    public String name;
    public String description;
    public HashMap&lt;String, Object&gt; details = new HashMap&lt;String, Object&gt;();
  
    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());
    }
  }
  </code></pre>
  
  <p>Plan</p>
  
  <pre><code>@Entity
  public class Plan implements Serializable {
    private static final long serialVersionUID = 7639611964474770505L;
  
    private static List&lt;String&gt; 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&lt;Rider&gt; 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 &amp;&amp; !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 &amp;&amp; this.planDetail.brochureSections != null) {
        this.planDetail.brochureSections.forEach((brochureSection) -&gt; {
          if (brochureSection.type.trim().equals("P1") &amp;&amp; brochureSection.order == 1) {
            this.description = this.description + " " + brochureSection.text.trim();
          }
        });
      }
  
      return this.description.trim();
    }
  
    public List&lt;HashMap&lt;String, Object&gt;&gt; getRiders() {
      List&lt;HashMap&lt;String, Object&gt;&gt; riders = new ArrayList&lt;HashMap&lt;String, Object&gt;&gt;();
      if (this.riders != null &amp;&amp; this.riders.size() &gt; 0) {
        this.riders.forEach((rider) -&gt; {
          HashMap&lt;String, Object&gt; planRider = new HashMap&lt;String, Object&gt;();
          planRider.put("name", rider.getName());
          planRider.put("price", rider.getPrice());
          planRider.put("description", rider.getDescription());
          riders.add(planRider);
        });
      }
      return riders;
    }
  }
  </code></pre>
  
  <p>Plan Detail</p>
  
  <pre><code>@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&lt;BrochureSection&gt; brochureSections;
  }
  </code></pre>
  
  <p>Brochure Section</p>
  
  <pre><code>@Entity
  public class BrochureSection implements Serializable {
    private static final long serialVersionUID = 1856191232387921427L;
  
    (Column definitions)
  }
  </code></pre>
  
  <p>Exception</p>
  
  <pre><code>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.&lt;init&gt;(PlanPresenter.java:20) ~[classes/:?]
  at com.controllers.v2.PlansController.show(PlansController.java:64) ~[classes/:?]
  </code></pre>
  
  <p>Any help would be appreciated.</p>