split UserComponentType out from ComponentType

and add CompositeType.replacePropertyValues()

this refactoring results in significant simplifications/cleanliness
This commit is contained in:
Gavin King 2023-07-22 13:00:38 +02:00
parent 47024e7bd5
commit 2c5ee8f088
6 changed files with 228 additions and 120 deletions

View File

@ -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<CompositeUserType<?>> 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;
}
}
}

View File

@ -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<CompositeUserType<?>> 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;
}
}

View File

@ -16,8 +16,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
/**
* Represents a <em>composite</em> type, a type which itself has typed attributes.
* <p>
* 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.
*

View File

@ -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;

View File

@ -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<Object, Object> 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
);
}
}

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<T> extends ComponentType {
private final CompositeUserType<T> compositeUserType;
public UserComponentType(
Component component,
int[] originalPropertyOrder,
CompositeUserType<T> 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<Object, Object> 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<Object, Object> 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() );
}
}