preliminary support for collections

This commit is contained in:
Steve Ebersole 2019-10-28 07:26:41 -05:00
parent cf09e47d40
commit 093f410953
12 changed files with 1054 additions and 48 deletions

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.boot.model.process.internal;
import org.hibernate.mapping.BasicValue;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
import org.hibernate.type.BasicType;
import org.hibernate.type.CustomType;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
/**
* @author Steve Ebersole
*/
public class EnumeratedValueResolution<E extends Enum<E>> implements BasicValue.Resolution<E> {
private final CustomType enumTypeMapping;
private final JavaTypeDescriptor<E> domainJtd;
private final JavaTypeDescriptor<?> jdbcJtd;
private final SqlTypeDescriptor std;
private final EnumValueConverter<E,?> valueConverter;
public EnumeratedValueResolution(
CustomType enumTypeMapping,
JavaTypeDescriptor<E> domainJtd,
JavaTypeDescriptor<?> jdbcJtd,
SqlTypeDescriptor std,
EnumValueConverter<E, ?> valueConverter) {
this.enumTypeMapping = enumTypeMapping;
this.domainJtd = domainJtd;
this.jdbcJtd = jdbcJtd;
this.std = std;
this.valueConverter = valueConverter;
}
@Override
public BasicType getResolvedBasicType() {
return enumTypeMapping;
}
@Override
public JavaTypeDescriptor<E> getDomainJavaDescriptor() {
return domainJtd;
}
@Override
public JavaTypeDescriptor<?> getRelationalJavaDescriptor() {
return jdbcJtd;
}
@Override
public SqlTypeDescriptor getRelationalSqlTypeDescriptor() {
return std;
}
@Override
public BasicValueConverter getValueConverter() {
return valueConverter;
}
@Override
public MutabilityPlan<E> getMutabilityPlan() {
return ImmutableMutabilityPlan.instance();
}
}

View File

