I'm seeing some unexpected (may be totally correct) behavior when using merge() in Hibernate 3.2.
I have an entity, A, that has a many-to-one mapping to another entity, User.
<class name="A" table="a">
<id name="id" column="id">
<generator class="native"/>
</id>
<many-to-one name="user"
column="user_id"
class="User"
not-null="true"/>
...
</class>
<class name="User" table="user">
<id name="id" column="id" type="stringToLong" >
<generator class="native"/>
</id>
<property name="username"/>
...
</class>
I have a query to find a user by their username:
public User findByName(String username) {
Query query = getCurrentSession().createQuery("from User u "
+ "where u.username = :username");
query.setParameter("username", username);
return (User)query.uniqueResult();
}
This query returns a concrete User object, which is what I expect.
However, if in the same session I first do a merge() on an entity A for that User, then if I use the above findByName query, I get back a CGLib proxy (User$$EnhancerByCGLIB). This causes problems for me, because I'm using XStream, which does not seem to handle CGLib proxies gracefully.
I did some digging, and I found that merge() call is creating a proxy and storing it on StatefulPersistenceContext. Here is the stack trace:
addProxy():874, org.hibernate.engine.StatefulPersistenceContext
createProxyIfNecessary():259, org.hibernate.event.def.DefaultLoadEventListener
proxyOrLoad():191, org.hibernate.event.def.DefaultLoadEventListener
onLoad():103, org.hibernate.event.def.DefaultLoadEventListener
fireLoad():878, org.hibernate.impl.SessionImpl
internalLoad():846, org.hibernate.impl.SessionImpl
resolveIdentifier():557, org.hibernate.type.EntityType
resolve():379, org.hibernate.type.EntityType
initializeEntity():116, org.hibernate.engine.TwoPhaseLoad
initializeEntitiesAndCollections():854, org.hibernate.loader.Loader
doQuery():729, org.hibernate.loader.Loader
doQueryAndInitializeNonLazyCollections():236, org.hibernate.loader.Loader
loadEntity():1860, org.hibernate.loader.Loader
load():48, org.hibernate.loader.entity.AbstractEntityLoader
load():42, org.hibernate.loader.entity.AbstractEntityLoader
load():3044, org.hibernate.persister.entity.AbstractEntityPersister
loadFromDatasource():395, org.hibernate.event.def.DefaultLoadEventListener
doLoad():375, org.hibernate.event.def.DefaultLoadEventListener
load():139, org.hibernate.event.def.DefaultLoadEventListener
proxyOrLoad():195, org.hibernate.event.def.DefaultLoadEventListener
onLoad():103, org.hibernate.event.def.DefaultLoadEventListener
fireLoad():878, org.hibernate.impl.SessionImpl
get():815, org.hibernate.impl.SessionImpl
entityIsDetached():229, org.hibernate.event.def.DefaultMergeEventListener
onMerge():120, org.hibernate.event.def.DefaultMergeEventListener
onMerge():53, org.hibernate.event.def.DefaultMergeEventListener
fireMerge():677, org.hibernate.impl.SessionImpl
merge():661, org.hibernate.impl.SessionImpl
When the query executes it finds the User (and it is fully populated), but StatefulPersistenceContext.proxyForEntityPersister persister, EntityKey key, Object impl) finds a proxy for it and wraps it instead of returning the original implementation.
It seems like the User query should not be concerned with a merge() for an unrelated (from its perspective) domain object (entity A). Is that correct or is this expected behavior? If it is expected behavior is there a way for me to either safely remove the proxy or to unwrap the proxy? I tried Hibernate.initialize(Object), but the object is already "initialized".
|