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:
parent
5b44aa5d44
commit
eb5afb0427
|
@ -5,6 +5,7 @@
|
|||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.tuple.component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
|
||||
|
@ -13,7 +14,6 @@ import org.hibernate.mapping.Component;
|
|||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
import org.hibernate.property.access.spi.Setter;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
|
||||
/**
|
||||
* Support for tuplizers relating to components.
|
||||
|
@ -25,10 +25,8 @@ public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
|
|||
protected final Getter[] getters;
|
||||
protected final Setter[] setters;
|
||||
protected final int propertySpan;
|
||||
protected final Instantiator instantiator;
|
||||
protected final boolean hasCustomAccessors;
|
||||
|
||||
protected abstract Instantiator buildInstantiator(Component component);
|
||||
protected abstract Getter buildGetter(Component component, Property prop);
|
||||
protected abstract Setter buildSetter(Component component, Property prop);
|
||||
|
||||
|
@ -51,7 +49,6 @@ public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
|
|||
i++;
|
||||
}
|
||||
hasCustomAccessors = foundCustomAccessor;
|
||||
instantiator = buildInstantiator( component );
|
||||
}
|
||||
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -35,13 +35,6 @@ public interface ComponentTuplizer extends Tuplizer, Serializable {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -12,8 +12,6 @@ import org.hibernate.mapping.Property;
|
|||
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
|
||||
import org.hibernate.property.access.spi.Getter;
|
||||
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.
|
||||
|
@ -27,10 +25,6 @@ public class DynamicMapComponentTuplizer extends AbstractComponentTuplizer {
|
|||
return Map.class;
|
||||
}
|
||||
|
||||
protected Instantiator buildInstantiator(Component component) {
|
||||
return new DynamicMapInstantiator();
|
||||
}
|
||||
|
||||
public DynamicMapComponentTuplizer(Component component) {
|
||||
super(component);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.hibernate.bytecode.spi.BytecodeProvider;
|
|||
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
|
||||
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Property;
|
||||
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.Setter;
|
||||
import org.hibernate.tuple.Instantiator;
|
||||
import org.hibernate.tuple.PojoInstantiator;
|
||||
|
||||
/**
|
||||
* A {@link ComponentTuplizer} specific to the pojo entity mode.
|
||||
|
@ -110,19 +108,6 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer {
|
|||
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) {
|
||||
return prop.getGetter( this.componentClass );
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.internal.util.StringHelper;
|
|||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.tuple.StandardProperty;
|
||||
import org.hibernate.tuple.ValueGeneration;
|
||||
|
@ -466,19 +467,20 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object deepCopy(Object component, SessionFactoryImplementor factory)
|
||||
throws HibernateException {
|
||||
public Object deepCopy(Object component, SessionFactoryImplementor factory) {
|
||||
if ( component == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Object[] values = getPropertyValues( component );
|
||||
final Object[] values = getPropertyValues( component );
|
||||
for ( int i = 0; i < propertySpan; i++ ) {
|
||||
values[i] = propertyTypes[i].deepCopy( values[i], factory );
|
||||
}
|
||||
|
||||
Object result = instantiate();
|
||||
setPropertyValues( result, values );
|
||||
final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiator();
|
||||
Object result = instantiator.instantiate( () -> values, factory );
|
||||
|
||||
//not absolutely necessary, but helps for some
|
||||
//equals()/hashCode() implementations
|
||||
|
@ -496,29 +498,42 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
Object target,
|
||||
SharedSessionContractImplementor session,
|
||||
Object owner,
|
||||
Map copyCache)
|
||||
throws HibernateException {
|
||||
Map copyCache) {
|
||||
|
||||
if ( original == null ) {
|
||||
return null;
|
||||
}
|
||||
//if ( original == target ) return target;
|
||||
|
||||
final Object result = target == null
|
||||
? instantiate( owner, session )
|
||||
: target;
|
||||
final Object[] originalValues = getPropertyValues( original );
|
||||
final Object[] resultValues;
|
||||
|
||||
Object[] values = TypeHelper.replace(
|
||||
getPropertyValues( original ),
|
||||
getPropertyValues( result ),
|
||||
if ( target == null ) {
|
||||
resultValues = new Object[originalValues.length];
|
||||
}
|
||||
else {
|
||||
resultValues = getPropertyValues( target );
|
||||
}
|
||||
|
||||
final Object[] replacedValues = TypeHelper.replace(
|
||||
originalValues,
|
||||
resultValues,
|
||||
propertyTypes,
|
||||
session,
|
||||
owner,
|
||||
copyCache
|
||||
);
|
||||
|
||||
setPropertyValues( result, values );
|
||||
return result;
|
||||
if ( target == null ) {
|
||||
final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiator();
|
||||
return instantiator.instantiate( () -> replacedValues, session.getSessionFactory() );
|
||||
}
|
||||
else {
|
||||
setPropertyValues( target, replacedValues );
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -528,21 +543,27 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
SharedSessionContractImplementor session,
|
||||
Object owner,
|
||||
Map copyCache,
|
||||
ForeignKeyDirection foreignKeyDirection)
|
||||
throws HibernateException {
|
||||
ForeignKeyDirection foreignKeyDirection) {
|
||||
|
||||
if ( original == null ) {
|
||||
return null;
|
||||
}
|
||||
//if ( original == target ) return target;
|
||||
|
||||
final Object result = target == null ?
|
||||
instantiate( owner, session ) :
|
||||
target;
|
||||
|
||||
Object[] values = TypeHelper.replace(
|
||||
getPropertyValues( original ),
|
||||
getPropertyValues( result ),
|
||||
final Object[] originalValues = getPropertyValues( original );
|
||||
final Object[] resultValues;
|
||||
|
||||
if ( target == null ) {
|
||||
resultValues = new Object[originalValues.length];
|
||||
}
|
||||
else {
|
||||
resultValues = getPropertyValues( target );
|
||||
}
|
||||
|
||||
final Object[] replacedValues = TypeHelper.replace(
|
||||
originalValues,
|
||||
resultValues,
|
||||
propertyTypes,
|
||||
session,
|
||||
owner,
|
||||
|
@ -550,30 +571,16 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
foreignKeyDirection
|
||||
);
|
||||
|
||||
setPropertyValues( result, values );
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does not populate the component parent
|
||||
*/
|
||||
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()
|
||||
);
|
||||
if ( target == null ) {
|
||||
final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiator();
|
||||
return instantiator.instantiate( () -> replacedValues, session.getSessionFactory() );
|
||||
}
|
||||
else {
|
||||
setPropertyValues( target, replacedValues );
|
||||
return target;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -615,9 +622,11 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
for ( int i = 0; i < propertyTypes.length; i++ ) {
|
||||
assembled[i] = propertyTypes[i].assemble( (Serializable) values[i], session, owner );
|
||||
}
|
||||
Object result = instantiate( owner, session );
|
||||
setPropertyValues( result, assembled );
|
||||
return result;
|
||||
|
||||
final EmbeddableInstantiator instantiator = mappingModelPart.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiator();
|
||||
return instantiator.instantiate( () -> assembled, session.getFactory() );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@ package org.hibernate.type;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.tuple.component.ComponentMetamodel;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
|
@ -28,15 +26,4 @@ public class EmbeddedComponentType extends ComponentType {
|
|||
public boolean isMethodOf(Method 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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,43 +8,40 @@ package org.hibernate.orm.test.component.proxy;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.type.spi.CompositeTypeImplementor;
|
||||
|
||||
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.junit.Assert.assertEquals;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Guillaume Smet
|
||||
* @author Oliver Libutzki
|
||||
*/
|
||||
public class ComponentBasicProxyTest extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[]{
|
||||
Person.class, Adult.class
|
||||
};
|
||||
}
|
||||
@DomainModel( annotatedClasses = { Person.class, Adult.class } )
|
||||
@SessionFactory
|
||||
public class ComponentBasicProxyTest {
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12786")
|
||||
public void testBasicProxyingWithProtectedMethodCalledInConstructor() {
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
public void testBasicProxyingWithProtectedMethodCalledInConstructor(SessionFactoryScope scope) {
|
||||
scope.inTransaction( (entityManager) -> {
|
||||
Adult adult = new Adult();
|
||||
adult.setName( "Arjun Kumar" );
|
||||
entityManager.persist( adult );
|
||||
} );
|
||||
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
scope.inTransaction( (entityManager) -> {
|
||||
List<Adult> adultsCalledArjun = entityManager
|
||||
.createQuery( "SELECT a from Adult a WHERE a.name = :name", Adult.class )
|
||||
.setParameter( "name", "Arjun Kumar" ).getResultList();
|
||||
|
@ -55,25 +52,18 @@ public class ComponentBasicProxyTest extends BaseEntityManagerFunctionalTestCase
|
|||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-12791")
|
||||
public void testOnlyOneProxyClassGenerated() {
|
||||
StandardServiceRegistry ssr = new StandardServiceRegistryBuilder().build();
|
||||
public void testOnlyOneProxyClassGenerated(DomainModelScope domainModelScope, SessionFactoryScope sfScope) {
|
||||
final SessionFactoryImplementor sessionFactory = sfScope.getSessionFactory();
|
||||
|
||||
try {
|
||||
Metadata metadata = new MetadataSources( ssr ).addAnnotatedClass( Person.class )
|
||||
.getMetadataBuilder()
|
||||
.build();
|
||||
PersistentClass persistentClass = metadata.getEntityBinding( Person.class.getName() );
|
||||
final PersistentClass personDescriptor = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() );
|
||||
final CompositeTypeImplementor componentType = (CompositeTypeImplementor) personDescriptor.getIdentifierMapper().getType();
|
||||
final EmbeddableValuedModelPart embedded = componentType.getMappingModelPart();
|
||||
final EmbeddableInstantiator instantiator = embedded.getEmbeddableTypeDescriptor()
|
||||
.getRepresentationStrategy()
|
||||
.getInstantiator();
|
||||
|
||||
ComponentType componentType1 = (ComponentType) persistentClass.getIdentifierMapper().getType();
|
||||
Object instance1 = componentType1.instantiate();
|
||||
|
||||
ComponentType componentType2 = (ComponentType) persistentClass.getIdentifierMapper().getType();
|
||||
Object instance2 = componentType2.instantiate();
|
||||
|
||||
assertEquals( instance1.getClass(), instance2.getClass() );
|
||||
}
|
||||
finally {
|
||||
StandardServiceRegistryBuilder.destroy( ssr );
|
||||
}
|
||||
final Object instance1 = instantiator.instantiate( null, sessionFactory );
|
||||
final Object instance2 = instantiator.instantiate( null, sessionFactory );
|
||||
assertThat( instance1.getClass() ).isEqualTo( instance2.getClass() );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue