使用延迟初始化属性分离JPA对象

时间:2010-09-04 12:01:05

标签: hibernate orm jpa transactions lazy-loading

有两个JPA实体:具有一对多关系的用户和订单。

/**
 * User DTO
 */
@Entity
@Table(name="user")
public class User implements Serializable {
    private static final long serialVersionUID = 8372128484215085291L;

    private Long id;
    private Set<Order> orders;

    public User() {}

    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequenceUser")
    public Long getId() {
        return this.id;
    }
    private void setId(Long id) {
        this.id = id;
    }

    @OneToMany(mappedBy="user", cascade=CascadeType.PERSIST, fetch=FetchType.LAZY)
    @LazyCollection(LazyCollectionOption.EXTRA)
    public Set<Order> getOrders() {
        return orders;
    }
    public void setOrders(Set<Order> orders) {
        this.orders = orders;
    }
 }


/**
 * Order DTO
 */
@Entity
@Table(name="order")
public class Order implements Serializable {
    private static final long serialVersionUID = 84504362507297200L;

    private Long id;
    private User user;

    public Order() {
    }

    @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="sequenceOrder")
    public Long getId() {
        return this.id;
    }
    private void setId(Long id) {
        this.id = id;
    }

    @ManyToOne
    @JoinColumn(name="user_id")
    public User getUser(){
        return user;
    }
    public void setUser(User user){
        this.user = user;
    }
}

我在服务层类中使用这些实体,其中每个方法都在事务中运行。一切都很好,除非服务层类的方法必须返回这些实体。

@Transactional(readOnly=true)
public Set<Order> getOrders() {
    Set<Order> orders = user.getOrders();

    return orders;
}

此方法可以很好地返回数据。但是当我尝试访问收到的集合元素时,我捕获异常:“org.hibernate.LazyInitializationException:无法懒惰地初始化角色集合:package.User.orders,没有会话或会话被关闭”。

所以,它被排除在外。我认为分离结果将解决我的问题,但这样的技巧

@Transactional(readOnly=true)
public Set<Order> getOrders() {
    Set<Order> orders = user.getOrders();

    for(Order order: orders)
        entityManager.detach(order);
    return orders;
}

没有改变任何事情:(

对我来说,无论用户是否参加订单,都无关紧要。我只是想使用这个集合,而不是去修改它。

任何人都可以帮助我吗? :)

2 个答案:

答案 0 :(得分:7)

  

此方法可以很好地返回数据。但是当我尝试访问收到的集合元素时,我捕获异常:“org.hibernate.LazyInitializationException:无法懒惰地初始化角色集合:package.User.orders,没有会话或会话被关闭”。

错误是自我解释:您正在尝试加载(延迟)加载的集合,但由于User实例已分离,因此不再存在活动会话。

  

所以,它被排除在外。我认为分离结果将解决我的问题,但这样的技巧

这不会改变任何事情。 EntityManager#detach(Object)方法不会加载任何内容,它会从持久化上下文中删除传递的实体,使其分离

  

对我来说,无论用户是否参加订单,都无关紧要。我只是想使用这个集合,而不是去修改它。

您需要将关联设置为EAGER(以便在检索用户时加载Orders)或在检索服务中的用户时使用FETCH JOIN:

SELECT u
FROM User u LEFT JOIN FETCH u.orders
WHERE u.id = :id

  

我是否正确理解hibernate没有强制加载当前对象的所有惰性关联的标准机制

Hibernate有一个Hibernate.initialize方法,但这显然不是标准的JPA(更喜欢可移植代码的获取连接)。

  

我没办法让hibernate忽略当前对象的懒惰关联(将它们设置为null)

什么?无视你的意思是什么?你为什么要那样做?

答案 1 :(得分:1)

发生LazyInitialisationException是因为在事务外部请求了数据。如果您需要这些数据,您必须在交易中请求它 - 在您的情况下,这将在服务方法中。

您可以通过多种方式请求加载数据。最简单的:

for(Order order: orders)
    order.getX()

(其中X是Id或版本以外的某些属性)

然而,这将在单独的查询中加载每个订单,如果订单很多,则查询速度很慢。更快的方法是发出返回该用户的所有订单的查询(JP-QL或Criteria)。 (编辑:请参阅Pascal的答案以获得合适的查询。)