如何使用延迟初始化? [LazyInitializationException]

时间:2018-06-30 19:50:25

标签: java hibernate lazy-loading

要获取惰性对象,您需要调用Hibernate.initialize(proxy),例如在您的服务类别中:

public <R> R fetchLazy(T entity, Function<T,R> proxyMapper) {
    entity = ensureEntityHasOpenedSession(entity);
    R proxy = proxyMapper.apply(entity);
    Hibernate.initialize(proxy);
    return proxy;
}

在服务范围之外,需要调用:

AnotherEntity another = service.fetchLazy(entity, Entity::getAnotherEntity);

现在,问题是为什么这样做有效:

another.getId();

而随后的通话没有:

entity.getAnotherEntity().getId(); // LazyInitializationException

第一次获取后,AnotherEntity还没有存储在Entity中吗?我是否总是需要致电

service.fetchLazy(entity, Entity::getAnotherEntity).getSomething(); 

如果是,Hibernate.initialize(Object)是在第二次调用时返回缓存的代理还是总是存在另一个数据库访问(查询执行)?

编辑

我在私有类字段上结合使用JPA注释和lombok@Getter @Setter注释。

2 个答案:

答案 0 :(得分:1)

您可能正在使用JPA字段访问,即您注释了实体类的Java字段而不是getter。这是Hibernate中对字段访问的已知限制。避免异常或显式初始化的唯一方法是改为使用属性访问。

另请参见以下JIRA问题

答案 1 :(得分:0)

后续调用的问题是@ChristianBeikov指出的与access strategy结合的ensureEntityHasOpenedSession(entity)方法的实现。

更具体地说,我已将上述方法实现为entityManager.find(entityClass, entityId),该方法返回实体的新实例。结果,AnotherEntity代理在新的实体实例中初始化,而随后的调用则在旧的实体实例中进行操作。自从我使用字段注释以来,任何方法调用都导致了JPA implementation patterns: Field access vs. property access中所述的代理初始化:

  

Hibernate的延迟加载实现始终会初始化一个延迟   调用该代理上的任何方法时的代理。唯一的例外   这是使用时用@Id注释注释的方法   属性访问。但是,当您使用字段访问时,没有这种方法   并且Hibernate即使在调用以下方法时也会初始化代理   返回实体的身份。

因此LazyInitializationException(会话关闭且尝试初始化代理的旧实体)。