Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.hibernate.PropertyNotFoundException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.IndexedCollection;
Expand All @@ -29,6 +30,8 @@

import jakarta.persistence.Convert;
import jakarta.persistence.JoinTable;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.type.internal.ParameterizedTypeImpl;

import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
Expand Down Expand Up @@ -419,6 +422,34 @@ else if ( value instanceof SimpleValue simpleValue ) {
}
}

static void setType(Value value, TypeDetails type) {
final var typeName = type.getName();
if ( value instanceof ToOne toOne ) {
toOne.setReferencedEntityName( typeName );
toOne.setTypeName( typeName );
}
else if ( value instanceof Component component ) {
// Avoid setting type name for generic components
if ( !component.isGeneric() ) {
component.setComponentClassName( typeName );
}
if ( component.getTypeName() != null ) {
component.setTypeName( typeName );
}
}
else if ( value instanceof SimpleValue simpleValue ) {
if ( value instanceof BasicValue basicValue ) {
basicValue.setImplicitJavaTypeAccess( typeConfiguration ->
type.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE
? ParameterizedTypeImpl.from( type.asParameterizedType() )
: type.determineRawClass().toJavaClass() );
}
else {
simpleValue.setTypeName( typeName );
}
}
}

private void addPropertyToJoin(Property property, MemberDetails memberDetails, ClassDetails declaringClass, Join join) {
if ( declaringClass != null ) {
final var inheritanceState = inheritanceStatePerClass.get( declaringClass );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.util.StringHelper;
Expand All @@ -59,6 +60,7 @@
import org.hibernate.mapping.Join;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
Expand All @@ -71,6 +73,7 @@
import org.hibernate.models.internal.ClassTypeDetailsImpl;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.ModelsContext;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.spi.NavigablePath;
Expand All @@ -97,6 +100,7 @@
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap;
import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap;
import static org.hibernate.boot.model.internal.ClassPropertyHolder.setType;
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation;
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverrideAnnotation;
import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable;
Expand All @@ -122,6 +126,7 @@
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
import static org.hibernate.jpa.event.internal.CallbackDefinitionResolver.resolveLifecycleCallbacks;
import static org.hibernate.models.spi.TypeDetailsHelper.resolveRelativeType;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;


Expand Down Expand Up @@ -1088,6 +1093,7 @@ private void processIdPropertiesIfNotAlready(
missingIdProperties.remove( propertyName );
}
}
addGenericProperties( persistentClass, inheritanceState, inheritanceStates );

if ( !missingIdProperties.isEmpty() ) {
throw new AnnotationException( "Entity '" + persistentClass.getEntityName()
Expand All @@ -1101,6 +1107,77 @@ else if ( !missingEntityProperties.isEmpty() ) {
}
}

private void addGenericProperties(
PersistentClass persistentClass,
InheritanceState inheritanceState,
Map<ClassDetails, InheritanceState> inheritanceStates) {
if ( persistentClass.isAbstract() == null || !persistentClass.isAbstract() ) {
var superclass = persistentClass.getSuperPersistentClass();
while ( superclass != null ) {
for ( Property declaredProperty : superclass.getDeclaredProperties() ) {
if ( declaredProperty.isGeneric() ) {
final var memberDetails = getMemberDetails( inheritanceState, inheritanceStates, declaredProperty, superclass );
final var typeDetails = resolveRelativeType( memberDetails.getType(), inheritanceState.getClassDetails() );
final var returnedClassName = typeDetails.getName();
final var actualProperty = declaredProperty.copy();
actualProperty.setGeneric( false );
actualProperty.setGenericSpecialization( true );
actualProperty.setReturnedClassName( returnedClassName );
final var value = actualProperty.getValue().copy();
setType( value, typeDetails );
actualProperty.setValue( value );
persistentClass.addProperty( actualProperty );
if ( value instanceof BasicValue basicValue ) {
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
final BasicValue originalBasicValue = (BasicValue) declaredProperty.getValue();
metadataCollector.addSecondPass( new SecondPass() {
@Override
public void doSecondPass(Map<String, PersistentClass> persistentClasses)
throws MappingException {
basicValue.setExplicitTypeParams( originalBasicValue.getExplicitTypeParams() );
basicValue.setTypeParameters( originalBasicValue.getTypeParameters() );
basicValue.setJpaAttributeConverterDescriptor( originalBasicValue.getJpaAttributeConverterDescriptor() );
// Don't copy over the implicit java type access, since we figure that out in ClassPropertyHolder#setType
// basicValue.setImplicitJavaTypeAccess( originalBasicValue.getImplicitJavaTypeAccess() );
basicValue.setExplicitJavaTypeAccess( originalBasicValue.getExplicitJavaTypeAccess() );
basicValue.setExplicitJdbcTypeAccess( originalBasicValue.getExplicitJdbcTypeAccess() );
basicValue.setExplicitMutabilityPlanAccess( originalBasicValue.getExplicitMutabilityPlanAccess() );
basicValue.setEnumerationStyle( originalBasicValue.getEnumeratedType() );
basicValue.setTimeZoneStorageType( originalBasicValue.getTimeZoneStorageType() );
basicValue.setTemporalPrecision( originalBasicValue.getTemporalPrecision() );
if ( originalBasicValue.isLob() ) {
basicValue.makeLob();
}
if ( originalBasicValue.isNationalized() ) {
basicValue.makeNationalized();
}
}
} );
metadataCollector.registerValueMappingResolver( basicValue::resolve );
}
}
}

superclass = superclass.getSuperPersistentClass();
}
}
}

private static MemberDetails getMemberDetails(InheritanceState inheritanceState, Map<ClassDetails, InheritanceState> inheritanceStates, Property declaredProperty, PersistentClass superclass) {
var superclassDetails = inheritanceState.getClassDetails().getSuperClass();
while ( !superclass.getClassName().equals( superclassDetails.getClassName()) ) {
superclassDetails = superclassDetails.getSuperClass();
}
final var superclassInheritanceState = inheritanceStates.get( superclassDetails );
final var elementsToProcess = superclassInheritanceState.getElementsToProcess();
for ( PropertyData element : elementsToProcess.getElements() ) {
if ( declaredProperty.getName().equals( element.getPropertyName() ) ) {
return element.getAttributeMember();
}
}
throw new IllegalArgumentException("Couldn't find PropertyData for [" + declaredProperty.getName() + "] in class: " + declaredProperty.getPersistentClass().getClassName() );
}

private static String getMissingPropertiesString(Set<String> propertyNames) {
final var missingProperties = new StringBuilder();
for ( String propertyName : propertyNames ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ public Boolean hasIdClassOrEmbeddedId() {
* guessing from @Id or @EmbeddedId presence if not specified.
* Change EntityBinder by side effect
*/
private ElementsToProcess getElementsToProcess() {
ElementsToProcess getElementsToProcess() {
if ( elementsToProcess == null ) {
final var inheritanceState = inheritanceStatePerClass.get( classDetails );
assert !inheritanceState.isEmbeddableSuperclass();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public class PropertyBinder {

private String name;
private String returnedClassName;
private boolean generic;
private boolean genericSpecialization;
private boolean lazy;
private String lazyGroup;
private AccessType accessType;
Expand Down Expand Up @@ -166,6 +168,14 @@ private void setReturnedClassName(String returnedClassName) {
this.returnedClassName = returnedClassName;
}

public void setGeneric(boolean generic) {
this.generic = generic;
}

public void setGenericSpecialization(boolean genericSpecialization) {
this.genericSpecialization = genericSpecialization;
}

public void setLazy(boolean lazy) {
this.lazy = lazy;
}
Expand Down Expand Up @@ -441,6 +451,8 @@ public Property makeProperty() {
property.setCascade( cascadeTypes );
property.setPropertyAccessorName( accessType.getType() );
property.setReturnedClassName( returnedClassName );
property.setGeneric( generic );
property.setGenericSpecialization( genericSpecialization );
// property.setPropertyAccessStrategy( propertyAccessStrategy );
handleValueGeneration( property );
handleNaturalId( property );
Expand Down Expand Up @@ -1002,6 +1014,11 @@ private AnnotatedColumns bindBasicOrComposite(
ClassDetails returnedClass) {
final var memberDetails = inferredData.getAttributeMember();

if ( propertyHolder.isEntity() && propertyHolder.getPersistentClass().isAbstract() ) {
// When the type of the member is a type variable, we mark it as generic for abstract classes
setGeneric( inferredData.getClassOrElementType().getTypeKind() == TypeDetails.Kind.TYPE_VARIABLE );
}

// overrides from @MapsId or @IdClass if needed
final PropertyData overridingProperty =
overridingProperty( propertyHolder, isIdentifierMapper, memberDetails );
Expand Down
21 changes: 21 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ public BasicValue(BasicValue original) {
this.isSoftDelete = original.isSoftDelete;
this.softDeleteStrategy = original.softDeleteStrategy;
this.aggregateColumn = original.aggregateColumn;
this.jdbcTypeCode = original.jdbcTypeCode;
}

@Override
Expand Down Expand Up @@ -215,6 +216,22 @@ public void setImplicitJavaTypeAccess(Function<TypeConfiguration, java.lang.refl
this.implicitJavaTypeAccess = implicitJavaTypeAccess;
}

public Function<TypeConfiguration, BasicJavaType<?>> getExplicitJavaTypeAccess() {
return explicitJavaTypeAccess;
}

public Function<TypeConfiguration, JdbcType> getExplicitJdbcTypeAccess() {
return explicitJdbcTypeAccess;
}

public Function<TypeConfiguration, MutabilityPlan<?>> getExplicitMutabilityPlanAccess() {
return explicitMutabilityPlanAccess;
}

public Function<TypeConfiguration, java.lang.reflect.Type> getImplicitJavaTypeAccess() {
return implicitJavaTypeAccess;
}

public Selectable getColumn() {
return getColumnSpan() == 0 ? null : getColumn( 0 );
}
Expand Down Expand Up @@ -1030,6 +1047,10 @@ public void setExplicitTypeParams(Map<String,String> explicitLocalTypeParams) {
this.explicitLocalTypeParams = explicitLocalTypeParams;
}

public Map<String, String> getExplicitTypeParams() {
return explicitLocalTypeParams;
}

public void setExplicitTypeName(String typeName) {
this.explicitTypeName = typeName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public static void checkPropertyColumnDuplication(
List<Property> properties,
String owner) throws MappingException {
for ( var property : properties ) {
if ( property.isUpdatable() || property.isInsertable() ) {
if ( ( property.isUpdatable() || property.isInsertable() ) && !property.isGenericSpecialization() ) {
property.getValue().checkColumnDuplication( distinctColumns, owner );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,8 @@ public boolean isExplicitPolymorphism() {

public abstract List<Property> getPropertyClosure();

public abstract List<Property> getAllPropertyClosure();

public abstract List<Table> getTableClosure();

public abstract List<KeyValue> getKeyClosure();
Expand Down Expand Up @@ -722,6 +724,23 @@ public int getJoinClosureSpan() {
}

public int getPropertyClosureSpan() {
int span = 0;
for ( Property property : properties ) {
if ( !property.isGeneric() ) {
span += 1;
}
}
for ( var join : joins ) {
for ( Property property : join.getProperties() ) {
if ( !property.isGeneric() ) {
span += 1;
}
}
}
return span;
}

public int getAllPropertyClosureSpan() {
int span = properties.size();
for ( var join : joins ) {
span += join.getPropertySpan();
Expand Down Expand Up @@ -754,6 +773,23 @@ public int getJoinNumber(Property prop) {
* @return A list over the "normal" properties.
*/
public List<Property> getProperties() {
final ArrayList<Property> list = new ArrayList<>();
for ( Property property : properties ) {
if ( !property.isGeneric() ) {
list.add( property );
}
}
for ( var join : joins ) {
for ( Property property : join.getProperties() ) {
if ( !property.isGeneric() ) {
list.add( property );
}
}
}
return list;
}

public List<Property> getAllProperties() {
final ArrayList<List<Property>> list = new ArrayList<>();
list.add( properties );
for ( var join : joins ) {
Expand Down
10 changes: 10 additions & 0 deletions hibernate-core/src/main/java/org/hibernate/mapping/Property.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public class Property implements Serializable, MetaAttributable {
private PersistentClass persistentClass;
private boolean naturalIdentifier;
private boolean isGeneric;
private boolean isGenericSpecialization;
private boolean lob;
private java.util.List<CallbackDefinition> callbackDefinitions;
private String returnedClassName;
Expand Down Expand Up @@ -490,6 +491,14 @@ public void setGeneric(boolean generic) {
this.isGeneric = generic;
}

public boolean isGenericSpecialization() {
return isGenericSpecialization;
}

public void setGenericSpecialization(boolean genericSpecialization) {
isGenericSpecialization = genericSpecialization;
}

public boolean isLob() {
return lob;
}
Expand Down Expand Up @@ -554,6 +563,7 @@ public Property copy() {
property.setPersistentClass( getPersistentClass() );
property.setNaturalIdentifier( isNaturalIdentifier() );
property.setGeneric( isGeneric() );
property.setGenericSpecialization( isGenericSpecialization() );
property.setLob( isLob() );
property.addCallbackDefinitions( getCallbackDefinitions() );
property.setReturnedClassName( getReturnedClassName() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public List<Property> getPropertyClosure() {
return getProperties();
}

@Override
public List<Property> getAllPropertyClosure() {
return getAllProperties();
}

@Override
public List<Table> getTableClosure() {
return List.of( getTable() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,13 @@ public SimpleValue(MetadataBuildingContext buildingContext, Table table) {
protected SimpleValue(SimpleValue original) {
this.buildingContext = original.buildingContext;
this.metadata = original.metadata;
this.columns.addAll( original.columns );
for ( Selectable selectable : original.columns ) {
if ( selectable instanceof Column column ) {
final Column newColumn = column.clone();
newColumn.setValue( this );
this.columns.add( newColumn );
}
}
this.insertability.addAll( original.insertability );
this.updatability.addAll( original.updatability );
this.partitionKey = original.partitionKey;
Expand Down
Loading
Loading