diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java index b66003fc0f..2b7a16cf22 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Component.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Component.java @@ -26,6 +26,7 @@ import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.source.internal.hbm.MappingDocument; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; +import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.Mapping; @@ -42,9 +43,13 @@ import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.Setter; import org.hibernate.generator.Generator; import org.hibernate.generator.BeforeExecutionGenerator; +import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; +import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; import org.hibernate.type.EmbeddedComponentType; +import org.hibernate.type.UserComponentType; +import org.hibernate.usertype.CompositeUserType; import static java.util.stream.Collectors.toList; import static org.hibernate.generator.EventType.INSERT; @@ -327,15 +332,20 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable } public Class getComponentClass() throws MappingException { - final ClassLoaderService classLoaderService = getMetadata() - .getMetadataBuildingOptions() - .getServiceRegistry() - .getService( ClassLoaderService.class ); - try { - return classLoaderService.classForName( componentClassName ); + if ( componentClassName == null ) { + return null; } - catch (ClassLoadingException e) { - throw new MappingException("component class not found: " + componentClassName, e); + else { + final ClassLoaderService classLoaderService = getMetadata() + .getMetadataBuildingOptions() + .getServiceRegistry() + .getService( ClassLoaderService.class ); + try { + return classLoaderService.classForName( componentClassName ); + } + catch (ClassLoadingException e) { + throw new MappingException("component class not found: " + componentClassName, e); + } } } @@ -371,12 +381,22 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable this.dynamic = dynamic; } + private CompositeUserType createCompositeUserType(Component component) { + final BootstrapContext bootstrapContext = getBuildingContext().getBootstrapContext(); + final Class> customTypeClass = + bootstrapContext.getClassLoaderAccess().classForName( component.getTypeName() ); + return getBuildingContext().getBuildingOptions().disallowExtensionsInCdi() + ? FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( customTypeClass ) + : bootstrapContext.getServiceRegistry().requireService( ManagedBeanRegistry.class ) + .getBean( customTypeClass ).getBeanInstance(); + } + @Override public CompositeType getType() throws MappingException { // Resolve the type of the value once and for all as this operation generates a proxy class // for each invocation. - // Unfortunately, there's no better way of doing that as none of the classes are immutable and - // we can't know for sure the current state of the property or the value. + // Unfortunately, there's no better way of doing that as none of the classes are immutable, + // and we can't know for sure the current state of the property or the value. CompositeType localType = type; if ( localType == null ) { @@ -387,11 +407,18 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable // Other components should be sorted already sortProperties( true ); - localType = isEmbedded() - ? new EmbeddedComponentType( this, originalPropertyOrder, getBuildingContext() ) - : new ComponentType( this, originalPropertyOrder, getBuildingContext() ); + final String typeName = getTypeName(); + if ( typeName == null ) { + localType = isEmbedded() + ? new EmbeddedComponentType( this, originalPropertyOrder ) + : new ComponentType( this, originalPropertyOrder ); + } + else { + final CompositeUserType compositeUserType = createCompositeUserType( this ); + localType = new UserComponentType<>( this, originalPropertyOrder, compositeUserType ); + } - this.type = localType; + type = localType; } } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java index 51e475f43a..9673fff5fe 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/ComponentType.java @@ -19,7 +19,6 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.PropertyNotFoundException; import org.hibernate.Remove; -import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.CascadeStyle; @@ -38,12 +37,9 @@ import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.sqm.SqmExpressible; -import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; -import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.type.descriptor.ValueExtractor; import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.spi.CompositeTypeImplementor; -import org.hibernate.usertype.CompositeUserType; import static org.hibernate.internal.util.ReflectHelper.isRecord; @@ -68,13 +64,22 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen private final boolean isKey; private boolean hasNotNullProperty; - @SuppressWarnings("rawtypes") - private final CompositeUserType compositeUserType; - private EmbeddableValuedModelPart mappingModelPart; - public ComponentType(Component component, int[] originalPropertyOrder, MetadataBuildingContext buildingContext) { + @Deprecated(forRemoval = true) + public ComponentType(Component component, int[] originalPropertyOrder, MetadataBuildingContext context) { + this( component, originalPropertyOrder ); + } + + public ComponentType(Component component, int[] originalPropertyOrder) { + this( component, originalPropertyOrder, + component.getComponentClassName() != null + && !isRecord( component.getComponentClass() ) ); + } + + public ComponentType(Component component, int[] originalPropertyOrder, boolean mutable) { this.componentClass = component.isDynamic() ? Map.class : component.getComponentClass(); + this.mutable = mutable; this.isAggregate = component.getAggregateColumn() != null; this.isKey = component.isKey(); this.propertySpan = component.getPropertySpan(); @@ -97,23 +102,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen } i++; } - - this.compositeUserType = - component.getTypeName() == null ? null : createCompositeUserType( component, buildingContext ); - this.mutable = !isRecord( componentClass ) && ( compositeUserType == null || compositeUserType.isMutable() ); - } - - private static CompositeUserType createCompositeUserType(Component component, MetadataBuildingContext buildingContext) { - final BootstrapContext bootstrapContext = buildingContext.getBootstrapContext(); - final Class> customTypeClass = - bootstrapContext.getClassLoaderAccess().classForName( component.getTypeName() ); - if ( buildingContext.getBuildingOptions().disallowExtensionsInCdi() ) { - return FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( customTypeClass ); - } - else { - return bootstrapContext.getServiceRegistry().requireService( ManagedBeanRegistry.class ) - .getBean( customTypeClass ).getBeanInstance(); - } } private boolean isAggregate() { @@ -178,9 +166,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( x == y ) { return true; } - if ( compositeUserType != null ) { - return compositeUserType.equals( x, y ); - } // null value and empty component are considered equivalent for ( int i = 0; i < propertySpan; i++ ) { if ( !propertyTypes[i].isEqual( getPropertyValue( x, i ), getPropertyValue( y, i ) ) ) { @@ -196,9 +181,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( x == y ) { return true; } - if ( compositeUserType != null ) { - return compositeUserType.equals( x, y ); - } // null value and empty component are considered equivalent for ( int i = 0; i < propertySpan; i++ ) { if ( !propertyTypes[i].isEqual( getPropertyValue( x, i ), getPropertyValue( y, i ), factory ) ) { @@ -242,9 +224,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen @Override public int getHashCode(final Object x) { - if ( compositeUserType != null ) { - return compositeUserType.hashCode( x ); - } int result = 17; for ( int i = 0; i < propertySpan; i++ ) { final Object y = getPropertyValue( x, i ); @@ -258,9 +237,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen @Override public int getHashCode(final Object x, final SessionFactoryImplementor factory) { - if ( compositeUserType != null ) { - return compositeUserType.hashCode( x ); - } int result = 17; for ( int i = 0; i < propertySpan; i++ ) { final Object y = getPropertyValue( x, i ); @@ -273,7 +249,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen } @Override - public boolean isDirty(final Object x, final Object y, final SharedSessionContractImplementor session) throws HibernateException { + public boolean isDirty(final Object x, final Object y, final SharedSessionContractImplementor session) + throws HibernateException { if ( x == y ) { return false; } @@ -335,7 +312,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen final int len = propertyTypes[i].getColumnSpan( session.getFactory() ); final boolean[] subcheckable = new boolean[len]; System.arraycopy( checkable, loc, subcheckable, 0, len ); - if ( propertyTypes[i].isModified( getPropertyValue( old, i ), getPropertyValue( current, i ), subcheckable, session ) ) { + if ( propertyTypes[i].isModified( getPropertyValue( old, i ), + getPropertyValue( current, i ), subcheckable, session ) ) { return true; } loc += len; @@ -493,9 +471,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen return null; } - if ( compositeUserType != null ) { - return compositeUserType.deepCopy( component ); - } final Object[] values = getPropertyValues( component ); for ( int i = 0; i < propertySpan; i++ ) { values[i] = propertyTypes[i].deepCopy( values[i], factory ); @@ -527,20 +502,10 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( original == null ) { return null; } - if ( compositeUserType != null ) { - return compositeUserType.replace( original, target, owner ); - } //if ( original == target ) return target; final Object[] originalValues = getPropertyValues( original ); - final Object[] resultValues; - - if ( target == null ) { - resultValues = new Object[originalValues.length]; - } - else { - resultValues = getPropertyValues( target ); - } + final Object[] resultValues = target == null ? new Object[originalValues.length] : getPropertyValues( target ); final Object[] replacedValues = TypeHelper.replace( originalValues, @@ -552,8 +517,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen ); if ( target == null ) { - return instantiator() - .instantiate( () -> replacedValues, session.getSessionFactory() ); + return instantiator().instantiate( () -> replacedValues, session.getSessionFactory() ); } else { setPropertyValues( target, replacedValues ); @@ -576,9 +540,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( original == null ) { return null; } - if ( compositeUserType != null ) { - return compositeUserType.replace( original, target, owner ); - } //if ( original == target ) return target; final Object[] originalValues = getPropertyValues( original ); @@ -610,6 +571,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen } } + + @Override public CascadeStyle getCascadeStyle(int i) { return cascade[i]; @@ -626,9 +589,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( value == null ) { return null; } - else if ( compositeUserType != null ) { - return compositeUserType.disassemble( value ); - } else { final Object[] values = getPropertyValues( value ); for ( int i = 0; i < propertyTypes.length; i++ ) { @@ -639,13 +599,11 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen } @Override - public Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) throws HibernateException { + public Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) + throws HibernateException { if ( value == null ) { return null; } - else if ( compositeUserType != null ) { - return compositeUserType.disassemble( value ); - } else { final Object[] values = getPropertyValues( value ); for ( int i = 0; i < propertyTypes.length; i++ ) { @@ -662,9 +620,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen if ( object == null ) { return null; } - else if ( compositeUserType != null ) { - return compositeUserType.assemble( object, owner ); - } else { final Object[] values = (Object[]) object; final Object[] assembled = new Object[values.length]; @@ -753,7 +708,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen } @Override - public Object extract(CallableStatement statement, int startIndex, SharedSessionContractImplementor session) throws SQLException { + public Object extract(CallableStatement statement, int startIndex, SharedSessionContractImplementor session) + throws SQLException { Object[] values; if ( isAggregate() ) { values = (Object[]) jdbcValueExtractor().extract( statement, startIndex, session ); @@ -842,8 +798,4 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen } return mappingModelPart; } - - public boolean isCompositeUserType() { - return compositeUserType != null; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/type/CompositeType.java b/hibernate-core/src/main/java/org/hibernate/type/CompositeType.java index fffc7afb05..3a7b606a2d 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CompositeType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CompositeType.java @@ -16,8 +16,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; /** * Represents a composite type, a type which itself has typed attributes. *

- * For example, a type representing an {@linkplain jakarta.persistence.Embeddable embeddable} - * class is a composite type. + * For example, a type representing an {@linkplain jakarta.persistence.Embeddable embeddable} class + * is a composite type, as is a type backed by a {@link org.hibernate.usertype.CompositeUserType}. * * @author Steve Ebersole */ @@ -55,7 +55,8 @@ public interface CompositeType extends Type { * * @throws HibernateException Indicates a problem access the property values. */ - Object[] getPropertyValues(Object component, SharedSessionContractImplementor session) throws HibernateException; + Object[] getPropertyValues(Object component, SharedSessionContractImplementor session) + throws HibernateException; /** * Extract the values of the component properties from the given component instance without access to the @@ -81,7 +82,8 @@ public interface CompositeType extends Type { * * @throws HibernateException Indicates a problem access the property value. */ - Object getPropertyValue(Object component, int index, SharedSessionContractImplementor session) throws HibernateException; + Object getPropertyValue(Object component, int index, SharedSessionContractImplementor session) + throws HibernateException; /** * Inject property values onto the given component instance @@ -95,6 +97,24 @@ public interface CompositeType extends Type { */ void setPropertyValues(Object component, Object[] values) throws HibernateException; + /** + * Inject property values onto the given component instance, or return a new + * instance with the given property values. + * + * @param component The component instance + * @param values The values to inject + * @return A new instance is necessary + * + * @throws HibernateException Indicates an issue performing the injection + * + * @since 6.3 + */ + default Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session) + throws HibernateException { + setPropertyValues( component, values ); + return component; + } + /** * Retrieve the cascade style of the indicated component property. * diff --git a/hibernate-core/src/main/java/org/hibernate/type/EmbeddedComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/EmbeddedComponentType.java index f31ad5d429..1923ae4129 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/EmbeddedComponentType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EmbeddedComponentType.java @@ -18,10 +18,16 @@ import org.hibernate.property.access.spi.Getter; * @author Gavin King */ public class EmbeddedComponentType extends ComponentType { + + @Deprecated(forRemoval = true) public EmbeddedComponentType(Component component, int[] originalPropertyOrder, MetadataBuildingContext buildingContext) { super( component, originalPropertyOrder, buildingContext ); } + public EmbeddedComponentType(Component component, int[] originalPropertyOrder) { + super( component, originalPropertyOrder ); + } + @Override public boolean isEmbedded() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java index fa6557bfba..8cbb386f21 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/type/TypeHelper.java @@ -11,7 +11,6 @@ import java.util.Map; import org.hibernate.Internal; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; @@ -195,34 +194,17 @@ public class TypeHelper { if ( type.isComponentType() ) { final CompositeType compositeType = (CompositeType) type; // need to extract the component values and check for subtype replacements... - final Type[] subtypes = compositeType.getSubtypes(); - final Object[] origComponentValues = currentOriginal == null - ? new Object[subtypes.length] - : compositeType.getPropertyValues( currentOriginal, session ); - final Object[] targetComponentValues = target[i] == null - ? new Object[subtypes.length] - : compositeType.getPropertyValues( target[i], session ); - final Object[] objects = replaceAssociations( - origComponentValues, - targetComponentValues, - subtypes, - session, - null, - copyCache, - foreignKeyDirection - ); - if ( target[i] != null && compositeType instanceof ComponentType ) { - final ComponentType componentType = (ComponentType) compositeType; - if ( componentType.isCompositeUserType() ) { - final EmbeddableInstantiator instantiator = ( (ComponentType) compositeType ).getMappingModelPart() - .getEmbeddableTypeDescriptor() - .getRepresentationStrategy() - .getInstantiator(); - target[i] = instantiator.instantiate( () -> objects, session.getSessionFactory() ); - } - else { - compositeType.setPropertyValues( target[i], objects ); - } + final Object[] objects = + replaceCompositeAssociations( + session, + copyCache, + foreignKeyDirection, + target[i], + currentOriginal, + compositeType + ); + if ( target[i] != null ) { + target[i] = compositeType.replacePropertyValues( target[i], objects, session ); } copied[i] = target[i]; } @@ -237,4 +219,26 @@ public class TypeHelper { return copied; } + private static Object[] replaceCompositeAssociations( + SharedSessionContractImplementor session, + Map copyCache, + ForeignKeyDirection foreignKeyDirection, + Object target, Object currentOriginal, + CompositeType compositeType) { + final Type[] subtypes = compositeType.getSubtypes(); + return replaceAssociations( + currentOriginal == null + ? new Object[subtypes.length] + : compositeType.getPropertyValues( currentOriginal, session ), + target == null + ? new Object[subtypes.length] + : compositeType.getPropertyValues( target, session ), + subtypes, + session, + null, + copyCache, + foreignKeyDirection + ); + } + } diff --git a/hibernate-core/src/main/java/org/hibernate/type/UserComponentType.java b/hibernate-core/src/main/java/org/hibernate/type/UserComponentType.java new file mode 100644 index 0000000000..531c140cdf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/UserComponentType.java @@ -0,0 +1,99 @@ +/* + * 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 . + */ +package org.hibernate.type; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.mapping.Component; +import org.hibernate.usertype.CompositeUserType; + +import java.io.Serializable; +import java.util.Map; + +/** + * Handles {@link CompositeUserType}s. + * + * @author Gavin King + * + * @since 6.3 + */ +public class UserComponentType extends ComponentType { + + private final CompositeUserType compositeUserType; + + public UserComponentType( + Component component, + int[] originalPropertyOrder, + CompositeUserType compositeUserType) { + super( component, originalPropertyOrder, compositeUserType.isMutable() ); + this.compositeUserType = compositeUserType; + } + + @Override + public boolean isEqual(Object x, Object y) throws HibernateException { + return x==y || compositeUserType.equals( (T) x, (T) y ); + } + + @Override + public boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) + throws HibernateException { + return isEqual( x, y ); + } + + @Override + public int getHashCode(Object x) { + return compositeUserType.hashCode( (T) x ); + } + + @Override + public int getHashCode(Object x, SessionFactoryImplementor factory) { + return getHashCode( x ); + } + + @Override + public Object deepCopy(Object component, SessionFactoryImplementor factory) { + return component == null ? null : compositeUserType.deepCopy( (T) component ); + } + + @Override + public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map copyCache) { + return original == null || !isMutable() ? original : compositeUserType.replace( (T) original, (T) target, owner ); + } + + @Override + public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner, Map copyCache, ForeignKeyDirection foreignKeyDirection) { + return replace( original, target, session, owner, copyCache ); + } + + @Override + public Serializable disassemble(Object value, SessionFactoryImplementor sessionFactory) + throws HibernateException { + return value == null ? null : compositeUserType.disassemble( (T) value ); + } + + @Override + public Serializable disassemble(Object value, SharedSessionContractImplementor session, Object owner) + throws HibernateException { + return disassemble( value, session.getFactory() ); + } + + @Override + public Object assemble(Serializable object, SharedSessionContractImplementor session, Object owner) + throws HibernateException { + return object == null ? null : compositeUserType.assemble( object, owner ); + } + + @Override + public Object replacePropertyValues(Object component, Object[] values, SharedSessionContractImplementor session) + throws HibernateException { + return getMappingModelPart() + .getEmbeddableTypeDescriptor() + .getRepresentationStrategy() + .getInstantiator().instantiate( () -> values, session.getSessionFactory() ); + } +}