Introduce `VirtualIdEmbeddable` and `IdClassEmbeddable` + instantiators

Move all component instantiations to use the new mapping model EmbeddableInstantiator

Still need to
  - integrate embedded forms.  `VirtualIdEmbeddable` does not really need it as it can use the id-mapping itself as the embedded form.  But `IdClassEmbedded` should really be integrated
  - integrate `VirtualKeyEmbeddable` and `VirtualKeyEmbedded` for use as inverse composite fks
  - share `#finishInit` handling for `EmbeddableMappingType`, `VirtualIdEmbeddable` and `IdClassEmbeddable`
  - ability to use the containing composite owner as the parent of a composite (legacy behavior is to always use the "first" entity
  - clean up ComponentType, esp wrt its use of ComponentTuplizer
This commit is contained in:
Steve Ebersole 2021-12-01 07:06:22 -06:00
parent 5b44aa5d44
commit eb5afb0427
7 changed files with 87 additions and 139 deletions

View File

@ -5,6 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.tuple.component; package org.hibernate.tuple.component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Iterator; import java.util.Iterator;
@ -13,7 +14,6 @@ import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.Instantiator;
/** /**
* Support for tuplizers relating to components. * Support for tuplizers relating to components.
@ -25,10 +25,8 @@ public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
protected final Getter[] getters; protected final Getter[] getters;
protected final Setter[] setters; protected final Setter[] setters;
protected final int propertySpan; protected final int propertySpan;
protected final Instantiator instantiator;
protected final boolean hasCustomAccessors; protected final boolean hasCustomAccessors;
protected abstract Instantiator buildInstantiator(Component component);
protected abstract Getter buildGetter(Component component, Property prop); protected abstract Getter buildGetter(Component component, Property prop);
protected abstract Setter buildSetter(Component component, Property prop); protected abstract Setter buildSetter(Component component, Property prop);
@ -51,7 +49,6 @@ public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
i++; i++;
} }
hasCustomAccessors = foundCustomAccessor; hasCustomAccessors = foundCustomAccessor;
instantiator = buildInstantiator( component );
} }
public Object getPropertyValue(Object component, int i) throws HibernateException { public Object getPropertyValue(Object component, int i) throws HibernateException {
@ -72,13 +69,6 @@ public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
} }
} }
/**
* This method does not populate the component parent
*/
public Object instantiate() throws HibernateException {
return instantiator.instantiate();
}
public boolean isMethodOf(Method method) { public boolean isMethodOf(Method method) {
return false; return false;
} }

View File

@ -35,13 +35,6 @@ public interface ComponentTuplizer extends Tuplizer, Serializable {
*/ */
public boolean isMethodOf(Method method); public boolean isMethodOf(Method method);
/**
* Generate a new, empty entity.
*
* @return The new, empty entity instance.
*/
public Object instantiate();
/** /**
* Extract the current values contained on the given entity. * Extract the current values contained on the given entity.
* *

View File

@ -12,8 +12,6 @@ import org.hibernate.mapping.Property;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.DynamicMapInstantiator;
import org.hibernate.tuple.Instantiator;
/** /**
* A {@link ComponentTuplizer} specific to the dynamic-map entity mode. * A {@link ComponentTuplizer} specific to the dynamic-map entity mode.
@ -27,10 +25,6 @@ public class DynamicMapComponentTuplizer extends AbstractComponentTuplizer {
return Map.class; return Map.class;
} }
protected Instantiator buildInstantiator(Component component) {
return new DynamicMapInstantiator();
}
public DynamicMapComponentTuplizer(Component component) { public DynamicMapComponentTuplizer(Component component) {
super(component); super(component);
} }

View File

@ -15,7 +15,6 @@ import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory; import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer; import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
@ -24,7 +23,6 @@ import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.Instantiator; import org.hibernate.tuple.Instantiator;
import org.hibernate.tuple.PojoInstantiator;
/** /**
* A {@link ComponentTuplizer} specific to the pojo entity mode. * A {@link ComponentTuplizer} specific to the pojo entity mode.
@ -110,19 +108,6 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer {
return false; return false;
} }
protected Instantiator buildInstantiator(Component component) {
if ( component.isEmbedded() && ReflectHelper.isAbstractClass( this.componentClass ) ) {
ProxyFactoryFactory proxyFactoryFactory = component.getServiceRegistry().getService( ProxyFactoryFactory.class );
return new ProxiedInstantiator( this.componentClass, proxyFactoryFactory );
}
if ( optimizer == null ) {
return new PojoInstantiator( this.componentClass, null );
}
else {
return new PojoInstantiator( this.componentClass, optimizer.getInstantiationOptimizer() );
}
}
protected Getter buildGetter(Component component, Property prop) { protected Getter buildGetter(Component component, Property prop) {
return prop.getGetter( this.componentClass ); return prop.getGetter( this.componentClass );
} }

View File

@ -29,6 +29,7 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.tuple.StandardProperty; import org.hibernate.tuple.StandardProperty;
import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.ValueGeneration;
@ -466,19 +467,20 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
} }
@Override @Override
public Object deepCopy(Object component, SessionFactoryImplementor factory) public Object deepCopy(Object component, SessionFactoryImplementor factory) {
throws HibernateException {
if ( component == null ) { if ( component == null ) {
return null; return null;
} }
Object[] values = getPropertyValues( component ); final Object[] values = getPropertyValues( component );
for ( int i = 0; i < propertySpan; i++ ) { for ( int i = 0; i < propertySpan; i++ ) {
values[i] = propertyTypes[i].deepCopy( values[i], factory ); values[i] = propertyTypes[i].deepCopy( values[i], factory );
} }
Object result = instantiate(); final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
setPropertyValues( result, values ); .getRepresentationStrategy()
.getInstantiator();
Object result = instantiator.instantiate( () -> values, factory );
//not absolutely necessary, but helps for some //not absolutely necessary, but helps for some
//equals()/hashCode() implementations //equals()/hashCode() implementations
@ -496,29 +498,42 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
Object target, Object target,
SharedSessionContractImplementor session, SharedSessionContractImplementor session,
Object owner, Object owner,
Map copyCache) Map copyCache) {
throws HibernateException {
if ( original == null ) { if ( original == null ) {
return null; return null;
} }
//if ( original == target ) return target; //if ( original == target ) return target;
final Object result = target == null final Object[] originalValues = getPropertyValues( original );
? instantiate( owner, session ) final Object[] resultValues;
: target;
Object[] values = TypeHelper.replace( if ( target == null ) {
getPropertyValues( original ), resultValues = new Object[originalValues.length];
getPropertyValues( result ), }
else {
resultValues = getPropertyValues( target );
}
final Object[] replacedValues = TypeHelper.replace(
originalValues,
resultValues,
propertyTypes, propertyTypes,
session, session,
owner, owner,
copyCache copyCache
); );
setPropertyValues( result, values ); if ( target == null ) {
return result; final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
.getInstantiator();
return instantiator.instantiate( () -> replacedValues, session.getSessionFactory() );
}
else {
setPropertyValues( target, replacedValues );
return target;
}
} }
@Override @Override
@ -528,21 +543,27 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
SharedSessionContractImplementor session, SharedSessionContractImplementor session,
Object owner, Object owner,
Map copyCache, Map copyCache,
ForeignKeyDirection foreignKeyDirection) ForeignKeyDirection foreignKeyDirection) {
throws HibernateException {
if ( original == null ) { if ( original == null ) {
return null; return null;
} }
//if ( original == target ) return target; //if ( original == target ) return target;
final Object result = target == null ?
instantiate( owner, session ) :
target;
Object[] values = TypeHelper.replace( final Object[] originalValues = getPropertyValues( original );
getPropertyValues( original ), final Object[] resultValues;
getPropertyValues( result ),
if ( target == null ) {
resultValues = new Object[originalValues.length];
}
else {
resultValues = getPropertyValues( target );
}
final Object[] replacedValues = TypeHelper.replace(
originalValues,
resultValues,
propertyTypes, propertyTypes,
session, session,
owner, owner,
@ -550,30 +571,16 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
foreignKeyDirection foreignKeyDirection
); );
setPropertyValues( result, values ); if ( target == null ) {
return result; final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
.getRepresentationStrategy()
.getInstantiator();
return instantiator.instantiate( () -> replacedValues, session.getSessionFactory() );
} }
else {
/** setPropertyValues( target, replacedValues );
* This method does not populate the component parent return target;
*/
public Object instantiate() {
return componentTuplizer.instantiate();
} }
public Object instantiate(Object parent, SharedSessionContractImplementor session) {
Object result = instantiate();
final PropertyAccess parentAccess = mappingModelPart().getParentInjectionAttributePropertyAccess();
if ( parentAccess != null && parent != null ) {
parentAccess.getSetter().set(
result,
session.getPersistenceContextInternal().proxyFor( parent ),
session.getFactory()
);
}
return result;
} }
@Override @Override
@ -615,9 +622,11 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
for ( int i = 0; i < propertyTypes.length; i++ ) { for ( int i = 0; i < propertyTypes.length; i++ ) {
assembled[i] = propertyTypes[i].assemble( (Serializable) values[i], session, owner ); assembled[i] = propertyTypes[i].assemble( (Serializable) values[i], session, owner );
} }
Object result = instantiate( owner, session );
setPropertyValues( result, assembled ); final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
return result; .getRepresentationStrategy()
.getInstantiator();
return instantiator.instantiate( () -> assembled, session.getFactory() );
} }
} }

