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.spi.RootGraphImplementor;
|
||||
import org.hibernate.internal.FilterImpl;
|
||||
import org.hibernate.loader.spi.InternalFetchProfile;
|
||||
import org.hibernate.loader.spi.CascadingFetchProfile;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,7 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private InternalFetchProfile enabledInternalFetchProfile;
|
||||
private CascadingFetchProfile enabledCascadingFetchProfile;
|
||||
|
||||
//Lazily initialized!
|
||||
private HashSet<String> enabledFetchProfileNames;
|
||||
|
@ -69,21 +69,21 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
|
||||
// internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
public void withInternalFetchProfile(InternalFetchProfile profile, InternalFetchProfileAction action) {
|
||||
final InternalFetchProfile previous = this.enabledInternalFetchProfile;
|
||||
this.enabledInternalFetchProfile = profile;
|
||||
public void withInternalFetchProfile(CascadingFetchProfile profile, InternalFetchProfileAction action) {
|
||||
final CascadingFetchProfile previous = this.enabledCascadingFetchProfile;
|
||||
this.enabledCascadingFetchProfile = profile;
|
||||
action.performAction();
|
||||
this.enabledInternalFetchProfile = previous;
|
||||
this.enabledCascadingFetchProfile = previous;
|
||||
}
|
||||
|
||||
public <T> T fromInternalFetchProfile(InternalFetchProfile profile, Supplier<T> supplier) {
|
||||
final InternalFetchProfile previous = this.enabledInternalFetchProfile;
|
||||
this.enabledInternalFetchProfile = profile;
|
||||
public <T> T fromInternalFetchProfile(CascadingFetchProfile profile, Supplier<T> supplier) {
|
||||
final CascadingFetchProfile previous = this.enabledCascadingFetchProfile;
|
||||
this.enabledCascadingFetchProfile = profile;
|
||||
try {
|
||||
return supplier.get();
|
||||
}
|
||||
finally {
|
||||
this.enabledInternalFetchProfile = previous;
|
||||
this.enabledCascadingFetchProfile = previous;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,34 +92,34 @@ public class LoadQueryInfluencers implements Serializable {
|
|||
void performAction();
|
||||
}
|
||||
|
||||
public InternalFetchProfile getEnabledInternalFetchProfile() {
|
||||
return enabledInternalFetchProfile;
|
||||
public CascadingFetchProfile getEnabledCascadingFetchProfile() {
|
||||
return enabledCascadingFetchProfile;
|
||||
}
|
||||
|
||||
public void setEnabledInternalFetchProfile(InternalFetchProfile enabledInternalFetchProfile) {
|
||||
public void setEnabledCascadingFetchProfile(CascadingFetchProfile enabledCascadingFetchProfile) {
|
||||
if ( sessionFactory == null ) {
|
||||
// thats the signal that this is the immutable, context-less
|
||||
// variety
|
||||
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
|
||||
public String getInternalFetchProfile() {
|
||||
return getEnabledInternalFetchProfile().getLegacyName();
|
||||
return getEnabledCascadingFetchProfile().getLegacyName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setEnabledInternalFetchProfile} instead
|
||||
* @deprecated Use {@link #setEnabledCascadingFetchProfile} instead
|
||||
*/
|
||||
@Deprecated
|
||||
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.internal.CoreLogging;
|
||||
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.proxy.HibernateProxy;
|
||||
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)
|
||||
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
||||
InternalFetchProfile.MERGE,
|
||||
CascadingFetchProfile.MERGE,
|
||||
() -> source.get( entityName, clonedIdentifier )
|
||||
|
||||
);
|
||||
|
|
|
@ -30,7 +30,7 @@ import org.hibernate.event.spi.RefreshEvent;
|
|||
import org.hibernate.event.spi.RefreshEventListener;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
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.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
@ -170,7 +170,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener {
|
|||
evictCachedCollections( persister, id, source );
|
||||
|
||||
final Object result = source.getLoadQueryInfluencers().fromInternalFetchProfile(
|
||||
InternalFetchProfile.REFRESH,
|
||||
CascadingFetchProfile.REFRESH,
|
||||
() -> 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;
|
||||
|
||||
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.loader.spi.CollectionLoader;
|
||||
|
@ -24,7 +27,6 @@ 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.internal.JdbcSelectExecutorStandardImpl;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Main implementation of CollectionLoader for handling a load of a single collection-key
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SingleCollectionKeyLoader implements CollectionLoader {
|
||||
private final PluralAttributeMapping pluralAttributeMapping;
|
||||
private final PluralAttributeMapping attributeMapping;
|
||||
|
||||
private final int keyJdbcCount;
|
||||
|
||||
private final SelectStatement sqlAst;
|
||||
private final List<JdbcParameter> jdbcParameters;
|
||||
|
||||
public SingleCollectionKeyLoader(
|
||||
PluralAttributeMapping pluralAttributeMapping,
|
||||
SelectStatement sqlAst, List<JdbcParameter> jdbcParameters) {
|
||||
this.pluralAttributeMapping = pluralAttributeMapping;
|
||||
this.sqlAst = sqlAst;
|
||||
this.jdbcParameters = jdbcParameters;
|
||||
PluralAttributeMapping attributeMapping,
|
||||
LoadQueryInfluencers influencers,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.attributeMapping = attributeMapping;
|
||||
|
||||
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
|
||||
public PluralAttributeMapping getLoadable() {
|
||||
return pluralAttributeMapping;
|
||||
return attributeMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
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 JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
|
@ -65,13 +86,11 @@ public class SingleCollectionKeyLoader implements CollectionLoader {
|
|||
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl(
|
||||
pluralAttributeMapping.getKeyDescriptor().getJdbcTypeCount( sessionFactory.getTypeConfiguration() )
|
||||
);
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount );
|
||||
|
||||
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
||||
|
||||
pluralAttributeMapping.getKeyDescriptor().visitJdbcValues(
|
||||
attributeMapping.getKeyDescriptor().visitJdbcValues(
|
||||
key,
|
||||
Clause.WHERE,
|
||||
(value, type) -> {
|
||||
|
@ -96,7 +115,7 @@ public class SingleCollectionKeyLoader implements CollectionLoader {
|
|||
);
|
||||
assert !paramItr.hasNext();
|
||||
|
||||
JdbcSelectExecutorStandardImpl.INSTANCE.list(
|
||||
jdbcServices.getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
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.SessionFactoryImplementor;
|
||||
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.sql.ast.tree.select.SelectStatement;
|
||||
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 {
|
||||
private EnumMap<LockMode, SingleIdLoadPlan> selectByLockMode = new EnumMap<>( LockMode.class );
|
||||
private EnumMap<InternalFetchProfile, SingleIdLoadPlan> selectByInternalCascadeProfile;
|
||||
private EnumMap<CascadingFetchProfile, SingleIdLoadPlan> selectByInternalCascadeProfile;
|
||||
|
||||
private AtomicInteger nonReusablePlansGenerated = new AtomicInteger();
|
||||
|
||||
|
@ -71,14 +71,14 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
|||
return createLoadPlan( lockOptions, loadQueryInfluencers, session.getFactory() );
|
||||
}
|
||||
|
||||
final InternalFetchProfile enabledInternalFetchProfile = loadQueryInfluencers.getEnabledInternalFetchProfile();
|
||||
if ( enabledInternalFetchProfile != null ) {
|
||||
final CascadingFetchProfile enabledCascadingFetchProfile = loadQueryInfluencers.getEnabledCascadingFetchProfile();
|
||||
if ( enabledCascadingFetchProfile != null ) {
|
||||
if ( LockMode.UPGRADE.greaterThan( lockOptions.getLockMode() ) ) {
|
||||
if ( selectByInternalCascadeProfile == null ) {
|
||||
selectByInternalCascadeProfile = new EnumMap<>( InternalFetchProfile.class );
|
||||
selectByInternalCascadeProfile = new EnumMap<>( CascadingFetchProfile.class );
|
||||
}
|
||||
else {
|
||||
final SingleIdLoadPlan existing = selectByInternalCascadeProfile.get( enabledInternalFetchProfile );
|
||||
final SingleIdLoadPlan existing = selectByInternalCascadeProfile.get( enabledCascadingFetchProfile );
|
||||
if ( existing != null ) {
|
||||
//noinspection unchecked
|
||||
return existing;
|
||||
|
@ -90,7 +90,7 @@ public class SingleIdEntityLoaderStandardImpl<T> extends SingleIdEntityLoaderSup
|
|||
loadQueryInfluencers,
|
||||
session.getFactory()
|
||||
);
|
||||
selectByInternalCascadeProfile.put( enabledInternalFetchProfile, plan );
|
||||
selectByInternalCascadeProfile.put( enabledCascadingFetchProfile, plan );
|
||||
return plan;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ import org.hibernate.internal.util.StringHelper;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public enum InternalFetchProfile {
|
||||
public enum CascadingFetchProfile {
|
||||
MERGE( "merge" ),
|
||||
REFRESH( "refresh" );
|
||||
|
||||
private final String legacyName;
|
||||
|
||||
InternalFetchProfile(String legacyName) {
|
||||
CascadingFetchProfile(String legacyName) {
|
||||
this.legacyName = legacyName;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ public enum InternalFetchProfile {
|
|||
return legacyName;
|
||||
}
|
||||
|
||||
public static InternalFetchProfile fromLegacyName(String legacyName) {
|
||||
public static CascadingFetchProfile fromLegacyName(String legacyName) {
|
||||
if ( StringHelper.isEmpty( legacyName ) ) {
|
||||
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.FetchTiming;
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.results.internal.domain.collection.EntityCollectionPartTableGroup;
|
||||
|
@ -26,14 +28,30 @@ import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
|
|||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityCollectionPart implements CollectionPart, EntityValuedModelPart {
|
||||
public class EntityCollectionPart implements CollectionPart, EntityAssociationMapping, EntityValuedModelPart {
|
||||
private final Nature nature;
|
||||
private final EntityMappingType entityMappingType;
|
||||
|
||||
private ModelPart fkTargetModelPart;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public EntityCollectionPart(Nature nature, EntityMappingType entityMappingType) {
|
||||
public EntityCollectionPart(
|
||||
Nature nature,
|
||||
EntityMappingType entityMappingType,
|
||||
String fkTargetModelPartName,
|
||||
MappingModelCreationProcess creationProcess) {
|
||||
this.nature = nature;
|
||||
this.entityMappingType = entityMappingType;
|
||||
|
||||
creationProcess.registerInitializationCallback(
|
||||
() -> {
|
||||
fkTargetModelPart = fkTargetModelPartName == null
|
||||
? entityMappingType.getIdentifierMapping()
|
||||
: entityMappingType.findSubPart( fkTargetModelPartName, entityMappingType );
|
||||
assert fkTargetModelPart != null;
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,6 +69,16 @@ public class EntityCollectionPart implements CollectionPart, EntityValuedModelPa
|
|||
return entityMappingType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getAssociatedEntityMappingType() {
|
||||
return getEntityMappingType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getKeyTargetMatchPart() {
|
||||
return fkTargetModelPart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JavaTypeDescriptor 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(
|
||||
|
@ -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(
|
||||
|
|
|
@ -136,9 +136,18 @@ public class PluralAttributeMappingImpl extends AbstractAttributeMapping impleme
|
|||
return baseIndex;
|
||||
}
|
||||
};
|
||||
|
||||
if ( collectionDescriptor instanceof Aware ) {
|
||||
( (Aware) collectionDescriptor ).injectAttributeMapping( this );
|
||||
}
|
||||
|
||||
if ( elementDescriptor instanceof Aware ) {
|
||||
( (Aware) elementDescriptor ).injectAttributeMapping( this );
|
||||
}
|
||||
|
||||
if ( indexDescriptor instanceof Aware ) {
|
||||
( (Aware) indexDescriptor ).injectAttributeMapping( this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,10 +10,12 @@ import org.hibernate.LockMode;
|
|||
import org.hibernate.engine.FetchStrategy;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.mapping.ToOne;
|
||||
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ManagedMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
|
||||
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.Fetch;
|
||||
import org.hibernate.sql.results.spi.FetchParent;
|
||||
import org.hibernate.sql.results.spi.Fetchable;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SingularAssociationAttributeMapping extends AbstractSingularAttributeMapping
|
||||
implements EntityValuedModelPart, TableGroupJoinProducer {
|
||||
implements EntityValuedModelPart, EntityAssociationMapping, TableGroupJoinProducer {
|
||||
private final String sqlAliasStem;
|
||||
private final boolean isNullable;
|
||||
private final boolean referringPrimaryKey;
|
||||
|
@ -278,14 +281,17 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
.getFromClauseAccess()
|
||||
.findTableGroup( parentParentNavigablePath )
|
||||
.getModelPart();
|
||||
final SingularAssociationAttributeMapping part = (SingularAssociationAttributeMapping) modelPart
|
||||
.findSubPart( panentNaviblePath.getLocalName(), null );
|
||||
// final SingularAssociationAttributeMapping part = (SingularAssociationAttributeMapping) modelPart
|
||||
// .findSubPart( panentNaviblePath.getLocalName(), null );
|
||||
final EntityAssociationMapping part = (EntityAssociationMapping) modelPart.findSubPart( panentNaviblePath.getLocalName(), null );
|
||||
|
||||
if ( panentNaviblePath.getLocalName().equals( referencedPropertyName )
|
||||
&& part.getFetchableName().equals( referencedPropertyName ) ) {
|
||||
return true;
|
||||
}
|
||||
else if ( part.getReferencedPropertyName() != null
|
||||
&& part.getReferencedPropertyName().equals( getAttributeName() ) ) {
|
||||
else if ( part.getKeyTargetMatchPart() != null
|
||||
// && part.getKeyTargetMatchPart().equals( this ) ) {
|
||||
&& part.getKeyTargetMatchPart().getPartName().equals( getAttributeName() ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -300,4 +306,14 @@ public class SingularAssociationAttributeMapping extends AbstractSingularAttribu
|
|||
public boolean isUnwrapProxy() {
|
||||
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.Filter;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
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.profile.Fetch;
|
||||
import org.hibernate.engine.profile.FetchProfile;
|
||||
import org.hibernate.engine.spi.CollectionKey;
|
||||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
|
||||
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.Expectations;
|
||||
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.SubSelectFetchCollectionLoader;
|
||||
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.predicate.ComparisonPredicate;
|
||||
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.type.AnyType;
|
||||
import org.hibernate.type.AssociationType;
|
||||
|
@ -859,21 +855,12 @@ public abstract class AbstractCollectionPersister
|
|||
}
|
||||
|
||||
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(
|
||||
attributeMapping,
|
||||
null,
|
||||
attributeMapping.getKeyDescriptor(),
|
||||
null,
|
||||
1,
|
||||
loadQueryInfluencers,
|
||||
LockOptions.READ,
|
||||
jdbcParameters::add,
|
||||
getFactory()
|
||||
);
|
||||
|
||||
return new SingleCollectionKeyLoader( attributeMapping, sqlAst, jdbcParameters );
|
||||
return new SingleCollectionKeyLoader( attributeMapping, loadQueryInfluencers, getFactory() );
|
||||
}
|
||||
|
||||
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.Expectations;
|
||||
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
||||
import org.hibernate.loader.BatchFetchStyle;
|
||||
import org.hibernate.loader.custom.sql.SQLQueryParser;
|
||||
import org.hibernate.loader.entity.BatchingEntityLoaderBuilder;
|
||||
import org.hibernate.loader.entity.CascadeEntityLoader;
|
||||
import org.hibernate.loader.entity.EntityLoader;
|
||||
import org.hibernate.loader.entity.UniqueEntityLoader;
|
||||
import org.hibernate.loader.internal.SingleIdEntityLoaderDynamicBatch;
|
||||
import org.hibernate.loader.internal.MultiIdEntityLoaderStandardImpl;
|
||||
import org.hibernate.loader.internal.NaturalIdLoaderStandardImpl;
|
||||
import org.hibernate.loader.internal.Preparable;
|
||||
import org.hibernate.loader.internal.SingleIdEntityLoaderLegacyBatch;
|
||||
import org.hibernate.loader.internal.SingleIdEntityLoaderPaddedBatch;
|
||||
import org.hibernate.loader.internal.SingleIdEntityLoaderDynamicBatch;
|
||||
import org.hibernate.loader.internal.SingleIdEntityLoaderProvidedQueryImpl;
|
||||
import org.hibernate.loader.internal.SingleIdEntityLoaderStandardImpl;
|
||||
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.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
|
@ -172,7 +168,6 @@ import org.hibernate.query.ComparisonOperator;
|
|||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.mutation.internal.SqmMutationStrategyHelper;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
|
||||
import org.hibernate.sql.Alias;
|
||||
import org.hibernate.sql.Delete;
|
||||
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.SqlAliasStemHelper;
|
||||
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.Expression;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
|
@ -1016,23 +1012,8 @@ public abstract class AbstractEntityPersister
|
|||
EntityMappingType entityDescriptor,
|
||||
int batchSize,
|
||||
SessionFactoryImplementor factory) {
|
||||
final BatchFetchStyle batchFetchStyle = factory.getSettings().getBatchFetchStyle();
|
||||
|
||||
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")
|
||||
private boolean determineWhetherToInvalidateCache(
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* 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;
|
||||
package org.hibernate.orm.test.loading.multiLoad;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
|
@ -4,7 +4,7 @@
|
|||
* 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;
|
||||
package org.hibernate.orm.test.loading.multiLoad;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -25,8 +25,8 @@ import org.hibernate.annotations.BatchSize;
|
|||
import org.hibernate.annotations.Fetch;
|
||||
import org.hibernate.annotations.FetchMode;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.stat.Statistics;
|
||||
|
||||
import org.hibernate.testing.FailureExpected;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
|
@ -83,6 +83,7 @@ public class MultiLoadSubSelectCollectionTest extends BaseNonConfigCoreFunctiona
|
|||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-12740" )
|
||||
@FailureExpected( jiraKey = "none yet", message = "subselect-fetching not triggered")
|
||||
public void testSubselect() {
|
||||
doInHibernate(
|
||||
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.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.StandardDomainModel;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
|
@ -96,6 +100,10 @@ public @interface DomainModel {
|
|||
ExtraQueryImport[] extraQueryImports() default {};
|
||||
Class<?>[] extraQueryImportClasses() default {};
|
||||
|
||||
SharedCacheMode sharedCacheMode() default SharedCacheMode.ENABLE_SELECTIVE;
|
||||
|
||||
AccessType accessType() default AccessType.READ_WRITE;
|
||||
|
||||
@interface ExtraQueryImport {
|
||||
String name();
|
||||
Class<?> importedClass();
|
||||
|
|
Loading…
Reference in New Issue