more efficient loading by multiple @NaturalIds
This commit is contained in:
parent
725083b767
commit
e368ac5bab
|
@ -7,7 +7,6 @@
|
||||||
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;
|
||||||
|
@ -128,12 +127,10 @@ public class DefaultResolveNaturalIdEventListener
|
||||||
final Serializable pk;
|
final Serializable pk;
|
||||||
EntityPersister persister = event.getEntityPersister();
|
EntityPersister persister = event.getEntityPersister();
|
||||||
LockOptions lockOptions = event.getLockOptions();
|
LockOptions lockOptions = event.getLockOptions();
|
||||||
if ( persister instanceof UniqueKeyLoadable
|
if ( persister instanceof UniqueKeyLoadable) {
|
||||||
&& naturalIdValues.length==1 ) {
|
|
||||||
UniqueKeyLoadable rootPersister = (UniqueKeyLoadable)
|
UniqueKeyLoadable rootPersister = (UniqueKeyLoadable)
|
||||||
persister.getFactory().getMetamodel().entityPersister( persister.getRootEntityName() );
|
persister.getFactory().getMetamodel().entityPersister( persister.getRootEntityName() );
|
||||||
Map.Entry<String, Object> e = event.getNaturalIdValues().entrySet().iterator().next();
|
Object entity = rootPersister.loadByNaturalId( naturalIdValues, lockOptions, session );
|
||||||
Object entity = rootPersister.loadByUniqueKey( e.getKey(), e.getValue(), lockOptions, session );
|
|
||||||
if ( entity == null ) {
|
if ( entity == null ) {
|
||||||
pk = null;
|
pk = null;
|
||||||
}
|
}
|
||||||
|
@ -141,7 +138,7 @@ public class DefaultResolveNaturalIdEventListener
|
||||||
if ( !persister.isInstance(entity) ) {
|
if ( !persister.isInstance(entity) ) {
|
||||||
throw new WrongClassException(
|
throw new WrongClassException(
|
||||||
"loaded object was of wrong class " + entity.getClass(),
|
"loaded object was of wrong class " + entity.getClass(),
|
||||||
e.getKey(),
|
naturalIdValues,
|
||||||
persister.getEntityName()
|
persister.getEntityName()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -237,6 +237,15 @@ public final class ArrayHelper {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean[] negate(boolean[] valueNullness) {
|
||||||
|
boolean[] result = new boolean[valueNullness.length];
|
||||||
|
for (int i = 0; i < valueNullness.length; i++) {
|
||||||
|
result[i] = !valueNullness[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static <T> void addAll(Collection<T> collection, T[] array) {
|
public static <T> void addAll(Collection<T> collection, T[] array) {
|
||||||
collection.addAll( Arrays.asList( array ) );
|
collection.addAll( Arrays.asList( array ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,28 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.entity;
|
package org.hibernate.loader.entity;
|
||||||
|
|
||||||
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.LockMode;
|
import org.hibernate.LockMode;
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.engine.jdbc.Size;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
|
import org.hibernate.engine.spi.Mapping;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.persister.entity.OuterJoinLoadable;
|
import org.hibernate.persister.entity.OuterJoinLoadable;
|
||||||
|
import org.hibernate.type.AbstractType;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
import java.sql.PreparedStatement;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
* Loads an entity instance using outerjoin fetching to fetch associated entities.
|
||||||
* <br>
|
* <br>
|
||||||
|
@ -97,7 +110,7 @@ public class EntityLoader extends AbstractEntityLoader {
|
||||||
loadQueryInfluencers
|
loadQueryInfluencers
|
||||||
);
|
);
|
||||||
initFromWalker( walker );
|
initFromWalker( walker );
|
||||||
this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||||
postInstantiate();
|
postInstantiate();
|
||||||
|
|
||||||
batchLoader = batchSize > 1;
|
batchLoader = batchSize > 1;
|
||||||
|
@ -126,7 +139,7 @@ public class EntityLoader extends AbstractEntityLoader {
|
||||||
loadQueryInfluencers
|
loadQueryInfluencers
|
||||||
);
|
);
|
||||||
initFromWalker( walker );
|
initFromWalker( walker );
|
||||||
this.compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||||
postInstantiate();
|
postInstantiate();
|
||||||
|
|
||||||
batchLoader = batchSize > 1;
|
batchLoader = batchSize > 1;
|
||||||
|
@ -140,6 +153,58 @@ public class EntityLoader extends AbstractEntityLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EntityLoader(
|
||||||
|
OuterJoinLoadable persister,
|
||||||
|
boolean[] valueNullness,
|
||||||
|
int batchSize,
|
||||||
|
LockOptions lockOptions,
|
||||||
|
SessionFactoryImplementor factory,
|
||||||
|
LoadQueryInfluencers loadQueryInfluencers) throws MappingException {
|
||||||
|
super( persister, new NaturalIdType( persister, valueNullness ), factory, loadQueryInfluencers );
|
||||||
|
|
||||||
|
EntityJoinWalker walker = new EntityJoinWalker(
|
||||||
|
persister,
|
||||||
|
naturalIdColumns( valueNullness ),
|
||||||
|
batchSize,
|
||||||
|
lockOptions,
|
||||||
|
factory,
|
||||||
|
loadQueryInfluencers
|
||||||
|
) {
|
||||||
|
@Override
|
||||||
|
protected StringBuilder whereString(String alias, String[] columnNames, int batchSize) {
|
||||||
|
StringBuilder sql = super.whereString(alias, columnNames, batchSize);
|
||||||
|
for (String nullCol : naturalIdColumns( ArrayHelper.negate( valueNullness ) ) ) {
|
||||||
|
sql.append(" and ").append( getAlias() ).append('.').append(nullCol).append(" is null");
|
||||||
|
}
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
initFromWalker( walker );
|
||||||
|
compositeKeyManyToOneTargetIndices = walker.getCompositeKeyManyToOneTargetIndices();
|
||||||
|
postInstantiate();
|
||||||
|
|
||||||
|
batchLoader = batchSize > 1;
|
||||||
|
|
||||||
|
if ( LOG.isDebugEnabled() ) {
|
||||||
|
LOG.debugf( "Static select for entity %s [%s:%s]: %s",
|
||||||
|
entityName,
|
||||||
|
lockOptions.getLockMode(),
|
||||||
|
lockOptions.getTimeOut(),
|
||||||
|
getSQLString() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] naturalIdColumns(boolean[] valueNullness) {
|
||||||
|
int i = 0;
|
||||||
|
List<String> columns = new ArrayList<>();
|
||||||
|
for ( int p : persister.getNaturalIdentifierProperties() ) {
|
||||||
|
if ( !valueNullness[i++] ) {
|
||||||
|
columns.addAll( Arrays.asList( persister.getPropertyColumnNames(p) ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columns.toArray(ArrayHelper.EMPTY_STRING_ARRAY);
|
||||||
|
}
|
||||||
|
|
||||||
public Object loadByUniqueKey(SharedSessionContractImplementor session, Object key) {
|
public Object loadByUniqueKey(SharedSessionContractImplementor session, Object key) {
|
||||||
return loadByUniqueKey( session, key, null );
|
return loadByUniqueKey( session, key, null );
|
||||||
}
|
}
|
||||||
|
@ -157,4 +222,118 @@ public class EntityLoader extends AbstractEntityLoader {
|
||||||
public int[][] getCompositeKeyManyToOneTargetIndices() {
|
public int[][] getCompositeKeyManyToOneTargetIndices() {
|
||||||
return compositeKeyManyToOneTargetIndices;
|
return compositeKeyManyToOneTargetIndices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class NaturalIdType extends AbstractType {
|
||||||
|
private OuterJoinLoadable persister;
|
||||||
|
private boolean[] valueNullness;
|
||||||
|
|
||||||
|
NaturalIdType(OuterJoinLoadable persister, boolean[] valueNullness) {
|
||||||
|
this.persister = persister;
|
||||||
|
this.valueNullness = valueNullness;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnSpan(Mapping mapping) throws MappingException {
|
||||||
|
int span = 0;
|
||||||
|
int i = 0;
|
||||||
|
for ( int p : persister.getNaturalIdentifierProperties() ) {
|
||||||
|
if ( !valueNullness[i++] ) {
|
||||||
|
span += persister.getPropertyColumnNames(p).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return span;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] sqlTypes(Mapping mapping) throws MappingException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size[] dictatedSizes(Mapping mapping) throws MappingException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Size[] defaultSizes(Mapping mapping) throws MappingException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getReturnedClass() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDirty(Object oldState, Object currentState, boolean[] checkable, SharedSessionContractImplementor session)
|
||||||
|
throws HibernateException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
|
||||||
|
throws HibernateException, SQLException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object nullSafeGet(ResultSet rs, String name, SharedSessionContractImplementor session, Object owner)
|
||||||
|
throws HibernateException, SQLException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void nullSafeSet(PreparedStatement st, Object value, int index, boolean[] settable, SharedSessionContractImplementor session)
|
||||||
|
throws HibernateException, SQLException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
|
||||||
|
throws HibernateException, SQLException {
|
||||||
|
Object[] keys = (Object[]) value;
|
||||||
|
int i = 0;
|
||||||
|
for ( int p : persister.getNaturalIdentifierProperties() ) {
|
||||||
|
if ( !valueNullness[i] ) {
|
||||||
|
persister.getPropertyTypes()[p].nullSafeSet( st, keys[i], index++, session );
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toLoggableString(Object value, SessionFactoryImplementor factory) {
|
||||||
|
return "natural id";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object deepCopy(Object value, SessionFactoryImplementor factory) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMutable() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object resolve(Object value, SharedSessionContractImplementor session, Object owner, Boolean overridingEager) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map copyCache) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean[] toColumnNullness(Object value, Mapping mapping) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2481,21 +2481,19 @@ public abstract class AbstractEntityPersister
|
||||||
return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey );
|
return getAppropriateUniqueKeyLoader( propertyName, session ).loadByUniqueKey( session, uniqueKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object loadByUniqueKey(
|
public Object loadByNaturalId(
|
||||||
String propertyName,
|
Object[] naturalIdValues,
|
||||||
Object uniqueKey,
|
|
||||||
LockOptions lockOptions,
|
LockOptions lockOptions,
|
||||||
SharedSessionContractImplementor session) throws HibernateException {
|
SharedSessionContractImplementor session) throws HibernateException {
|
||||||
//TODO: cache this
|
//TODO: cache this
|
||||||
return new EntityLoader(
|
return new EntityLoader(
|
||||||
this,
|
this,
|
||||||
propertyMapping.toColumns( propertyName ),
|
determineValueNullness( naturalIdValues ),
|
||||||
propertyMapping.toType( propertyName ),
|
|
||||||
1,
|
1,
|
||||||
lockOptions,
|
lockOptions,
|
||||||
getFactory(),
|
getFactory(),
|
||||||
session.getLoadQueryInfluencers()
|
session.getLoadQueryInfluencers()
|
||||||
).loadByUniqueKey( session, uniqueKey );
|
).loadByUniqueKey( session, naturalIdValues );
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SharedSessionContractImplementor session) {
|
private EntityLoader getAppropriateUniqueKeyLoader(String propertyName, SharedSessionContractImplementor session) {
|
||||||
|
|
|
@ -25,9 +25,8 @@ public interface UniqueKeyLoadable extends Loadable {
|
||||||
/**
|
/**
|
||||||
* Load an instance of the persistent class, by a natural id.
|
* Load an instance of the persistent class, by a natural id.
|
||||||
*/
|
*/
|
||||||
Object loadByUniqueKey(
|
Object loadByNaturalId(
|
||||||
String propertyName,
|
Object[] naturalIds,
|
||||||
Object uniqueKey,
|
|
||||||
LockOptions lockOptions,
|
LockOptions lockOptions,
|
||||||
SharedSessionContractImplementor session);
|
SharedSessionContractImplementor session);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue