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(); ManagedMappingType getDeclaringType();
default boolean isDeclaredOnTypeOrSuperType(ManagedMappingType targetType) {
if ( getDeclaringType() instanceof EntityMappingType ) {
return ( (EntityMappingType) getDeclaringType() ).isTypeOrSuperType( targetType );
}
return false;
}
PropertyAccess getPropertyAccess(); PropertyAccess getPropertyAccess();
} }

View File

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

View File

@ -6,9 +6,17 @@
*/ */
package org.hibernate.metamodel.mapping; package org.hibernate.metamodel.mapping;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.persister.entity.EntityPersister; 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? * todo (6.0) : make this implement RootTableGroupProducer, etc instead of EntityPersister?
@ -29,6 +37,53 @@ public interface EntityMappingType extends ManagedMappingType {
return getEntityPersister().getEntityName(); 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(); EntityIdentifierMapping getIdentifierMapping();
EntityVersionMapping getVersionMapping(); EntityVersionMapping getVersionMapping();
@ -39,19 +94,6 @@ public interface EntityMappingType extends ManagedMappingType {
NaturalIdMapping getNaturalIdMapping(); 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 * Visit the mappings, but limited to just attributes defined
* in the targetType or its super-type(s) if any. * in the targetType or its super-type(s) if any.
@ -80,35 +122,41 @@ public interface EntityMappingType extends ManagedMappingType {
@Override @Override
default void visitAttributeMappings(Consumer<AttributeMapping> action) { default void visitAttributeMappings(Consumer<AttributeMapping> action) {
visitAttributeMappings( action, null ); getAttributeMappings().forEach( action );
} }
/** // Customer <- DomesticCustomer <- OtherCustomer
* Visit the mappings, but limited to just attributes defined
* in the targetType or its super-type(s) if any. default Object[] extractConcreteTypeStateValues(
* Map<AttributeMapping, DomainResultAssembler> assemblerMapping,
* @apiNote Passing {@code null} indicates that subclasses should be included. This RowProcessingState rowProcessingState) {
* matches legacy non-TREAT behavior and meets the need for EntityGraph processing // todo (6.0) : getNumberOfAttributeMappings() needs to be fixed for this to work - bad walking of hierarchy
*/ final Object[] values = new Object[ getNumberOfAttributeMappings() ];
default void visitStateArrayContributors(Consumer<StateArrayContributorMapping> mappingConsumer, EntityMappingType targetType) {
visitAttributeMappings( visitFetchables(
modelPart -> { new Consumer<Fetchable>() {
if ( modelPart instanceof StateArrayContributorMapping ) { private int index;
if ( targetType == null
|| ( (StateArrayContributorMapping) modelPart ).isDeclaredOnTypeOrSuperType( targetType ) ) { @Override
mappingConsumer.accept( ( (StateArrayContributorMapping) modelPart ) ); 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 @Override
default void visitStateArrayContributors(Consumer<StateArrayContributorMapping> mappingConsumer) { 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.Collection;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.sql.results.spi.FetchableContainer; import org.hibernate.sql.results.spi.FetchableContainer;
/** /**
@ -18,14 +19,23 @@ import org.hibernate.sql.results.spi.FetchableContainer;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ManagedMappingType extends MappingType, FetchableContainer { 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(); Collection<AttributeMapping> getAttributeMappings();
/** /**
* @todo (6.0) : consider dropping this in favor of a form passing the ManagedMappingType * Visit attributes defined on this class and any supers
* which indicates the type to limit the attribute search to (the type and its super-type)
*/ */
void visitAttributeMappings(Consumer<AttributeMapping> action); 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.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; 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.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.DomainResultCreationState;
import org.hibernate.sql.results.spi.Fetch; import org.hibernate.sql.results.spi.Fetch;
import org.hibernate.sql.results.spi.FetchParent; import org.hibernate.sql.results.spi.FetchParent;
@ -75,6 +77,31 @@ public class EntityDiscriminatorMappingImpl implements EntityDiscriminatorMappin
return FetchStrategy.IMMEDIATE_JOIN; 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 @Override
public Fetch generateFetch( public Fetch generateFetch(
FetchParent fetchParent, FetchParent fetchParent,
@ -104,6 +131,7 @@ public class EntityDiscriminatorMappingImpl implements EntityDiscriminatorMappin
creationState creationState
); );
} }
private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) { private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();

View File

@ -5294,15 +5294,19 @@ public abstract class AbstractEntityPersister
accessOptimizer.setPropertyValues( object, values ); accessOptimizer.setPropertyValues( object, values );
} }
else { else {
int i = 0; visitFetchables(
for ( Map.Entry<String, AttributeMapping> entries : declaredAttributeMappings.entrySet() ) { fetchable -> {
AttributeMapping attributeMapping = entries.getValue(); final AttributeMapping attribute = (AttributeMapping) fetchable;
final Setter setter = attributeMapping final int stateArrayPosition = ( (StateArrayContributorMapping) attribute ).getStateArrayPosition();
.getPropertyAccess() final Object value = values[stateArrayPosition];
.getSetter(); if ( value != UNFETCHED_PROPERTY ) {
setter.set( object, values[i], getFactory() ); final Setter setter = attribute.getPropertyAccess().getSetter();
i++; setter.set( object, value, getFactory() );
} }
},
null
);
} }
} }
@ -5320,21 +5324,13 @@ public abstract class AbstractEntityPersister
} }
else { else {
final Object[] values = new Object[ getNumberOfAttributeMappings() ]; final Object[] values = new Object[ getNumberOfAttributeMappings() ];
final AtomicInteger index = new AtomicInteger( 0 ); for ( int i = 0; i < attributeMappings.size(); i++ ) {
values[ i ] = attributeMappings.get( i ).getAttributeMetadataAccess()
//noinspection Convert2Lambda .resolveAttributeMetadata( this )
visitAttributeMappings(
new Consumer<AttributeMapping>() {
@Override
public void accept(AttributeMapping mapping) {
values[ index.getAndIncrement() ] = mapping.getAttributeMetadataAccess()
.resolveAttributeMetadata( AbstractEntityPersister.this )
.getPropertyAccess() .getPropertyAccess()
.getGetter() .getGetter()
.get( object ); .get( object );
} }
}
);
return values; return values;
} }
@ -5441,16 +5437,15 @@ public abstract class AbstractEntityPersister
return accessOptimizer.getPropertyValues( entity ); return accessOptimizer.getPropertyValues( entity );
} }
final Object[] result = new Object[declaredAttributeMappings.size()]; final Collection<AttributeMapping> attributeMappings = getAttributeMappings();
int i = 0;
for ( Map.Entry<String, AttributeMapping> entries : declaredAttributeMappings.entrySet() ) { final Object[] result = new Object[this.attributeMappings.size()];
AttributeMapping attributeMapping = entries.getValue(); for ( int i = 0; i < this.attributeMappings.size(); i++ ) {
result[i] = attributeMapping.getPropertyAccess().getGetter().getForInsert( result[i] = this.attributeMappings.get( i ).getPropertyAccess().getGetter().getForInsert(
entity, entity,
mergeMap, mergeMap,
session session
); );
i++;
} }
return result; return result;
} }
@ -6059,31 +6054,35 @@ public abstract class AbstractEntityPersister
private EntityDiscriminatorMapping discriminatorMapping; private EntityDiscriminatorMapping discriminatorMapping;
private SortedMap<String, AttributeMapping> declaredAttributeMappings = new TreeMap<>(); private SortedMap<String, AttributeMapping> declaredAttributeMappings = new TreeMap<>();
private Collection<AttributeMapping> attributeMappings; private List<AttributeMapping> attributeMappings;
private ReflectionOptimizer.AccessOptimizer accessOptimizer; private ReflectionOptimizer.AccessOptimizer accessOptimizer;
@Override
public void visitAttributeMappings(Consumer<AttributeMapping> action) {
attributeMappings.forEach( action );
}
@Override @Override
public void prepareMappingModel(MappingModelCreationProcess creationProcess) { public void prepareMappingModel(MappingModelCreationProcess creationProcess) {
if ( identifierMapping != null ) { if ( identifierMapping != null ) {
return; return;
} }
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext(); if ( superMappingType != null ) {
( (InFlightEntityMappingType) superMappingType ).prepareMappingModel( creationProcess );
final PersistentClass bootEntityDescriptor = creationContext
.getBootModel()
.getEntityBinding( getEntityName() );
// todo (6.0) : should we create these only on root?
this.identifierMapping = superMappingType.getIdentifierMapping();
this.versionMapping = superMappingType.getVersionMapping();
this.discriminatorMapping = superMappingType.getDiscriminatorMapping();
this.naturalIdMapping = superMappingType.getNaturalIdMapping();
}
else {
identifierMapping = creationProcess.processSubPart( identifierMapping = creationProcess.processSubPart(
EntityIdentifierMapping.ROLE_LOCAL_NAME, EntityIdentifierMapping.ROLE_LOCAL_NAME,
(role, creationProcess1) -> generateIdentifierMapping( creationProcess ) (role, creationProcess1) -> generateIdentifierMapping( creationProcess )
); );
naturalIdMapping = null;
if ( getVersionType() == null ) { if ( getVersionType() == null ) {
versionMapping = 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(); final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings(); int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings();
@ -6117,6 +6126,10 @@ public abstract class AbstractEntityPersister
final NonIdentifierAttribute runtimeAttrDefinition = currentEntityMetamodel.getProperties()[i]; final NonIdentifierAttribute runtimeAttrDefinition = currentEntityMetamodel.getProperties()[i];
final Property bootProperty = bootEntityDescriptor.getProperty( runtimeAttrDefinition.getName() ); 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( declaredAttributeMappings.put(
runtimeAttrDefinition.getName(), runtimeAttrDefinition.getName(),
generateNonIdAttributeMapping( generateNonIdAttributeMapping(
@ -6128,6 +6141,9 @@ public abstract class AbstractEntityPersister
) )
); );
} }
}
getAttributeMappings();
final ReflectionOptimizer reflectionOptimizer = representationStrategy.getReflectionOptimizer(); final ReflectionOptimizer reflectionOptimizer = representationStrategy.getReflectionOptimizer();
@ -6172,6 +6188,21 @@ public abstract class AbstractEntityPersister
return declaredAttributeMappings.size(); 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 @Override
public boolean isTypeOrSuperType(EntityMappingType targetType) { public boolean isTypeOrSuperType(EntityMappingType targetType) {
if ( targetType == null ) { if ( targetType == null ) {
@ -6317,11 +6348,7 @@ public abstract class AbstractEntityPersister
attributeMappings = new ArrayList<>(); attributeMappings = new ArrayList<>();
if ( superMappingType != null ) { if ( superMappingType != null ) {
superMappingType.visitAttributeMappings( superMappingType.visitAttributeMappings( attributeMappings::add );
attributeMappings::add,
// only walk up the hierarchy
superMappingType
);
} }
attributeMappings.addAll( declaredAttributeMappings.values() ); attributeMappings.addAll( declaredAttributeMappings.values() );
@ -6332,6 +6359,28 @@ public abstract class AbstractEntityPersister
return attributeMappings; 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 @Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
LOG.tracef( "#findSubPart(`%s`)", name ); LOG.tracef( "#findSubPart(`%s`)", name );
@ -6397,7 +6446,6 @@ public abstract class AbstractEntityPersister
EntityMappingType treatTargetType) { EntityMappingType treatTargetType) {
if ( getIdentifierMapping() instanceof FetchableContainer ) { if ( getIdentifierMapping() instanceof FetchableContainer ) {
// essentially means the entity has a composite id - ask the embeddable to visit its fetchables // 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 ); ( (FetchableContainer) getIdentifierMapping() ).visitFetchables( fetchableConsumer, treatTargetType );
} }
@ -6408,41 +6456,23 @@ public abstract class AbstractEntityPersister
public void visitFetchables( public void visitFetchables(
Consumer<Fetchable> fetchableConsumer, Consumer<Fetchable> fetchableConsumer,
EntityMappingType treatTargetType) { EntityMappingType treatTargetType) {
//noinspection unchecked for ( int i = 0; i < attributeMappings.size(); i++ ) {
visitStateArrayContributors( fetchableConsumer.accept( (Fetchable) attributeMappings.get( i ) );
(Consumer) fetchableConsumer,
treatTargetType
);
} }
@Override if ( treatTargetType == null || treatTargetType.isTypeOrSuperType( this ) ) {
public void visitStateArrayContributors( visitSubTypeAttributeMappings(
Consumer<StateArrayContributorMapping> mappingConsumer, attributeMapping -> fetchableConsumer.accept( (Fetchable) attributeMapping )
EntityMappingType targetType) {
//noinspection Convert2Lambda
visitAttributeMappings(
new Consumer<AttributeMapping>() {
@Override
public void accept(AttributeMapping attributeMapping) {
if ( attributeMapping instanceof StateArrayContributorMapping ) {
mappingConsumer.accept( (StateArrayContributorMapping) attributeMapping );
}
}
},
targetType
); );
} }
}
@Override @Override
public void visitAttributeMappings( public void visitAttributeMappings(
Consumer<AttributeMapping> action, Consumer<AttributeMapping> action,
EntityMappingType targetType) { EntityMappingType targetType) {
visitSuperTypeAttributeMappings( action ); for ( int i = 0; i < attributeMappings.size(); i++ ) {
action.accept( attributeMappings.get( i ) );
declaredAttributeMappings.values().forEach( action );
if ( targetType == null || targetType.isTypeOrSuperType( this ) ) {
visitSubTypeAttributeMappings( action );
} }
} }
@ -6456,8 +6486,11 @@ public abstract class AbstractEntityPersister
@Override @Override
public void visitSubTypeAttributeMappings(Consumer<AttributeMapping> action) { public void visitSubTypeAttributeMappings(Consumer<AttributeMapping> action) {
if ( subclassMappingTypes != null ) { if ( subclassMappingTypes != null ) {
subclassMappingTypes.values().forEach( subclassMappingTypes.forEach(
subclassMappingTypes -> subclassMappingTypes.visitSubTypeAttributeMappings( action ) (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.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.WrongClassException; import org.hibernate.WrongClassException;
import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.access.EntityDataAccess;
import org.hibernate.cache.spi.entry.CacheEntry; 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.PreLoadEvent;
import org.hibernate.event.spi.PreLoadEventListener; import org.hibernate.event.spi.PreLoadEventListener;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.internal.NullValueAssembler;
@ -67,6 +67,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// these // these
private final EntityPersister entityDescriptor; private final EntityPersister entityDescriptor;
private final EntityPersister rootEntityDescriptor;
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final LockMode lockMode; private final LockMode lockMode;
@ -76,7 +77,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private final DomainResultAssembler discriminatorAssembler; private final DomainResultAssembler discriminatorAssembler;
private final DomainResultAssembler versionAssembler; private final DomainResultAssembler versionAssembler;
private final Map<StateArrayContributorMapping, DomainResultAssembler> assemblerMap; private final Map<AttributeMapping, DomainResultAssembler> assemblerMap;
// per-row state // per-row state
private EntityPersister concreteDescriptor; private EntityPersister concreteDescriptor;
@ -98,7 +99,17 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
Consumer<Initializer> initializerConsumer, Consumer<Initializer> initializerConsumer,
AssemblerCreationState creationState) { AssemblerCreationState creationState) {
super( ); super( );
this.entityDescriptor = (EntityPersister) resultDescriptor.getEntityValuedModelPart().getEntityMappingType(); 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.navigablePath = navigablePath;
this.lockMode = lockMode; this.lockMode = lockMode;
@ -135,13 +146,15 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
assemblerMap = new IdentityHashMap<>( entityDescriptor.getNumberOfAttributeMappings() ); assemblerMap = new IdentityHashMap<>( entityDescriptor.getNumberOfAttributeMappings() );
entityDescriptor.visitStateArrayContributors( entityDescriptor.visitFetchables(
attributeMapping -> { fetchable -> {
final AttributeMapping attributeMapping = (AttributeMapping) fetchable;
// todo (6.0) : somehow we need to track whether all state is loaded/resolved // todo (6.0) : somehow we need to track whether all state is loaded/resolved
// note that lazy proxies or uninitialized collections count against // note that lazy proxies or uninitialized collections count against
// that in the affirmative // that in the affirmative
final Fetch fetch = resultDescriptor.findFetch( attributeMapping.getAttributeName() ); final Fetch fetch = resultDescriptor.findFetch( fetchable.getFetchableName() );
final DomainResultAssembler stateAssembler; final DomainResultAssembler stateAssembler;
if ( fetch == null ) { if ( fetch == null ) {
@ -156,7 +169,8 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
} }
assemblerMap.put( attributeMapping, stateAssembler ); assemblerMap.put( attributeMapping, stateAssembler );
} },
null
); );
initializerConsumer.accept( this ); initializerConsumer.accept( this );
@ -242,31 +256,33 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
private EntityPersister determineConcreteEntityDescriptor( private EntityPersister determineConcreteEntityDescriptor(
RowProcessingState rowProcessingState, RowProcessingState rowProcessingState,
SharedSessionContractImplementor persistenceContext) throws WrongClassException { SharedSessionContractImplementor session) throws WrongClassException {
if ( discriminatorAssembler == null ) { if ( discriminatorAssembler == null ) {
return entityDescriptor; return entityDescriptor;
} }
throw new NotYetImplementedFor6Exception( getClass() ); final Object discriminatorValue = discriminatorAssembler.assemble(
// final Object discriminatorValue = discriminatorAssembler.assemble( rowProcessingState,
// rowProcessingState, rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions()
// rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions() );
// );
// final String concreteEntityName = ( (Loadable) entityDescriptor ).getSubclassForDiscriminatorValue( discriminatorValue );
// final String result = entityDescriptor.getDiscriminatorDescriptor()
// .getDiscriminatorMappings() if ( concreteEntityName == null ) {
// .discriminatorValueToEntityName( discriminatorValue ); // oops - we got an instance of another class hierarchy branch
// throw new WrongClassException(
// if ( result == null ) { "Discriminator: " + discriminatorValue,
// // oops - we got an instance of another class hierarchy branch entityKey.getIdentifier(),
// throw new WrongClassException( entityDescriptor.getEntityName()
// "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
// return persistenceContext.getFactory().getMetamodel().findEntityDescriptor( result ); assert concreteType.isTypeOrSuperType( entityDescriptor );
return concreteType;
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -454,12 +470,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
entityDescriptor.setIdentifier( entityInstance, entityIdentifier, session ); entityDescriptor.setIdentifier( entityInstance, entityIdentifier, session );
resolvedEntityState = new Object[ assemblerMap.size() ]; resolvedEntityState = concreteDescriptor.extractConcreteTypeStateValues(
assemblerMap.forEach( assemblerMap,
(key, value) -> resolvedEntityState[ key.getStateArrayPosition() ] = value.assemble( rowProcessingState ) rowProcessingState
); );
entityDescriptor.setPropertyValues( entityInstance, resolvedEntityState ); concreteDescriptor.setPropertyValues( entityInstance, resolvedEntityState );
persistenceContext.addEntity( persistenceContext.addEntity(
entityKey, entityKey,
@ -483,12 +499,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
version, version,
lockMode, lockMode,
true, true,
entityDescriptor, concreteDescriptor,
false false
); );
final SessionFactoryImplementor factory = session.getFactory(); final SessionFactoryImplementor factory = session.getFactory();
final EntityDataAccess cacheAccess = entityDescriptor.getCacheAccessStrategy(); final EntityDataAccess cacheAccess = concreteDescriptor.getCacheAccessStrategy();
if ( cacheAccess != null && session.getCacheMode().isPutEnabled() ) { if ( cacheAccess != null && session.getCacheMode().isPutEnabled() ) {
if ( EntityLoadingLogger.DEBUG_ENABLED ) { 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( final Object cacheKey = cacheAccess.generateCacheKey(
entityIdentifier, entityIdentifier,
entityDescriptor, rootEntityDescriptor,
factory, factory,
session.getTenantIdentifier() session.getTenantIdentifier()
); );
@ -512,11 +528,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
// 2) Session#clear + some form of load // 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 // 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( cacheAccess.update(
session, session,
cacheKey, cacheKey,
entityDescriptor.getCacheEntryStructure().structure( entry ), rootEntityDescriptor.getCacheEntryStructure().structure( entry ),
version, version,
version version
); );
@ -528,14 +544,14 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
final boolean put = cacheAccess.putFromLoad( final boolean put = cacheAccess.putFromLoad(
session, session,
cacheKey, cacheKey,
entityDescriptor.getCacheEntryStructure().structure( entry ), rootEntityDescriptor.getCacheEntryStructure().structure( entry ),
version, version,
//useMinimalPuts( session, entityEntry ) //useMinimalPuts( session, entityEntry )
false false
); );
if ( put && factory.getStatistics().isStatisticsEnabled() ) { if ( put && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatistics().entityCachePut( entityDescriptor.getNavigableRole(), cacheAccess.getRegion().getName() ); factory.getStatistics().entityCachePut( rootEntityDescriptor.getNavigableRole(), cacheAccess.getRegion().getName() );
} }
} }
finally { finally {
@ -554,7 +570,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
} }
boolean isReallyReadOnly = isReadOnly( rowProcessingState, session ); boolean isReallyReadOnly = isReadOnly( rowProcessingState, session );
if ( ! entityDescriptor.isMutable() ) { if ( ! concreteDescriptor.isMutable() ) {
isReallyReadOnly = true; isReallyReadOnly = true;
} }
else { else {
@ -575,7 +591,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
else { else {
//take a snapshot //take a snapshot
TypeHelper.deepCopy( TypeHelper.deepCopy(
entityDescriptor, concreteDescriptor,
resolvedEntityState, resolvedEntityState,
resolvedEntityState, resolvedEntityState,
attributeMapping -> attributeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( concreteDescriptor ).isUpdatable() attributeMapping -> attributeMapping.getAttributeMetadataAccess().resolveAttributeMetadata( concreteDescriptor ).isUpdatable()
@ -583,7 +599,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
persistenceContext.setEntryStatus( entityEntry, Status.MANAGED ); persistenceContext.setEntryStatus( entityEntry, Status.MANAGED );
} }
entityDescriptor.afterInitialize( entityInstance, session ); concreteDescriptor.afterInitialize( entityInstance, session );
if ( EntityLoadingLogger.DEBUG_ENABLED ) { if ( EntityLoadingLogger.DEBUG_ENABLED ) {
EntityLoadingLogger.INSTANCE.debugf( EntityLoadingLogger.INSTANCE.debugf(
@ -593,7 +609,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces
} }
if ( factory.getStatistics().isStatisticsEnabled() ) { if ( factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatistics().loadEntity( entityDescriptor.getEntityName() ); factory.getStatistics().loadEntity( concreteDescriptor.getEntityName() );
} }
postLoad( rowProcessingState ); postLoad( rowProcessingState );

View File

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

View File

@ -72,36 +72,13 @@ public class InheritanceTests {
assert foreignCustomerDescriptor.isTypeOrSuperType( foreignCustomerDescriptor ); 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 @Test
@FailureExpected // @FailureExpected
public void rootQueryExecutionTest(SessionFactoryScope scope) { public void rootQueryExecutionTest(SessionFactoryScope scope) {
scope.inTransaction( scope.inTransaction(
session -> { session -> {
{ {
// [name, taxId, vat]
final List<Customer> results = session.createQuery( final List<Customer> results = session.createQuery(
"select c from Customer c", "select c from Customer c",
Customer.class Customer.class
@ -112,11 +89,15 @@ public class InheritanceTests {
for ( Customer result : results ) { for ( Customer result : results ) {
if ( result.getId() == 1 ) { if ( result.getId() == 1 ) {
assertThat( result, instanceOf( DomesticCustomer.class ) ); 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 { else {
assertThat( result.getId(), is( 2 ) ); 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" ) @Entity( name = "Customer" )
@Inheritance( strategy = InheritanceType.SINGLE_TABLE ) @Inheritance( strategy = InheritanceType.SINGLE_TABLE )
@Table( name = "customer" ) @Table( name = "customer" )

View File

@ -7,14 +7,23 @@
package org.hibernate.orm.test.sql.exec; package org.hibernate.orm.test.sql.exec;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.List; 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.Component;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.Gender; 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.OtherEntity;
import org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity; import org.hibernate.orm.test.metamodel.mapping.SmokeTests.SimpleEntity;
import org.hibernate.query.Query; import org.hibernate.query.Query;
import org.hibernate.query.spi.QueryImplementor; 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.DomainModel;
import org.hibernate.testing.orm.junit.FailureExpected; 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 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 ) @SessionFactory( exportSchema = true )
public class SmokeTests { 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 // Dynamic instantiations

View File

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