通过无知解决LazyInitializationException

时间:2015-09-06 12:38:00

标签: java hibernate jpa orm hibernate-mapping

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

但是,是否可以简单地告诉Hibernate忽略该问题并假装该集合为空?在我的情况下,不提取它只是意味着我不在乎。

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

我有像

这样的课程
class Detail {
    @ManyToOne(optional=false) Master master;
    ...
}

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

并希望提供两种请求:一种请求返回单个master及其所有details,另一种请求返回master s列表而不包含details。结果将由Gson转换为JSON。

我已尝试session.clearsession.evict(master),但他们没有触及代替details使用的代理。有用的是

 master.setDetails(nullOrSomeCollection)

感觉相当hacky。我更喜欢&#34;无知&#34;因为它通常适用,而不知道代理的部分是什么。

写一个Gson TypeAdapter忽略AbstractPersistentCollectioninitialized=false的实例可能是一种方式,但这取决于org.hibernate.collection.internal,这肯定不是好事。在TypeAdapter中捕获异常听起来不太好。

一些答案​​后更新

我的目标不是&#34;加载数据而不是例外&#34; ,但&#34;如何获取< strong> null 而不是异常&#34;

Dragan raises忘记获取并返回错误数据的有效点将比异常更糟糕。但是有一个简单的方法:

  • 仅为收藏品执行此操作
  • 从不使用null
  • 返回null而不是空集合作为未获取数据的指示

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

3 个答案:

答案 0 :(得分:5)

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

所以,在TypeAdapter中,您可以添加以下内容:

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

然而,在我的谦虚意见中,你的方法通常不是可行的方法。

“在我的情况下,不提取它只是意味着我不在乎。”

或者它意味着您忘记获取它,现在您返回错误数据(比获取异常更糟糕;服务的使用者认为该集合为空,但事实并非如此)。

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

答案 1 :(得分: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)

一起使用

答案 2 :(得分:1)

解决方案是使用查询而不是关联(一对多或多对多)。甚至Hibernate的一位原作者也说Collections are a feature而不是最终目标。

在您的情况下,您可以更灵活地删除集合映射,并在数据访问层中需要时简单地获取关联的关系。