more work on multi-id entity loading and key-based loading in general
This commit is contained in:
parent
1b7f60c348
commit
0c6c8b4406
|
@ -21,7 +21,7 @@ import org.hibernate.UnknownProfileException;
|
||||||
import org.hibernate.graph.GraphSemantic;
|
import org.hibernate.graph.GraphSemantic;
|
||||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||||
import org.hibernate.internal.FilterImpl;
|
import org.hibernate.internal.FilterImpl;
|
||||||
import org.hibernate.loader.spi.InternalFetchProfile;
|
import org.hibernate.loader.spi.CascadingFetchProfile;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,7 +44,7 @@ public class LoadQueryInfluencers implements Serializable {
|
||||||
|
|
||||||
private final SessionFactoryImplementor sessionFactory;
|
private final SessionFactoryImplementor sessionFactory;
|
||||||
|
|
||||||
private InternalFetchProfile enabledInternalFetchProfile;
|
private CascadingFetchProfile enabledCascadingFetchProfile;
|
||||||
|
|
||||||
//Lazily initialized!
|
//Lazily initialized!
|
||||||
private HashSet<String> enabledFetchProfileNames;
|
private HashSet<String> enabledFetchProfileNames;
|
||||||
|
@ -69,21 +69,21 @@ public class LoadQueryInfluencers implements Serializable {
|
||||||
|
|
||||||
// internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
public void withInternalFetchProfile(InternalFetchProfile profile, InternalFetchProfileAction action) {
|
public void withInternalFetchProfile(CascadingFetchProfile profile, InternalFetchProfileAction action) {
|
||||||
final InternalFetchProfile previous = this.enabledInternalFetchProfile;
|
final CascadingFetchProfile previous = this.enabledCascadingFetchProfile;
|
||||||
this.enabledInternalFetchProfile = profile;
|
this.enabledCascadingFetchProfile = profile;
|
||||||
action.performAction();
|
action.performAction();
|
||||||
this.enabledInternalFetchProfile = previous;
|
this.enabledCascadingFetchProfile = previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T fromInternalFetchProfile(InternalFetchProfile profile, Supplier<T> supplier) {
|
public <T> T fromInternalFetchProfile(CascadingFetchProfile profile, Supplier<T> supplier) {
|
||||||
final InternalFetchProfile previous = this.enabledInternalFetchProfile;
|
final CascadingFetchProfile previous = this.enabledCascadingFetchProfile;
|
||||||
this.enabledInternalFetchProfile = profile;
|
this.enabledCascadingFetchProfile = profile;
|
||||||
try {
|
try {
|
||||||
return supplier.get();
|
return supplier.get();
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
this.enabledInternalFetchProfile = previous;
|
this.enabledCascadingFetchProfile = previous;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,34 +92,34 @@ public class LoadQueryInfluencers implements Serializable {
|
||||||
void performAction();
|
void performAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InternalFetchProfile getEnabledInternalFetchProfile() {
|
public CascadingFetchProfile getEnabledCascadingFetchProfile() {
|
||||||
return enabledInternalFetchProfile;
|
return enabledCascadingFetchProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabledInternalFetchProfile(InternalFetchProfile enabledInternalFetchProfile) {
|
public void setEnabledCascadingFetchProfile(CascadingFetchProfile enabledCascadingFetchProfile) {
|
||||||
if ( sessionFactory == null ) {
|
if ( sessionFactory == null ) {
|
||||||
// thats the signal that this is the immutable, context-less
|
// thats the signal that this is the immutable, context-less
|
||||||
// variety
|
// variety
|
||||||
throw new IllegalStateException( "Cannot modify context-less LoadQueryInfluencers" );
|
throw new IllegalStateException( "Cannot modify context-less LoadQueryInfluencers" );
|
||||||
}
|
}
|
||||||
|
|
||||||
this.enabledInternalFetchProfile = enabledInternalFetchProfile;
|
this.enabledCascadingFetchProfile = enabledCascadingFetchProfile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #getEnabledInternalFetchProfile} instead
|
* @deprecated Use {@link #getEnabledCascadingFetchProfile} instead
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public String getInternalFetchProfile() {
|
public String getInternalFetchProfile() {
|
||||||
return getEnabledInternalFetchProfile().getLegacyName();
|
return getEnabledCascadingFetchProfile().getLegacyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #setEnabledInternalFetchProfile} instead
|
* @deprecated Use {@link #setEnabledCascadingFetchProfile} instead
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setInternalFetchProfile(String internalFetchProfile) {
|
public void setInternalFetchProfile(String internalFetchProfile) {
|
||||||
setEnabledInternalFetchProfile( InternalFetchProfile.fromLegacyName( internalFetchProfile ) );
|
setEnabledCascadingFetchProfile( CascadingFetchProfile.fromLegacyName( internalFetchProfile ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ import org.hibernate.event.spi.MergeEvent;
|
||||||
import org.hibernate.event.spi.MergeEventListener;
|
import org.hibernate.event.spi.MergeEventListener;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.loader.spi.InternalFetchProfile;
|
import org.hibernate.loader.spi.CascadingFetchProfile;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.proxy.LazyInitializer;
|
import org.hibernate.proxy.LazyInitializer;
|
||||||
|
@ -303,7 +303,7 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme
|
||||||
|
|
||||||
// apply the special MERGE fetch profile and perform the resolution (Session#get)
|
// apply the special MERGE fetch profile and perform the resolution (Session#get)
|
||||||
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
||||||
InternalFetchProfile.MERGE,
|
CascadingFetchProfile.MERGE,
|
||||||
() -> source.get( entityName, clonedIdentifier )
|
() -> source.get( entityName, clonedIdentifier )
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.hibernate.event.spi.RefreshEvent;
|
||||||
import org.hibernate.event.spi.RefreshEventListener;
|
import org.hibernate.event.spi.RefreshEventListener;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.loader.spi.InternalFetchProfile;
|
import org.hibernate.loader.spi.CascadingFetchProfile;
|
||||||
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
import org.hibernate.metamodel.spi.MetamodelImplementor;
|
||||||
import org.hibernate.persister.collection.CollectionPersister;
|
import org.hibernate.persister.collection.CollectionPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
@ -170,7 +170,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
||||||
evictCachedCollections( persister, id, source );
|
evictCachedCollections( persister, id, source );
|
||||||
|
|
||||||
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
||||||
InternalFetchProfile.REFRESH,
|
CascadingFetchProfile.REFRESH,
|
||||||
() -> doRefresh( event, source, object, e, persister, id, persistenceContext )
|
() -> doRefresh( event, source, object, e, persister, id, persistenceContext )
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.loader.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
|
import org.hibernate.engine.spi.CollectionKey;
|
||||||
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
|
import org.hibernate.loader.spi.CollectionLoader;
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
|
import org.hibernate.query.spi.QueryParameterBindings;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
|
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||||
|
import org.hibernate.sql.exec.spi.Callback;
|
||||||
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||||
|
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class BatchCollectionKeyLoader implements CollectionLoader {
|
||||||
|
private static final Logger log = Logger.getLogger( BatchCollectionKeyLoader.class );
|
||||||
|
|
||||||
|
private final PluralAttributeMapping attributeMapping;
|
||||||
|
private final int batchSize;
|
||||||
|
|
||||||
|
private final int keyJdbcCount;
|
||||||
|
|
||||||
|
private SelectStatement batchSizeSqlAst;
|
||||||
|
private List<JdbcParameter> batchSizeJdbcParameters;
|
||||||
|
|
||||||
|
public BatchCollectionKeyLoader(
|
||||||
|
PluralAttributeMapping attributeMapping,
|
||||||
|
int batchSize,
|
||||||
|
LoadQueryInfluencers influencers,
|
||||||
|
SessionFactoryImplementor sessionFactory) {
|
||||||
|
this.attributeMapping = attributeMapping;
|
||||||
|
this.batchSize = batchSize;
|
||||||
|
|
||||||
|
this.keyJdbcCount = attributeMapping.getKeyDescriptor().getJdbcTypeCount( sessionFactory.getTypeConfiguration() );
|
||||||
|
|
||||||
|
this.batchSizeJdbcParameters = new ArrayList<>();
|
||||||
|
this.batchSizeSqlAst = MetamodelSelectBuilderProcess.createSelect(
|
||||||
|
attributeMapping,
|
||||||
|
null,
|
||||||
|
attributeMapping.getKeyDescriptor(),
|
||||||
|
null,
|
||||||
|
batchSize,
|
||||||
|
influencers,
|
||||||
|
LockOptions.READ,
|
||||||
|
batchSizeJdbcParameters::add,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PluralAttributeMapping getLoadable() {
|
||||||
|
return attributeMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PersistentCollection load(
|
||||||
|
Object key,
|
||||||
|
SharedSessionContractImplementor session) {
|
||||||
|
final Object[] batchIds = session.getPersistenceContextInternal()
|
||||||
|
.getBatchFetchQueue()
|
||||||
|
.getCollectionBatch( getLoadable().getCollectionDescriptor(), key, batchSize );
|
||||||
|
|
||||||
|
final int numberOfIds = ArrayHelper.countNonNull( batchIds );
|
||||||
|
|
||||||
|
if ( numberOfIds == 1 ) {
|
||||||
|
final List<JdbcParameter> jdbcParameters = new ArrayList<>( keyJdbcCount );
|
||||||
|
final SelectStatement sqlAst = MetamodelSelectBuilderProcess.createSelect(
|
||||||
|
attributeMapping,
|
||||||
|
null,
|
||||||
|
attributeMapping.getKeyDescriptor(),
|
||||||
|
null,
|
||||||
|
batchSize,
|
||||||
|
session.getLoadQueryInfluencers(),
|
||||||
|
LockOptions.READ,
|
||||||
|
jdbcParameters::add,
|
||||||
|
session.getFactory()
|
||||||
|
);
|
||||||
|
|
||||||
|
new SingleIdLoadPlan( attributeMapping.getKeyDescriptor(), sqlAst, jdbcParameters ).load( key, LockOptions.READ, session );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
batchLoad( batchIds, session );
|
||||||
|
}
|
||||||
|
|
||||||
|
final CollectionKey collectionKey = new CollectionKey( attributeMapping.getCollectionDescriptor(), key );
|
||||||
|
return session.getPersistenceContext().getCollection( collectionKey );
|
||||||
|
}
|
||||||
|
|
||||||
|
private void batchLoad(
|
||||||
|
Object[] batchIds,
|
||||||
|
SharedSessionContractImplementor session) {
|
||||||
|
if ( log.isDebugEnabled() ) {
|
||||||
|
log.debugf(
|
||||||
|
"Batch loading collection [%s] : %s",
|
||||||
|
getLoadable().getCollectionDescriptor().getRole(),
|
||||||
|
batchIds
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int smallBatchStart = 0;
|
||||||
|
int smallBatchLength = Math.min( batchIds.length, batchSize );
|
||||||
|
|
||||||
|
while ( true ) {
|
||||||
|
final List<JdbcParameter> jdbcParameters;
|
||||||
|
final SelectStatement sqlAst;
|
||||||
|
|
||||||
|
if ( smallBatchLength == batchSize ) {
|
||||||
|
jdbcParameters = this.batchSizeJdbcParameters;
|
||||||
|
sqlAst = this.batchSizeSqlAst;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcParameters = new ArrayList<>();
|
||||||
|
sqlAst = MetamodelSelectBuilderProcess.createSelect(
|
||||||
|
getLoadable(),
|
||||||
|
// null here means to select everything
|
||||||
|
null,
|
||||||
|
getLoadable().getKeyDescriptor(),
|
||||||
|
null,
|
||||||
|
batchIds.length,
|
||||||
|
session.getLoadQueryInfluencers(),
|
||||||
|
LockOptions.READ,
|
||||||
|
jdbcParameters::add,
|
||||||
|
session.getFactory()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||||
|
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||||
|
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||||
|
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||||
|
|
||||||
|
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate(
|
||||||
|
sqlAst );
|
||||||
|
|
||||||
|
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount * smallBatchLength );
|
||||||
|
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
||||||
|
|
||||||
|
for ( int i = smallBatchStart; i < smallBatchStart + smallBatchLength; i++ ) {
|
||||||
|
getLoadable().getKeyDescriptor().visitJdbcValues(
|
||||||
|
batchIds[i],
|
||||||
|
Clause.WHERE,
|
||||||
|
(value, type) -> {
|
||||||
|
assert paramItr.hasNext();
|
||||||
|
final JdbcParameter parameter = paramItr.next();
|
||||||
|
jdbcParameterBindings.addBinding(
|
||||||
|
parameter,
|
||||||
|
new JdbcParameterBinding() {
|
||||||
|
@Override
|
||||||
|
public JdbcMapping getBindType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBindValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
session
|
||||||
|
);
|
||||||
|
|
||||||
|
jdbcServices.getJdbcSelectExecutor().list(
|
||||||
|
jdbcSelect,
|
||||||
|
jdbcParameterBindings,
|
||||||
|
new ExecutionContext() {
|
||||||
|
@Override
|
||||||
|
public SharedSessionContractImplementor getSession() {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryOptions getQueryOptions() {
|
||||||
|
return QueryOptions.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public QueryParameterBindings getQueryParameterBindings() {
|
||||||
|
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Callback getCallback() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RowTransformerPassThruImpl.instance()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert !paramItr.hasNext();
|
||||||
|
|
||||||
|
// prepare for the next round...
|
||||||
|
smallBatchStart += smallBatchLength;
|
||||||
|
if ( smallBatchStart >= batchIds.length ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
smallBatchLength = Math.min( batchIds.length - smallBatchStart, batchSize );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,13 +6,16 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.loader.internal;
|
package org.hibernate.loader.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
import org.hibernate.collection.spi.PersistentCollection;
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.CollectionKey;
|
import org.hibernate.engine.spi.CollectionKey;
|
||||||
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
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.loader.spi.CollectionLoader;
|
import org.hibernate.loader.spi.CollectionLoader;
|
||||||
|
@ -24,7 +27,6 @@ import org.hibernate.sql.ast.Clause;
|
||||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||||
import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl;
|
|
||||||
import org.hibernate.sql.exec.spi.Callback;
|
import org.hibernate.sql.exec.spi.Callback;
|
||||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameter;
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
@ -34,29 +36,48 @@ import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||||
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
|
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Main implementation of CollectionLoader for handling a load of a single collection-key
|
||||||
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SingleCollectionKeyLoader implements CollectionLoader {
|
public class SingleCollectionKeyLoader implements CollectionLoader {
|
||||||
private final PluralAttributeMapping pluralAttributeMapping;
|
private final PluralAttributeMapping attributeMapping;
|
||||||
|
|
||||||
|
private final int keyJdbcCount;
|
||||||
|
|
||||||
private final SelectStatement sqlAst;
|
private final SelectStatement sqlAst;
|
||||||
private final List<JdbcParameter> jdbcParameters;
|
private final List<JdbcParameter> jdbcParameters;
|
||||||
|
|
||||||
public SingleCollectionKeyLoader(
|
public SingleCollectionKeyLoader(
|
||||||
PluralAttributeMapping pluralAttributeMapping,
|
PluralAttributeMapping attributeMapping,
|
||||||
SelectStatement sqlAst, List<JdbcParameter> jdbcParameters) {
|
LoadQueryInfluencers influencers,
|
||||||
this.pluralAttributeMapping = pluralAttributeMapping;
|
SessionFactoryImplementor sessionFactory) {
|
||||||
this.sqlAst = sqlAst;
|
this.attributeMapping = attributeMapping;
|
||||||
this.jdbcParameters = jdbcParameters;
|
|
||||||
|
this.keyJdbcCount = attributeMapping.getKeyDescriptor().getJdbcTypeCount( sessionFactory.getTypeConfiguration() );
|
||||||
|
|
||||||
|
this.jdbcParameters = new ArrayList<>();
|
||||||
|
this.sqlAst = MetamodelSelectBuilderProcess.createSelect(
|
||||||
|
attributeMapping,
|
||||||
|
null,
|
||||||
|
attributeMapping.getKeyDescriptor(),
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
influencers,
|
||||||
|
LockOptions.READ,
|
||||||
|
jdbcParameters::add,
|
||||||
|
sessionFactory
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluralAttributeMapping getLoadable() {
|
public PluralAttributeMapping getLoadable() {
|
||||||
return pluralAttributeMapping;
|
return attributeMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PersistentCollection load(Object key, SharedSessionContractImplementor session) {
|
public PersistentCollection load(Object key, SharedSessionContractImplementor session) {
|
||||||
final CollectionKey collectionKey = new CollectionKey( pluralAttributeMapping.getCollectionDescriptor(), key );
|
final CollectionKey collectionKey = new CollectionKey( attributeMapping.getCollectionDescriptor(), key );
|
||||||
|
|
||||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||||
|
@ -65,13 +86,11 @@ public class SingleCollectionKeyLoader implements CollectionLoader {
|
||||||
|
|
||||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
||||||
|
|
||||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(
|
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount );
|
||||||
pluralAttributeMapping.getKeyDescriptor().getJdbcTypeCount( sessionFactory.getTypeConfiguration() )
|
|
||||||
);
|
|
||||||
|
|
||||||
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
||||||
|
|
||||||
pluralAttributeMapping.getKeyDescriptor().visitJdbcValues(
|
attributeMapping.getKeyDescriptor().visitJdbcValues(
|
||||||
key,
|
key,
|
||||||
Clause.WHERE,
|
Clause.WHERE,
|
||||||
(value, type) -> {
|
(value, type) -> {
|
||||||
|
@ -96,7 +115,7 @@ public class SingleCollectionKeyLoader implements CollectionLoader {
|
||||||
);
|
);
|
||||||
assert !paramItr.hasNext();
|
assert !paramItr.hasNext();
|
||||||
|
|
||||||
JdbcSelectExecutorStandardImpl.INSTANCE.list(
|
jdbcServices.getJdbcSelectExecutor().list(
|
||||||
jdbcSelect,
|
jdbcSelect,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
new ExecutionContext() {
|
new ExecutionContext() {
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
|
||||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.internal;
|
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class SingleIdEntityLoaderLegacyBatch<T> extends SingleIdEntityLoaderSupport<T> {
|
|
||||||
private final int batchSize;
|
|
||||||
|
|
||||||
public SingleIdEntityLoaderLegacyBatch(
|
|
||||||
EntityMappingType entityDescriptor,
|
|
||||||
int batchSize,
|
|
||||||
SessionFactoryImplementor sessionFactory) {
|
|
||||||
super( entityDescriptor, sessionFactory );
|
|
||||||
this.batchSize = batchSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T load(Object pkValue, LockOptions lockOptions, SharedSessionContractImplementor session) {
|
|
||||||
throw new NotYetImplementedFor6Exception( "Support for " + BatchFetchStyle.LEGACY + " not yet implemented" );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
|
||||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
|
||||||
*/
|
|
||||||
package org.hibernate.loader.internal;
|
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class SingleIdEntityLoaderPaddedBatch<T> extends SingleIdEntityLoaderSupport<T> implements Preparable {
|
|
||||||
private final int batchSize;
|
|
||||||
|
|
||||||
public SingleIdEntityLoaderPaddedBatch(
|
|
||||||
EntityMappingType entityDescriptor,
|
|
||||||
int batchSize,
|
|
||||||
SessionFactoryImplementor sessionFactory) {
|
|
||||||
super( entityDescriptor, sessionFactory );
|
|
||||||
this.batchSize = batchSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepare() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T load(Object pkValue, LockOptions lockOptions, SharedSessionContractImplementor session) {
|
|
||||||
throw new NotYetImplementedFor6Exception( "Support for " + BatchFetchStyle.PADDED + " not yet implemented" );
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,7 @@ import org.hibernate.LockOptions;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
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.loader.spi.InternalFetchProfile;
|
import org.hibernate.loader.spi.CascadingFetchProfile;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameter;
|
import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
|
@ -28,7 +28,7 @@ import org.hibernate.sql.exec.spi.JdbcParameter;
|
||||||
*/
|
*/
|
||||||
public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSupport<T> implements Preparable {
|
public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSupport<T> implements Preparable {
|
||||||
private EnumMap<LockMode, SingleIdLoadPlan> selectByLockMode = new EnumMap<>( LockMode.class );
|
private EnumMap<LockMode, SingleIdLoadPlan> selectByLockMode = new EnumMap<>( LockMode.class );
|
||||||
private EnumMap<InternalFetchProfile, SingleIdLoadPlan> selectByInternalCascadeProfile;
|
private EnumMap<CascadingFetchProfile, SingleIdLoadPlan> selectByInternalCascadeProfile;
|
||||||
|
|
||||||
private AtomicInteger nonReusablePlansGenerated = new AtomicInteger();
|
private AtomicInteger nonReusablePlansGenerated = new AtomicInteger();
|
||||||
|
|
||||||
|
@ -71,14 +71,14 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
||||||
return createLoadPlan( lockOptions, loadQueryInfluencers, session.getFactory() );
|
return createLoadPlan( lockOptions, loadQueryInfluencers, session.getFactory() );
|
||||||
}
|
}
|
||||||
|
|
||||||
final InternalFetchProfile enabledInternalFetchProfile = loadQueryInfluencers.getEnabledInternalFetchProfile();
|
final CascadingFetchProfile enabledCascadingFetchProfile = loadQueryInfluencers.getEnabledCascadingFetchProfile();
|
||||||
if ( enabledInternalFetchProfile != null ) {
|
if ( enabledCascadingFetchProfile != null ) {
|
||||||
if ( LockMode.UPGRADE.greaterThan( lockOptions.getLockMode() ) ) {
|
if ( LockMode.UPGRADE.greaterThan( lockOptions.getLockMode() ) ) {
|
||||||
if ( selectByInternalCascadeProfile == null ) {
|
if ( selectByInternalCascadeProfile == null ) {
|
||||||
selectByInternalCascadeProfile = new EnumMap<>( InternalFetchProfile.class );
|
selectByInternalCascadeProfile = new EnumMap<>( CascadingFetchProfile.class );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
final SingleIdLoadPlan existing = selectByInternalCascadeProfile.get( enabledInternalFetchProfile );
|
final SingleIdLoadPlan existing = selectByInternalCascadeProfile.get( enabledCascadingFetchProfile );
|
||||||
if ( existing != null ) {
|
if ( existing != null ) {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return existing;
|
return existing;
|
||||||
|
@ -90,7 +90,7 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
||||||
loadQueryInfluencers,
|
loadQueryInfluencers,
|
||||||
session.getFactory()
|
session.getFactory()
|
||||||
);
|
);
|
||||||
selectByInternalCascadeProfile.put( enabledInternalFetchProfile, plan );
|
selectByInternalCascadeProfile.put( enabledCascadingFetchProfile, plan );
|
||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,13 +11,13 @@ import org.hibernate.internal.util.StringHelper;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public enum InternalFetchProfile {
|
public enum CascadingFetchProfile {
|
||||||
MERGE( "merge" ),
|
MERGE( "merge" ),
|
||||||
REFRESH( "refresh" );
|
REFRESH( "refresh" );
|
||||||
|
|
||||||
private final String legacyName;
|
private final String legacyName;
|
||||||
|
|
||||||
InternalFetchProfile(String legacyName) {
|
CascadingFetchProfile(String legacyName) {
|
||||||
this.legacyName = legacyName;
|
this.legacyName = legacyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ public enum InternalFetchProfile {
|
||||||
return legacyName;
|
return legacyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InternalFetchProfile fromLegacyName(String legacyName) {
|
public static CascadingFetchProfile fromLegacyName(String legacyName) {
|
||||||
if ( StringHelper.isEmpty( legacyName ) ) {
|
if ( StringHelper.isEmpty( legacyName ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.metamodel.mapping;
|
||||||
|
|
||||||
|
import org.hibernate.sql.results.spi.Fetchable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commonality between `many-to-one`, `one-to-one` and `any`, as well as entity-valued collection elements and map-keys
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public interface EntityAssociationMapping extends ModelPart, Fetchable {
|
||||||
|
@Override
|
||||||
|
default String getFetchableName() {
|
||||||
|
return getPartName();
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityMappingType getAssociatedEntityMappingType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model sub-part relative to the associated entity type that is the target
|
||||||
|
* of this association's foreign-key
|
||||||
|
*/
|
||||||
|
ModelPart getKeyTargetMatchPart();
|
||||||
|
}
|
|
@ -11,8 +11,10 @@ import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.results.internal.domain.collection.EntityCollectionPartTableGroup;
|
import org.hibernate.sql.results.internal.domain.collection.EntityCollectionPartTableGroup;
|
||||||
|
@ -26,14 +28,30 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class EntityCollectionPart implements CollectionPart, EntityValuedModelPart {
|
public class EntityCollectionPart implements CollectionPart, EntityAssociationMapping, EntityValuedModelPart {
|
||||||
private final Nature nature;
|
private final Nature nature;
|
||||||
private final EntityMappingType entityMappingType;
|
private final EntityMappingType entityMappingType;
|
||||||
|
|
||||||
|
private ModelPart fkTargetModelPart;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
public EntityCollectionPart(Nature nature, EntityMappingType entityMappingType) {
|
public EntityCollectionPart(
|
||||||
|
Nature nature,
|
||||||
|
EntityMappingType entityMappingType,
|
||||||
|
String fkTargetModelPartName,
|
||||||
|
MappingModelCreationProcess creationProcess) {
|
||||||
this.nature = nature;
|
this.nature = nature;
|
||||||
this.entityMappingType = entityMappingType;
|
this.entityMappingType = entityMappingType;
|
||||||
|
|
||||||
|
creationProcess.registerInitializationCallback(
|
||||||
|
() -> {
|
||||||
|
fkTargetModelPart = fkTargetModelPartName == null
|
||||||
|
? entityMappingType.getIdentifierMapping()
|
||||||
|
: entityMappingType.findSubPart( fkTargetModelPartName, entityMappingType );
|
||||||
|
assert fkTargetModelPart != null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,6 +69,16 @@ public class EntityCollectionPart implements CollectionPart, EntityValuedModelPa
|
||||||
return entityMappingType;
|
return entityMappingType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityMappingType getAssociatedEntityMappingType() {
|
||||||
|
return getEntityMappingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelPart getKeyTargetMatchPart() {
|
||||||
|
return fkTargetModelPart;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
public JavaTypeDescriptor getJavaTypeDescriptor() {
|
||||||
return getEntityMappingType().getJavaTypeDescriptor();
|
return getEntityMappingType().getJavaTypeDescriptor();
|
||||||
|
|
|
@ -1098,7 +1098,14 @@ public class MappingModelCreationHelper {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EntityCollectionPart( CollectionPart.Nature.ELEMENT, associatedEntity );
|
final EntityType indexEntityType = (EntityType) collectionDescriptor.getIndexType();
|
||||||
|
|
||||||
|
return new EntityCollectionPart(
|
||||||
|
CollectionPart.Nature.ELEMENT,
|
||||||
|
associatedEntity,
|
||||||
|
indexEntityType.getRHSUniqueKeyPropertyName(),
|
||||||
|
creationProcess
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotYetImplementedFor6Exception(
|
throw new NotYetImplementedFor6Exception(
|
||||||
|
@ -1171,7 +1178,14 @@ public class MappingModelCreationHelper {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EntityCollectionPart( CollectionPart.Nature.ELEMENT, associatedEntity );
|
final EntityType indexEntityType = (EntityType) collectionDescriptor.getElementType();
|
||||||
|
|
||||||
|
return new EntityCollectionPart(
|
||||||
|
CollectionPart.Nature.ELEMENT,
|
||||||
|
associatedEntity,
|
||||||
|
indexEntityType.getRHSUniqueKeyPropertyName(),
|
||||||
|
creationProcess
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new NotYetImplementedFor6Exception(
|
throw new NotYetImplementedFor6Exception(
|
||||||
|
|
|
@ -136,9 +136,18 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
|
||||||
return baseIndex;
|
return baseIndex;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( collectionDescriptor instanceof Aware ) {
|
if ( collectionDescriptor instanceof Aware ) {
|
||||||
( (Aware) collectionDescriptor ).injectAttributeMapping( this );
|
( (Aware) collectionDescriptor ).injectAttributeMapping( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( elementDescriptor instanceof Aware ) {
|
||||||
|
( (Aware) elementDescriptor ).injectAttributeMapping( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( indexDescriptor instanceof Aware ) {
|
||||||
|
( (Aware) indexDescriptor ).injectAttributeMapping( this );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -10,10 +10,12 @@ import org.hibernate.LockMode;
|
||||||
import org.hibernate.engine.FetchStrategy;
|
import org.hibernate.engine.FetchStrategy;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.mapping.ToOne;
|
import org.hibernate.mapping.ToOne;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
@ -40,12 +42,13 @@ import org.hibernate.sql.results.spi.DomainResult;
|
||||||
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
import org.hibernate.sql.results.spi.DomainResultCreationState;
|
||||||
import org.hibernate.sql.results.spi.Fetch;
|
import org.hibernate.sql.results.spi.Fetch;
|
||||||
import org.hibernate.sql.results.spi.FetchParent;
|
import org.hibernate.sql.results.spi.FetchParent;
|
||||||
|
import org.hibernate.sql.results.spi.Fetchable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class SingularAssociationAttributeMapping extends AbstractSingularAttributeMapping
|
public class SingularAssociationAttributeMapping extends AbstractSingularAttributeMapping
|
||||||
implements EntityValuedModelPart, TableGroupJoinProducer {
|
implements EntityValuedModelPart, EntityAssociationMapping, TableGroupJoinProducer {
|
||||||
private final String sqlAliasStem;
|
private final String sqlAliasStem;
|
||||||
private final boolean isNullable;
|
private final boolean isNullable;
|
||||||
private final boolean referringPrimaryKey;
|
private final boolean referringPrimaryKey;
|
||||||
|
@ -278,14 +281,17 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
||||||
.getFromClauseAccess()
|
.getFromClauseAccess()
|
||||||
.findTableGroup( parentParentNavigablePath )
|
.findTableGroup( parentParentNavigablePath )
|
||||||
.getModelPart();
|
.getModelPart();
|
||||||
final SingularAssociationAttributeMapping part = (SingularAssociationAttributeMapping) modelPart
|
// final SingularAssociationAttributeMapping part = (SingularAssociationAttributeMapping) modelPart
|
||||||
.findSubPart( panentNaviblePath.getLocalName(), null );
|
// .findSubPart( panentNaviblePath.getLocalName(), null );
|
||||||
|
final EntityAssociationMapping part = (EntityAssociationMapping) modelPart.findSubPart( panentNaviblePath.getLocalName(), null );
|
||||||
|
|
||||||
if ( panentNaviblePath.getLocalName().equals( referencedPropertyName )
|
if ( panentNaviblePath.getLocalName().equals( referencedPropertyName )
|
||||||
&& part.getFetchableName().equals( referencedPropertyName ) ) {
|
&& part.getFetchableName().equals( referencedPropertyName ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if ( part.getReferencedPropertyName() != null
|
else if ( part.getKeyTargetMatchPart() != null
|
||||||
&& part.getReferencedPropertyName().equals( getAttributeName() ) ) {
|
// && part.getKeyTargetMatchPart().equals( this ) ) {
|
||||||
|
&& part.getKeyTargetMatchPart().getPartName().equals( getAttributeName() ) ) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,4 +306,14 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
||||||
public boolean isUnwrapProxy() {
|
public boolean isUnwrapProxy() {
|
||||||
return unwrapProxy;
|
return unwrapProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EntityMappingType getAssociatedEntityMappingType() {
|
||||||
|
return getEntityMappingType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ModelPart getKeyTargetMatchPart() {
|
||||||
|
return foreignKeyDescriptor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.FetchMode;
|
import org.hibernate.FetchMode;
|
||||||
import org.hibernate.Filter;
|
import org.hibernate.Filter;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.LockOptions;
|
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.NotYetImplementedFor6Exception;
|
import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.QueryException;
|
import org.hibernate.QueryException;
|
||||||
|
@ -43,7 +42,6 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||||
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
|
||||||
import org.hibernate.engine.profile.Fetch;
|
import org.hibernate.engine.profile.Fetch;
|
||||||
import org.hibernate.engine.profile.FetchProfile;
|
import org.hibernate.engine.profile.FetchProfile;
|
||||||
import org.hibernate.engine.spi.CollectionKey;
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
|
@ -61,7 +59,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.jdbc.Expectation;
|
import org.hibernate.jdbc.Expectation;
|
||||||
import org.hibernate.jdbc.Expectations;
|
import org.hibernate.jdbc.Expectations;
|
||||||
import org.hibernate.loader.collection.CollectionInitializer;
|
import org.hibernate.loader.collection.CollectionInitializer;
|
||||||
import org.hibernate.loader.internal.MetamodelSelectBuilderProcess;
|
import org.hibernate.loader.internal.BatchCollectionKeyLoader;
|
||||||
import org.hibernate.loader.internal.SingleCollectionKeyLoader;
|
import org.hibernate.loader.internal.SingleCollectionKeyLoader;
|
||||||
import org.hibernate.loader.internal.SubSelectFetchCollectionLoader;
|
import org.hibernate.loader.internal.SubSelectFetchCollectionLoader;
|
||||||
import org.hibernate.loader.spi.CollectionLoader;
|
import org.hibernate.loader.spi.CollectionLoader;
|
||||||
|
@ -115,8 +113,6 @@ import org.hibernate.sql.ast.tree.from.TableReferenceCollector;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
||||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameter;
|
|
||||||
import org.hibernate.sql.results.spi.DomainResult;
|
import org.hibernate.sql.results.spi.DomainResult;
|
||||||
import org.hibernate.type.AnyType;
|
import org.hibernate.type.AnyType;
|
||||||
import org.hibernate.type.AssociationType;
|
import org.hibernate.type.AssociationType;
|
||||||
|
@ -859,21 +855,12 @@ public abstract class AbstractCollectionPersister
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CollectionLoader createCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
|
protected CollectionLoader createCollectionLoader(LoadQueryInfluencers loadQueryInfluencers) {
|
||||||
final java.util.List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
final int batchSize = getBatchSize();
|
||||||
|
if ( batchSize > 1 ) {
|
||||||
|
return new BatchCollectionKeyLoader( attributeMapping, batchSize, loadQueryInfluencers, getFactory() );
|
||||||
|
}
|
||||||
|
|
||||||
final SelectStatement sqlAst = MetamodelSelectBuilderProcess.createSelect(
|
return new SingleCollectionKeyLoader( attributeMapping, loadQueryInfluencers, getFactory() );
|
||||||
attributeMapping,
|
|
||||||
null,
|
|
||||||
attributeMapping.getKeyDescriptor(),
|
|
||||||
null,
|
|
||||||
1,
|
|
||||||
loadQueryInfluencers,
|
|
||||||
LockOptions.READ,
|
|
||||||
jdbcParameters::add,
|
|
||||||
getFactory()
|
|
||||||
);
|
|
||||||
|
|
||||||
return new SingleCollectionKeyLoader( attributeMapping, sqlAst, jdbcParameters );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CollectionInitializer getAppropriateInitializer(Object key, SharedSessionContractImplementor session) {
|
protected CollectionInitializer getAppropriateInitializer(Object key, SharedSessionContractImplementor session) {
|
||||||
|
|
|
@ -107,18 +107,15 @@ import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.jdbc.Expectation;
|
import org.hibernate.jdbc.Expectation;
|
||||||
import org.hibernate.jdbc.Expectations;
|
import org.hibernate.jdbc.Expectations;
|
||||||
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
||||||
import org.hibernate.loader.BatchFetchStyle;
|
|
||||||
import org.hibernate.loader.custom.sql.SQLQueryParser;
|
import org.hibernate.loader.custom.sql.SQLQueryParser;
|
||||||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
||||||
import org.hibernate.loader.entity.CascadeEntityLoader;
|
import org.hibernate.loader.entity.CascadeEntityLoader;
|
||||||
import org.hibernate.loader.entity.EntityLoader;
|
import org.hibernate.loader.entity.EntityLoader;
|
||||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||||
import org.hibernate.loader.internal.SingleIdEntityLoaderDynamicBatch;
|
|
||||||
import org.hibernate.loader.internal.MultiIdEntityLoaderStandardImpl;
|
import org.hibernate.loader.internal.MultiIdEntityLoaderStandardImpl;
|
||||||
import org.hibernate.loader.internal.NaturalIdLoaderStandardImpl;
|
import org.hibernate.loader.internal.NaturalIdLoaderStandardImpl;
|
||||||
import org.hibernate.loader.internal.Preparable;
|
import org.hibernate.loader.internal.Preparable;
|
||||||
import org.hibernate.loader.internal.SingleIdEntityLoaderLegacyBatch;
|
import org.hibernate.loader.internal.SingleIdEntityLoaderDynamicBatch;
|
||||||
import org.hibernate.loader.internal.SingleIdEntityLoaderPaddedBatch;
|
|
||||||
import org.hibernate.loader.internal.SingleIdEntityLoaderProvidedQueryImpl;
|
import org.hibernate.loader.internal.SingleIdEntityLoaderProvidedQueryImpl;
|
||||||
import org.hibernate.loader.internal.SingleIdEntityLoaderStandardImpl;
|
import org.hibernate.loader.internal.SingleIdEntityLoaderStandardImpl;
|
||||||
import org.hibernate.loader.spi.Loader;
|
import org.hibernate.loader.spi.Loader;
|
||||||
|
@ -144,7 +141,6 @@ import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
||||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||||
|
@ -172,7 +168,6 @@ import org.hibernate.query.ComparisonOperator;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
||||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
|
||||||
import org.hibernate.sql.Alias;
|
import org.hibernate.sql.Alias;
|
||||||
import org.hibernate.sql.Delete;
|
import org.hibernate.sql.Delete;
|
||||||
import org.hibernate.sql.Insert;
|
import org.hibernate.sql.Insert;
|
||||||
|
@ -188,6 +183,7 @@ import org.hibernate.sql.ast.spi.SqlAliasBase;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
|
||||||
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
|
||||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
@ -1016,22 +1012,7 @@ public abstract class AbstractEntityPersister
|
||||||
EntityMappingType entityDescriptor,
|
EntityMappingType entityDescriptor,
|
||||||
int batchSize,
|
int batchSize,
|
||||||
SessionFactoryImplementor factory) {
|
SessionFactoryImplementor factory) {
|
||||||
final BatchFetchStyle batchFetchStyle = factory.getSettings().getBatchFetchStyle();
|
return new SingleIdEntityLoaderDynamicBatch( entityDescriptor, batchSize, factory );
|
||||||
|
|
||||||
switch ( batchFetchStyle ) {
|
|
||||||
case LEGACY: {
|
|
||||||
return new SingleIdEntityLoaderLegacyBatch( entityDescriptor, batchSize, factory );
|
|
||||||
}
|
|
||||||
case DYNAMIC: {
|
|
||||||
return new SingleIdEntityLoaderDynamicBatch( entityDescriptor, batchSize, factory );
|
|
||||||
}
|
|
||||||
case PADDED: {
|
|
||||||
return new SingleIdEntityLoaderPaddedBatch( entityDescriptor, batchSize, factory );
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new UnsupportedOperationException( "BatchFetchStyle [" + batchFetchStyle.name() + "] not supported" );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("RedundantIfStatement")
|
@SuppressWarnings("RedundantIfStatement")
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.loading;
|
package org.hibernate.orm.test.loading.multiLoad;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
|
@ -4,7 +4,7 @@
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.ops.multiLoad;
|
package org.hibernate.orm.test.loading.multiLoad;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -25,8 +25,8 @@ import org.hibernate.annotations.BatchSize;
|
||||||
import org.hibernate.annotations.Fetch;
|
import org.hibernate.annotations.Fetch;
|
||||||
import org.hibernate.annotations.FetchMode;
|
import org.hibernate.annotations.FetchMode;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.stat.Statistics;
|
|
||||||
|
|
||||||
|
import org.hibernate.testing.FailureExpected;
|
||||||
import org.hibernate.testing.TestForIssue;
|
import org.hibernate.testing.TestForIssue;
|
||||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -83,6 +83,7 @@ public class MultiLoadSubSelectCollectionTest extends BaseNonConfigCoreFunctiona
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestForIssue( jiraKey = "HHH-12740" )
|
@TestForIssue( jiraKey = "HHH-12740" )
|
||||||
|
@FailureExpected( jiraKey = "none yet", message = "subselect-fetching not triggered")
|
||||||
public void testSubselect() {
|
public void testSubselect() {
|
||||||
doInHibernate(
|
doInHibernate(
|
||||||
this::sessionFactory, session -> {
|
this::sessionFactory, session -> {
|
|
@ -0,0 +1,497 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.loading.multiLoad;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import javax.persistence.Cacheable;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.SharedCacheMode;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.CacheMode;
|
||||||
|
import org.hibernate.annotations.BatchSize;
|
||||||
|
import org.hibernate.boot.SessionFactoryBuilder;
|
||||||
|
import org.hibernate.boot.spi.MetadataImplementor;
|
||||||
|
import org.hibernate.cache.spi.access.AccessType;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
|
import org.hibernate.engine.spi.EntityEntry;
|
||||||
|
import org.hibernate.engine.spi.EntityKey;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
|
import org.hibernate.engine.spi.Status;
|
||||||
|
import org.hibernate.stat.Statistics;
|
||||||
|
import org.hibernate.tool.schema.Action;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.FailureExpected;
|
||||||
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryProducer;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
@ServiceRegistry(
|
||||||
|
settings = {
|
||||||
|
@ServiceRegistry.Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "true" ),
|
||||||
|
@ServiceRegistry.Setting( name = AvailableSettings.GENERATE_STATISTICS, value = "true" ),
|
||||||
|
@ServiceRegistry.Setting( name = AvailableSettings.HBM2DDL_DATABASE_ACTION, value = "create-drop" )
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = MultiLoadTest.SimpleEntity.class,
|
||||||
|
sharedCacheMode = SharedCacheMode.ENABLE_SELECTIVE,
|
||||||
|
accessType = AccessType.READ_WRITE
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class MultiLoadTest implements SessionFactoryProducer {
|
||||||
|
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SessionFactoryImplementor produceSessionFactory(MetadataImplementor model) {
|
||||||
|
final SessionFactoryBuilder sessionFactoryBuilder = model.getSessionFactoryBuilder();
|
||||||
|
sqlStatementInterceptor = new SQLStatementInterceptor( sessionFactoryBuilder );
|
||||||
|
return (SessionFactoryImplementor) sessionFactoryBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.setCacheMode( CacheMode.IGNORE );
|
||||||
|
for ( int i = 1; i <= 60; i++ ) {
|
||||||
|
session.save( new SimpleEntity( i, "Entity #" + i ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.createQuery( "delete SimpleEntity" ).executeUpdate();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicMultiLoad(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids( 5 ) );
|
||||||
|
assertEquals( 5, list.size() );
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?, ?, ?, ?, ?)" ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-10984" )
|
||||||
|
public void testUnflushedDeleteAndThenMultiLoad(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// delete one of them (but do not flush)...
|
||||||
|
session.delete( session.load( SimpleEntity.class, 5 ) );
|
||||||
|
|
||||||
|
// as a baseline, assert based on how load() handles it
|
||||||
|
SimpleEntity s5 = session.load( SimpleEntity.class, 5 );
|
||||||
|
assertNotNull( s5 );
|
||||||
|
|
||||||
|
// and then, assert how get() handles it
|
||||||
|
s5 = session.get( SimpleEntity.class, 5 );
|
||||||
|
assertNull( s5 );
|
||||||
|
|
||||||
|
// finally assert how multiLoad handles it
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) );
|
||||||
|
assertEquals( 56, list.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-10617" )
|
||||||
|
public void testDuplicatedRequestedIds(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// ordered multiLoad
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( 1, 2, 3, 2, 2 );
|
||||||
|
assertEquals( 5, list.size() );
|
||||||
|
assertSame( list.get( 1 ), list.get( 3 ) );
|
||||||
|
assertSame( list.get( 1 ), list.get( 4 ) );
|
||||||
|
|
||||||
|
// un-ordered multiLoad
|
||||||
|
list = session.byMultipleIds( SimpleEntity.class ).enableOrderedReturn( false ).multiLoad( 1, 2, 3, 2, 2 );
|
||||||
|
assertEquals( 3, list.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue( jiraKey = "HHH-10617" )
|
||||||
|
public void testNonExistentIdRequest(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// ordered multiLoad
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( 1, 699, 2 );
|
||||||
|
assertEquals( 3, list.size() );
|
||||||
|
assertNull( list.get( 1 ) );
|
||||||
|
|
||||||
|
// un-ordered multiLoad
|
||||||
|
list = session.byMultipleIds( SimpleEntity.class ).enableOrderedReturn( false ).multiLoad( 1, 699, 2 );
|
||||||
|
assertEquals( 2, list.size() );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicMultiLoadWithManagedAndNoChecking(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 );
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids( 56 ) );
|
||||||
|
assertEquals( 56, list.size() );
|
||||||
|
// this check is HIGHLY specific to implementation in the batch loader
|
||||||
|
// which puts existing managed entities first...
|
||||||
|
assertSame( first, list.get( 0 ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicMultiLoadWithManagedAndChecking(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 );
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.multiLoad( ids( 56 ) );
|
||||||
|
assertEquals( 56, list.size() );
|
||||||
|
// this check is HIGHLY specific to implementation in the batch loader
|
||||||
|
// which puts existing managed entities first...
|
||||||
|
assertSame( first, list.get( 0 ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12944")
|
||||||
|
@FailureExpected( reason = "Caching/CacheMode supported not yet implemented" )
|
||||||
|
public void testMultiLoadFrom2ndLevelCache(SessionFactoryScope scope) {
|
||||||
|
Statistics statistics = scope.getSessionFactory().getStatistics();
|
||||||
|
scope.getSessionFactory().getCache().evictAll();
|
||||||
|
statistics.clear();
|
||||||
|
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// Load 1 of the items directly
|
||||||
|
SimpleEntity entity = session.get( SimpleEntity.class, 2 );
|
||||||
|
assertNotNull( entity );
|
||||||
|
|
||||||
|
assertEquals( 1, statistics.getSecondLevelCacheMissCount() );
|
||||||
|
assertEquals( 0, statistics.getSecondLevelCacheHitCount() );
|
||||||
|
assertEquals( 1, statistics.getSecondLevelCachePutCount() );
|
||||||
|
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
statistics.clear();
|
||||||
|
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// Validate that the entity is still in the Level 2 cache
|
||||||
|
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
||||||
|
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.NORMAL )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.multiLoad( ids( 3 ) );
|
||||||
|
assertEquals( 3, entities.size() );
|
||||||
|
assertEquals( 1, statistics.getSecondLevelCacheHitCount() );
|
||||||
|
|
||||||
|
for(SimpleEntity entity: entities) {
|
||||||
|
assertTrue( session.contains( entity ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?, ?)" ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12944")
|
||||||
|
@FailureExpected( reason = "Caching/CacheMode supported not yet implemented" )
|
||||||
|
public void testUnorderedMultiLoadFrom2ndLevelCache(SessionFactoryScope scope) {
|
||||||
|
Statistics statistics = scope.getSessionFactory().getStatistics();
|
||||||
|
scope.getSessionFactory().getCache().evictAll();
|
||||||
|
statistics.clear();
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// Load 1 of the items directly
|
||||||
|
SimpleEntity entity = session.get( SimpleEntity.class, 2 );
|
||||||
|
assertNotNull( entity );
|
||||||
|
|
||||||
|
assertEquals( 1, statistics.getSecondLevelCacheMissCount() );
|
||||||
|
assertEquals( 0, statistics.getSecondLevelCacheHitCount() );
|
||||||
|
assertEquals( 1, statistics.getSecondLevelCachePutCount() );
|
||||||
|
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
statistics.clear();
|
||||||
|
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
|
||||||
|
// Validate that the entity is still in the Level 2 cache
|
||||||
|
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
||||||
|
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.NORMAL )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.enableOrderedReturn( false )
|
||||||
|
.multiLoad( ids( 3 ) );
|
||||||
|
assertEquals( 3, entities.size() );
|
||||||
|
assertEquals( 1, statistics.getSecondLevelCacheHitCount() );
|
||||||
|
|
||||||
|
for(SimpleEntity entity: entities) {
|
||||||
|
assertTrue( session.contains( entity ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?, ?)" ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12944")
|
||||||
|
@FailureExpected( reason = "Caching/CacheMode supported not yet implemented" )
|
||||||
|
public void testOrderedMultiLoadFrom2ndLevelCachePendingDelete(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.remove( session.find( SimpleEntity.class, 2 ) );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
||||||
|
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.NORMAL )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.enableOrderedReturn( true )
|
||||||
|
.multiLoad( ids( 3 ) );
|
||||||
|
assertEquals( 3, entities.size() );
|
||||||
|
|
||||||
|
assertNull( entities.get(1) );
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12944")
|
||||||
|
public void testOrderedMultiLoadFrom2ndLevelCachePendingDeleteReturnRemoved(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.remove( session.find( SimpleEntity.class, 2 ) );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
||||||
|
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.NORMAL )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.enableOrderedReturn( true )
|
||||||
|
.enableReturnOfDeletedEntities( true )
|
||||||
|
.multiLoad( ids( 3 ) );
|
||||||
|
assertEquals( 3, entities.size() );
|
||||||
|
|
||||||
|
SimpleEntity deletedEntity = entities.get(1);
|
||||||
|
assertNotNull( deletedEntity );
|
||||||
|
|
||||||
|
final EntityEntry entry = ((SharedSessionContractImplementor) session).getPersistenceContext().getEntry( deletedEntity );
|
||||||
|
assertTrue( entry.getStatus() == Status.DELETED || entry.getStatus() == Status.GONE );
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?, ?)" ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12944")
|
||||||
|
@FailureExpected( reason = "Caching/CacheMode supported not yet implemented" )
|
||||||
|
public void testUnorderedMultiLoadFrom2ndLevelCachePendingDelete(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
|
||||||
|
session.remove( session.find( SimpleEntity.class, 2 ) );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
||||||
|
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.NORMAL )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.enableOrderedReturn( false )
|
||||||
|
.multiLoad( ids( 3 ) );
|
||||||
|
assertEquals( 3, entities.size() );
|
||||||
|
|
||||||
|
assertTrue( entities.stream().anyMatch( Objects::isNull ) );
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12944")
|
||||||
|
public void testUnorderedMultiLoadFrom2ndLevelCachePendingDeleteReturnRemoved(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
|
||||||
|
session.remove( session.find( SimpleEntity.class, 2 ) );
|
||||||
|
|
||||||
|
sqlStatementInterceptor.getSqlQueries().clear();
|
||||||
|
|
||||||
|
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
||||||
|
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.NORMAL )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.enableOrderedReturn( false )
|
||||||
|
.enableReturnOfDeletedEntities( true )
|
||||||
|
.multiLoad( ids( 3 ) );
|
||||||
|
assertEquals( 3, entities.size() );
|
||||||
|
|
||||||
|
SimpleEntity deletedEntity = entities.stream().filter( simpleEntity -> simpleEntity.getId().equals( 2 ) ).findAny().orElse( null );
|
||||||
|
assertNotNull( deletedEntity );
|
||||||
|
|
||||||
|
final EntityEntry entry = ((SharedSessionContractImplementor) session).getPersistenceContext().getEntry( deletedEntity );
|
||||||
|
assertTrue( entry.getStatus() == Status.DELETED || entry.getStatus() == Status.GONE );
|
||||||
|
|
||||||
|
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?, ?)" ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@FailureExpected( reason = "CacheMode not yet implemented" )
|
||||||
|
public void testMultiLoadWithCacheModeIgnore(SessionFactoryScope scope) {
|
||||||
|
// do the multi-load, telling Hibernate to IGNORE the L2 cache -
|
||||||
|
// the end result should be that the cache is (still) empty afterwards
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
session.getTransaction().begin();
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.with( CacheMode.IGNORE )
|
||||||
|
.multiLoad( ids( 56 ) );
|
||||||
|
session.getTransaction().commit();
|
||||||
|
session.close();
|
||||||
|
|
||||||
|
assertEquals( 56, list.size() );
|
||||||
|
for ( SimpleEntity entity : list ) {
|
||||||
|
assertFalse( scope.getSessionFactory().getCache().containsEntity( SimpleEntity.class, entity.getId() ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultiLoadClearsBatchFetchQueue(SessionFactoryScope scope) {
|
||||||
|
final EntityKey entityKey = new EntityKey(
|
||||||
|
1,
|
||||||
|
scope.getSessionFactory().getEntityPersister( SimpleEntity.class.getName() )
|
||||||
|
);
|
||||||
|
|
||||||
|
scope.inTransaction(
|
||||||
|
session -> {
|
||||||
|
// create a proxy, which should add an entry to the BatchFetchQueue
|
||||||
|
SimpleEntity first = session.byId( SimpleEntity.class ).getReference( 1 );
|
||||||
|
assertTrue( session.getPersistenceContext()
|
||||||
|
.getBatchFetchQueue()
|
||||||
|
.containsEntityKey( entityKey ) );
|
||||||
|
|
||||||
|
// now bulk load, which should clean up the BatchFetchQueue entry
|
||||||
|
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class )
|
||||||
|
.enableSessionCheck( true )
|
||||||
|
.multiLoad( ids( 56 ) );
|
||||||
|
|
||||||
|
assertEquals( 56, list.size() );
|
||||||
|
assertFalse( session.getPersistenceContext()
|
||||||
|
.getBatchFetchQueue()
|
||||||
|
.containsEntityKey( entityKey ) );
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer[] ids(int count) {
|
||||||
|
Integer[] ids = new Integer[count];
|
||||||
|
for ( int i = 1; i <= count; i++ ) {
|
||||||
|
ids[i-1] = i;
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity( name = "SimpleEntity" )
|
||||||
|
@Table( name = "SimpleEntity" )
|
||||||
|
@Cacheable()
|
||||||
|
@BatchSize( size = 15 )
|
||||||
|
public static class SimpleEntity {
|
||||||
|
Integer id;
|
||||||
|
String text;
|
||||||
|
|
||||||
|
public SimpleEntity() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleEntity(Integer id, String text) {
|
||||||
|
this.id = id;
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Id
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,470 +0,0 @@
|
||||||
/*
|
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
|
||||||
*
|
|
||||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
|
||||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
|
||||||
*/
|
|
||||||
package org.hibernate.test.ops.multiLoad;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import javax.persistence.Cacheable;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.SharedCacheMode;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
|
|
||||||
import org.hibernate.CacheMode;
|
|
||||||
import org.hibernate.Session;
|
|
||||||
import org.hibernate.annotations.BatchSize;
|
|
||||||
import org.hibernate.boot.MetadataBuilder;
|
|
||||||
import org.hibernate.boot.SessionFactoryBuilder;
|
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
|
||||||
import org.hibernate.cache.spi.access.AccessType;
|
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
|
||||||
import org.hibernate.engine.spi.EntityEntry;
|
|
||||||
import org.hibernate.engine.spi.EntityKey;
|
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|
||||||
import org.hibernate.engine.spi.Status;
|
|
||||||
import org.hibernate.stat.Statistics;
|
|
||||||
|
|
||||||
import org.hibernate.testing.TestForIssue;
|
|
||||||
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
|
||||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Steve Ebersole
|
|
||||||
*/
|
|
||||||
public class MultiLoadTest extends BaseNonConfigCoreFunctionalTestCase {
|
|
||||||
|
|
||||||
private SQLStatementInterceptor sqlStatementInterceptor;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
|
||||||
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class[] getAnnotatedClasses() {
|
|
||||||
return new Class[] { SimpleEntity.class };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
|
||||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
|
||||||
ssrb.applySetting( AvailableSettings.USE_SECOND_LEVEL_CACHE, true );
|
|
||||||
ssrb.applySetting( AvailableSettings.GENERATE_STATISTICS, Boolean.TRUE.toString() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void configureMetadataBuilder(MetadataBuilder metadataBuilder) {
|
|
||||||
super.configureMetadataBuilder( metadataBuilder );
|
|
||||||
|
|
||||||
metadataBuilder.applySharedCacheMode( SharedCacheMode.ENABLE_SELECTIVE );
|
|
||||||
metadataBuilder.applyAccessType( AccessType.READ_WRITE );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
Session session = sessionFactory().openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
session.setCacheMode( CacheMode.IGNORE );
|
|
||||||
for ( int i = 1; i <= 60; i++ ) {
|
|
||||||
session.save( new SimpleEntity( i, "Entity #" + i ) );
|
|
||||||
}
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void after() {
|
|
||||||
Session session = sessionFactory().openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
session.createQuery( "delete SimpleEntity" ).executeUpdate();
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBasicMultiLoad() {
|
|
||||||
doInHibernate(
|
|
||||||
this::sessionFactory, session -> {
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids( 5 ) );
|
|
||||||
assertEquals( 5, list.size() );
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?,?,?,?)" ) );
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue( jiraKey = "HHH-10984" )
|
|
||||||
public void testUnflushedDeleteAndThenMultiLoad() {
|
|
||||||
doInHibernate(
|
|
||||||
this::sessionFactory, session -> {
|
|
||||||
// delete one of them (but do not flush)...
|
|
||||||
session.delete( session.load( SimpleEntity.class, 5 ) );
|
|
||||||
|
|
||||||
// as a baseline, assert based on how load() handles it
|
|
||||||
SimpleEntity s5 = session.load( SimpleEntity.class, 5 );
|
|
||||||
assertNotNull( s5 );
|
|
||||||
|
|
||||||
// and then, assert how get() handles it
|
|
||||||
s5 = session.get( SimpleEntity.class, 5 );
|
|
||||||
assertNull( s5 );
|
|
||||||
|
|
||||||
// finally assert how multiLoad handles it
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) );
|
|
||||||
assertEquals( 56, list.size() );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue( jiraKey = "HHH-10617" )
|
|
||||||
public void testDuplicatedRequestedIds() {
|
|
||||||
doInHibernate(
|
|
||||||
this::sessionFactory, session -> {
|
|
||||||
// ordered multiLoad
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( 1, 2, 3, 2, 2 );
|
|
||||||
assertEquals( 5, list.size() );
|
|
||||||
assertSame( list.get( 1 ), list.get( 3 ) );
|
|
||||||
assertSame( list.get( 1 ), list.get( 4 ) );
|
|
||||||
|
|
||||||
// un-ordered multiLoad
|
|
||||||
list = session.byMultipleIds( SimpleEntity.class ).enableOrderedReturn( false ).multiLoad( 1, 2, 3, 2, 2 );
|
|
||||||
assertEquals( 3, list.size() );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue( jiraKey = "HHH-10617" )
|
|
||||||
public void testNonExistentIdRequest() {
|
|
||||||
doInHibernate(
|
|
||||||
this::sessionFactory, session -> {
|
|
||||||
// ordered multiLoad
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( 1, 699, 2 );
|
|
||||||
assertEquals( 3, list.size() );
|
|
||||||
assertNull( list.get( 1 ) );
|
|
||||||
|
|
||||||
// un-ordered multiLoad
|
|
||||||
list = session.byMultipleIds( SimpleEntity.class ).enableOrderedReturn( false ).multiLoad( 1, 699, 2 );
|
|
||||||
assertEquals( 2, list.size() );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBasicMultiLoadWithManagedAndNoChecking() {
|
|
||||||
Session session = openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 );
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).multiLoad( ids(56) );
|
|
||||||
assertEquals( 56, list.size() );
|
|
||||||
// this check is HIGHLY specific to implementation in the batch loader
|
|
||||||
// which puts existing managed entities first...
|
|
||||||
assertSame( first, list.get( 0 ) );
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBasicMultiLoadWithManagedAndChecking() {
|
|
||||||
Session session = openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
SimpleEntity first = session.byId( SimpleEntity.class ).load( 1 );
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).enableSessionCheck( true ).multiLoad( ids(56) );
|
|
||||||
assertEquals( 56, list.size() );
|
|
||||||
// this check is HIGHLY specific to implementation in the batch loader
|
|
||||||
// which puts existing managed entities first...
|
|
||||||
assertSame( first, list.get( 0 ) );
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-12944")
|
|
||||||
public void testMultiLoadFrom2ndLevelCache() {
|
|
||||||
Statistics statistics = sessionFactory().getStatistics();
|
|
||||||
sessionFactory().getCache().evictAll();
|
|
||||||
statistics.clear();
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
// Load 1 of the items directly
|
|
||||||
SimpleEntity entity = session.get( SimpleEntity.class, 2 );
|
|
||||||
assertNotNull( entity );
|
|
||||||
|
|
||||||
assertEquals( 1, statistics.getSecondLevelCacheMissCount() );
|
|
||||||
assertEquals( 0, statistics.getSecondLevelCacheHitCount() );
|
|
||||||
assertEquals( 1, statistics.getSecondLevelCachePutCount() );
|
|
||||||
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
|
||||||
} );
|
|
||||||
|
|
||||||
statistics.clear();
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
// Validate that the entity is still in the Level 2 cache
|
|
||||||
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
|
||||||
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
|
||||||
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.NORMAL )
|
|
||||||
.enableSessionCheck( true )
|
|
||||||
.multiLoad( ids( 3 ) );
|
|
||||||
assertEquals( 3, entities.size() );
|
|
||||||
assertEquals( 1, statistics.getSecondLevelCacheHitCount() );
|
|
||||||
|
|
||||||
for(SimpleEntity entity: entities) {
|
|
||||||
assertTrue( session.contains( entity ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-12944")
|
|
||||||
public void testUnorderedMultiLoadFrom2ndLevelCache() {
|
|
||||||
Statistics statistics = sessionFactory().getStatistics();
|
|
||||||
sessionFactory().getCache().evictAll();
|
|
||||||
statistics.clear();
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
// Load 1 of the items directly
|
|
||||||
SimpleEntity entity = session.get( SimpleEntity.class, 2 );
|
|
||||||
assertNotNull( entity );
|
|
||||||
|
|
||||||
assertEquals( 1, statistics.getSecondLevelCacheMissCount() );
|
|
||||||
assertEquals( 0, statistics.getSecondLevelCacheHitCount() );
|
|
||||||
assertEquals( 1, statistics.getSecondLevelCachePutCount() );
|
|
||||||
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
|
||||||
} );
|
|
||||||
|
|
||||||
statistics.clear();
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
// Validate that the entity is still in the Level 2 cache
|
|
||||||
assertTrue( session.getSessionFactory().getCache().containsEntity( SimpleEntity.class, 2 ) );
|
|
||||||
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
|
||||||
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.NORMAL )
|
|
||||||
.enableSessionCheck( true )
|
|
||||||
.enableOrderedReturn( false )
|
|
||||||
.multiLoad( ids( 3 ) );
|
|
||||||
assertEquals( 3, entities.size() );
|
|
||||||
assertEquals( 1, statistics.getSecondLevelCacheHitCount() );
|
|
||||||
|
|
||||||
for(SimpleEntity entity: entities) {
|
|
||||||
assertTrue( session.contains( entity ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-12944")
|
|
||||||
public void testOrderedMultiLoadFrom2ndLevelCachePendingDelete() {
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
session.remove( session.find( SimpleEntity.class, 2 ) );
|
|
||||||
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
|
||||||
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.NORMAL )
|
|
||||||
.enableSessionCheck( true )
|
|
||||||
.enableOrderedReturn( true )
|
|
||||||
.multiLoad( ids( 3 ) );
|
|
||||||
assertEquals( 3, entities.size() );
|
|
||||||
|
|
||||||
assertNull( entities.get(1) );
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-12944")
|
|
||||||
public void testOrderedMultiLoadFrom2ndLevelCachePendingDeleteReturnRemoved() {
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
session.remove( session.find( SimpleEntity.class, 2 ) );
|
|
||||||
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
|
||||||
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.NORMAL )
|
|
||||||
.enableSessionCheck( true )
|
|
||||||
.enableOrderedReturn( true )
|
|
||||||
.enableReturnOfDeletedEntities( true )
|
|
||||||
.multiLoad( ids( 3 ) );
|
|
||||||
assertEquals( 3, entities.size() );
|
|
||||||
|
|
||||||
SimpleEntity deletedEntity = entities.get(1);
|
|
||||||
assertNotNull( deletedEntity );
|
|
||||||
|
|
||||||
final EntityEntry entry = ((SharedSessionContractImplementor) session).getPersistenceContext().getEntry( deletedEntity );
|
|
||||||
assertTrue( entry.getStatus() == Status.DELETED || entry.getStatus() == Status.GONE );
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-12944")
|
|
||||||
public void testUnorderedMultiLoadFrom2ndLevelCachePendingDelete() {
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
session.remove( session.find( SimpleEntity.class, 2 ) );
|
|
||||||
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
|
||||||
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.NORMAL )
|
|
||||||
.enableSessionCheck( true )
|
|
||||||
.enableOrderedReturn( false )
|
|
||||||
.multiLoad( ids( 3 ) );
|
|
||||||
assertEquals( 3, entities.size() );
|
|
||||||
|
|
||||||
assertTrue( entities.stream().anyMatch( Objects::isNull ) );
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@TestForIssue(jiraKey = "HHH-12944")
|
|
||||||
public void testUnorderedMultiLoadFrom2ndLevelCachePendingDeleteReturnRemoved() {
|
|
||||||
|
|
||||||
doInHibernate( this::sessionFactory, session -> {
|
|
||||||
session.remove( session.find( SimpleEntity.class, 2 ) );
|
|
||||||
|
|
||||||
sqlStatementInterceptor.getSqlQueries().clear();
|
|
||||||
|
|
||||||
// Multiload 3 items and ensure that multiload pulls 2 from the database & 1 from the cache.
|
|
||||||
List<SimpleEntity> entities = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.NORMAL )
|
|
||||||
.enableSessionCheck( true )
|
|
||||||
.enableOrderedReturn( false )
|
|
||||||
.enableReturnOfDeletedEntities( true )
|
|
||||||
.multiLoad( ids( 3 ) );
|
|
||||||
assertEquals( 3, entities.size() );
|
|
||||||
|
|
||||||
SimpleEntity deletedEntity = entities.stream().filter( simpleEntity -> simpleEntity.getId().equals( 2 ) ).findAny().orElse( null );
|
|
||||||
assertNotNull( deletedEntity );
|
|
||||||
|
|
||||||
final EntityEntry entry = ((SharedSessionContractImplementor) session).getPersistenceContext().getEntry( deletedEntity );
|
|
||||||
assertTrue( entry.getStatus() == Status.DELETED || entry.getStatus() == Status.GONE );
|
|
||||||
|
|
||||||
assertTrue( sqlStatementInterceptor.getSqlQueries().getFirst().endsWith( "id in (?,?)" ) );
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMultiLoadWithCacheModeIgnore() {
|
|
||||||
// do the multi-load, telling Hibernate to IGNORE the L2 cache -
|
|
||||||
// the end result should be that the cache is (still) empty afterwards
|
|
||||||
Session session = openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class )
|
|
||||||
.with( CacheMode.IGNORE )
|
|
||||||
.multiLoad( ids(56) );
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
|
|
||||||
assertEquals( 56, list.size() );
|
|
||||||
for ( SimpleEntity entity : list ) {
|
|
||||||
assertFalse( sessionFactory().getCache().containsEntity( SimpleEntity.class, entity.getId() ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMultiLoadClearsBatchFetchQueue() {
|
|
||||||
final EntityKey entityKey = new EntityKey(
|
|
||||||
1,
|
|
||||||
sessionFactory().getEntityPersister( SimpleEntity.class.getName() )
|
|
||||||
);
|
|
||||||
|
|
||||||
Session session = openSession();
|
|
||||||
session.getTransaction().begin();
|
|
||||||
// create a proxy, which should add an entry to the BatchFetchQueue
|
|
||||||
SimpleEntity first = session.byId( SimpleEntity.class ).getReference( 1 );
|
|
||||||
assertTrue( ( (SessionImplementor) session ).getPersistenceContext().getBatchFetchQueue().containsEntityKey( entityKey ) );
|
|
||||||
|
|
||||||
// now bulk load, which should clean up the BatchFetchQueue entry
|
|
||||||
List<SimpleEntity> list = session.byMultipleIds( SimpleEntity.class ).enableSessionCheck( true ).multiLoad( ids(56) );
|
|
||||||
|
|
||||||
assertEquals( 56, list.size() );
|
|
||||||
assertFalse( ( (SessionImplementor) session ).getPersistenceContext().getBatchFetchQueue().containsEntityKey( entityKey ) );
|
|
||||||
|
|
||||||
session.getTransaction().commit();
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Integer[] ids(int count) {
|
|
||||||
Integer[] ids = new Integer[count];
|
|
||||||
for ( int i = 1; i <= count; i++ ) {
|
|
||||||
ids[i-1] = i;
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Entity( name = "SimpleEntity" )
|
|
||||||
@Table( name = "SimpleEntity" )
|
|
||||||
@Cacheable()
|
|
||||||
@BatchSize( size = 15 )
|
|
||||||
public static class SimpleEntity {
|
|
||||||
Integer id;
|
|
||||||
String text;
|
|
||||||
|
|
||||||
public SimpleEntity() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public SimpleEntity(Integer id, String text) {
|
|
||||||
this.id = id;
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Id
|
|
||||||
public Integer getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Integer id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,6 +12,10 @@ import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import javax.persistence.SharedCacheMode;
|
||||||
|
|
||||||
|
import org.hibernate.cache.spi.access.AccessType;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.domain.DomainModelDescriptor;
|
import org.hibernate.testing.orm.domain.DomainModelDescriptor;
|
||||||
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
import org.hibernate.testing.orm.domain.StandardDomainModel;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
|
@ -96,6 +100,10 @@ public @interface DomainModel {
|
||||||
ExtraQueryImport[] extraQueryImports() default {};
|
ExtraQueryImport[] extraQueryImports() default {};
|
||||||
Class<?>[] extraQueryImportClasses() default {};
|
Class<?>[] extraQueryImportClasses() default {};
|
||||||
|
|
||||||
|
SharedCacheMode sharedCacheMode() default SharedCacheMode.ENABLE_SELECTIVE;
|
||||||
|
|
||||||
|
AccessType accessType() default AccessType.READ_WRITE;
|
||||||
|
|
||||||
@interface ExtraQueryImport {
|
@interface ExtraQueryImport {
|
||||||
String name();
|
String name();
|
||||||
Class<?> importedClass();
|
Class<?> importedClass();
|
||||||
|
|
Loading…
Reference in New Issue