View File

@ -8,8 +8,6 @@ package org.hibernate.type;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.tuple.component.ComponentMetamodel; import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -28,15 +26,4 @@ public class EmbeddedComponentType extends ComponentType {
public boolean isMethodOf(Method method) { public boolean isMethodOf(Method method) {
return componentTuplizer.isMethodOf( method ); return componentTuplizer.isMethodOf( method );
} }
@Override
public Object instantiate(Object parent, SharedSessionContractImplementor session) throws HibernateException {
final boolean useParent = parent != null &&
//TODO: Yuck! This is not quite good enough, it's a quick
//hack around the problem of having a to-one association
//that refers to an embedded component:
super.getReturnedClass().isInstance( parent );
return useParent ? parent : super.instantiate( parent, session );
}
} }

View File

@ -8,43 +8,40 @@ package org.hibernate.orm.test.component.proxy;
import java.util.List; import java.util.List;
import org.hibernate.boot.Metadata; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.type.ComponentType; import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.junit.Test; import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.NotImplementedYet;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
/** /**
* @author Guillaume Smet * @author Guillaume Smet
* @author Oliver Libutzki * @author Oliver Libutzki
*/ */
public class ComponentBasicProxyTest extends BaseEntityManagerFunctionalTestCase { @DomainModel( annotatedClasses = { Person.class, Adult.class } )
@SessionFactory
@Override public class ComponentBasicProxyTest {
protected Class<?>[] getAnnotatedClasses() {
return new Class[]{
Person.class, Adult.class
};
}
@Test @Test
@TestForIssue(jiraKey = "HHH-12786") @TestForIssue(jiraKey = "HHH-12786")
public void testBasicProxyingWithProtectedMethodCalledInConstructor() { public void testBasicProxyingWithProtectedMethodCalledInConstructor(SessionFactoryScope scope) {
doInJPA( this::entityManagerFactory, entityManager -> { scope.inTransaction( (entityManager) -> {
Adult adult = new Adult(); Adult adult = new Adult();
adult.setName( "Arjun Kumar" ); adult.setName( "Arjun Kumar" );
entityManager.persist( adult ); entityManager.persist( adult );
} ); } );
doInJPA( this::entityManagerFactory, entityManager -> { scope.inTransaction( (entityManager) -> {
List<Adult> adultsCalledArjun = entityManager List<Adult> adultsCalledArjun = entityManager
.createQuery( "SELECT a from Adult a WHERE a.name = :name", Adult.class ) .createQuery( "SELECT a from Adult a WHERE a.name = :name", Adult.class )
.setParameter( "name", "Arjun Kumar" ).getResultList(); .setParameter( "name", "Arjun Kumar" ).getResultList();
@ -55,25 +52,18 @@ public class ComponentBasicProxyTest extends BaseEntityManagerFunctionalTestCase
@Test @Test
@TestForIssue(jiraKey = "HHH-12791") @TestForIssue(jiraKey = "HHH-12791")
public void testOnlyOneProxyClassGenerated() { public void testOnlyOneProxyClassGenerated(DomainModelScope domainModelScope, SessionFactoryScope sfScope) {
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build(); final SessionFactoryImplementor sessionFactory = sfScope.getSessionFactory();
try { final PersistentClass personDescriptor = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() );
Metadata metadata = new MetadataSources( ssr ).addAnnotatedClass( Person.class ) final CompositeTypeImplementor componentType = (CompositeTypeImplementor) personDescriptor.getIdentifierMapper().getType();
.getMetadataBuilder() final EmbeddableValuedModelPart embedded = componentType.getMappingModelPart();
.build(); final EmbeddableInstantiator instantiator = embedded.getEmbeddableTypeDescriptor()
PersistentClass persistentClass = metadata.getEntityBinding( Person.class.getName() ); .getRepresentationStrategy()
.getInstantiator();
ComponentType componentType1 = (ComponentType) persistentClass.getIdentifierMapper().getType(); final Object instance1 = instantiator.instantiate( null, sessionFactory );
Object instance1 = componentType1.instantiate(); final Object instance2 = instantiator.instantiate( null, sessionFactory );
assertThat( instance1.getClass() ).isEqualTo( instance2.getClass() );
ComponentType componentType2 = (ComponentType) persistentClass.getIdentifierMapper().getType();
Object instance2 = componentType2.instantiate();
assertEquals( instance1.getClass(), instance2.getClass() );
}
finally {
StandardServiceRegistryBuilder.destroy( ssr );
}
} }
} }