Question

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?


}
Was it helpful?

Solution

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.

OTHER TIPS

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 !

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top