Best way to handle JPA merge?

ManuPanu picture ManuPanu · Apr 5, 2013 · Viewed 21.8k times · Source

I'm new to the whole JPA thing so I have multiple questions about the best way to handle JPA merge and persist.

  1. I have an user object which should be updated (some values like date and name). Do I have to merge the passed object first or is it safer to find the new object?

    Currently my code for updating a user looks like this:

    public void updateUserName(User user, String name) {
         // maybe first merge it?
         user.setName(name);
         user.setChangeDate(new Date());
         em.merge(user);   
     }
    

    How can I be sure that the user has not been manipulated before the update method is called? Is it safer to do something like this:

     public void updateUserName(int userId, String name) {
         User user = em.getReference(User.class, userId);
         user.setName(name);
         user.setChangeDate(new Date());
         em.merge(user);   
     }
    

    Maybe other solutions? I watched multiple videos and saw many examples but they all were different and nobody explained what the best practice is.

  2. What is the best approach to add children to relationships? For example my user object has a connection to multiple groups. Should I call the JPA handler for users and just add the new group to the user group list or should I create the group in a so group handler with persist and add it manually to my user object?

Hope somebody here has a clue ;)

Answer

Piotr Nowicki picture Piotr Nowicki · Apr 5, 2013
  1. It depends on what you want to achieve and how much information you have about the origin of the object you're trying to merge.

    Firstly, if you invoke em.merge(user) in the first line of your method or in the end it doesn't matter. If you use JTA and CMT, your entity will be updated when method invocation finishes. The only difference is that if you invoke em.merge(user) before changing the user you should use the returned instance instead of your parameter, so either it is:

    public void updateUserName(User user, String name) {
       User managedUser = em.merge(user);
       managedUser.setChangeDate(new Date());
       // no need of additional em.merge(-) here.
       // JTA automatically commits the transaction for this business method.
    }
    

    or

    public void updateUserName(User user, String name) {
       user.setChangeDate(new Date());
       em.merge(user);
       // JTA automatically commits the transaction for this business method.
    }
    

    Now about updating entity.
    If you just want to update some well-defined fields in your entity - use the second approach as it's safer. You can't be sure if a client of your method hasn't modified some other fields of your entity. Therefore, em.merge(-) will update them as well which might not be what you wanted to achieve.

    On the other hand - if you want to accept all changes made by user and just override / add some properties like changeDate in your example, the first approach is also fine (merge whole entity passed to the business method.) It really depends on your use-case.

  2. I guess it depends on your cascading settings. If you want to automatically persist / merge all Groups whenever the User entity is changed - it's safe to just add it to the User's collection (something like User#addGroup(Group g) { groups.add(g)}. If you don't want cascading, you can always create your own methods that will propagate to the other side of the relationship. It might be something like: User#addGroup(Group g) that automatically invokes g.addUser(this);.