【问题标题】:How to fix Lazy Load exception by having a spring context opened?如何通过打开弹簧上下文来修复延迟加载异常?
【发布时间】:2021-08-30 07:38:02
【问题描述】:

我遇到了org.hibernate.LazyInitializationException,原因在this question 中有很好的解释。我认为,我的代码与链接中引用的问题存在相同的问题。代码如下:

合同类:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor

public class Contract implements Serializable {

    @Id
    @Column(name = "uuid")
    private UUID uuid;

    @Column(name = "settlement_date")
    private LocalDate settlementDate;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "contract")
    private Set<ContractFile> files;
    
}

合同文件类:

@Entity
@NoArgsConstructor
@Getter
@Setter
public class ContractFile implements Serializable {

    @Id
    @Column(name = "uuid")
    private UUID uuid;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "contract_uuid")
    private Contract contract;

    public ContractFile(final Contract contract) {
        uuid = UUID.randomUUID();
        this.contract = contract;
    }

}

这就是我遇到麻烦的地方。这是一个 DTO,我需要通过从 ContractFile 对象中检索合同来从合同中获取结算日期。

@Getter
public class DTO {
    private String reference;

    public DTO(final ContractFile contractFile) {
        final Contract contract = contractFile.getContract();
        this.reference = "reference_" + receivable.getSettlementDate();
    }
}

据我了解,我在文件 this.reference = "reference_" + receivable.getSettlementDate(); 上得到了 org.hibernate.LazyInitializationException,因为由于缺少 Spring Context,合同对象一开始就没有正确初始化。如果这个假设是正确的,我该如何做到这一点,以便当我在这个 DTO 中执行 contractFile.getContract() 时,我打开了正确的代理?我已经看到了很多关于为什么我的代码不能工作以及它应该在哪个环境中工作的解释,但是没有示例代码,如果没有一个 repository 对象直接从数据库中检索合同,我想不出如何做到这一点。

编辑 1 这是在另一个类中使用 DTO 的方式,负责端点通信:

public String report(Set<DTO> dto) {
        return executePost(properties.getUri(), new DTOList(dto), String.class);
    }

编辑 2 对我来说,诀窍是将 @Transactional 设置为从数据库获取 ContractFiles 的方法。

//this transactional opens the context for all
//methods called from reportAll.
@Transactional 
public void reportAll() {
    final Set<ContractFile> contracts = contractFileService
            .findAll();
    contracts.forEach(this::report);
}


public void report(final ContractFile contractFile) {
    final String response = infraService.report(contractFile);
    log.info("Reported successfully");
}

【问题讨论】:

  • DTO 在哪里使用?
  • 在另一个类中,负责端点通信,如问题中所编辑。
  • LazyInitializationException 如果您尝试在无法检索到属性的位置访问 Lazy 属性(即没有可用的会话可通过询问 DB/Persistence 来检索该属性),则会发生。您的问题应该通过使用“OpenSessionInViewFilter”来解决。这应该如何完成取决于您的项目设置。

标签: java spring hibernate jpa


【解决方案1】:

正如链接的问题所解释的那样,错误很可能是因为您在单独的事务中获取 ContractEntity 的实例,虽然不需要合同,但由于懒惰而没有填充。

如果不愿意使用 eager,您仍然可以强制填充合约,但您需要在获取实体的事务上下文中执行此操作。

您可能有一段代码检索此实体并最终将其传递给 DTO 构造函数:

ContractFile cf = repo.findById(...);
// tx ends
...
DTO dto = new DTO(cf); // fails

现在,在 repo 调用调用合约 getter 之后的 tx 结束之前,比如

ContractFile cf = repo.findById(...);
Contract contract = contractFile.getContract();
// maybe do something else with it
// tx ends
DTO dto = new DTO(cf); // should now work    

但是,首先您可能需要检查代码流,因为它可能能够在同一事务中重构以避免这种黑客攻击。

【讨论】:

    【解决方案2】:

    发生这种情况是因为 DTO 中没有打开任何事务(对象分离)。无论您从 DB 调用 contractFile.getContract() 获取对象的位置,以便 ORM 框架加载延迟加载的对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-03-23
      • 2019-08-15
      • 2020-01-10
      • 2010-09-15
      • 2018-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多