initial discriminator hierarchy support

This commit is contained in:
Steve Ebersole 2019-10-22 14:45:14 -05:00
parent 3c65085123
commit f5c3ae181c
11 changed files with 446 additions and 251 deletions

View File

@ -20,13 +20,5 @@ public interface AttributeMapping extends ModelPart, ValueMapping {
ManagedMappingType getDeclaringType();
default boolean isDeclaredOnTypeOrSuperType(ManagedMappingType targetType) {
if ( getDeclaringType() instanceof EntityMappingType ) {
return ( (EntityMappingType) getDeclaringType() ).isTypeOrSuperType( targetType );
}
return false;
}
PropertyAccess getPropertyAccess();
}

View File

@ -278,8 +278,8 @@ public class EmbeddableMappingType implements ManagedMappingType {
}
@Override
public int getNumberOfDeclaredAttributeMappings() {
return getNumberOfAttributeMappings();
public AttributeMapping findAttributeMapping(String name) {
return attributeMappings.get( name );
}
@Override
@ -292,11 +292,6 @@ public class EmbeddableMappingType implements ManagedMappingType {
attributeMappings.values().forEach( action );
}
@Override
public boolean isTypeOrSuperType(ManagedMappingType targetType) {
return targetType == null || targetType == this;
}
@Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
return attributeMappings.get( name );

View File

@ -6,9 +6,17 @@
*/
package org.hibernate.metamodel.mapping;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.sql.results.spi.DomainResultAssembler;
import org.hibernate.sql.results.spi.Fetchable;
import org.hibernate.sql.results.spi.RowProcessingState;
import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY;
/**
* todo (6.0) : make this implement RootTableGroupProducer, etc instead of EntityPersister?
@ -29,6 +37,53 @@ public interface EntityMappingType extends ManagedMappingType {
return getEntityPersister().getEntityName();
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Inheritance
default AttributeMapping findDeclaredAttributeMapping(String name) {
return null;
}
/**
* Get the number of attributes defined on this class - do not access attributes defined on the super
*/
default int getNumberOfDeclaredAttributeMappings() {
return getDeclaredAttributeMappings().size();
}
/**
* Get access to the attributes defined on this class - do not access attributes defined on the super
*/
default Collection<AttributeMapping> getDeclaredAttributeMappings() {
throw new NotYetImplementedFor6Exception( getClass() );
}
/**
* Visit attributes defined on this class - do not visit attributes defined on the super
*/
default void visitDeclaredAttributeMappings(Consumer<AttributeMapping> action) {
throw new NotYetImplementedFor6Exception( getClass() );
}
default EntityMappingType getSuperMappingType() {
return null;
}
default boolean isTypeOrSuperType(EntityMappingType targetType) {
return targetType == this;
}
default boolean isTypeOrSuperType(ManagedMappingType targetType) {
if ( targetType instanceof EntityMappingType ) {
return isTypeOrSuperType( (EntityMappingType) targetType );
}
return false;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Special model parts - identifier, discriminator, etc
EntityIdentifierMapping getIdentifierMapping();
EntityVersionMapping getVersionMapping();
@ -39,19 +94,6 @@ public interface EntityMappingType extends ManagedMappingType {
NaturalIdMapping getNaturalIdMapping();
@Override
default boolean isTypeOrSuperType(ManagedMappingType targetType) {
if ( targetType instanceof EntityMappingType ) {
return isTypeOrSuperType( (EntityMappingType) targetType );
}
return false;
}
default boolean isTypeOrSuperType(EntityMappingType targetType) {
return targetType == this;
}
/**
* Visit the mappings, but limited to just attributes defined
* in the targetType or its super-type(s) if any.
@ -80,35 +122,41 @@ public interface EntityMappingType extends ManagedMappingType {
@Override
default void visitAttributeMappings(Consumer<AttributeMapping> action) {
visitAttributeMappings( action, null );
getAttributeMappings().forEach( action );
}
/**
* Visit the mappings, but limited to just attributes defined
* in the targetType or its super-type(s) if any.
*
* @apiNote Passing {@code null} indicates that subclasses should be included. This
* matches legacy non-TREAT behavior and meets the need for EntityGraph processing
*/
default void visitStateArrayContributors(Consumer<StateArrayContributorMapping> mappingConsumer, EntityMappingType targetType) {
visitAttributeMappings(
modelPart -> {
if ( modelPart instanceof StateArrayContributorMapping ) {
if ( targetType == null
|| ( (StateArrayContributorMapping) modelPart ).isDeclaredOnTypeOrSuperType( targetType ) ) {
mappingConsumer.accept( ( (StateArrayContributorMapping) modelPart ) );
}
// Customer <- DomesticCustomer <- OtherCustomer
default Object[] extractConcreteTypeStateValues(
Map<AttributeMapping, DomainResultAssembler> assemblerMapping,
RowProcessingState rowProcessingState) {
// todo (6.0) : getNumberOfAttributeMappings() needs to be fixed for this to work - bad walking of hierarchy
final Object[] values = new Object[ getNumberOfAttributeMappings() ];
visitFetchables(
new Consumer<Fetchable>() {
private int index;
@Override
public void accept(Fetchable fetchable) {
assert fetchable instanceof StateArrayContributorMapping;
final DomainResultAssembler assembler = assemblerMapping.get( fetchable );
final Object value = assembler == null ? UNFETCHED_PROPERTY : assembler.assemble( rowProcessingState );
values[index++] = value;
}
},
targetType
null
);
return values;
}
@Override
default void visitStateArrayContributors(Consumer<StateArrayContributorMapping> mappingConsumer) {
visitStateArrayContributors( mappingConsumer, null );
visitAttributeMappings(
attributeMapping -> mappingConsumer.accept( (StateArrayContributorMapping) attributeMapping )
);
}
// todo (6.0) : not sure we actually need this distinction at the mapping model level...
}

View File

@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping;
import java.util.Collection;
import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.sql.results.spi.FetchableContainer;
/**
@ -18,14 +19,23 @@ import org.hibernate.sql.results.spi.FetchableContainer;
* @author Steve Ebersole
*/
public interface ManagedMappingType extends MappingType, FetchableContainer {
int getNumberOfAttributeMappings();
int getNumberOfDeclaredAttributeMappings();
/**
* Get the number of attributes defined on this class and any supers
*/
int getNumberOfAttributeMappings();
default AttributeMapping findAttributeMapping(String name) {
return null;
}
/**
* Get access to the attributes defined on this class and any supers
*/
Collection<AttributeMapping> getAttributeMappings();
/**
* @todo (6.0) : consider dropping this in favor of a form passing the ManagedMappingType
* which indicates the type to limit the attribute search to (the type and its super-type)
* Visit attributes defined on this class and any supers
*/
void visitAttributeMappings(Consumer<AttributeMapping> action);
@ -42,6 +52,4 @@ public interface ManagedMappingType extends MappingType, FetchableContainer {
}
);
}
boolean isTypeOrSuperType(ManagedMappingType targetType);
}

View File

@ -22,6 +22,8 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.internal.domain.basic.BasicFetch;
import org.hibernate.sql.results.internal.domain.basic.BasicResult;
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;
@ -75,6 +77,31 @@ public class EntityDiscriminatorMappingImpl implements EntityDiscriminatorMappin
return FetchStrategy.IMMEDIATE_JOIN;
}
@Override
public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
String resultVariable,
DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, creationState );
//noinspection unchecked
return new BasicResult(
sqlSelection.getValuesArrayPosition(),
resultVariable,
getJavaTypeDescriptor(),
navigablePath
);
}
@Override
public void applySqlSelections(
NavigablePath navigablePath,
TableGroup tableGroup,
DomainResultCreationState creationState) {
resolveSqlSelection( tableGroup, creationState );
}
@Override
public Fetch generateFetch(
FetchParent fetchParent,
@ -104,6 +131,7 @@ public class EntityDiscriminatorMappingImpl implements EntityDiscriminatorMappin
creationState
);
}
private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();

