让我们在一个应用程序中说你有一个名为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就会在某个地方的某个地方创建。
一个明显的选择是将文章的获取类型设置为渴望,但这并不理想,因为在大多数情况下,当我使用User对象时,我不需要加载或显示文章。
在我的方法返回之前,有没有办法强制泽西解释和构造jsp?是否有某种onResponse或postResponse监听器我可以通过我的代码来关闭会话?
我对处理打开/关闭hibernate会话的方式有什么不同吗?从谷歌搜索,每个请求一个会话似乎是大多数人提倡使用,所以我想我会尝试。
答案 0 :(得分:0)
听起来你感兴趣的是the Open Session In View pattern,虽然有些人consider it an anti-pattern.
答案 1 :(得分:0)
"open session in view"方法确实是解决此问题的方法。
另一种方法是使用EJB或Spring。然后他们将接管交易处理。当与JPA EntityManager
一起使用时,EJB完全透明地执行它,而Spring为此提供了org.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;
}
不再需要担心交易和延迟提取。
答案 2 :(得分:0)
你说“在大多数情况下,当我使用User对象时,我不需要加载或显示文章”,但在这种情况下,你显然确实需要这些数据。这是一个很好的例子,说明如何在每个用例中更好地处理获取策略而不是映射中的静态。
例如,在这里,大概是您的getUserById
命名查询类似于:
select u from User u where u.id = :id
而是使用以下查询(创建新的命名查询getUserByIdWithArticles
:
select u from User u join fetch u.articles where u.id = :id
每个用例所需的数据几乎总是因用例而异。获得恰当数量的数据始终是提高性能的最佳途径。