【问题标题】:JPA How can I get the generated id/object when using merge from parent but child is created?JPA 如何在使用来自父级的合并但创建子级时获取生成的 ID/对象?
【发布时间】:2013-07-22 21:03:27
【问题描述】:

我有一个实体,该实体之前已被持久化,并且与另一个实体具有@OneToMany 关系。为了添加一个新实体,我只需在托管对象中添加我的新实体并使用cascadeType.ALL 来保存更改。有没有办法可以获取新创建对象的 id 或获取与合并一起使用的原始(非托管)对象以更新其 id?

在伪代码中,我预计会发生以下情况:

  1. 将为合并的实体返回新副本
  2. 将针对新实体更新旧副本

示例: 父母 A,id=13 孩子 B,id=0

本质上,我想在父级上发出merge,但在子级上级联persist(以便更新原始子实例,而不是复制)。

显然这不会发生。 我正在使用休眠作为提供程序。

【问题讨论】:

  • 您使用的是什么数据库、JPA 提供程序和 id 生成策略?使用 Oracle 11g + Hibernate + @SequenceGenerator,对象级联很好,并返回新分配的 id。
  • 我正在使用 MySql 和 @GeneratedValue。我不喜欢池化 id,我认为这就是它适用于 Oracle/SequenceGenerator 的原因
  • @johnd 我对问题进行了编辑以试图澄清;如果我误解了,请撤回我的更改。
  • 感谢您的更新。我找到了另一种解决方法,即加载父对象,添加我的新对象和persist 而不是合并。这是迄今为止最好的解决方案,但是能够使用合并并且只将persist 级联到新实体会更好。

标签: java jpa


【解决方案1】:

只要您进行研究,Stackoverflow postJPA documentation 就会得到答案。

做我想做的事的方法是在 managed 父级上使用persist。这将忽略父级上的任何更改,但将级联persist(前提是它设置为级联)。之后子对象将具有正确的 id。

....
JPAEntity newObject=new JPAEntity();
managedObject.addChild(newObject);
em.persist(managedObject)
newObject.getId() //work fine!

【讨论】:

    【解决方案2】:

    您应该能够“看到”为新实体生成的 ID:

    • 事务提交后,或

    • 在事务处于活动状态时,在em.flush()(其中em 是您的EntityManager)之后。

    还要注意,实体之间的所有关系需要在 Java 数据结构中在持久化之前解决。孩子对父母的引用需要“设置”,反之亦然。

    【讨论】:

    • 如果我循环遍历返回(合并)的实体,可以看到id已经设置好了,没问题。但我在原始(子/非持久对象)上看不到 id。
    • 合并操作会创建一个新的对象托管副本,实体管理器不会跟踪、更改或以其他方式“管理”原始非托管副本。据我了解你的问题,你运气不好。
    【解决方案3】:

    我在持久性提供程序 eclipselink 上遇到了类似的问题。我需要将具有临时“客户订单 ID”的客户数据库中的新订单添加到服务器数据库中的现有客户。因为我需要“服务器 orderID”来处理进一步的客户端请求,所以我想创建一个映射(clientID、serverID)并将其发送回客户端。 基本上我的方法是获取对新添加的子订单副本的引用。它将始终添加到父类客户列表的末尾,该事实用于检索它。

    我使用实体类 Customer 和 Order,其中 Customer 有一个订单列表 (LinkedList)。

    父客户类: ...

        @OneToMany(mappedBy = "customer", cascade=CascadeType.ALL, orphanRemoval = true)
        private List<Order> orders = new LinkedList<Order>();
    
        public Order getLastOrder() {
          return orders.get(orders.size()-1);
        }
    

    子订单类:

        @ManyToOne(optional=false)
        private Customer customer;
    

    小服务程序:

        Customer customer = customerService.findByID(customerID); //existing customer
        Order order = new Order();
        //add attributes to new order
        customer.addOrder(order);
        customerService.update(customer); //generates keys for children
        order = customer.getLastOrder();
        Long orderID = order.getID; //Nullpointer exception
    

    CustomerService 使用 EntityManager.merge 更新客户,但我忘记更新 servlet 中的引用:

    客户服务类:

        public void update(Customer entity) {
          entityManager.merge(entity);
        }  
    

    我是这样解决的: 客户服务类:

        public Customer update(Customer entity) {
          return entityManager.merge(entity);
        }
    

    小服务程序: ....

        customer = customerService.update(customer); //generates keys for children
    

    现在一切正常。

    【讨论】:

      【解决方案4】:

      确保在保持更改之前设置关系的两个站点。

      child.setParent(parent);
      parent.getChildren().add(child);
      Parent parentWithId = em.merge(parent);
      em.flush(); // make sure that the persistence context and database are in sync
      parent.getId(); // works
      parent.getChildren().get(0).getId(); // should also work
      

      【讨论】:

      • 我正在设置关系的双方,是的,上述工作。但是我不想遍历集合找到新的id(我是atm,但我不喜欢它)。还有其他解决方法,但我希望有更直接的解决方案。
      • jonh,这不是你最初问的。如果您想使用原始对象,请在父对象上使用persist,刷新并刷新。 stackoverflow.com/questions/4870863/…
      • @AnthonyAccioly 这是正确答案。 thw 中显示的方法接受了插入重复的父记录。
      猜你喜欢
      • 2016-11-17
      • 2017-09-24
      • 1970-01-01
      • 2020-03-14
      • 1970-01-01
      • 2015-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多