解决懒惰初始化通过无知的异常

2022-09-03 07:34:24

这里有无数的问题,如何通过急切获取,保持交易打开,打开另一个交易等等来解决“无法初始化代理”的问题。OpenEntityManagerInViewFilter

但是,是否可以简单地告诉Hibernate忽略问题并假装集合是空的?就我而言,之前不获取它只是意味着我不在乎。

这实际上是以下 Y 的 XY 问题:

我正在上课,比如

class Detail {
    @ManyToOne(optional=false) Master master;
    ...
}

class Master {
    @OneToMany(mappedBy="master") List<Detail> details;
    ...
}

并希望为两种类型的请求提供服务:一种返回一个包含所有请求的单个请求,另一种返回一个没有 .结果由Gson转换为JSON。masterdetailsmasterdetails

我已经尝试过和,但是它们不会触及代替 .奏效的是session.clearsession.evict(master)details

 master.setDetails(nullOrSomeCollection)

这感觉相当笨拙。我更喜欢“无知”,因为它通常适用,而不知道代理了什么的哪些部分。

编写一个忽略 with 实例的 Gson 可能是一种方法,但这取决于 ,这肯定不是一件好事。在 中捕获异常听起来也好不到哪里去。TypeAdapterAbstractPersistentCollectioninitialized=falseorg.hibernate.collection.internalTypeAdapter

在一些答案后更新

我的目标不是加载数据而不是异常”,而是“如何获取空值而不是异常”

Dragan提出了一个有效的观点,即忘记获取并返回错误数据将比异常情况糟糕得多。但是有一个简单的方法可以解决这个问题:

  • 仅对集合执行此操作
  • 从不为他们使用null
  • 返回而不是空集合作为未提取数据的指示null

这样,结果就永远不会被错误地解释。如果我忘记获取某些内容,响应将包含无效的内容。null


答案 1

你可以利用Hibernate.isInitialized,它是Hibernate公共API的一部分。

因此,在 您可以添加如下内容:TypeAdapter

if ((value instanceof Collection) && !Hibernate.isInitialized(value)) {
   result = new ArrayList();
}

但是,在我看来,您的方法通常不是要走的路。

“就我而言,之前不获取它只是意味着我不在乎。

或者这意味着你忘了获取它,现在你返回了错误的数据(比获得异常更糟糕;服务的使用者认为集合是空的,但它不是)。

我不想提出“更好”的解决方案(这不是问题的主题,每种方法都有自己的优点),但是我在大多数用例中解决此类问题的方式(这是通常采用的方法之一)是使用DTO:简单地定义一个代表服务响应的DTO, 在事务上下文中填充它(没有 s),并将其提供给框架,该框架将它转换为服务响应(json,xml等)。LazyInitializationException


答案 2

您可以尝试如下解决方案。

创建名为LazyLoader

@FunctionalInterface // Java 8
public interface LazyLoader<T> {
    void load(T t);
}

并为您服务

public class Service {
    List<Master> getWithDetails(LazyLoader<Master> loader) {
        // Code to get masterList from session
        for(Master master:masterList) {
            loader.load(master);
        }        
    }
}

并调用此服务如下

Service.getWithDetails(new LazyLoader<Master>() {
    public void load(Master master) {
        for(Detail detail:master.getDetails()) {
            detail.getId(); // This will load detail
        }
    }
});

在Java 8中,您可以使用Lambda,因为它是一个单一抽象方法(SAM)。

Service.getWithDetails((master) -> {
    for(Detail detail:master.getDetails()) {
        detail.getId(); // This will load detail
    }
});

您可以将上述解决方案与 和 一起使用session.clearsession.evict(master)


推荐