JPA/Hibernate Spring @Transactional 与 JOIN FETCH

JPA / Hibernate Spring @Transactional vs. JOIN FETCH

我正面临有趣的 LazyInitializationException 解决方案。 为了防止这种情况(在 OneToMany 或 ManyToMany 上),一个已知的解决方案是使用 JOIN FETCH 查询。 你可以看到她的几个例子之一:https://thoughts-on-java.org/best-practices-for-many-to-many-associations-with-hibernate-and-jpa/

其他更简单的解决方案是使用来自 Spring 的@Transactional。 例如像这样:

@DeleteMapping(value ="/product/{tagId}")
    @ResponseBody
    @Transactional
    public String deleteProductWithoutRelation(@PathVariable String product, Model model) {     
        Optional<Product> pr = productService.selectProduct(product);
        if (pr.isPresent()) {
            tag.get().getCustomer().size(); //usualy throws LazyInitializationException, 
                                            //without JOIN-FETCH Statment or @Transactional
        return deletedTagId;    
    }

当然,你也可以在repository service中放置一些方法的@Transactional,来封装这个方案。 那么这两种解决方案的优点或缺点是什么?

这里有几件事我们需要理清。

  1. @Transactional 意味着 Spring 确保打开数据库连接(+ 事务)并再次关闭它。就是这样。
  2. 当您 select 一个包含惰性字段的实体时,您实质上是在说:我正在 selecting "some" 来自我的实体的字段,惰性字段除外。
  3. 但是,如果您稍后需要该惰性字段,因为您试图在您的视图中访问它(.html、.ftl、.jsp,等等),您需要发布另一个 select 到数据库来获取它。
  4. 问题:此时,如果您在 @Transactional 方法外部,您将不再有打开的数据库连接,因此会出现 LazyInitException。
  5. 总结一下:您的提取确保为所有数据发出 1 select。如果你不这样做,你需要一个开放的数据库connection/transaction,这是@Transactional 给你的。

建议:您应该尝试使用适当的 JPQL/Criteria/SQL 语句获取呈现视图所需的所有数据,而不是依赖重新select 惰性字段太多了。