@ -49,6 +49,7 @@
import org.hibernate.type.CharacterArrayClobType;
import org.hibernate.type.CharacterArrayNClobType;
import org.hibernate.type.CharacterNCharType;
import org.hibernate.type.EnumType;
import org.hibernate.type.PrimitiveCharacterArrayClobType;
import org.hibernate.type.PrimitiveCharacterArrayNClobType;
import org.hibernate.type.SerializableToBlobType;
@ -162,15 +163,15 @@ public void setType(XProperty property, XClass returnedClass, String declaringCl
}
Type annType = null;
if ( (!key && property.isAnnotationPresent( Type.class ))
|| (key && property.isAnnotationPresent( MapKeyType.class )) ) {
if ( key ) {
MapKeyType ann = property.getAnnotation( MapKeyType.class );
if ( key ) {
final MapKeyType ann = property.getAnnotation( MapKeyType.class );
if ( ann != null ) {
annType = ann.value();
}
else {
annType = property.getAnnotation( Type.class );
}
}
else {
annType = property.getAnnotation( Type.class );
}
if ( annType != null ) {
@ -313,7 +314,8 @@ else if ( buildingContext.getBootstrapContext().getReflectionManager().equals( r
}
if ( BinderHelper.ANNOTATION_STRING_DEFAULT.equals( type ) ) {
if ( returnedClassOrElement.isEnum() ) {
if ( this.enumerationStyle == null && returnedClassOrElement.isEnum() ) {
type = EnumType.class.getName();
this.enumerationStyle = determineEnumType( property, key );
}
}
@ -439,15 +441,17 @@ public SimpleValue make() {
simpleValue = new BasicValue( buildingContext, table );
if ( enumerationStyle != null ) {
simpleValue.setEnumerationStyle( enumerationStyle );
simpleValue.setEnumerationStyle( enumerationStyle, returnedClassName );
}
if ( isVersion ) {
simpleValue.makeVersion();
}
if ( isNationalized ) {
simpleValue.makeNationalized();
}
if ( isLob ) {
simpleValue.makeLob();
}
@ -503,9 +507,6 @@ public void fillSimpleValue() {
);
simpleValue.setJpaAttributeConverterDescriptor( attributeConverterDescriptor );
}
else if ( enumerationStyle != null ) {
simpleValue.setEnumerationStyle( enumerationStyle );
}
else {
String type;

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.mapping;
import java.sql.Types;
import java.util.Properties;
import java.util.function.Function;
import java.util.function.Supplier;
@ -17,6 +18,7 @@
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
import org.hibernate.boot.model.process.internal.EnumeratedValueResolution;
import org.hibernate.boot.model.process.internal.InferredBasicValueResolution;
import org.hibernate.boot.model.process.internal.InferredBasicValueResolver;
import org.hibernate.boot.model.process.internal.NamedBasicTypeResolution;
@ -29,6 +31,7 @@
import org.hibernate.metamodel.model.convert.internal.NamedEnumValueConverter;
import org.hibernate.metamodel.model.convert.internal.OrdinalEnumValueConverter;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.model.convert.spi.EnumValueConverter;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.BasicType;
@ -42,8 +45,10 @@
import org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor;
import org.hibernate.type.descriptor.java.RowVersionTypeDescriptor;
import org.hibernate.type.descriptor.java.TemporalJavaTypeDescriptor;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
import org.hibernate.type.descriptor.sql.spi.SqlTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -118,10 +123,15 @@ public void setTypeUsingReflection(String className, String propertyName) throws
this.propertyName = propertyName;
}
public void setEnumerationStyle(EnumType enumerationStyle) {
public void setEnumerationStyle(EnumType enumerationStyle, String enumJavaTypeName) {
this.enumerationStyle = enumerationStyle;
this.resolvedJavaClass = getBuildingContext().getBootstrapContext()
.getServiceRegistry()
.getService( ClassLoaderService.class )
.classForName( enumJavaTypeName );
}
@SuppressWarnings("WeakerAccess")
public EnumType getEnumerationStyle() {
return enumerationStyle;
}
@ -157,6 +167,8 @@ public Resolution<?> resolve() {
if ( explicitTypeName != null ) {
resolution = interpretExplicitlyNamedType(
explicitTypeName,
enumerationStyle,
resolvedJavaClass,
explicitJavaTypeAccess,
explicitSqlTypeAccess,
attributeConverterDescriptor,
@ -178,6 +190,7 @@ public Resolution<?> resolve() {
this,
() -> {
if ( resolvedJavaClass != null ) {
//noinspection unchecked
return getBuildingContext().getBootstrapContext()
.getTypeConfiguration()
.getJavaTypeDescriptorRegistry()
@ -200,6 +213,7 @@ else if ( ownerName != null && propertyName != null ) {
return basicType.getJavaTypeDescriptor();
}
//noinspection unchecked
return getBuildingContext().getBootstrapContext()
.getTypeConfiguration()
.getJavaTypeDescriptorRegistry()
@ -228,8 +242,10 @@ else if ( dependentValue != null ) {
@SuppressWarnings("unchecked")
private static Resolution interpretExplicitlyNamedType(
String name,
Function<TypeConfiguration,JavaTypeDescriptor<?>> explicitJtdAccess,
Function<TypeConfiguration,SqlTypeDescriptor> explicitStdAccess,
EnumType enumerationStyle,
Class resolvedJavaClass,
Function<TypeConfiguration, JavaTypeDescriptor<?>> explicitJtdAccess,
Function<TypeConfiguration, SqlTypeDescriptor> explicitStdAccess,
ConverterDescriptor converterDescriptor,
MutabilityPlan explicitMutabilityPlan,
Properties localTypeParams,
@ -272,6 +288,45 @@ public TypeConfiguration getTypeConfiguration() {
);
}
if ( enumerationStyle != null ) {
assert resolvedJavaClass != null;
assert resolvedJavaClass.isEnum();
final JavaTypeDescriptorRegistry jtdRegistry = typeConfiguration.getJavaTypeDescriptorRegistry();
final SqlTypeDescriptorRegistry stdRegistry = typeConfiguration.getSqlTypeDescriptorRegistry();
final EnumJavaTypeDescriptor domainJtd = (EnumJavaTypeDescriptor) jtdRegistry.getDescriptor( resolvedJavaClass );
final JavaTypeDescriptor jdbcJtd;
final SqlTypeDescriptor std;
final EnumValueConverter<?,?> valueConverter;
if ( enumerationStyle == EnumType.ORDINAL ) {
jdbcJtd = jtdRegistry.getDescriptor( Integer.class );
final SqlTypeDescriptor explicitStd = explicitStdAccess == null ? null : explicitStdAccess.apply( typeConfiguration );
std = explicitStd != null ? explicitStd : stdRegistry.getDescriptor( Types.INTEGER );
valueConverter = new OrdinalEnumValueConverter( domainJtd, std, jdbcJtd );
}
else {
jdbcJtd = jtdRegistry.getDescriptor( String.class );
std = jdbcJtd.getJdbcRecommendedSqlType( stdIndicators );
valueConverter = new NamedEnumValueConverter( domainJtd, std, jdbcJtd );
}
final CustomType valueMapper = new CustomType(
new org.hibernate.type.EnumType( resolvedJavaClass, valueConverter, typeConfiguration ),
typeConfiguration
);
return new EnumeratedValueResolution(
valueMapper,
domainJtd,
jdbcJtd,
std,
valueConverter
);
}
// see if it is a named basic type
final BasicType basicTypeByName = typeConfiguration.getBasicTypeRegistry().getRegisteredType( name );
if ( basicTypeByName != null ) {

View File

@ -0,0 +1,19 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.mapping;
import org.hibernate.collection.spi.CollectionSemantics;
/**
* MappingType for collections. Not that this is the descriptor for
* the collection Java type (List, Set, etc)
*
* @author Steve Ebersole
*/
public interface CollectionMappingType<C> extends MappingType {
CollectionSemantics<C> getCCollectionSemantics();
}

View File

@ -0,0 +1,177 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.mapping.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.LockMode;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
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;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Models a basic collection element/value or index/key
*
* @author Steve Ebersole
*/
public class BasicValuedCollectionPart implements BasicValuedModelPart {
public BasicValuedCollectionPart(
Nature nature,
BasicType mapper,
BasicValueConverter valueConverter,
String tableExpression,
String columnExpression) {
this.nature = nature;
this.mapper = mapper;
this.valueConverter = valueConverter;
this.tableExpression = tableExpression;
this.columnExpression = columnExpression;
}
enum Nature { ELEMENT, INDEX }
private final Nature nature;
private final BasicType mapper;
private final BasicValueConverter valueConverter;
private final String tableExpression;
private final String columnExpression;
@Override
public String getContainingTableExpression() {
return tableExpression;
}
@Override
public String getMappedColumnExpression() {
return columnExpression;
}
@Override
public BasicValueConverter getConverter() {
return valueConverter;
}
@Override
public JavaTypeDescriptor getJavaTypeDescriptor() {
return mapper.getMappedJavaTypeDescriptor();
}
@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()
);
}
private SqlSelection resolveSqlSelection(TableGroup tableGroup, DomainResultCreationState creationState) {
final SqlExpressionResolver exprResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
return exprResolver.resolveSqlSelection(
exprResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableExpression, columnExpression ),
sqlAstProcessingState -> new ColumnReference(
tableGroup.resolveTableReference( tableExpression ).getIdentificationVariable(),
columnExpression,
mapper,
creationState.getSqlAstCreationState().getCreationContext().getSessionFactory()
)
),
getJavaTypeDescriptor(),
creationState.getSqlAstCreationState().getCreationContext().getDomainModel().getTypeConfiguration()
);
}
@Override
public void applySqlSelections(
NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) {
}
@Override
public JdbcMapping getJdbcMapping() {
return mapper;
}
@Override
public MappingType getMappedTypeDescriptor() {
return mapper;
}
@Override
public String getFetchableName() {
return nature == Nature.ELEMENT ? "{value}" : "{key}";
}
@Override
public FetchStrategy getMappedFetchStrategy() {
return FetchStrategy.IMMEDIATE_JOIN;
}
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection(
creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( fetchablePath.getParent() ),
creationState
);
return new BasicFetch(
sqlSelection.getValuesArrayPosition(),
fetchParent,
fetchablePath,
this,
false,
valueConverter,
FetchTiming.IMMEDIATE,
creationState
);
}
@Override
public int getJdbcTypeCount(TypeConfiguration typeConfiguration) {
return 1;
}
@Override
public List<JdbcMapping> getJdbcMappings(TypeConfiguration typeConfiguration) {
return Collections.singletonList( getJdbcMapping() );
}
}

View File

@ -10,29 +10,55 @@
import java.util.function.Consumer;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.collection.internal.StandardArraySemantics;
import org.hibernate.collection.internal.StandardBagSemantics;
import org.hibernate.collection.internal.StandardIdentifierBagSemantics;
import org.hibernate.collection.internal.StandardListSemantics;
import org.hibernate.collection.internal.StandardMapSemantics;
import org.hibernate.collection.internal.StandardOrderedMapSemantics;
import org.hibernate.collection.internal.StandardOrderedSetSemantics;
import org.hibernate.collection.internal.StandardSetSemantics;
import org.hibernate.collection.internal.StandardSortedMapSemantics;
import org.hibernate.collection.internal.StandardSortedSetSemantics;
import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.Array;
import org.hibernate.mapping.Bag;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IdentifierBag;
import org.hibernate.mapping.List;
import org.hibernate.mapping.Map;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.PrimitiveArray;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Set;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadata;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.persister.walking.internal.FetchStrategyHelper;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqlExpressionResolver;
@ -41,8 +67,8 @@
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.internal.domain.basic.BasicFetch;
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;
@ -53,6 +79,7 @@
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import org.hibernate.type.descriptor.java.spi.JavaTypeDescriptorRegistry;
import org.hibernate.type.spi.TypeConfiguration;
/**
@ -580,4 +607,180 @@ public CascadeStyle getCascadeStyle() {
return (EmbeddedAttributeMapping) embeddableMappingType.getEmbeddedValueMapping();
}
public static PluralAttributeMapping buildPluralAttributeMapping(
String attrName,
int stateArrayPosition,
Property bootProperty,
ManagedMappingType declaringType,
String tableExpression,
String[] attrColumnExpressions,
PropertyAccess propertyAccess,
CascadeStyle cascadeStyle,
MappingModelCreationProcess creationProcess) {
final Collection bootValueMapping = (Collection) bootProperty.getValue();
final RuntimeModelCreationContext creationContext = creationProcess.getCreationContext();
final CollectionPersister collectionDescriptor = creationContext.getDomainModel().findCollectionDescriptor( bootValueMapping.getRole() );
assert collectionDescriptor != null;
final CollectionMappingType<?> collectionMappingType;
final JavaTypeDescriptorRegistry jtdRegistry = creationContext.getJavaTypeDescriptorRegistry();
if ( bootValueMapping instanceof Array ) {
if ( bootValueMapping instanceof PrimitiveArray ) {
throw new NotYetImplementedFor6Exception();
}
else {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( Object[].class ),
StandardArraySemantics.INSTANCE
);
}
}
else if ( bootValueMapping instanceof Bag ) {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.Collection.class ),
StandardBagSemantics.INSTANCE
);
}
else if ( bootValueMapping instanceof IdentifierBag ) {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.Collection.class ),
StandardIdentifierBagSemantics.INSTANCE
);
}
else if ( bootValueMapping instanceof List ) {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.List.class ),
StandardListSemantics.INSTANCE
);
}
else if ( bootValueMapping instanceof Map ) {
if ( bootValueMapping.isSorted() ) {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.SortedMap.class ),
StandardSortedMapSemantics.INSTANCE
);
}
else {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.Map.class ),
bootValueMapping.hasOrder()
? StandardOrderedMapSemantics.INSTANCE
: StandardMapSemantics.INSTANCE
);
}
}
else if ( bootValueMapping instanceof Set ) {
if ( bootValueMapping.isSorted() ) {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.SortedSet.class ),
StandardSortedSetSemantics.INSTANCE
);
}
else {
collectionMappingType = new CollectionMappingTypeImpl(
jtdRegistry.getDescriptor( java.util.Set.class ),
bootValueMapping.hasOrder()
? StandardOrderedSetSemantics.INSTANCE
: StandardSetSemantics.INSTANCE
);
}
}
else {
throw new MappingException( "Unexpected org.hibernate.mapping.Collection impl : " + bootValueMapping );
}
final StateArrayContributorMetadata contributorMetadata = new StateArrayContributorMetadata() {
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public MutabilityPlan getMutabilityPlan() {
return ImmutableMutabilityPlan.instance();
}
@Override
public boolean isNullable() {
return bootProperty.isOptional();
}
@Override
public boolean isInsertable() {
return bootProperty.isInsertable();
}
@Override
public boolean isUpdatable() {
return bootProperty.isUpdateable();
}
@Override
public boolean isIncludedInDirtyChecking() {
return false;
}
@Override
public boolean isIncludedInOptimisticLocking() {
return bootProperty.isOptimisticLocked();
}
};
final FetchStyle style = FetchStrategyHelper.determineFetchStyleByMetadata(
( (OuterJoinLoadable) declaringType ).getFetchMode( stateArrayPosition ),
collectionDescriptor.getCollectionType(),
creationContext.getSessionFactory()
);
return new PluralAttributeMappingImpl(
attrName,
propertyAccess,
entityMappingType -> contributorMetadata,
collectionMappingType,
stateArrayPosition,
tableExpression,
attrColumnExpressions,
null,
null,
new FetchStrategy(
FetchStrategyHelper.determineFetchTiming(
style,
collectionDescriptor.getCollectionType(),
creationContext.getSessionFactory()
),
style
),
cascadeStyle,
declaringType,
collectionDescriptor
);
}
private static class CollectionMappingTypeImpl implements CollectionMappingType {
private final JavaTypeDescriptor collectionJtd;
private final CollectionSemantics semantics;
@SuppressWarnings("WeakerAccess")
public CollectionMappingTypeImpl(
JavaTypeDescriptor collectionJtd,
CollectionSemantics semantics) {
this.collectionJtd = collectionJtd;
this.semantics = semantics;
}
@Override
public CollectionSemantics getCCollectionSemantics() {
return semantics;
}
@Override
public JavaTypeDescriptor getMappedJavaTypeDescriptor() {
return collectionJtd;
}
}
}

View File

@ -0,0 +1,135 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.LockMode;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.engine.FetchStrategy;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqlAstCreationState;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.spi.DomainResultCreationState;
import org.hibernate.sql.results.spi.Fetch;
import org.hibernate.sql.results.spi.FetchParent;
/**
* @author Steve Ebersole
*/
public class PluralAttributeMappingImpl extends AbstractAttributeMapping implements PluralAttributeMapping {
private final int stateArrayPosition;
private final PropertyAccess propertyAccess;
private final StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess;
private final FetchStrategy fetchStrategy;
private final String tableExpression;
private final String[] attrColumnExpressions;
private final ModelPart elementDescriptor;
private final ModelPart indexDescriptor;
private final CascadeStyle cascadeStyle;
private final CollectionPersister collectionDescriptor;
@SuppressWarnings("WeakerAccess")
public PluralAttributeMappingImpl(
String attributeName,
PropertyAccess propertyAccess,
StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess,
CollectionMappingType collectionMappingType,
int stateArrayPosition,
String tableExpression,
String[] attrColumnExpressions,
ModelPart elementDescriptor,
ModelPart indexDescriptor,
FetchStrategy fetchStrategy,
CascadeStyle cascadeStyle,
ManagedMappingType declaringType,
CollectionPersister collectionDescriptor) {
super( attributeName, collectionMappingType, declaringType );
this.propertyAccess = propertyAccess;
this.stateArrayContributorMetadataAccess = stateArrayContributorMetadataAccess;
this.stateArrayPosition = stateArrayPosition;
this.tableExpression = tableExpression;
this.attrColumnExpressions = attrColumnExpressions;
this.elementDescriptor = elementDescriptor;
this.indexDescriptor = indexDescriptor;
this.fetchStrategy = fetchStrategy;
this.cascadeStyle = cascadeStyle;
this.collectionDescriptor = collectionDescriptor;
}
@Override
public CollectionMappingType getMappedTypeDescriptor() {
return (CollectionMappingType) super.getMappedTypeDescriptor();
}
@Override
public CollectionPersister getCollectionDescriptor() {
return collectionDescriptor;
}
@Override
public ModelPart getValueDescriptor() {
return elementDescriptor;
}
@Override
public ModelPart getIndexDescriptor() {
return indexDescriptor;
}
@Override
public int getStateArrayPosition() {
return stateArrayPosition;
}
@Override
public StateArrayContributorMetadataAccess getAttributeMetadataAccess() {
return stateArrayContributorMetadataAccess;
}
@Override
public PropertyAccess getPropertyAccess() {
return propertyAccess;
}
@Override
public String getFetchableName() {
return getAttributeName();
}
@Override
public FetchStrategy getMappedFetchStrategy() {
return fetchStrategy;
}
@Override
public Fetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
LockMode lockMode,
String resultVariable,
DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final TableGroup collectionTableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( fetchablePath );
throw new NotYetImplementedFor6Exception( getClass() );
}
}

View File

@ -5338,12 +5338,25 @@ public Object[] getPropertyValues(Object object) {
@Override
public Object getPropertyValue(Object object, int i) {
return getEntityTuplizer().getPropertyValue( object, i );
return attributeMappings.get( i ).getAttributeMetadataAccess()
.resolveAttributeMetadata( this )
.getPropertyAccess()
.getGetter()
.get( object );
}
@Override
public Object getPropertyValue(Object object, String propertyName) {
return getEntityTuplizer().getPropertyValue( object, propertyName );
for ( int i = 0; i < attributeMappings.size(); i++ ) {
if ( attributeMappings.get( i ).getAttributeName().equals( propertyName ) ) {
return attributeMappings.get( i ).getAttributeMetadataAccess()
.resolveAttributeMetadata( this )
.getPropertyAccess()
.getGetter()
.get( object );
}
}
return null;
}
@Override
@ -6316,6 +6329,19 @@ else if ( attrType instanceof CompositeType ) {
creationProcess
);
}
else if ( attrType instanceof CollectionType ) {
return MappingModelCreationHelper.buildPluralAttributeMapping(
attrName,
stateArrayPosition,
bootProperty,
declaringType,
tableExpression,
attrColumnNames,
propertyAccess,
tupleAttrDefinition.getCascadeStyle(),
creationProcess
);
}
// todo (6.0) : for now ignore any non basic-typed attributes

View File

@ -36,6 +36,7 @@ public class StandardRowReader<T> implements RowReader<T> {
private final Object[] resultRow;
@SuppressWarnings("WeakerAccess")
public StandardRowReader(
List<DomainResultAssembler> resultAssemblers,
List<Initializer> initializers,
@ -123,23 +124,23 @@ private void coordinateInitializers(
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// old
for ( Initializer initializer : initializers ) {
initializer.resolveKey( rowProcessingState );
for ( int i = 0; i < initializers.size(); i++ ) {
initializers.get( i ).resolveKey( rowProcessingState );
}
for ( Initializer initializer : initializers ) {
initializer.resolveInstance( rowProcessingState );
for ( int i = 0; i < initializers.size(); i++ ) {
initializers.get( i ).resolveInstance( rowProcessingState );
}
for ( Initializer initializer : initializers ) {
initializer.initializeInstance( rowProcessingState );
for ( int i = 0; i < initializers.size(); i++ ) {
initializers.get( i ).initializeInstance( rowProcessingState );
}
}
@Override
public void finishUp(JdbcValuesSourceProcessingState processingState) {
for ( Initializer initializer : initializers ) {
initializer.endLoading( processingState.getExecutionContext() );
for ( int i = 0; i < initializers.size(); i++ ) {
initializers.get( i ).endLoading( processingState.getExecutionContext() );
}
// todo : use Callback to execute AfterLoadActions

View File

@ -6,12 +6,7 @@
*/
package org.hibernate.sql.results.internal.domain.entity;
import java.util.ArrayList;
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;
@ -37,8 +32,7 @@ public abstract class AbstractEntityResultNode extends AbstractFetchParent imple
private final EntityMappingType targetType;
private final List<DomainResult> attributeDomainResults;
@SuppressWarnings("WeakerAccess")
public AbstractEntityResultNode(
EntityValuedModelPart referencedModelPart,
LockMode lockMode,
@ -47,6 +41,7 @@ public AbstractEntityResultNode(
this( referencedModelPart, lockMode, navigablePath, null, creationState );
}
@SuppressWarnings("WeakerAccess")
public AbstractEntityResultNode(
EntityValuedModelPart referencedModelPart,
LockMode lockMode,
@ -93,21 +88,6 @@ public AbstractEntityResultNode(
creationState
);
}
// todo (6.0) : handle other special navigables such as discriminator, row-id, tenant-id, etc
attributeDomainResults = CollectionHelper.arrayList( entityDescriptor.getNumberOfAttributeMappings() );
entityDescriptor.visitAttributeMappings(
mapping -> attributeDomainResults.add(
mapping.createDomainResult(
navigablePath.append( mapping.getAttributeName() ),
entityTableGroup,
null,
creationState
)
)
);
}
@Override
@ -129,14 +109,17 @@ public LockMode getLockMode() {
return lockMode;
}
@SuppressWarnings("WeakerAccess")
protected DomainResult getIdentifierResult() {
return identifierResult;
}
@SuppressWarnings("WeakerAccess")
protected DomainResult getDiscriminatorResult() {
return discriminatorResult;
}
@SuppressWarnings("WeakerAccess")
protected DomainResult getVersionResult() {
return versionResult;
}

View File

@ -16,6 +16,11 @@
public class ImmutableMutabilityPlan<T> implements MutabilityPlan<T> {
public static final ImmutableMutabilityPlan INSTANCE = new ImmutableMutabilityPlan();
public static <X> ImmutableMutabilityPlan<X> instance() {
//noinspection unchecked
return INSTANCE;
}
@Override
public boolean isMutable() {
return false;

View File

@ -0,0 +1,330 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.test.metamodel.mapping;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.CascadeType;
import javax.persistence.ElementCollection;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OrderColumn;
import javax.persistence.Table;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
/**
* @author Steve Ebersole
*/
@DomainModel(
annotatedClasses = {
PluralAttributeTests.SimpleEntity.class,
PluralAttributeTests.EntityContainingLists.class,
PluralAttributeTests.Component.class
}
)
@ServiceRegistry
@SessionFactory
@SuppressWarnings("WeakerAccess")
public class PluralAttributeTests {
@Test
public void testLists(SessionFactoryScope scope) {
System.out.println( "test" );
}
@BeforeAll
public void createTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
final EntityContainingLists entityContainingLists = new EntityContainingLists( 1, "first" );
entityContainingLists.addBasic( "abc" );
entityContainingLists.addBasic( "def" );
entityContainingLists.addBasic( "ghi" );
entityContainingLists.addConvertedBasic( EnumValue.TWO );
entityContainingLists.addEnum( EnumValue.ONE );
entityContainingLists.addEnum( EnumValue.THREE );
entityContainingLists.addComponent( new Component( "first-a1", "first-another-a1" ) );
entityContainingLists.addComponent( new Component( "first-a2", "first-another-a2" ) );
entityContainingLists.addSimpleEntity( new SimpleEntity( 1, "simple-1" ) );
entityContainingLists.addSimpleEntity( new SimpleEntity( 2, "simple-2" ) );
session.save( entityContainingLists );
}
);
}
@AfterAll
public void deleteTestData(SessionFactoryScope scope) {
scope.inTransaction(
session -> session.doWork(
conn -> {
try ( Statement stmnt = conn.createStatement() ) {
stmnt.execute( "delete from EntityContainingLists_listOfEnums" );
stmnt.execute( "delete from EntityContainingLists_listOfConvertedBasics" );
stmnt.execute( "delete from EntityContainingLists_listOfComponents" );
stmnt.execute( "delete from EntityContainingLists_listOfBasics" );
stmnt.execute( "delete from entity_containing_lists_simple_entity" );
stmnt.execute( "delete from entity_containing_lists" );
}
}
)
);
}
public enum EnumValue {
ONE( "first" ),
TWO( "second" ),
THREE( "third" );
private final String code;
EnumValue(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public static EnumValue fromCode(String code) {
if ( code == null || code.isEmpty() ) {
return null;
}
switch ( code ) {
case "first" : {
return ONE;
}
case "second" : {
return TWO;
}
case "third" : {
return THREE;
}
default: {
throw new RuntimeException( "Could not convert enum code : " + code );
}
}
}
}
public static class Converter implements AttributeConverter<EnumValue,String> {
@Override
public String convertToDatabaseColumn(EnumValue domainValue) {
return domainValue == null ? null : domainValue.getCode();
}
@Override
public EnumValue convertToEntityAttribute(String dbData) {
return EnumValue.fromCode( dbData );
}
}
@Entity( name = "EntityContainingLists" )
@Table( name = "entity_containing_lists" )
public static class EntityContainingLists {
private Integer id;
private String name;
private List<String> listOfBasics;
private List<EnumValue> listOfConvertedBasics;
private List<EnumValue> listOfEnums;
private List<Component> listOfComponents;
private List<SimpleEntity> listOfEntities;
public EntityContainingLists() {
}
public EntityContainingLists(Integer id, String name) {
this.id = id;
this.name = name;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ElementCollection
@OrderColumn
public List<String> getListOfBasics() {
return listOfBasics;
}
public void setListOfBasics(List<String> listOfBasics) {
this.listOfBasics = listOfBasics;
}
public void addBasic(String basic) {
if ( listOfBasics == null ) {
listOfBasics = new ArrayList<>();
}
listOfBasics.add( basic );
}
@ElementCollection
@OrderColumn
public List<EnumValue> getListOfConvertedBasics() {
return listOfConvertedBasics;
}
public void setListOfConvertedBasics(List<EnumValue> listOfConvertedBasics) {
this.listOfConvertedBasics = listOfConvertedBasics;
}
public void addConvertedBasic(EnumValue value) {
if ( listOfConvertedBasics == null ) {
listOfConvertedBasics = new ArrayList<>();
}
listOfConvertedBasics.add( value );
}
@ElementCollection
@Enumerated( EnumType.STRING )
@OrderColumn
public List<EnumValue> getListOfEnums() {
return listOfEnums;
}
public void setListOfEnums(List<EnumValue> listOfEnums) {
this.listOfEnums = listOfEnums;
}
public void addEnum(EnumValue value) {
if ( listOfEnums == null ) {
listOfEnums = new ArrayList<>();
}
listOfEnums.add( value );
}
@ElementCollection
@OrderColumn
public List<Component> getListOfComponents() {
return listOfComponents;
}
public void setListOfComponents(List<Component> listOfComponents) {
this.listOfComponents = listOfComponents;
}
public void addComponent(Component value) {
if ( listOfComponents == null ) {
listOfComponents = new ArrayList<>();
}
listOfComponents.add( value );
}
@OneToMany( cascade = CascadeType.ALL )
@OrderColumn
public List<SimpleEntity> getListOfEntities() {
return listOfEntities;
}
public void setListOfEntities(List<SimpleEntity> listOfEntities) {
this.listOfEntities = listOfEntities;
}
public void addSimpleEntity(SimpleEntity value) {
if ( listOfEntities == null ) {
listOfEntities = new ArrayList<>();
}
listOfEntities.add( value );
}
}
@Entity( name = "SimpleEntity" )
@Table( name = "simple_entity" )
public static class SimpleEntity {
private Integer id;
private String name;
public SimpleEntity() {
}
public SimpleEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Embeddable
public static class Component {
private String anAttribute;
private String anotherAttribute;
public Component() {
}
public Component(String anAttribute, String anotherAttribute) {
this.anAttribute = anAttribute;
this.anotherAttribute = anotherAttribute;
}
public String getAnAttribute() {
return anAttribute;
}
public void setAnAttribute(String anAttribute) {
this.anAttribute = anAttribute;
}
public String getAnotherAttribute() {
return anotherAttribute;
}
public void setAnotherAttribute(String anotherAttribute) {
this.anotherAttribute = anotherAttribute;
}
}
}