Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Data Access

Reply
 
Thread Tools Display Modes
  #1  
Old Mar 13th, 2007, 08:17 AM
dxxvi dxxvi is offline
Member
 
Join Date: May 2006
Posts: 54
Default 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.
Reply With Quote
  #2  
Old Mar 13th, 2007, 02:35 PM
karldmoore karldmoore is offline
Senior Member
 
Join Date: Sep 2006
Posts: 8,425
Default

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
Reply With Quote
  #3  
Old Mar 13th, 2007, 04:17 PM
BNoll BNoll is offline
Junior Member
 
Join Date: Aug 2005
Posts: 6
Default

That's the way it I thought it read too, but in practice, it threw an exception.
Reply With Quote
  #4  
Old Mar 13th, 2007, 04:19 PM
karldmoore karldmoore is offline
Senior Member
 
Join Date: Sep 2006
Posts: 8,425
Default

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

Quote:
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
Reply With Quote
  #5  
Old Mar 13th, 2007, 04:31 PM
BNoll BNoll is offline
Junior Member
 
Join Date: Aug 2005
Posts: 6
Default

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>
Reply With Quote
  #6  
Old Mar 13th, 2007, 04:33 PM
dxxvi dxxvi is offline
Member
 
Join Date: May 2006
Posts: 54
Default

Quote:
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.
Reply With Quote
  #7  
Old Mar 13th, 2007, 05:04 PM
BNoll BNoll is offline
Junior Member
 
Join Date: Aug 2005
Posts: 6
Default 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/
Reply With Quote
  #8  
Old Mar 13th, 2007, 05:44 PM
BNoll BNoll is offline
Junior Member
 
Join Date: Aug 2005
Posts: 6
Default 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.
Reply With Quote
  #9  
Old Mar 14th, 2007, 07:18 AM
karldmoore karldmoore is offline
Senior Member
 
Join Date: Sep 2006
Posts: 8,425
Default

Quote:
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!
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 08:08 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.