Make delicious recipes!


Get vs Merge queries ... Not the same?

(Hibernate Debugging Continued ...)


As explained in Persist vs Merge page, Hibernate has an API: Session.merge(Object)
This loads the object from the database and then merges its state into the current object.
For doing so, one would expect the merge() to generate same queries as get().
However, this is generally not the case and is usually a cause of confusion.

Consider the stack trace obtained for each of the cases:




SqlStatementLogger.logStatement() line: 94
StatementPreparerImpl.prepareQueryStatement() line: 160
EntityLoader(AbstractLoadPlanBasedLoader).prepareQueryStatement() line: 257
EntityLoader(AbstractLoadPlanBasedLoader).executeQueryStatement() line: 201
EntityLoader(AbstractLoadPlanBasedLoader).executeload() line: 137
EntityLoader(AbstractLoadPlanBasedLoader).executeload() line: 102
EntityLoader(AbstractLoadPlanBasedEntityLoader).load() line: 186

SingleTableEntityPersister(AbstractEntityPersister).load() line: 4120
DefaultLoadEventListener.loadFromDatasource() line: 502
DefaultLoadEventListener.doload() line: 467
DefaultLoadEventListener.load() line: 212
DefaultLoadEventListener.proxyOrload() line: 274
DefaultLoadEventListener.onload() line: 150
SessionImpl.fireload() line: 1066
SessionImpl.access$2000() line: 176
SessionImpl$IdentifierLoadAccessImpl.load() line: 2540

SessionImpl.get(Class, Serializable) line: 951
EntityManagerImpl(AbstractEntityManagerImpl).find() line: 1110
EntityManagerImpl(AbstractEntityManagerImpl).find() line: 1068




SqlStatementLogger.logStatement() line: 94
StatementPreparerImpl.prepareQueryStatement() line: 160
CascadeEntityLoader(Loader).prepareQueryStatement() line: 1884
CascadeEntityLoader(Loader).executeQueryStatement() line: 1861
CascadeEntityLoader(Loader).executeQueryStatement() line: 1838
CascadeEntityLoader(Loader).doQuery() line: 909
CascadeEntityLoader(Loader).doQueryAndInitializeNonLazyCollections() line: 354
CascadeEntityLoader(Loader).doQueryAndInitializeNonLazyCollections() line: 324
CascadeEntityLoader(Loader).loadEntity() line: 2146
CascadeEntityLoader(AbstractEntityLoader).load() line: 78
CascadeEntityLoader(AbstractEntityLoader).load() line: 68

SingleTableEntityPersister(AbstractEntityPersister).load() line: 4120
DefaultLoadEventListener.loadFromDatasource() line: 502
DefaultLoadEventListener.doload() line: 467
DefaultLoadEventListener.load() line: 212
DefaultLoadEventListener.proxyOrload() line: 274
DefaultLoadEventListener.onload() line: 150
SessionImpl.fireload() line: 1066
SessionImpl.access$2000() line: 176
SessionImpl$IdentifierLoadAccessImpl.load() line: 2540

SessionImpl.get(String, Serializable) line: 956
DefaultMergeEventListener.entityIsDetached(MergeEvent, Map) line: 271
DefaultMergeEventListener.onMerge(MergeEvent, Map) line: 151
DefaultMergeEventListener.onMerge(MergeEvent) line: 76
SessionImpl.fireMerge(MergeEvent) line: 872
SessionImpl.merge(String, Object) line: 854
SessionImpl.merge(Object) line: 859
* All code here is from Hibernate version 4.3.1.Final


Reading the stack traces from bottom to up, it can be seen that both get() and merge() call same function on #21:
SessionImpl$IdentifierLoadAccessImpl.load() line: 2540
But they both digress at #13
This is because of the following function:
(AbstractEntityPersister.java, line 4119)
public Object load(
    Serializable id, Object optionalObject, 
    LockOptions lockOptions, SessionImplementor session)
{
    if ( LOG.isTraceEnabled() ) {
        LOG.tracev( "Fetching entity: {0}", MessageHelper.infoString( this, id, getFactory() ) );
    }

    final UniqueEntityLoader loader = getAppropriateLoader(lockOptions, session );
    return loader.load( id, optionalObject, session, lockOptions );
}

Hibernate populates all the EntityLoaders in a HashMap as:
(AbstractEntityPersister.java, line 4027)
protected void createLoaders()
{
    final Map loaders = getLoaders();
    loaders.put( LockMode.NONE, createEntityLoader( LockMode.NONE ) );
    loaders.put( LockMode.OPTIMISTIC, createEntityLoader( LockMode.OPTIMISTIC) );
    //... Similar code for all other LockModes
    
    // Only for merge/refresh, put a different EntityLoader
    loaders.put( "merge", new CascadeEntityLoader( this, CascadingActions.MERGE, getFactory()));
    loaders.put( "refresh", new CascadeEntityLoader( this, CascadingActions.REFRESH, getFactory()));
}


So the EntityLoader selected for get() is different from that of merge() and since both of
them handle entity-loading differently, they fire different queries as well.


The CustomEntityLoader fires extra queries to load non-lazy collections as shown by the following stack trace:
CollectionLoader(AbstractLoadPlanBasedLoader).executeQueryStatement() line: 201
CollectionLoader(AbstractLoadPlanBasedLoader).executeload() line: 137
CollectionLoader(AbstractLoadPlanBasedLoader).executeload() line: 102
CollectionLoader(AbstractLoadPlanBasedCollectionInitializer).initialize() line: 100
OneToManyPersister(AbstractCollectionPersister).initialize() line: 693
DefaultInitializeCollectionEventListener.onInitializeCollection() line: 92
SessionImpl.initializeCollection() line: 1893
PersistentMap(AbstractPersistentCollection).forceInitialization() line: 668
StatefulPersistenceContext.initializeNonLazyCollections() line: 885
CascadeEntityLoader(Loader).doQueryAndInitializeNonLazyCollections() line: 359
CascadeEntityLoader(Loader).doQueryAndInitializeNonLazyCollections() line: 324
CascadeEntityLoader(Loader).loadEntity() line: 2146
CascadeEntityLoader(AbstractEntityLoader).load() line: 78
CascadeEntityLoader(AbstractEntityLoader).load() line: 68
SingleTableEntityPersister(AbstractEntityPersister).load() line: 4120
DefaultLoadEventListener.loadFromDatasource() line: 502






Like us on Facebook to remain in touch
with the latest in technology and tutorials!


Got a thought to share or found a
bug in the code?
We'd love to hear from you:

Name:
Email: (Your email is not shared with anybody)
Comment:

Facebook comments:

Site Owner: Sachin Goyal