English 中文(简体)
Getting the ID of the persisted child object in a one-to-many relationship
原标题:

I have two entity classes A and B which looks as follows.

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;

}

Class B:

public class B{

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

    @ManyToOne
    private A a;

    //Other class members;

}

I have a method which adds a B object to an A object. I want to return the id of the newly added B object.

eg:

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?


}
最佳回答

I have a method which adds a B object to an A object. I want to return the id of the newly added B object.

Then just do it! After the new B instance has been persisted (and the changed flushed to the database), its id has been assigned, just return it. Here is a test method that illustrates this behavior:

@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());
}

By the way, your code is a bit messy, you don t need to commit after each interaction with the EM.

问题回答

I don t think the accepted answer is correct. See https://coderanch.com/t/628230/framework/Spring-Data-obtain-id-added

tldr; You should just create a repository for the child B so you can save the child completely independently from its parent. Once you have the saved B entity then associate it to its parent A.

Here is some sample code with Todo being the parent and Comment being the child.

@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.
}

If this was modeled in spring data, you create 2 repositories. TodoRepository and CommentRepository which are Autowired in.

Given a rest endpoint capable of receiving POST /api/todos/1/comments to associate a new comment with a given todo 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...
    }

If instead you did the below and saved the supplied parameter comment. The only way to get the new comment is iterate through todo.getComments() and find the supplied comment which is annoying and impractical imo if the collection is a Set.

  @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...
    }

You should persist your newly created object first, then add it to its container. Additionaly, the save method of org.hibernate.Session returns the identifier of a newly persisted object. So you just have to update your code and/or your DAO to behave like this:

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

Anyway, for all object with generated ids, you can always do something like this:

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

In case somebody doesn t find a solution on previous comments, another option is to add

@GeneratedValue(strategy = yourChosenStrategy)

over the ID of the entity you are persisting (or over the its getter). In this case, when persist is called, the id will be automatically set in the persisted object.

Hope it helps !





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签