GeekLondon.com Help icon Syndication Feed icon 

Persist is not save

Here's a silly Hibernate 3 Annotations trap that I know I've fallen into in the past. Hopefully next time I forget and do this again I'll come across my own note when searching the web for solutions!

I'll make no excuses for it though, it's stupid.

Setting up the role associations with a user account entity, I created the following annotation based mapping from the account entity's perspective:

@ManyToMany(fetch=FetchType.LAZY,cascade=CascadeType.PERSIST)
@JoinTable(
   name="user_role",
   joinColumns={@JoinColumn(name="user")},
   inverseJoinColumns={@JoinColumn(name="role")}
)	
public Set getRoles() {
   return roles;
}

Lots of people seem to get terribly confused about cascade rules, probably because they get them mixed up with the foreign key handling when in fact the two are completely unrelated. This example, as it happens, is exactly right for my needs. I want the UserRole entities to be saved (if they're transient) when I add them to the UserAccount's roles set.

Why didn't it work?

Because in the DAO I had blithely called session.save(...) instead of session.persist(...) and the PERSIST cascade type is quite specific - it applies to calls to persist, and it doesn't apply to save at all. In fact EJB3 doesn't admit the existence of "save" in an entity's lifecycle at all, so in the unlikely event that you want to draw the distinction between Hibernate's save and JSR220 semantics, you would have to use the Hibernate @Cascade annotations and the org.hibernate.annotations.CascadeType enumeration instead of javax.persistence.CascadeType anyway.

The typical symptom, for the benefit of those arriving here the hard way, is something like this:

26-May-2007 20:49:45 org.hibernate.event.def.AbstractFlushingEventListener performExecutions
SEVERE: Could not synchronize database state with session
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.apress.timesheets.entity.UserRole
	at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:219)
	at org.hibernate.type.EntityType.getIdentifier(EntityType.java:397)
	at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:78)
	at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:755)
etc.

PS: I can't abide the name of that method: getEntityIdentifierIfNotUnsaved. Use of double-negatives in method names is poor practice, but Hibernate as a whole is shere genius, so we'll forgive them.

Comments (0)

Posted at May 26, 2007 8:18:58 PM, and last updated May 27, 2007 12:06:42 PM