View File

@ -5294,15 +5294,19 @@ public abstract class AbstractEntityPersister
accessOptimizer.setPropertyValues( object, values );
}
else {
int i = 0;
for ( Map.Entry<String, AttributeMapping> entries : declaredAttributeMappings.entrySet() ) {
AttributeMapping attributeMapping = entries.getValue();
final Setter setter = attributeMapping
.getPropertyAccess()
.getSetter();
setter.set( object, values[i], getFactory() );
i++;
visitFetchables(
fetchable -> {
final AttributeMapping attribute = (AttributeMapping) fetchable;
final int stateArrayPosition = ( (StateArrayContributorMapping) attribute ).getStateArrayPosition();
final Object value = values[stateArrayPosition];
if ( value != UNFETCHED_PROPERTY ) {
final Setter setter = attribute.getPropertyAccess().getSetter();
setter.set( object, value, getFactory() );
}
},
null
);
}
}
@ -5320,21 +5324,13 @@ public abstract class AbstractEntityPersister
}
else {
final Object[] values = new Object[ getNumberOfAttributeMappings() ];
final AtomicInteger index = new AtomicInteger( 0 );
//noinspection Convert2Lambda
visitAttributeMappings(
new Consumer<AttributeMapping>() {
@Override
public void accept(AttributeMapping mapping) {
values[ index.getAndIncrement() ] = mapping.getAttributeMetadataAccess()
.resolveAttributeMetadata( AbstractEntityPersister.this )
for ( int i = 0; i < attributeMappings.size(); i++ ) {
values[ i ] = attributeMappings.get( i ).getAttributeMetadataAccess()
.resolveAttributeMetadata( this )
.getPropertyAccess()
.getGetter()
.get( object );
}
}
);
return values;
}
@ -5441,16 +5437,15 @@ public abstract class AbstractEntityPersister
return accessOptimizer.getPropertyValues( entity );
}
final Object[] result = new Object[declaredAttributeMappings.size()];
int i = 0;
for ( Map.Entry<String, AttributeMapping> entries : declaredAttributeMappings.entrySet() ) {
AttributeMapping attributeMapping = entries.getValue();
result[i] = attributeMapping.getPropertyAccess().getGetter().getForInsert(
final Collection<AttributeMapping> attributeMappings = getAttributeMappings();
final Object[] result = new Object[this.attributeMappings.size()];
for ( int i = 0; i < this.attributeMappings.size(); i++ ) {
result[i] = this.attributeMappings.get( i ).getPropertyAccess().getGetter().getForInsert(
entity,
mergeMap,
session
);
i++;
}
return result;
}
@ -6059,31 +6054,35 @@ public abstract class AbstractEntityPersister
private EntityDiscriminatorMapping discriminatorMapping;
private SortedMap<String, AttributeMapping> declaredAttributeMappings = new TreeMap<>();
private Collection<AttributeMapping> attributeMappings;
private List<AttributeMapping> attributeMappings;
private ReflectionOptimizer.AccessOptimizer accessOptimizer;
@Override
public void visitAttributeMappings(Consumer<AttributeMapping> action) {
attributeMappings.forEach( action );
}
@Override
public void prepareMappingModel(MappingModelCreationProcess creationProcess) {
if ( identifierMapping != null ) {
return;
}
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
final PersistentClass bootEntityDescriptor = creationContext
.getBootModel()
.getEntityBinding( getEntityName() );
// todo (6.0) : should we create these only on root?
if ( superMappingType != null ) {
( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess );
this.identifierMapping = superMappingType.getIdentifierMapping();
this.versionMapping = superMappingType.getVersionMapping();
this.discriminatorMapping = superMappingType.getDiscriminatorMapping();
this.naturalIdMapping = superMappingType.getNaturalIdMapping();
}
else {
identifierMapping = creationProcess.processSubPart(
EntityIdentifierMapping.ROLE_LOCAL_NAME,
(role, creationProcess1) -> generateIdentifierMapping( creationProcess )
);
naturalIdMapping = null;
if ( getVersionType() == null ) {
versionMapping = null;
}
@ -6109,6 +6108,16 @@ public abstract class AbstractEntityPersister
);
}
// todo (6.0) : support for natural-id not yet implemented
naturalIdMapping = null;
}
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
final PersistentClass bootEntityDescriptor = creationContext
.getBootModel()
.getEntityBinding( getEntityName() );
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings();
@ -6117,6 +6126,10 @@ public abstract class AbstractEntityPersister
final NonIdentifierAttribute runtimeAttrDefinition = currentEntityMetamodel.getProperties()[i];
final Property bootProperty = bootEntityDescriptor.getProperty( runtimeAttrDefinition.getName() );
if ( superMappingType != null && superMappingType.findAttributeMapping( bootProperty.getName() ) != null ) {
// its defined on the super-type, skip it here
}
else {
declaredAttributeMappings.put(
runtimeAttrDefinition.getName(),
generateNonIdAttributeMapping(
@ -6128,6 +6141,9 @@ public abstract class AbstractEntityPersister
)
);
}
}
getAttributeMappings();
final ReflectionOptimizer reflectionOptimizer = representationStrategy.getReflectionOptimizer();
@ -6172,6 +6188,21 @@ public abstract class AbstractEntityPersister
return declaredAttributeMappings.size();
}
@Override
public Collection<AttributeMapping> getDeclaredAttributeMappings() {
return declaredAttributeMappings.values();
}
@Override
public void visitDeclaredAttributeMappings(Consumer<AttributeMapping> action) {
declaredAttributeMappings.forEach( (key,value) -> action.accept( value ) );
}
@Override
public EntityMappingType getSuperMappingType() {
return superMappingType;
}
@Override
public boolean isTypeOrSuperType(EntityMappingType targetType) {
if ( targetType == null ) {
@ -6317,11 +6348,7 @@ public abstract class AbstractEntityPersister
attributeMappings = new ArrayList<>();
if ( superMappingType != null ) {
superMappingType.visitAttributeMappings(
attributeMappings::add,
// only walk up the hierarchy
superMappingType
);
superMappingType.visitAttributeMappings( attributeMappings::add );
}
attributeMappings.addAll( declaredAttributeMappings.values() );
@ -6332,6 +6359,28 @@ public abstract class AbstractEntityPersister
return attributeMappings;
}
@Override
public AttributeMapping findDeclaredAttributeMapping(String name) {
return declaredAttributeMappings.get( name );
}
@Override
public AttributeMapping findAttributeMapping(String name) {
final AttributeMapping declaredAttribute = declaredAttributeMappings.get( name );
if ( declaredAttribute != null ) {
return declaredAttribute;
}
if ( superMappingType != null ) {
final AttributeMapping fromSuperType = superMappingType.findAttributeMapping( name );
if ( fromSuperType != null ) {
return fromSuperType;
}
}
return null;
}
@Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
LOG.tracef( "#findSubPart(`%s`)", name );
@ -6397,7 +6446,6 @@ public abstract class AbstractEntityPersister
EntityMappingType treatTargetType) {
if ( getIdentifierMapping() instanceof FetchableContainer ) {
// essentially means the entity has a composite id - ask the embeddable to visit its fetchables
// - todo (6.0) : determine whether this should call `#visitFetchables` or `#visitKeyFetchables`
( (FetchableContainer) getIdentifierMapping() ).visitFetchables( fetchableConsumer, treatTargetType );
}
@ -6408,41 +6456,23 @@ public abstract class AbstractEntityPersister
public void visitFetchables(
Consumer<Fetchable> fetchableConsumer,
EntityMappingType treatTargetType) {
//noinspection unchecked
visitStateArrayContributors(
(Consumer) fetchableConsumer,
treatTargetType
);
for ( int i = 0; i < attributeMappings.size(); i++ ) {
fetchableConsumer.accept( (Fetchable) attributeMappings.get( i ) );
}
@Override
public void visitStateArrayContributors(
Consumer<StateArrayContributorMapping> mappingConsumer,
EntityMappingType targetType) {
//noinspection Convert2Lambda
visitAttributeMappings(
new Consumer<AttributeMapping>() {
@Override
public void accept(AttributeMapping attributeMapping) {
if ( attributeMapping instanceof StateArrayContributorMapping ) {
mappingConsumer.accept( (StateArrayContributorMapping) attributeMapping );
}
}
},
targetType
if ( treatTargetType == null || treatTargetType.isTypeOrSuperType( this ) ) {
visitSubTypeAttributeMappings(
attributeMapping -> fetchableConsumer.accept( (Fetchable) attributeMapping )
);
}
}
@Override
public void visitAttributeMappings(
Consumer<AttributeMapping> action,
EntityMappingType targetType) {
visitSuperTypeAttributeMappings( action );
declaredAttributeMappings.values().forEach( action );
if ( targetType == null || targetType.isTypeOrSuperType( this ) ) {
visitSubTypeAttributeMappings( action );
for ( int i = 0; i < attributeMappings.size(); i++ ) {
action.accept( attributeMappings.get( i ) );
}
}
@ -6456,8 +6486,11 @@ public abstract class AbstractEntityPersister
@Override
public void visitSubTypeAttributeMappings(Consumer<AttributeMapping> action) {
if ( subclassMappingTypes != null ) {
subclassMappingTypes.values().forEach(
subclassMappingTypes -> subclassMappingTypes.visitSubTypeAttributeMappings( action )
subclassMappingTypes.forEach(
(s, subType) -> {
subType.visitDeclaredAttributeMappings( action );
subType.visitSubTypeAttributeMappings( action );
}
);
}
}

View File

@ -8,14 +8,12 @@ package org.hibernate.sql.results.internal.domain.entity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.WrongClassException;
import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.entry.CacheEntry;
@ -35,8 +33,10 @@ import org.hibernate.event.spi.PostLoadEventListener;
import org.hibernate.event.spi.PreLoadEvent;
import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.internal.NullValueAssembler;
@ -67,6 +67,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// these
private final EntityPersister entityDescriptor;
private final EntityPersister rootEntityDescriptor;
private final NavigablePath navigablePath;
private final LockMode lockMode;
@ -76,7 +77,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private final DomainResultAssembler discriminatorAssembler;
private final DomainResultAssembler versionAssembler;
private final Map<StateArrayContributorMapping, DomainResultAssembler> assemblerMap;
private final Map<AttributeMapping, DomainResultAssembler> assemblerMap;
// per-row state
private EntityPersister concreteDescriptor;
@ -98,7 +99,17 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
Consumer<Initializer> initializerConsumer,
AssemblerCreationState creationState) {
super( );
this.entityDescriptor = (EntityPersister) resultDescriptor.getEntityValuedModelPart().getEntityMappingType();
final String rootEntityName = entityDescriptor.getRootEntityName();
if ( rootEntityName == null || rootEntityName.equals( entityDescriptor.getEntityName() ) ) {
this.rootEntityDescriptor = entityDescriptor;
}
else {
this.rootEntityDescriptor = creationState.getSqlAstCreationContext().getDomainModel().findEntityDescriptor( rootEntityName );
}
this.navigablePath = navigablePath;
this.lockMode = lockMode;
@ -135,13 +146,15 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
assemblerMap = new IdentityHashMap<>( entityDescriptor.getNumberOfAttributeMappings() );
entityDescriptor.visitStateArrayContributors(
attributeMapping -> {
entityDescriptor.visitFetchables(
fetchable -> {
final AttributeMapping attributeMapping = (AttributeMapping) fetchable;
// todo (6.0) : somehow we need to track whether all state is loaded/resolved
// note that lazy proxies or uninitialized collections count against
// that in the affirmative
final Fetch fetch = resultDescriptor.findFetch( attributeMapping.getAttributeName() );
final Fetch fetch = resultDescriptor.findFetch( fetchable.getFetchableName() );
final DomainResultAssembler stateAssembler;
if ( fetch == null ) {
@ -156,7 +169,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
assemblerMap.put( attributeMapping, stateAssembler );
}
},
null
);
initializerConsumer.accept( this );
@ -242,31 +256,33 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private EntityPersister determineConcreteEntityDescriptor(
RowProcessingState rowProcessingState,
SharedSessionContractImplementor persistenceContext) throws WrongClassException {
SharedSessionContractImplementor session) throws WrongClassException {
if ( discriminatorAssembler == null ) {
return entityDescriptor;
}
throw new NotYetImplementedFor6Exception( getClass() );
// final Object discriminatorValue = discriminatorAssembler.assemble(
// rowProcessingState,
// rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
// );
//
// final String result = entityDescriptor.getDiscriminatorDescriptor()
// .getDiscriminatorMappings()
// .discriminatorValueToEntityName( discriminatorValue );
//
// if ( result == null ) {
// // oops - we got an instance of another class hierarchy branch
// throw new WrongClassException(
// "Discriminator: " + discriminatorValue,
// entityKey.getIdentifier(),
// entityDescriptor.getEntityName()
// );
// }
//
// return persistenceContext.getFactory().getMetamodel().findEntityDescriptor( result );
final Object discriminatorValue = discriminatorAssembler.assemble(
rowProcessingState,
rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
);
final String concreteEntityName = ( (Loadable) entityDescriptor ).getSubclassForDiscriminatorValue( discriminatorValue );
if ( concreteEntityName == null ) {
// oops - we got an instance of another class hierarchy branch
throw new WrongClassException(
"Discriminator: " + discriminatorValue,
entityKey.getIdentifier(),
entityDescriptor.getEntityName()
);
}
final EntityPersister concreteType = session.getFactory().getMetamodel().findEntityDescriptor( concreteEntityName );
// verify that the `entityDescriptor` is either == concreteType or its super-type
assert concreteType.isTypeOrSuperType( entityDescriptor );
return concreteType;
}
@SuppressWarnings("WeakerAccess")
@ -454,12 +470,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
entityDescriptor.setIdentifier( entityInstance, entityIdentifier, session );
resolvedEntityState = new Object[ assemblerMap.size() ];
assemblerMap.forEach(
(key, value) -> resolvedEntityState[ key.getStateArrayPosition() ] = value.assemble( rowProcessingState )
resolvedEntityState = concreteDescriptor.extractConcreteTypeStateValues(
assemblerMap,
rowProcessingState
);
entityDescriptor.setPropertyValues( entityInstance, resolvedEntityState );
concreteDescriptor.setPropertyValues( entityInstance, resolvedEntityState );
persistenceContext.addEntity(
entityKey,
@ -483,12 +499,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
version,
lockMode,
true,
entityDescriptor,
concreteDescriptor,
false
);
final SessionFactoryImplementor factory = session.getFactory();
final EntityDataAccess cacheAccess = entityDescriptor.getCacheAccessStrategy();
final EntityDataAccess cacheAccess = concreteDescriptor.getCacheAccessStrategy();
if ( cacheAccess != null && session.getCacheMode().isPutEnabled() ) {
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
@ -498,10 +514,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
);
}
final CacheEntry entry = entityDescriptor.buildCacheEntry( entityInstance, resolvedEntityState, version, session );
final CacheEntry entry = concreteDescriptor.buildCacheEntry( entityInstance, resolvedEntityState, version, session );
final Object cacheKey = cacheAccess.generateCacheKey(
entityIdentifier,
entityDescriptor,
rootEntityDescriptor,
factory,
session.getTenantIdentifier()
);
@ -512,11 +528,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// 2) Session#clear + some form of load
//
// we need to be careful not to clobber the lock here in the cache so that it can be rolled back if need be
if ( persistenceContext.wasInsertedDuringTransaction( entityDescriptor, entityIdentifier ) ) {
if ( persistenceContext.wasInsertedDuringTransaction( concreteDescriptor, entityIdentifier ) ) {
cacheAccess.update(
session,
cacheKey,
entityDescriptor.getCacheEntryStructure().structure( entry ),
rootEntityDescriptor.getCacheEntryStructure().structure( entry ),
version,
version
);
@ -528,14 +544,14 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
final boolean put = cacheAccess.putFromLoad(
session,
cacheKey,
entityDescriptor.getCacheEntryStructure().structure( entry ),
rootEntityDescriptor.getCacheEntryStructure().structure( entry ),
version,
//useMinimalPuts( session, entityEntry )
false
);
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatistics().entityCachePut( entityDescriptor.getNavigableRole(), cacheAccess.getRegion().getName() );
factory.getStatistics().entityCachePut( rootEntityDescriptor.getNavigableRole(), cacheAccess.getRegion().getName() );
}
}
finally {
@ -554,7 +570,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
boolean isReallyReadOnly = isReadOnly( rowProcessingState, session );
if ( ! entityDescriptor.isMutable() ) {
if ( ! concreteDescriptor.isMutable() ) {
isReallyReadOnly = true;
}
else {
@ -575,7 +591,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
else {
//take a snapshot
TypeHelper.deepCopy(
entityDescriptor,
concreteDescriptor,
resolvedEntityState,
resolvedEntityState,
attributeMapping -> attributeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( concreteDescriptor ).isUpdatable()
@ -583,7 +599,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
persistenceContext.setEntryStatus( entityEntry, Status.MANAGED );
}
entityDescriptor.afterInitialize( entityInstance, session );
concreteDescriptor.afterInitialize( entityInstance, session );
if ( EntityLoadingLogger.DEBUG_ENABLED ) {
EntityLoadingLogger.INSTANCE.debugf(
@ -593,7 +609,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
}
if ( factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatistics().loadEntity( entityDescriptor.getEntityName() );
factory.getStatistics().loadEntity( concreteDescriptor.getEntityName() );
}
postLoad( rowProcessingState );

View File

@ -12,6 +12,7 @@ import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
@ -68,18 +69,17 @@ public abstract class AbstractEntityResultNode extends AbstractFetchParent imple
creationState
);
// final DiscriminatorMappDescriptor<?> discriminatorDescriptor = entityDescriptor.getHierarchy().getDiscriminatorDescriptor();
// if ( discriminatorDescriptor == null ) {
// discriminatorResult = null;
// }
// else {
// discriminatorResult = discriminatorDescriptor.createDomainResult(
// navigablePath.append( DiscriminatorDescriptor.NAVIGABLE_NAME ),
// null,
// creationState
// );
// }
if ( entityDescriptor.getDiscriminatorMapping() != null ) {
discriminatorResult = entityDescriptor.getDiscriminatorMapping().createDomainResult(
navigablePath.append( EntityDiscriminatorMapping.ROLE_NAME ),
entityTableGroup,
null,
creationState
);
}
else {
discriminatorResult = null;
}
final EntityVersionMapping versionDescriptor = entityDescriptor.getVersionMapping();
if ( versionDescriptor == null ) {

View File

@ -72,36 +72,13 @@ public class InheritanceTests {
assert foreignCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor );
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist( new DomesticCustomer( 1, "domestic", "123" ) );
session.persist( new ForeignCustomer( 2, "foreign", "987" ) );
}
);
}
@AfterEach
public void cleanupTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "from DomesticCustomer", DomesticCustomer.class ).list().forEach(
cust -> session.delete( cust )
);
session.createQuery( "from ForeignCustomer", ForeignCustomer.class ).list().forEach(
cust -> session.delete( cust )
);
}
);
}
@Test
@FailureExpected
// @FailureExpected
public void rootQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
{
// [name, taxId, vat]
final List<Customer> results = session.createQuery(
"select c from Customer c",
Customer.class
@ -112,11 +89,15 @@ public class InheritanceTests {
for ( Customer result : results ) {
if ( result.getId() == 1 ) {
assertThat( result, instanceOf( DomesticCustomer.class ) );
assertThat( ( (DomesticCustomer) result ).getTaxId(), is( "123" ) );
final DomesticCustomer customer = (DomesticCustomer) result;
assertThat( customer.getName(), is( "domestic" ) );
assertThat( (customer).getTaxId(), is( "123" ) );
}
else {
assertThat( result.getId(), is( 2 ) );
assertThat( ( (ForeignCustomer) result ).getVat(), is( "987" ) );
final ForeignCustomer customer = (ForeignCustomer) result;
assertThat( customer.getName(), is( "foreign" ) );
assertThat( (customer).getVat(), is( "987" ) );
}
}
@ -156,6 +137,30 @@ public class InheritanceTests {
);
}
@BeforeEach
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.persist( new DomesticCustomer( 1, "domestic", "123" ) );
session.persist( new ForeignCustomer( 2, "foreign", "987" ) );
}
);
}
@AfterEach
public void cleanupTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
session.createQuery( "from DomesticCustomer", DomesticCustomer.class ).list().forEach(
cust -> session.delete( cust )
);
session.createQuery( "from ForeignCustomer", ForeignCustomer.class ).list().forEach(
cust -> session.delete( cust )
);
}
);
}
@Entity( name = "Customer" )
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@Table( name = "customer" )

View File

@ -7,14 +7,23 @@
package org.hibernate.orm.test.sql.exec;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Component;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.OtherEntity;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity;
import org.hibernate.query.Query;
import org.hibernate.query.spi.QueryImplementor;
import org.hibernate.stat.spi.StatisticsImplementor;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected;
@ -46,7 +55,12 @@ import static org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender.MALE;
SmokeTests.BasicSetterBasedDto.class
}
)
@ServiceRegistry
@ServiceRegistry(
settings = {
@ServiceRegistry.Setting( name = AvailableSettings.POOL_SIZE, value = "15" ),
@ServiceRegistry.Setting( name = AvailableSettings.USE_SECOND_LEVEL_CACHE, value = "false" )
}
)
@SessionFactory( exportSchema = true )
public class SmokeTests {
@ -279,6 +293,60 @@ public class SmokeTests {
);
}
@Test
public void testQueryConcurrency(SessionFactoryScope scope) throws InterruptedException {
final StatisticsImplementor statistics = scope.getSessionFactory().getStatistics();
statistics.clear();
final int numberOfIterations = 400;
final int numberOfForks = 50;
final ExecutorService executor = Executors.newFixedThreadPool( 5 );
try {
for ( int f = 0; f < numberOfForks; f++ ) {
final ArrayList<Callable<String>> tasks = CollectionHelper.arrayList( numberOfIterations );
for ( int i = 0; i < numberOfIterations; i++ ) {
tasks.add( () -> executeQueriesForConcurrency( scope ) );
}
executor.invokeAll( tasks );
}
}
finally {
// make sure all iterations/tasks have completed
executor.shutdown();
executor.awaitTermination( 15, TimeUnit.SECONDS );
}
}
public String executeQueriesForConcurrency(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final QueryImplementor<Component> query1 = session.createQuery(
"select e.component from SimpleEntity e where e.component.attribute1 = :param",
Component.class
);
query1.setParameter( "param", "a1" ).list();
final QueryImplementor<Component> query2 = session.createQuery(
"select e.component from SimpleEntity e where e.component.attribute1 = :param",
Component.class
);
query2.setParameter( "param", "b1" ).list();
final QueryImplementor<Component> query3 = session.createQuery(
"select e from SimpleEntity e where e.component.attribute1 = :param",
Component.class
);
query3.setParameter( "param", "a1" ).list();
}
);
return null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Dynamic instantiations

View File

@ -84,7 +84,9 @@ public class SessionFactoryExtension
sessionFactoryBuilder.applyName( sessionFactoryConfig.sessionFactoryName() );
}
sessionFactoryBuilder.applyStatisticsSupport( sessionFactoryConfig.generateStatistics() );
if ( sessionFactoryConfig.generateStatistics() ) {
sessionFactoryBuilder.applyStatisticsSupport( true );
}
if ( ! sessionFactoryConfig.interceptorClass().equals( Interceptor.class ) ) {
sessionFactoryBuilder.applyInterceptor( sessionFactoryConfig.interceptorClass().newInstance() );