1

假设在应用程序中,您有一个名为 User 的实体。每个用户可以拥有许多使用延迟获取加载的文章。

@Entity
@Table(name = "Users")
public class User {
  private Integer id;
  private Set<Article> articles = new HashSet<Article>();

  ...

  @OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
  public Set<Article> getArticles() {...}
}

在 Web 服务端,我想通过 Id 加载用户并将其传递给球衣 Viewable JSP 以返回和显示。我还想为每个请求使用一个会话。

@GET
public Response getUser() {
  Session session = getSessionFactory().openSession();
  session.beginTransaction();

  User user = (User) session.getNamedQuery("getUserById").setInteger("id", 1).uniqueResult();
  Map<String, Object> dataMap = new HashMap<String, Object>();
  dataMap.put("user", user);
  Viewable v = new Viewable("/user", dataMap);
  Response r = Response.ok(v).build();

  session.getTransaction().commit();
  session.close();
  return r;
}

在 user.jsp 中,我想访问文章并将它们打印出来。

<c:forEach var="a" items="${it.user.articles}">
  <span>${a.title}</span>
</c:forEach>

运行此代码并尝试请求 JSP 最终会引发异常:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: my.package.User.articles, no session or session was closed

发生这种情况是因为显然创建 Viewable、将其传递给 ResponseBuilder 并调用 build() 实际上并没有创建 JSP。在我的方法返回并且会话已关闭之后,JSP 将在更远的地方创建。

一个明显的选择是将文章的获取类型设置为 eager,但这并不理想,因为在大多数情况下,当我使用 User 对象时,我不需要加载或显示文章。

有没有办法在我的方法返回之前强制 jersey 解释和构造 jsp?是否有某种 onResponse 或 postResponse 侦听器可以传递我的代码来关闭会话?

一般来说,我在处理打开/关闭休眠会话的方式上应该做些什么不同的事情?从谷歌搜索来看,每个请求一个会话似乎是大多数人提倡使用的,所以我想我会尝试一下。

4

3 回答 3

0

听起来您感兴趣的是Open Session In View 模式,尽管有些人认为它是一种反模式。

于 2012-05-22T16:04:01.860 回答
0

“视图中的开放会话”方法确实是解决这个问题的方法。

另一种方法是使用 EJB 或 Spring。然后他们将接管事务处理。当与 JPA 一起使用时,EJB 完全透明地完成了它,EntityManager而 Springorg.springframework.orm.jpa.support.OpenEntityManagerInViewFilter为此提供了过滤器。

如果您的容器支持 EJB,那么只需创建一个@Stateless

@Stateless
public class UserService {

    @PersistenceContext
    private EntityManager em;

    public User find(Integer id) {
        return em.createNamedQuery("getUserById", User.class)
            .setParameter("id", id)
            .getSingleResult();
    }

}

并在您的网络服务中使用它,如下所示:

@EJB
private UserService userService;

@GET
public Response getUser() {
    User user = userService.find(1);

    Map<String, Object> dataMap = new HashMap<String, Object>();
    dataMap.put("user", user);
    Viewable v = new Viewable("/user", dataMap);
    Response r = Response.ok(v).build();
    return r;
}

不再需要担心事务和延迟获取。

于 2012-05-22T16:08:59.580 回答
0

您说“在大多数情况下,当我使用用户对象时,我不需要加载或显示文章”,但在这种情况下,您显然确实需要这些数据。这是一个很好的例子,说明如何在每个用例中更好地处理获取策略,而不是在映射中静态处理。

例如,在这里,您的getUserById命名查询可能类似于:

select u from User u where u.id = :id

相反,使用以下查询(创建一个新的命名查询getUserByIdWithArticles

select u from User u join fetch u.articles where u.id = :id

每个用例需要多少数据几乎总是会因用例而异。获得恰到好处的数据量始终是提高性能的最佳途径。

于 2012-05-22T17:55:53.873 回答