HHH-16750 ClassCastException when an Entity with an ElementCollection has an EmbeddableId with just one field and Batch is enabled
This commit is contained in:
parent
254d01484d
commit
37aa5b21a3
|
@ -35,7 +35,7 @@ public abstract class AbstractCollectionBatchLoader implements CollectionBatchLo
|
||||||
|
|
||||||
private final int keyJdbcCount;
|
private final int keyJdbcCount;
|
||||||
|
|
||||||
private final CollectionLoaderSingleKey singleKeyLoader;
|
final CollectionLoaderSingleKey singleKeyLoader;
|
||||||
|
|
||||||
public AbstractCollectionBatchLoader(
|
public AbstractCollectionBatchLoader(
|
||||||
int domainBatchSize,
|
int domainBatchSize,
|
||||||
|
@ -91,10 +91,14 @@ public abstract class AbstractCollectionBatchLoader implements CollectionBatchLo
|
||||||
|
|
||||||
initializeKeys( key, keys, session );
|
initializeKeys( key, keys, session );
|
||||||
|
|
||||||
|
finishInitializingKeys( keys, session );
|
||||||
|
|
||||||
final CollectionKey collectionKey = new CollectionKey( getLoadable().getCollectionDescriptor(), key );
|
final CollectionKey collectionKey = new CollectionKey( getLoadable().getCollectionDescriptor(), key );
|
||||||
return session.getPersistenceContext().getCollection( collectionKey );
|
return session.getPersistenceContext().getCollection( collectionKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract void finishInitializingKeys(Object[] key, SharedSessionContractImplementor session);
|
||||||
|
|
||||||
protected void finishInitializingKey(Object key, SharedSessionContractImplementor session) {
|
protected void finishInitializingKey(Object key, SharedSessionContractImplementor session) {
|
||||||
if ( key == null ) {
|
if ( key == null ) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.loader.ast.internal;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
|
|
||||||
import org.hibernate.LockOptions;
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.collection.spi.PersistentCollection;
|
||||||
|
import org.hibernate.engine.spi.CollectionKey;
|
||||||
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;
|
||||||
|
@ -18,7 +20,6 @@ import org.hibernate.loader.ast.spi.SqlArrayMultiKeyLoader;
|
||||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
|
@ -32,6 +33,8 @@ import org.hibernate.sql.results.internal.RowTransformerStandardImpl;
|
||||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||||
import org.hibernate.type.BasicType;
|
import org.hibernate.type.BasicType;
|
||||||
|
|
||||||
|
import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.hasSingleId;
|
||||||
|
import static org.hibernate.loader.ast.internal.MultiKeyLoadHelper.trimIdBatch;
|
||||||
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_DEBUG_ENABLED;
|
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_DEBUG_ENABLED;
|
||||||
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
|
import static org.hibernate.loader.ast.internal.MultiKeyLoadLogging.MULTI_KEY_LOAD_LOGGER;
|
||||||
|
|
||||||
|
@ -67,15 +70,12 @@ public class CollectionBatchLoaderArrayParam
|
||||||
final Class<?> keyType = keyDescriptor.getJavaType().getJavaTypeClass();
|
final Class<?> keyType = keyDescriptor.getJavaType().getJavaTypeClass();
|
||||||
final Class<?> arrayClass = Array.newInstance( keyType, 0 ).getClass();
|
final Class<?> arrayClass = Array.newInstance( keyType, 0 ).getClass();
|
||||||
|
|
||||||
// this typecast is always safe because we don't instantiate this class unless the FK is "simple"
|
|
||||||
final SimpleForeignKeyDescriptor simpleKeyDescriptor = (SimpleForeignKeyDescriptor) keyDescriptor;
|
|
||||||
|
|
||||||
final BasicType<?> arrayBasicType = getSessionFactory().getTypeConfiguration()
|
final BasicType<?> arrayBasicType = getSessionFactory().getTypeConfiguration()
|
||||||
.getBasicTypeRegistry()
|
.getBasicTypeRegistry()
|
||||||
.getRegisteredType( arrayClass );
|
.getRegisteredType( arrayClass );
|
||||||
arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping(
|
arrayJdbcMapping = MultiKeyLoadHelper.resolveArrayJdbcMapping(
|
||||||
arrayBasicType,
|
arrayBasicType,
|
||||||
simpleKeyDescriptor.getJdbcMapping(),
|
keyDescriptor.getSingleJdbcMapping(),
|
||||||
arrayClass,
|
arrayClass,
|
||||||
getSessionFactory()
|
getSessionFactory()
|
||||||
);
|
);
|
||||||
|
@ -83,7 +83,7 @@ public class CollectionBatchLoaderArrayParam
|
||||||
jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping );
|
jdbcParameter = new JdbcParameterImpl( arrayJdbcMapping );
|
||||||
sqlSelect = LoaderSelectBuilder.createSelectBySingleArrayParameter(
|
sqlSelect = LoaderSelectBuilder.createSelectBySingleArrayParameter(
|
||||||
getLoadable(),
|
getLoadable(),
|
||||||
simpleKeyDescriptor.getKeyPart(),
|
keyDescriptor.getKeyPart(),
|
||||||
getInfluencers(),
|
getInfluencers(),
|
||||||
LockOptions.NONE,
|
LockOptions.NONE,
|
||||||
jdbcParameter,
|
jdbcParameter,
|
||||||
|
@ -97,6 +97,72 @@ public class CollectionBatchLoaderArrayParam
|
||||||
.translate( JdbcParameterBindings.NO_BINDINGS, QueryOptions.NONE );
|
.translate( JdbcParameterBindings.NO_BINDINGS, QueryOptions.NONE );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PersistentCollection<?> load(Object keyBeingLoaded, SharedSessionContractImplementor session) {
|
||||||
|
final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor();
|
||||||
|
if ( keyDescriptor.isEmbedded() ) {
|
||||||
|
assert keyDescriptor.getJdbcTypeCount() == 1;
|
||||||
|
return loadEmbeddable( keyBeingLoaded, session, keyDescriptor );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return super.load( keyBeingLoaded, session );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private PersistentCollection<?> loadEmbeddable(
|
||||||
|
Object keyBeingLoaded,
|
||||||
|
SharedSessionContractImplementor session,
|
||||||
|
ForeignKeyDescriptor keyDescriptor) {
|
||||||
|
if ( MULTI_KEY_LOAD_DEBUG_ENABLED ) {
|
||||||
|
MULTI_KEY_LOAD_LOGGER.debugf(
|
||||||
|
"Batch fetching collection: %s.%s",
|
||||||
|
getLoadable().getNavigableRole().getFullPath(), keyBeingLoaded
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int length = getDomainBatchSize();
|
||||||
|
final Object[] keysToInitialize = (Object[]) Array.newInstance(
|
||||||
|
keyDescriptor.getSingleJdbcMapping().getJdbcJavaType().getJavaTypeClass(),
|
||||||
|
length
|
||||||
|
);
|
||||||
|
final Object[] embeddedKeys = (Object[]) Array.newInstance(
|
||||||
|
keyDescriptor.getJavaType().getJavaTypeClass(),
|
||||||
|
length
|
||||||
|
);
|
||||||
|
session.getPersistenceContextInternal().getBatchFetchQueue()
|
||||||
|
.collectBatchLoadableCollectionKeys(
|
||||||
|
length,
|
||||||
|
(index, key) ->
|
||||||
|
keyDescriptor.forEachJdbcValue( key, (i, value, jdbcMapping) -> {
|
||||||
|
keysToInitialize[index] = value;
|
||||||
|
embeddedKeys[index] = key;
|
||||||
|
}, session )
|
||||||
|
,
|
||||||
|
keyBeingLoaded,
|
||||||
|
getLoadable()
|
||||||
|
);
|
||||||
|
// now trim down the array to the number of keys we found
|
||||||
|
final Object[] keys = trimIdBatch( length, keysToInitialize );
|
||||||
|
|
||||||
|
if ( hasSingleId( keys ) ) {
|
||||||
|
return singleKeyLoader.load( keyBeingLoaded, session );
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeKeys( keyBeingLoaded, keys, session );
|
||||||
|
|
||||||
|
for ( Object initializedKey : embeddedKeys ) {
|
||||||
|
if ( initializedKey != null ) {
|
||||||
|
finishInitializingKey( initializedKey, session );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final CollectionKey collectionKey = new CollectionKey(
|
||||||
|
getLoadable().getCollectionDescriptor(),
|
||||||
|
keysToInitialize
|
||||||
|
);
|
||||||
|
return session.getPersistenceContext().getCollection( collectionKey );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void initializeKeys(Object key, Object[] keysToInitialize, SharedSessionContractImplementor session) {
|
void initializeKeys(Object key, Object[] keysToInitialize, SharedSessionContractImplementor session) {
|
||||||
if ( MULTI_KEY_LOAD_DEBUG_ENABLED ) {
|
if ( MULTI_KEY_LOAD_DEBUG_ENABLED ) {
|
||||||
|
@ -134,9 +200,36 @@ public class CollectionBatchLoaderArrayParam
|
||||||
RowTransformerStandardImpl.instance(),
|
RowTransformerStandardImpl.instance(),
|
||||||
ListResultsConsumer.UniqueSemantic.FILTER
|
ListResultsConsumer.UniqueSemantic.FILTER
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for ( Object initializedKey : keysToInitialize ) {
|
@Override
|
||||||
|
void finishInitializingKeys(Object[] keys, SharedSessionContractImplementor session) {
|
||||||
|
for ( Object initializedKey : keys ) {
|
||||||
finishInitializingKey( initializedKey, session );
|
finishInitializingKey( initializedKey, session );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Object[] resolveKeysToInitialize(Object keyBeingLoaded, SharedSessionContractImplementor session) {
|
||||||
|
final ForeignKeyDescriptor keyDescriptor = getLoadable().getKeyDescriptor();
|
||||||
|
if( keyDescriptor.isEmbedded()){
|
||||||
|
assert keyDescriptor.getJdbcTypeCount() == 1;
|
||||||
|
final int length = getDomainBatchSize();
|
||||||
|
final Object[] keysToInitialize = (Object[]) Array.newInstance( keyDescriptor.getSingleJdbcMapping().getJdbcJavaType().getJavaTypeClass(), length );
|
||||||
|
session.getPersistenceContextInternal().getBatchFetchQueue()
|
||||||
|
.collectBatchLoadableCollectionKeys(
|
||||||
|
length,
|
||||||
|
(index, key) ->
|
||||||
|
keyDescriptor.forEachJdbcValue( key, (i, value, jdbcMapping) -> {
|
||||||
|
keysToInitialize[index] = value;
|
||||||
|
}, session )
|
||||||
|
,
|
||||||
|
keyBeingLoaded,
|
||||||
|
getLoadable()
|
||||||
|
);
|
||||||
|
// now trim down the array to the number of keys we found
|
||||||
|
return trimIdBatch( length, keysToInitialize );
|
||||||
|
}
|
||||||
|
return super.resolveKeysToInitialize( keyBeingLoaded, session );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,4 +152,8 @@ public class CollectionBatchLoaderInPredicate
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void finishInitializingKeys(Object[] key, SharedSessionContractImplementor session) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.Restrictable;
|
import org.hibernate.metamodel.mapping.Restrictable;
|
||||||
import org.hibernate.metamodel.mapping.SelectableMapping;
|
import org.hibernate.metamodel.mapping.SelectableMapping;
|
||||||
|
import org.hibernate.metamodel.mapping.ValuedModelPart;
|
||||||
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
|
||||||
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
|
||||||
|
@ -142,7 +143,7 @@ public class LoaderSelectBuilder {
|
||||||
*/
|
*/
|
||||||
public static SelectStatement createSelectBySingleArrayParameter(
|
public static SelectStatement createSelectBySingleArrayParameter(
|
||||||
Loadable loadable,
|
Loadable loadable,
|
||||||
BasicValuedModelPart restrictedPart,
|
ValuedModelPart restrictedPart,
|
||||||
LoadQueryInfluencers influencers,
|
LoadQueryInfluencers influencers,
|
||||||
LockOptions lockOptions,
|
LockOptions lockOptions,
|
||||||
JdbcParameter jdbcArrayParameter,
|
JdbcParameter jdbcArrayParameter,
|
||||||
|
@ -202,9 +203,10 @@ public class LoaderSelectBuilder {
|
||||||
QuerySpec rootQuerySpec,
|
QuerySpec rootQuerySpec,
|
||||||
NavigablePath rootNavigablePath,
|
NavigablePath rootNavigablePath,
|
||||||
TableGroup rootTableGroup,
|
TableGroup rootTableGroup,
|
||||||
BasicValuedModelPart restrictedPart,
|
ValuedModelPart restrictedPart,
|
||||||
JdbcParameter jdbcArrayParameter,
|
JdbcParameter jdbcArrayParameter,
|
||||||
LoaderSqlAstCreationState sqlAstCreationState) {
|
LoaderSqlAstCreationState sqlAstCreationState) {
|
||||||
|
assert restrictedPart.getJdbcTypeCount() == 1;
|
||||||
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
|
||||||
final SelectableMapping restrictedPartMapping = restrictedPart.getSelectable( 0 );
|
final SelectableMapping restrictedPartMapping = restrictedPart.getSelectable( 0 );
|
||||||
final NavigablePath restrictionPath = rootNavigablePath.append( restrictedPart.getNavigableRole().getNavigableName() );
|
final NavigablePath restrictionPath = rootNavigablePath.append( restrictedPart.getNavigableRole().getNavigableName() );
|
||||||
|
|
|
@ -198,4 +198,7 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart
|
||||||
ValuedModelPart getModelPart();
|
ValuedModelPart getModelPart();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isEmbedded();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -670,4 +670,9 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
|
||||||
public boolean hasPartitionedSelectionMapping() {
|
public boolean hasPartitionedSelectionMapping() {
|
||||||
return keySide.getModelPart().hasPartitionedSelectionMapping();
|
return keySide.getModelPart().hasPartitionedSelectionMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmbedded() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -719,4 +719,9 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
|
||||||
targetSide.getModelPart().getSelectionExpression()
|
targetSide.getModelPart().getSelectionExpression()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmbedded() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue