initial discriminator hierarchy support
This commit is contained in:
parent
3c65085123
commit
f5c3ae181c
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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...
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 )
|
||||
.getPropertyAccess()
|
||||
.getGetter()
|
||||
.get( object );
|
||||
}
|
||||
}
|
||||
);
|
||||
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,56 +6054,70 @@ 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;
|
||||
}
|
||||
|
||||
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 )
|
||||
);
|
||||
|
||||
if ( getVersionType() == null ) {
|
||||
versionMapping = null;
|
||||
}
|
||||
else {
|
||||
final int versionPropertyIndex = getVersionProperty();
|
||||
final String versionPropertyName = getPropertyNames()[ versionPropertyIndex ];
|
||||
|
||||
versionMapping = creationProcess.processSubPart(
|
||||
versionPropertyName,
|
||||
(role, creationProcess1) -> generateVersionMapping( this, creationProcess )
|
||||
);
|
||||
}
|
||||
|
||||
if ( getDiscriminatorType() == null ) {
|
||||
discriminatorMapping = null;
|
||||
}
|
||||
else {
|
||||
discriminatorMapping = new EntityDiscriminatorMappingImpl(
|
||||
this,
|
||||
getRootTableName(),
|
||||
getDiscriminatorColumnName(),
|
||||
(BasicType) getDiscriminatorType()
|
||||
);
|
||||
}
|
||||
|
||||
// todo (6.0) : support for natural-id not yet implemented
|
||||
naturalIdMapping = null;
|
||||
}
|
||||
|
||||
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
|
||||
|
||||
final PersistentClass bootEntityDescriptor = creationContext
|
||||
.getBootModel()
|
||||
.getEntityBinding( getEntityName() );
|
||||
|
||||
// todo (6.0) : should we create these only on root?
|
||||
|
||||
identifierMapping = creationProcess.processSubPart(
|
||||
EntityIdentifierMapping.ROLE_LOCAL_NAME,
|
||||
(role, creationProcess1) -> generateIdentifierMapping( creationProcess )
|
||||
);
|
||||
|
||||
naturalIdMapping = null;
|
||||
|
||||
if ( getVersionType() == null ) {
|
||||
versionMapping = null;
|
||||
}
|
||||
else {
|
||||
final int versionPropertyIndex = getVersionProperty();
|
||||
final String versionPropertyName = getPropertyNames()[ versionPropertyIndex ];
|
||||
|
||||
versionMapping = creationProcess.processSubPart(
|
||||
versionPropertyName,
|
||||
(role, creationProcess1) -> generateVersionMapping( this, creationProcess )
|
||||
);
|
||||
}
|
||||
|
||||
if ( getDiscriminatorType() == null ) {
|
||||
discriminatorMapping = null;
|
||||
}
|
||||
else {
|
||||
discriminatorMapping = new EntityDiscriminatorMappingImpl(
|
||||
this,
|
||||
getRootTableName(),
|
||||
getDiscriminatorColumnName(),
|
||||
(BasicType) getDiscriminatorType()
|
||||
);
|
||||
}
|
||||
|
||||
final EntityMetamodel currentEntityMetamodel = this.getEntityMetamodel();
|
||||
|
||||
int stateArrayPosition = superMappingType == null ? 0 : superMappingType.getNumberOfAttributeMappings();
|
||||
|
@ -6117,18 +6126,25 @@ public abstract class AbstractEntityPersister
|
|||
final NonIdentifierAttribute runtimeAttrDefinition = currentEntityMetamodel.getProperties()[i];
|
||||
final Property bootProperty = bootEntityDescriptor.getProperty( runtimeAttrDefinition.getName() );
|
||||
|
||||
declaredAttributeMappings.put(
|
||||
runtimeAttrDefinition.getName(),
|
||||
generateNonIdAttributeMapping(
|
||||
runtimeAttrDefinition,
|
||||
bootProperty,
|
||||
stateArrayPosition++,
|
||||
this,
|
||||
creationProcess
|
||||
)
|
||||
);
|
||||
if ( superMappingType != null && superMappingType.findAttributeMapping( bootProperty.getName() ) != null ) {
|
||||
// its defined on the super-type, skip it here
|
||||
}
|
||||
else {
|
||||
declaredAttributeMappings.put(
|
||||
runtimeAttrDefinition.getName(),
|
||||
generateNonIdAttributeMapping(
|
||||
runtimeAttrDefinition,
|
||||
bootProperty,
|
||||
stateArrayPosition++,
|
||||
this,
|
||||
creationProcess
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getAttributeMappings();
|
||||
|
||||
final ReflectionOptimizer reflectionOptimizer = representationStrategy.getReflectionOptimizer();
|
||||
|
||||
if ( reflectionOptimizer != null ) {
|
||||
|
@ -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 );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
// );
|
||||
// }
|
||||
discriminatorResult = null;
|
||||
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 ) {
|
||||
|
|
|
@ -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" )
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() );
|
||||
|
|
Loading…
Reference in New Issue