more efficient loading by @NaturalId
For entities with a single @NaturalId property only. Uses a unique key EntityLoader instead of two selects.
This commit is contained in:
parent
e61eff2913
commit
725083b767
|
@ -7,11 +7,12 @@
|
||||||
package org.hibernate.event.internal;
|
package org.hibernate.event.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.cache.spi.access.NaturalIdDataAccess;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.WrongClassException;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.event.spi.EventSource;
|
import org.hibernate.event.spi.EventSource;
|
||||||
import org.hibernate.event.spi.ResolveNaturalIdEvent;
|
import org.hibernate.event.spi.ResolveNaturalIdEvent;
|
||||||
|
@ -19,6 +20,7 @@ import org.hibernate.event.spi.ResolveNaturalIdEventListener;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.UniqueKeyLoadable;
|
||||||
import org.hibernate.pretty.MessageHelper;
|
import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||||
|
|
||||||
|
@ -96,7 +98,8 @@ public class DefaultResolveNaturalIdEventListener
|
||||||
* @return The entity from the cache, or null.
|
* @return The entity from the cache, or null.
|
||||||
*/
|
*/
|
||||||
protected Serializable resolveFromCache(final ResolveNaturalIdEvent event) {
|
protected Serializable resolveFromCache(final ResolveNaturalIdEvent event) {
|
||||||
return event.getSession().getPersistenceContextInternal().getNaturalIdHelper().findCachedNaturalIdResolution(
|
return event.getSession().getPersistenceContextInternal().getNaturalIdHelper()
|
||||||
|
.findCachedNaturalIdResolution(
|
||||||
event.getEntityPersister(),
|
event.getEntityPersister(),
|
||||||
event.getOrderedNaturalIdValues()
|
event.getOrderedNaturalIdValues()
|
||||||
);
|
);
|
||||||
|
@ -120,29 +123,48 @@ public class DefaultResolveNaturalIdEventListener
|
||||||
startTime = System.nanoTime();
|
startTime = System.nanoTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Serializable pk = event.getEntityPersister().loadEntityIdByNaturalId(
|
Object[] naturalIdValues = event.getOrderedNaturalIdValues();
|
||||||
event.getOrderedNaturalIdValues(),
|
|
||||||
event.getLockOptions(),
|
final Serializable pk;
|
||||||
session
|
EntityPersister persister = event.getEntityPersister();
|
||||||
|
LockOptions lockOptions = event.getLockOptions();
|
||||||
|
if ( persister instanceof UniqueKeyLoadable
|
||||||
|
&& naturalIdValues.length==1 ) {
|
||||||
|
UniqueKeyLoadable rootPersister = (UniqueKeyLoadable)
|
||||||
|
persister.getFactory().getMetamodel().entityPersister( persister.getRootEntityName() );
|
||||||
|
Map.Entry<String, Object> e = event.getNaturalIdValues().entrySet().iterator().next();
|
||||||
|
Object entity = rootPersister.loadByUniqueKey( e.getKey(), e.getValue(), lockOptions, session );
|
||||||
|
if ( entity == null ) {
|
||||||
|
pk = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( !persister.isInstance(entity) ) {
|
||||||
|
throw new WrongClassException(
|
||||||
|
"loaded object was of wrong class " + entity.getClass(),
|
||||||
|
e.getKey(),
|
||||||
|
persister.getEntityName()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
pk = persister.getIdentifier( entity, session );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pk = persister.loadEntityIdByNaturalId( naturalIdValues, lockOptions, session );
|
||||||
|
}
|
||||||
|
|
||||||
if ( stats ) {
|
if ( stats ) {
|
||||||
final long endTime = System.nanoTime();
|
final long endTime = System.nanoTime();
|
||||||
final long milliseconds = TimeUnit.MILLISECONDS.convert( endTime - startTime, TimeUnit.NANOSECONDS );
|
final long milliseconds = TimeUnit.MILLISECONDS.convert( endTime - startTime, TimeUnit.NANOSECONDS );
|
||||||
statistics.naturalIdQueryExecuted(
|
statistics.naturalIdQueryExecuted(
|
||||||
event.getEntityPersister().getRootEntityName(),
|
persister.getRootEntityName(),
|
||||||
milliseconds
|
milliseconds
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//PK can be null if the entity doesn't exist
|
//PK can be null if the entity doesn't exist
|
||||||
if (pk != null) {
|
if (pk != null) {
|
||||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
session.getPersistenceContextInternal().getNaturalIdHelper()
|
||||||
persistenceContext.getNaturalIdHelper().cacheNaturalIdCrossReferenceFromLoad(
|
.cacheNaturalIdCrossReferenceFromLoad( persister, pk, naturalIdValues );
|
||||||
event.getEntityPersister(),
|
|
||||||
pk,
|
|
||||||
event.getOrderedNaturalIdValues()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pk;
|
return pk;
|
||||||
|
|
|
@ -2481,6 +2481,23 @@ public abstract class AbstractEntityPersister
|
||||||
return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey );
|
return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object loadByUniqueKey(
|
||||||
|
String propertyName,
|
||||||
|
Object uniqueKey,
|
||||||
|
LockOptions lockOptions,
|
||||||
|
SharedSessionContractImplementor session) throws HibernateException {
|
||||||
|
//TODO: cache this
|
||||||
|
return new EntityLoader(
|
||||||
|
this,
|
||||||
|
propertyMapping.toColumns( propertyName ),
|
||||||
|
propertyMapping.toType( propertyName ),
|
||||||
|
1,
|
||||||
|
lockOptions,
|
||||||
|
getFactory(),
|
||||||
|
session.getLoadQueryInfluencers()
|
||||||
|
).loadByUniqueKey( session, uniqueKey );
|
||||||
|
}
|
||||||
|
|
||||||
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SharedSessionContractImplementor session) {
|
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SharedSessionContractImplementor session) {
|
||||||
final boolean useStaticLoader = !session.getLoadQueryInfluencers().hasEnabledFilters()
|
final boolean useStaticLoader = !session.getLoadQueryInfluencers().hasEnabledFilters()
|
||||||
&& !session.getLoadQueryInfluencers().hasEnabledFetchProfiles()
|
&& !session.getLoadQueryInfluencers().hasEnabledFetchProfiles()
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.persister.entity;
|
package org.hibernate.persister.entity;
|
||||||
|
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +17,19 @@ public interface UniqueKeyLoadable extends Loadable {
|
||||||
* Load an instance of the persistent class, by a unique key other
|
* Load an instance of the persistent class, by a unique key other
|
||||||
* than the primary key.
|
* than the primary key.
|
||||||
*/
|
*/
|
||||||
Object loadByUniqueKey(String propertyName, Object uniqueKey, SharedSessionContractImplementor session);
|
Object loadByUniqueKey(
|
||||||
|
String propertyName,
|
||||||
|
Object uniqueKey,
|
||||||
|
SharedSessionContractImplementor session);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load an instance of the persistent class, by a natural id.
|
||||||
|
*/
|
||||||
|
Object loadByUniqueKey(
|
||||||
|
String propertyName,
|
||||||
|
Object uniqueKey,
|
||||||
|
LockOptions lockOptions,
|
||||||
|
SharedSessionContractImplementor session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the property number of the unique key property
|
* Get the property number of the unique key property
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.naturalid.inheritance.cache;
|
package org.hibernate.test.naturalid.inheritance.cache;
|
||||||
|
|
||||||
|
import org.hibernate.WrongClassException;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.cfg.Configuration;
|
import org.hibernate.cfg.Configuration;
|
||||||
|
|
||||||
|
@ -14,7 +15,7 @@ import org.junit.Test;
|
||||||
|
|
||||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
public class InheritedNaturalIdNoCacheTest extends BaseCoreFunctionalTestCase {
|
public class InheritedNaturalIdNoCacheTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@ -57,10 +58,21 @@ public class InheritedNaturalIdNoCacheTest extends BaseCoreFunctionalTestCase {
|
||||||
});
|
});
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
doInHibernate( this::sessionFactory, session -> {
|
||||||
ExtendedEntity user = session.byNaturalId( ExtendedEntity.class )
|
try {
|
||||||
|
session.byNaturalId( ExtendedEntity.class )
|
||||||
.using( "uid", "base" )
|
.using( "uid", "base" )
|
||||||
.load();
|
.load();
|
||||||
assertNull( user );
|
fail( "Expecting WrongClassException" );
|
||||||
|
}
|
||||||
|
catch (WrongClassException expected) {
|
||||||
|
// expected outcome
|
||||||
|
}
|
||||||
|
catch (Exception other) {
|
||||||
|
throw new AssertionError(
|
||||||
|
"Unexpected exception type : " + other.getClass().getName(),
|
||||||
|
other
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue