Announcement Announcement Module
Collapse
No announcement yet.
Where's the saveOrUpdate method of JpaTemplate? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Where's the saveOrUpdate method of JpaTemplate?

    HibernateTemplate has the saveOrUpdate method, but JpaTemplate doesn't. JpaTemplate has merge and persist methods. Is it very difficult to write mergeOrPersist method for JpaTemplate?

    Right now I have to use a class called DaoUtils (it's from AppFuse 2.0M3) to know when to persist and when to merge. That file is here:
    Code:
    public class DaoUtils {
        protected static final Log log = LogFactory.getLog(DaoUtils.class);
    
        private static final String GET_INITIALS = "get";
    
        @SuppressWarnings("unchecked")
        public static Object getPersistentId(Object o) throws PersistenceException {
            Object objId = null;
            final String eMsg = "Error executing get<IdValue> method on persistent class.";
            String logMsg = "Persistent identity for object of type '?1' is accessible with method '?2'";
            boolean hasId = false;
    
            boolean fieldIsAnnotated = false;
            try {
                final AccessibleObject annotatedAccessibleObject = getAnnotatedAccessibleObject(
                        o.getClass(), Id.class, EmbeddedId.class);
    
                if (annotatedAccessibleObject != null) {
                    log.debug("'" + annotatedAccessibleObject + "' was annotated as the identifier of '" + o.getClass().getName() + "'");
                    hasId = true;
                    Method getter = null;
                    if (annotatedAccessibleObject instanceof Method) {
                        getter = (Method) annotatedAccessibleObject;
                    } else if (annotatedAccessibleObject instanceof Field) {
                        fieldIsAnnotated = true;
                        getter = findGetter(o.getClass(),
                                ((Field) annotatedAccessibleObject).getName());
                    }
                    objId = getter.invoke(o);
                    if (log.isDebugEnabled()) {
                        logMsg = logMsg.replace("?1", o.getClass().getName());
                        logMsg = logMsg.replace("?2", getter.getName());
                        log.debug(logMsg);
                    }
                }
            } catch (IllegalArgumentException e) {
                throw new PersistenceException(eMsg, e);
            } catch (IllegalAccessException e) {
                throw new PersistenceException(eMsg, e);
            } catch (InvocationTargetException e) {
                throw new PersistenceException(eMsg, e);
            } catch (SecurityException e) {
                throw new PersistenceException(eMsg, e);
            } catch (NoSuchMethodException e) {
                if (fieldIsAnnotated) {
                    throw new PersistenceException("A field (as opposed to a method) " +
                      "was annotated as the identifier of '" + o.getClass().getName() + 
                      "', but a no corresponding getter method conforming to " +
                      "JavaBean conventions was found." , e);
                } else {
                    throw new PersistenceException("Attempting to invoke getter method " +
                      "to return the identifer of '" + o.getClass().getName() + "' failed.", e);
                }
            }
            
            if (!hasId) {
                throw new PersistenceException("Object of type '"
                        + o.getClass().getName()
                        + "' does not have an @Id or @EmbeddedId annotation.");
            }
    
            return objId;
        }
    
        private static AccessibleObject getAnnotatedAccessibleObject(Class c, Class<? extends Annotation>... annotations)
                throws PersistenceException {
    
            final Set<AccessibleObject> members = new HashSet<AccessibleObject>();
            members.addAll(Arrays.asList(c.getDeclaredMethods()));
            members.addAll(Arrays.asList(c.getDeclaredFields()));
    
            for (AccessibleObject member : members) {
                for (Class<? extends Annotation> annotation : annotations)
                    if (member.isAnnotationPresent(annotation))
                        return member;
            }
            
            if (c.getSuperclass() != null)
                return getAnnotatedAccessibleObject (c.getSuperclass(), annotations);
    
            return null;
        }
    
        private static Method findGetter(Class type, String property) throws NoSuchMethodException {
            String methodName = GET_INITIALS
                    + Character.toUpperCase(property.charAt(0))
                    + property.substring(1);
                return type.getMethod(methodName);
        }
    }
    If there were mergeOrPersist in JpaTemplate, my code would be 4-5 lines shorter and I could get rid of a file.

  • #2
    I'm not much of a JPA expert, but doesn't merge do this for you?
    http://www.hibernate.org/hib_docs/en...single/#d0e897

    Comment


    • #3
      That's the way it I thought it read too, but in practice, it threw an exception.

      Comment


      • #4
        If that's the case, the reference manual doesn't seem very useful. Either that or I'm really reading it wrong.

        The merge operation is clever enough to automatically detect whether the merging of the detached instance has to result in an insert or update. In other words, you don't have to worry about passing a new instance (and not a detached instance) to merge(), the entity manager will figure this out for you:
        Code:
        // In the first entity manager
        Cat cat = firstEntityManager.find(Cat.class, catID);
        
        // In a higher layer of the application, detached
        Cat mate = new Cat();
        cat.setMate(mate);
        
        // Later, in a new entity manager
        secondEntityManager.merge(cat);   // update existing state
        secondEntityManager.merge(mate);  // save the new instance

        Comment


        • #5
          Yeah... it was surprising and frustrating to me to, as I read it the same way you are. I don't have the exact exception it gave me at the time, but the difference may be explained by this piece of the documentation.


          <quote>
          Merging vs. saveOrUpdate/saveOrUpdateCopy

          Merging in EJB3 is similar to the saveOrUpdateCopy() method in native Hibernate. However, it is not the same as the saveOrUpdate() method, the given instance is not reattached with the persistence context, but a managed instance is returned by the merge() method.
          </quote>

          Comment


          • #6
            Originally posted by karldmoore View Post
            I'm not much of a JPA expert...
            but it seems to me that you are You answers (at least until now) are very accurate. Thank you.

            Comment


            • #7
              the code

              So, more detail can be seen here if anyone is interested or has any feedback. Thanks...

              http://www.nabble.com/Do-we-need-Dao....html#a9463723


              If you want to get to that code:

              https://appfuse.dev.java.net/servlets/ProjectSource


              The specific module being referred to:

              https://appfuse.dev.java.net/source/...jpa-hibernate/

              Comment


              • #8
                got it

                This was bothering me, so I started playing around with it a bit more. In order to get this to work, we'd have to change the UniversalDao (and UniversalDaoJpa of course) to return the result of the entityManager.save method. (The same obviously applies to UserDao and UserDaoJpa.)

                EG...

                UniversalDao.save should become:

                public Object save(Object o);


                UniversalDaoJpa.save should become:

                public Object save(Object o) {
                return this.entityManager.merge(o);
                }

                Line 41 of UniversalDaoTest should change from:

                universalDao.save(user);

                ... to:

                user = (User)universalDao.save(user);


                That makes the test past. The documentation was right, I just wasn't using the method correctly. The difference in laymen's terms between JPA's merge and Hibernate's saveOrUpdate is that saveOrUpdate copies the newly managed persistent state onto the object passed into the method, and merge does not.

                Comment


                • #9
                  Originally posted by BNoll View Post
                  The documentation was right, I just wasn't using the method correctly. The difference in laymen's terms between JPA's merge and Hibernate's saveOrUpdate is that saveOrUpdate copies the newly managed persistent state onto the object passed into the method, and merge does not.
                  That's good to know. The documentation wasn't unless and was making sense after all . Thanks for posting back!

                  Comment

                  Working...
                  X