【问题标题】:Implementing session-per-request + lazy fetching with hibernate and jersey JSP viewables使用 hibernate 和 jersey JSP 可视项实现 session-per-request + 延迟获取
【发布时间】:2012-05-22 15:59:38
【问题描述】:

假设在应用程序中您有一个名为 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 会在更远的地方创建。

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

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

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

【问题讨论】:

    标签: java hibernate jsp jersey lazy-loading


    【解决方案1】:

    "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】:

      听起来你感兴趣的是the Open Session In View pattern,虽然有些人是consider it an anti-pattern.

      【讨论】:

        【解决方案3】:

        您说“在大多数情况下,当我使用 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
        

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

        【讨论】:

          猜你喜欢
          • 2023-04-07
          • 1970-01-01
          • 2011-04-04
          • 2017-11-23
          • 2012-11-16
          • 2012-07-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多