【问题标题】:What Dropwizard-Hibernate doc is trying to say?Dropwizard-Hibernate 文档想说什么?
【发布时间】:2016-05-03 20:14:28
【问题描述】:

我遇到了LazyInitializationException,然后我又遇到了官方doc下面的一段话。不幸的是,这对我来说完全没有意义。请帮忙。

(文档中段落上方的代码块。)

@GET
@Timed
@UnitOfWork
public Person findPerson(@PathParam("id") LongParam id) {
    return dao.findById(id.get());
}

重要

Hibernate 会话在您的资源方法返回之前关闭 值(例如,数据库中的 Person),表示您的资源 方法(或 DAO)负责初始化所有延迟加载的 收藏等,在返回之前。否则,你会得到一个 LazyInitializationException 在您的模板中抛出(或空值 杰克逊制作)。

首先The Hibernate session is closed before your resource method’s return value。这怎么可能?如果我的资源的 return 语句周围有一个 try-finally 块,这是可能的,但这里不是这种情况。

我的资源应该被另一个方法调用了,我猜它会在我的资源方法被调用之前打开 Hibernate 会话,然后在我的资源方法返回后关闭会话。如何在我的方法返回之前关闭它。没看懂。

最重要的部分 - which means your resource method (or DAO) is responsible for initializing all lazily-loaded collections, etc., before returning. 我没有 Hibernate 经验。我现在是第一次使用它。我如何初始化,或者更确切地说,在 Hibernate 的上下文中“初始化”到底是什么意思?一个代码示例会有很大帮助。

PS:这个问题可能看起来很奇怪,粗略一看甚至可能会建议将其移至“英语语言和用法”,但请仔细阅读。这是一个技术问题,而不是段落剖析。

编辑: 从文档中添加了代码块,否则对任何人都没有意义。我还从我的问题中删除了一段,这对我来说很清楚,在发布问题后立即。

【问题讨论】:

  • 它表示 Hibernate 会话在 findPerson 返回之前关闭,因此 dao 负责初始化延迟加载的集合。阅读更多文档,我发现dao 负责“大部分 SessionFactory 的常见操作”,其中大概包括打开会话。在我看来,如果您不熟悉 Hibernate 的工作原理,您对 Dropwizard 文档的期望过高,所以在您的情况下,我认为您必须阅读 Hibernate 文档或等待 LazyInitializationException 来进一步研究问题。

标签: java hibernate dropwizard


【解决方案1】:

首先,Hibernate 会话在您的资源方法之前关闭 返回值。这怎么可能?这本来是可能的 我的资源的 return 语句周围有一个 try-finally 块, 但这里不是这样。

我对 Dropwizard 一无所知。所以让我们看看源代码(我稍微改了一下)。

来自UnitOfWorkAwareProxyFactory

class UnitOfWorkAwareProxyFactory {

public <T> T create(Class<T> clazz) {
        final ProxyFactory factory = new ProxyFactory();
        factory.setSuperclass(clazz);

        final Proxy proxy = (Proxy) factory.createClass().newInstance();

        proxy.setHandler(new MethodHandler() {
            @Override
            public Object invoke(Object self, Method overridden, 
                    Method proceed, Object[] args) {
                final UnitOfWork unitOfWork = overridden.getAnnotation(UnitOfWork.class);
                final UnitOfWorkAspect unitOfWorkAspect = new UnitOfWorkAspect(sessionFactories);
                try {
                    unitOfWorkAspect.beforeStart(unitOfWork);
                    Object result = proceed.invoke(self, args);
                    unitOfWorkAspect.afterEnd();
                    return result;
                } catch (Exception e) {
                    unitOfWorkAspect.onError();
                    throw e;
                }
            }
        });
        return (T) proxy;
}

}

如果你有课

class PersonDao {

    @UnitOfWork
    public Person findPerson(LongParam id) {
        return dao.findById(id.get());
    }

}

你可以这样做

UnitOfWorkAwareProxyFactory factory = new UnitOfWorkAwareProxyFactory();

PersonDao proxy = factory.create(PersonDao.class);

当你这样做时

Person person = proxy.findPerson(1L);

那一行变成了

unitOfWorkAspect.beforeStart(unitOfWork);
Object result = findPerson.invoke(proxy, 1L);
unitOfWorkAspect.afterEnd();

return result;  

方法unitOfWorkAspect.beforeStart(unitOfWork)unitOfWorkAspect.afterEnd()来自源UnitOfWorkAspect

class UnitOfWorkAspect {

    public void beforeStart(UnitOfWork unitOfWork) {
        session = sessionFactory.openSession();

        configureSession();
        beginTransaction();
    }

    public void afterEnd() {
        try {
            commitTransaction();
        } catch (Exception e) {
            rollbackTransaction();
            throw e;
        } finally {
            session.close();
        }

    }
}

最重要的部分 - 这意味着您的资源方法(或 DAO)负责在返回之前初始化所有延迟加载的集合等。我没有休眠经验。我现在是第一次使用它。我如何初始化,或者更确切地说,在 Hibernate 上下文中“初始化”到底是什么意思?

在此上下文中初始化意味着应从数据库加载集合数据。一些初始化方法

1.以预加载为例

class User {

  @ManyToMany(fetch = FetchType.EAGER)
  private List<Role> roles; 

}

当您获得 User 实体时,Hibernate 将通过连接或子选择加载 roles

  1. 使用Hibernate.initialize(user.getRoles())
  2. 在 HQL 中使用 join fetchfrom User user left join fetch user.roles
  3. 使用CriteriasetFetchMode()
  4. 使用获取配置文件、实体图。不知道实体图能否与会话一起使用,它是 JPA 功能:http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/fetching/Fetching.html
  5. 如果您不需要获取集合,您可以使用部分对象加载并转换为根实体:How to transform a flat result set using Hibernate

【讨论】:

  • 非常感谢。然而,在 Dropwizard 中,不是 DAO 的方法用@UnitOfWork 修饰,而是那些用这个注解修饰的 Jersey 资源方法。他们反过来访问 DAO。尽管如此,就我而言,我有一个@OneToMany 关系(user.getEmails())。奇怪地访问这个getEmails()有时结果到LazyInitializationException。由于整个代码是在proceed.invoke(self, args); 期间调用的,所以Hibernate 会话必须存在。所以,这个错误的唯一原因可能是user 有时会被分离。但为什么呢?
  • @AppleGrew 默认情况下,会话绑定到当前线程,因此在其他线程中获取user.getEmails() 可能是一个原因。无论如何,在我看来,最好在获取user 时初始化所有惰性数据(例如,使用left join fetch)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-18
  • 2013-07-07
  • 2022-12-07
  • 1970-01-01
  • 2023-01-09
相关资源
最近更新 更多