Spring Data JPA - "无法初始化代理 - 无会话" - 使用标记为事务的方法

dou*_*lee 27 java hibernate c3p0 spring-data-jpa spring-boot

我有一个模型有一个相当大的子实体图和hibernate最终制作了大约9个语句懒洋洋地获取所需的所有数据但大约4级深度我得到一个"无法初始化代理 - 没有会话"错误,我是不知道为什么.

调节器

@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;
}
Run Code Online (Sandbox Code Playgroud)

主持人

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());
  }
}
Run Code Online (Sandbox Code Playgroud)

计划

@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;
  }
}
Run Code Online (Sandbox Code Playgroud)

计划细节

@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;
}
Run Code Online (Sandbox Code Playgroud)

宣传册部分

@Entity
public class BrochureSection implements Serializable {
  private static final long serialVersionUID = 1856191232387921427L;

  (Column definitions)
}
Run Code Online (Sandbox Code Playgroud)

例外

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/:?]
Run Code Online (Sandbox Code Playgroud)

任何帮助,将不胜感激.

小智 96

如果您想保留Lazy Load并且使用Spring Boot,只需在application.properties中添加以下配置:

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
Run Code Online (Sandbox Code Playgroud)

  • 实际上这是一个反模式,有关进一步说明,请查看https://vladmihalcea.com/2016/09/05/the-hibernate-enable_lazy_load_no_trans-anti-pattern/ (21认同)
  • 这是有趣的信息,但问题更多的是为什么"@Transactional"不足以使交易保持开放状态. (7认同)
  • 肯定有另一种解决问题的方法吗?也许直接在jpql中查询查询? (4认同)
  • @Jules同意,并非所有的应用程序都需要最高的性能,但它是一个通常无效的解决方案的例子,并且存在高度适得其反的风险(这是维基百科https://en.wikipedia上的反模式的定义.组织/维基/反模式). (3认同)
  • 只是因为我很好奇.你在哪里找到这个? (2认同)
  • @darkled - 我认为“在使用它之前应该注意潜在的性能影响”和“这是一种反模式”之间实际上存在差异。并非每个应用程序都需要从其数据库访问代码中获得尽可能高的性能,有时不必花费数小时的开发人员时间来定义提前加载哪些属性以及哪些查询更重要。 (2认同)
  • 我和特里斯坦有同样的问题。\ @Transactional的合同将保持交易,从而保持会话开放,直到至少在\ @Transactional注释的方法结束之前。因此,不需要这种技巧,如果使用这种技巧,则可能导致第二笔交易开始,这是不希望的。 (2认同)
  • 它非常适合在存储库上选择断言实体属性的测试中使用:`@SpringBootTest(properties = "spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true")` (2认同)

小智 5

无需设置enable_lazy_load_no_trans参数即可保留延迟加载。我发现的最简单的解决方案是使用 Spring Data JPA 时的 @NamedEntityGraph。https://www.baeldung.com/spring-data-jpa-named-entity-graphs

缺点是 @NamedEntityGraph 中不能有多个集合。添加第二个集合导致异常org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags:

因此,如果您不想使用反模式,并且只尝试加载一个集合,则 @NamedEntityGraph 和 @EntityGrpah 可以与 Spring Data JPA 配合使用。


归档时间:

查看次数:

40940 次

最近记录:

9 年,6 月 前