【问题标题】:Getting the ID of the persisted child object in a one-to-many relationship以一对多关系获取持久化子对象的 ID
【发布时间】:2010-08-09 11:15:41
【问题描述】:

我有两个实体类 A 和 B,如下所示。

public class A{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;    

    @OneToMany(mappedBy = "a", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
    private List<B> blist = new ArrayList<B>();

    //Other class members;

}

B类:

public class B{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne
    private A a;

    //Other class members;

}

我有一个将 B 对象添加到 A 对象的方法。我想返回新添加的B对象的id。

例如:

public Long addBtoA(long aID){

            EntityTransaction tx = myDAO.getEntityManagerTransaction();

            tx.begin();
            A aObject = myDAO.load(aID);
            tx.commit();

            B bObject = new B();

            bObject.addB(bObject);

            tx.begin();
            myDAO.save(aObject);
            tx.commit();

            //Here I want to return the ID of the saved bObject.
            // After saving  aObject it's list of B objects has the newly added bObject with it's id. 
            // What is the best way to get its id?


}

【问题讨论】:

    标签: java orm jpa one-to-many persist


    【解决方案1】:

    我认为接受的答案不正确。见https://coderanch.com/t/628230/framework/Spring-Data-obtain-id-added

    tldr; 您应该只为子 B 创建一个存储库,这样您就可以完全独立于其父项来保存子项。保存 B entity 后,将其关联到其父 A

    这是一些示例代码,Todo 是父级,Comment 是子级。

    @Entity
    public class Todo {
    
        @OneToMany(mappedBy = "todo", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
        private Set<Comment> comments = new HashSet<>();
    
        // getters/setters omitted.
    }
    
    @Entity
    public class Comment {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @ManyToOne
        @JoinColumn(name = "todo_id")
        private Todo todo;
    
        // getters/setters omitted.
    }
    

    如果这是在 spring 数据中建模的,您将创建 2 个存储库。 TodoRepositoryCommentRepository 分别是 Autowired

    给定一个能够接收 POST /api/todos/1/comments 以将新评论与给定待办事项 ID 相关联的休息端点。

        @PostMapping(value = "/api/todos/{todoId}/comments")
        public ResponseEntity<Resource<Comment>> comments(@PathVariable("todoId") Long todoId,
                                                          @RequestBody Comment comment) {
    
            Todo todo = todoRepository.findOne(todoId);
    
            // SAVE the comment first so its filled with the id from the DB.
            Comment savedComment = commentRepository.save(comment);
    
            // Associate the saved comment to the parent Todo.
            todo.addComment(savedComment);
    
            // Will update the comment with todo id FK.
            todoRepository.save(todo);
    
            // return payload...
        }
    

    如果您执行以下操作并保存提供的参数comment。获得新评论的唯一方法是遍历todo.getComments() 并找到提供的comment,如果集合是Set,这在imo 中很烦人且不切实际。

      @PostMapping(value = "/api/todos/{todoId}/comments")
        public ResponseEntity<Resource<Comment>> comments(@PathVariable("todoId") Long todoId,
                                                          @RequestBody Comment comment) {
    
            Todo todo = todoRepository.findOne(todoId);
    
            // Associate the supplied comment to the parent Todo.
            todo.addComment(comment);
    
            // Save the todo which will cascade the save into the child 
            // Comment table providing cascade on the parent is set 
            // to persist or all etc.
            Todo savedTodo = todoRepository.save(todo);
    
            // You cant do comment.getId
            // Hibernate creates a copy of comment and persists it or something.
            // The only way to get the new id is iterate through 
            // todo.getComments() and find the matching comment which is 
            // impractical especially if the collection is a set. 
    
            // return payload...
        }
    

    【讨论】:

    • 我喜欢这个解决方案,但它有一个缺点——你现在有两个交易。如果由于某种原因您的进程在第一个和第二个之间崩溃 - 您的数据库中将有一个悬空的评论,它没有连接到任何待办事项。
    • 公平点,我不认为在这种情况下,我提出的解决方案毕竟不是那么好,只是一个 hack。我放弃了寻找正确的解决方案,因为我找不到任何东西。很惊讶没有任何关于这个的信息,因为不能做这样一个基本的事情来让一个 PK 退出......
    • @MatanRubin 在“cmets”方法上打上'@Transactional'注解,就会变成1个事务。
    【解决方案2】:

    我有一个将 B 对象添加到 A 对象的方法。我要返回新添加的B对象的id。

    那就去做吧!在新的 B 实例被持久化(并将更改刷新到数据库)之后,它的id 已被分配,只需返回它。下面是一个说明这种行为的测试方法:

    @Test
    public void test_Add_B_To_A() {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("MyPu");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
    
        A a = em.find(A.class, 1L);
    
        B b = new B();
        A.addToBs(b); // convenient method that manages the bidirectional association
    
        em.getTransaction().commit(); // pending changes are flushed
    
        em.close();
        emf.close();
    
        assertNotNull(b.getId());
    }
    

    顺便说一下,你的代码有点乱,每次和EM交互后不需要commit

    【讨论】:

      【解决方案3】:

      您应该首先持久化新创建的对象,然后将其添加到其容器中。另外,org.hibernate.Sessionsave 方法返回一个新持久化对象的标识符。因此,您只需更新您的代码和/或您的 DAO 以使其行为如下:

      newObject.setContainer(container); // facultative (only if the underlying SGBD forbids null references to the container)
      Long id = (Long) hibernateSession.save(newObject); // assuming your identifier is a Long
      container.add(newObject);
      // now, id contains the id of your new object
      

      无论如何,对于所有生成 id 的对象,您总是可以这样做:

      hibernateSession.persist(object); // persist returns void...
      return object.getId(); // ... but you should have a getId method anyway
      

      【讨论】:

        【解决方案4】:

        如果有人在以前的 cmets 上找不到解决方案,另一种选择是添加

        @GeneratedValue(strategy = yourChosenStrategy)
        

        通过您要持久化的实体的 ID(或通过它的 getter)。在这种情况下,当调用persist时,id会自动设置在持久化对象中。

        希望对你有帮助!

        【讨论】:

          猜你喜欢
          • 2011-11-28
          • 2023-03-20
          • 2012-10-08
          • 2017-01-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多