【问题标题】:Java EE6/EJB3/JPA2: How to add an existing (but detached) entity to a new entity in ManyToOne?Java EE6/EJB3/JPA2:如何将现有(但分离的)实体添加到 ManyToOne 中的新实体?
【发布时间】:2012-09-02 21:52:39
【问题描述】:

我在将现有但分离的实体设置为 ManyToOne 关系时遇到问题,Hibernate 抛出 PersistentObjectException 说:“分离的实体传递给持久化:model.persons.Customer”。

我的用例如下:

我打开一个 JSF2 视图,它允许我通过命名 bean 和对无状态 OrderDAO EJB 的调用创建订单。在该视图中,我可以从下拉列表中选择一个客户。下拉列表使用另一个无状态 CustomerDAO EJB 填充,该 EJB 从数据库中获取客户列表。我选择客户并将客户设置为订单。当我使用无状态 OrderDAO EJB 保存订单时,会引发上述异常。

我的实体如下所示:

@Entity
public class Order extends AbstractEntity implements Serializable {

    private static final long serialVersionUID = -8061887078955032972L;

    @ManyToOne(cascade={CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST}, optional = false)
    private Customer customer = null;

    ... 
}

@Entity
@DiscriminatorValue("customer")
public class Customer extends Person implements Serializable {

    private static final long serialVersionUID = 2788107135840578322L;

    @OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
    private List<Order> orders = null;

    ...
}

涉及的代码如下:

@Named
@ConversationScoped
public class OrderController implements Serializable {

    private static final long serialVersionUID = -4868506512979135651L;

    @EJB
    private OrderEJB orderBean;
    private Order order;
    ...
    public Order getOrder() {
        if (order == null) {
            if (id == null) {
                order = orderBean.create();
            } else {
                order = orderBean.findById(id);
            }
        }
        return order;
    }

    public String saveOrder() {
        order = orderBean.save(order);
        return "savedOrder";
    }
}

@Stateless
public class OrderEJB extends GenericDAO<Order> {
}

public class GenericDAO<T extends AbstractEntity> {

    public T create() {
        try {
            return getClassType().newInstance();
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Error creating new instance of "+getClassType(), e);
        }
        return null;
    }

    public T save(T entity) {
        if (entity.getId() == null) {
            saveNew(entity);
        } else {
            entity = update(entity);
        }
        return entity;
    }

    private void saveNew(T entity) {
        em.persist(entity);
    }

    private T update(T entity) {
        return em.merge(entity);
    }

}

我使用两个不同的 EJB,一个用于订单,一个用于客户,这是否有问题?

【问题讨论】:

  • 当我阅读有关类似主题的整个互联网时 :) 这可能是有状态 EJB 和 PersistenceContextType.EXTENDED 的用例吗?
  • 在 GenericDAO#create 中,只传播 /a 异常而不是返回 null 可能会更好。将空值作为“失败值”而不是数据缺失指示符返回是一种不好的做法(例如,请参阅 Clean Code)

标签: hibernate jpa-2.0 ejb-3.0


【解决方案1】:

问题是您保存(持久化)一个新实体,该实体引用了一个分离实体,并启用了级联以在该关系上持久化(通过全部级联)。

这是一个不受支持的组合。

JPA 希望此关系中的整个链都是新对象。如果一个人已经有一个持久的身份(附加或分离),它将无法工作。

有两种解决方案:

删除从客户到订单的关系级联。客户仍将获得对现有订单的引用,但 JPA 不会尝试保留订单本身。您将失去一次性为新客户保留新订单的能力,但如果这种情况永远不会发生,那么这可能是一个很好的解决方案。

第二种解决方案有点反直觉,但确实有效;为具有现有订单的新客户调用 em.merge() 而不是 em.persist()。

Merge 实际上具有“saveOrUpdate”语义,因此无需关心对象是否为新对象即可进行保存。

【讨论】:

  • 不幸的是,我希望新客户和新订单也能一口气坚持下去。是的,我已经尝试过合并并且它有效。这让我想到为什么坚持存在:)
  • @joysn 好吧,为什么坚持存在?好问题。也许在这里发布一个新问题是一个好问题;)我认为这是为了验证图中的所有对象都是新的,所以当你想添加时不会意外更新。奇怪的是,没有纯粹的更新方法,只更新不添加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-21
  • 2010-09-29
  • 1970-01-01
  • 2014-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多