HHH-14950 - Support mapping of embeddables with no setters (assuming a custom instantiator or repo-strategy is used)

Tests;
Remove `ComponentTuplizer` and friends;
Remove `ComponentMetadata`
This commit is contained in:
Steve Ebersole 2021-12-08 21:17:05 -06:00
parent 09cc6d7b00
commit fa4b76702c
30 changed files with 116 additions and 833 deletions

View File

@ -1688,12 +1688,8 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
props,
false
);
if ( isEmptyCompositesEnabled ) {
// It would be nice to do this logging in ComponentMetamodel, where
// AvailableSettings.CREATE_EMPTY_COMPOSITES_ENABLED is actually used.
// Unfortunately that would end up logging a message several times for
// each embeddable/composite. Doing it here will log the message only
// once.
LOG.emptyCompositesEnabled();
}
}

View File

@ -8,7 +8,6 @@ package org.hibernate.mapping;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -16,8 +15,8 @@ import java.util.Objects;
import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.source.internal.hbm.MappingDocument;
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.MetadataBuildingContext;
@ -28,10 +27,8 @@ import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.metamodel.EmbeddableInstantiator;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.ComponentType;
import org.hibernate.type.EmbeddedComponentType;
import org.hibernate.type.Type;
@ -56,7 +53,6 @@ public class Component extends SimpleValue implements MetaAttributable {
private String roleName;
private Class<? extends EmbeddableInstantiator> customInstantiator;
private Map<RepresentationMode,String> tuplizerImpls;
// cache the status of the type
private volatile Type type;
@ -205,14 +201,9 @@ public class Component extends SimpleValue implements MetaAttributable {
// Other components should be sorted already
sortProperties( true );
// TODO : temporary initial step towards HHH-1907
final ComponentMetamodel metamodel = new ComponentMetamodel(
this,
getMetadata().getMetadataBuildingOptions()
);
localType = isEmbedded()
? new EmbeddedComponentType( getBuildingContext().getBootstrapContext().getTypeConfiguration(), metamodel, originalPropertyOrder )
: new ComponentType( getBuildingContext().getBootstrapContext().getTypeConfiguration(), metamodel, originalPropertyOrder );
? new EmbeddedComponentType( this, originalPropertyOrder, getBuildingContext() )
: new ComponentType( this, originalPropertyOrder, getBuildingContext() );
this.type = localType;
}
@ -329,29 +320,6 @@ public class Component extends SimpleValue implements MetaAttributable {
return componentClassName!=null;
}
public void addTuplizer(RepresentationMode representationMode, String implClassName) {
if ( tuplizerImpls == null ) {
tuplizerImpls = new HashMap<>();
}
tuplizerImpls.put( representationMode, implClassName );
}
public String getTuplizerImplClassName(RepresentationMode mode) {
// todo : remove this once ComponentMetamodel is complete and merged
if ( tuplizerImpls == null ) {
return null;
}
return tuplizerImpls.get( mode );
}
@SuppressWarnings("UnusedDeclaration")
public Map getTuplizerMap() {
if ( tuplizerImpls == null ) {
return null;
}
return java.util.Collections.unmodifiableMap( tuplizerImpls );
}
/**
* Returns the {@link Property} at the specified position in this {@link Component}.
*

View File

@ -30,9 +30,6 @@ import org.hibernate.property.access.spi.Getter;
* </ul>
* </p>
*
* @see org.hibernate.tuple.entity.EntityTuplizer
* @see org.hibernate.tuple.component.ComponentTuplizer
*
* @author Steve Ebersole
*
* @deprecated See {@link ManagedTypeRepresentationStrategy}

View File

@ -1,83 +0,0 @@
/*
* 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.tuple.component;
import java.lang.reflect.Method;
import java.util.Iterator;
import org.hibernate.HibernateException;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.Setter;
/**
* Support for tuplizers relating to components.
*
* @author Gavin King
* @author Steve Ebersole
*/
public abstract class AbstractComponentTuplizer implements ComponentTuplizer {
protected final Getter[] getters;
protected final Setter[] setters;
protected final int propertySpan;
protected final boolean hasCustomAccessors;
protected abstract Getter buildGetter(Component component, Property prop);
protected abstract Setter buildSetter(Component component, Property prop);
protected AbstractComponentTuplizer(Component component) {
setComponentClass( component );
propertySpan = component.getPropertySpan();
getters = new Getter[propertySpan];
setters = new Setter[propertySpan];
Iterator iter = component.getPropertyIterator();
boolean foundCustomAccessor=false;
int i = 0;
while ( iter.hasNext() ) {
Property prop = ( Property ) iter.next();
getters[i] = buildGetter( component, prop );
setters[i] = buildSetter( component, prop );
if ( !prop.isBasicPropertyAccessor() ) {
foundCustomAccessor = true;
}
i++;
}
hasCustomAccessors = foundCustomAccessor;
}
public Object getPropertyValue(Object component, int i) throws HibernateException {
return getters[i].get( component );
}
public Object[] getPropertyValues(Object component) throws HibernateException {
Object[] values = new Object[propertySpan];
for ( int i = 0; i < propertySpan; i++ ) {
values[i] = getPropertyValue( component, i );
}
return values;
}
public void setPropertyValues(Object component, Object[] values) throws HibernateException {
for ( int i = 0; i < propertySpan; i++ ) {
setters[i].set( component, values[i] );
}
}
public boolean isMethodOf(Method method) {
return false;
}
public Getter getGetter(int i) {
return getters[i];
}
// It should be an abstract method but not sure if this can break any customer extension
protected void setComponentClass(Component component){
}
}

View File

@ -1,127 +0,0 @@
/*
* 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.tuple.component;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.RepresentationMode;
import org.hibernate.tuple.PropertyFactory;
import org.hibernate.tuple.StandardProperty;
/**
* Centralizes metamodel information about a component.
*
* @author Steve Ebersole
*/
public class ComponentMetamodel implements Serializable {
// TODO : will need reference to session factory to fully complete HHH-1907
private final String role;
private final boolean isKey;
private final StandardProperty[] properties;
private final RepresentationMode representationMode;
private final ComponentTuplizer componentTuplizer;
// cached for efficiency...
private final int propertySpan;
private final Map propertyIndexes = new HashMap();
private final boolean createEmptyCompositesEnabled;
@Deprecated
public ComponentMetamodel(Component component, MetadataBuildingOptions metadataBuildingOptions) {
this( component, new ComponentTuplizerFactory( metadataBuildingOptions ) );
}
private ComponentMetamodel(Component component, ComponentTuplizerFactory componentTuplizerFactory){
this.role = component.getRoleName();
this.isKey = component.isKey();
propertySpan = component.getPropertySpan();
properties = new StandardProperty[propertySpan];
Iterator itr = component.getPropertyIterator();
int i = 0;
while ( itr.hasNext() ) {
Property property = ( Property ) itr.next();
properties[i] = PropertyFactory.buildStandardProperty( property, false );
propertyIndexes.put( property.getName(), i );
i++;
}
representationMode = component.hasPojoRepresentation() ? RepresentationMode.POJO : RepresentationMode.MAP;
// todo : move this to SF per HHH-3517; also see HHH-1907 and ComponentMetamodel
final String tuplizerClassName = component.getTuplizerImplClassName( representationMode );
this.componentTuplizer = tuplizerClassName == null ? componentTuplizerFactory.constructDefaultTuplizer(
representationMode,
component
) : componentTuplizerFactory.constructTuplizer( tuplizerClassName, component );
final ConfigurationService cs = component.getMetadata().getMetadataBuildingOptions().getServiceRegistry()
.getService(ConfigurationService.class);
this.createEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
cs.getSettings(),
false
);
}
public boolean isKey() {
return isKey;
}
public int getPropertySpan() {
return propertySpan;
}
public StandardProperty[] getProperties() {
return properties;
}
public StandardProperty getProperty(int index) {
if ( index < 0 || index >= propertySpan ) {
throw new IllegalArgumentException( "illegal index value for component property access [request=" + index + ", span=" + propertySpan + "]" );
}
return properties[index];
}
public int getPropertyIndex(String propertyName) {
Integer index = ( Integer ) propertyIndexes.get( propertyName );
if ( index == null ) {
throw new HibernateException( "component does not contain such a property [" + propertyName + "]" );
}
return index;
}
public StandardProperty getProperty(String propertyName) {
return getProperty( getPropertyIndex( propertyName ) );
}
public RepresentationMode getRepresentationMode() {
return representationMode;
}
public ComponentTuplizer getComponentTuplizer() {
return componentTuplizer;
}
public boolean isCreateEmptyCompositesEnabled() {
return createEmptyCompositesEnabled;
}
}

View File

@ -1,53 +0,0 @@
/*
* 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.tuple.component;
import java.io.Serializable;
import java.lang.reflect.Method;
import org.hibernate.tuple.Tuplizer;
/**
* Defines further responsibilities regarding tuplization based on
* a mapped components.
* </p>
* ComponentTuplizer implementations should have the following constructor signature:
* (org.hibernate.mapping.Component)
*
* @author Gavin King
* @author Steve Ebersole
*
* @deprecated for removal in 6.0. See instead `ManagedTypeRepresentationStrategy`
* and `RepresentationMode` in 6.0
*/
@Deprecated
public interface ComponentTuplizer extends Tuplizer, Serializable {
/**
* Is the given method available via the managed component as a property getter?
*
* @param method The method which to check against the managed component.
* @return True if the managed component is available from the managed component; else false.
*/
public boolean isMethodOf(Method method);
/**
* Extract the current values contained on the given entity.
*
* @param entity The entity from which to extract values.
* @return The current property values.
*/
public Object[] getPropertyValues(Object entity);
/**
* Inject the given values into the given entity.
*
* @param entity The entity.
* @param values The values to be injected.
*/
public void setPropertyValues(Object entity, Object[] values);
}

View File

@ -1,172 +0,0 @@
/*
* 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.tuple.component;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.HibernateException;
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
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.ClassLoaderAccess;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.Component;
import org.hibernate.metamodel.RepresentationMode;
/**
* A registry allowing users to define the default {@link ComponentTuplizer} class to use per {@link RepresentationMode}.
*
* @author Steve Ebersole
*/
public class ComponentTuplizerFactory implements Serializable {
private static final Class[] COMPONENT_TUP_CTOR_SIG = new Class[] { Component.class };
private final Map<RepresentationMode,Class<? extends ComponentTuplizer>> defaultImplClassByMode = buildBaseMapping();
private final ClassLoaderAccess classLoaderAccess;
/**
* @deprecated Use {@link ComponentTuplizerFactory#ComponentTuplizerFactory(BootstrapContext)} instead.
*/
@Deprecated
public ComponentTuplizerFactory(MetadataBuildingOptions metadataBuildingOptions) {
classLoaderAccess = new ClassLoaderAccessImpl(
metadataBuildingOptions.getTempClassLoader(),
metadataBuildingOptions.getServiceRegistry().getService( ClassLoaderService.class )
);
}
public ComponentTuplizerFactory(BootstrapContext bootstrapContext) {
classLoaderAccess = bootstrapContext.getClassLoaderAccess();
}
/**
* Method allowing registration of the tuplizer class to use as default for a particular entity-mode.
*
* @param representationMode The entity-mode for which to register the tuplizer class
* @param tuplizerClass The class to use as the default tuplizer for the given entity-mode.
*/
@SuppressWarnings({ "UnusedDeclaration" })
public void registerDefaultTuplizerClass(RepresentationMode representationMode, Class<? extends ComponentTuplizer> tuplizerClass) {
assert isComponentTuplizerImplementor( tuplizerClass )
: "Specified tuplizer class [" + tuplizerClass.getName() + "] does not implement " + ComponentTuplizer.class.getName();
assert hasProperConstructor( tuplizerClass )
: "Specified tuplizer class [" + tuplizerClass.getName() + "] is not properly instantiatable";
defaultImplClassByMode.put( representationMode, tuplizerClass );
}
/**
* Construct an instance of the given tuplizer class.
*
* @param tuplizerClassName The name of the tuplizer class to instantiate
* @param metadata The metadata for the component.
*
* @return The instantiated tuplizer
*
* @throws HibernateException If class name cannot be resolved to a class reference, or if the
* {@link Constructor#newInstance} call fails.
*/
public ComponentTuplizer constructTuplizer(String tuplizerClassName, Component metadata) {
try {
Class<? extends ComponentTuplizer> tuplizerClass = classLoaderAccess.classForName( tuplizerClassName );
return constructTuplizer( tuplizerClass, metadata );
}
catch ( ClassLoadingException e ) {
throw new HibernateException( "Could not locate specified tuplizer class [" + tuplizerClassName + "]" );
}
}
/**
* Construct an instance of the given tuplizer class.
*
* @param tuplizerClass The tuplizer class to instantiate
* @param metadata The metadata for the component.
*
* @return The instantiated tuplizer
*
* @throws HibernateException if the {@link Constructor#newInstance} call fails.
*/
public ComponentTuplizer constructTuplizer(Class<? extends ComponentTuplizer> tuplizerClass, Component metadata) {
Constructor<? extends ComponentTuplizer> constructor = getProperConstructor( tuplizerClass );
assert constructor != null : "Unable to locate proper constructor for tuplizer [" + tuplizerClass.getName() + "]";
try {
return constructor.newInstance( metadata );
}
catch (Exception e) {
throw new HibernateException(
String.format(
Locale.ROOT,
"Unable to instantiate tuplizer [%s] for component: `%s` (%s)",
tuplizerClass.getName(),
metadata.getComponentClassName(),
metadata.getRoleName()
),
e
);
}
}
/**
* Construct am instance of the default tuplizer for the given entity-mode.
*
* @param representationMode The entity mode for which to build a default tuplizer.
* @param metadata The metadata for the component.
*
* @return The instantiated tuplizer
*
* @throws HibernateException If no default tuplizer found for that entity-mode; may be re-thrown from
* {@link #constructTuplizer} too.
*/
public ComponentTuplizer constructDefaultTuplizer(RepresentationMode representationMode, Component metadata) {
Class<? extends ComponentTuplizer> tuplizerClass = defaultImplClassByMode.get( representationMode );
if ( tuplizerClass == null ) {
throw new HibernateException( "could not determine default tuplizer class to use [" + representationMode + "]" );
}
return constructTuplizer( tuplizerClass, metadata );
}
private boolean isComponentTuplizerImplementor(Class tuplizerClass) {
return ReflectHelper.implementsInterface( tuplizerClass, ComponentTuplizer.class );
}
@SuppressWarnings({ "unchecked" })
private boolean hasProperConstructor(Class tuplizerClass) {
return getProperConstructor( tuplizerClass ) != null;
}
private Constructor<? extends ComponentTuplizer> getProperConstructor(Class<? extends ComponentTuplizer> clazz) {
Constructor<? extends ComponentTuplizer> constructor = null;
try {
constructor = clazz.getDeclaredConstructor( COMPONENT_TUP_CTOR_SIG );
try {
ReflectHelper.ensureAccessibility( constructor );
}
catch ( SecurityException e ) {
constructor = null;
}
}
catch ( NoSuchMethodException ignore ) {
}
return constructor;
}
private static Map<RepresentationMode,Class<? extends ComponentTuplizer>> buildBaseMapping() {
Map<RepresentationMode,Class<? extends ComponentTuplizer>> map = new ConcurrentHashMap<>();
map.put( RepresentationMode.POJO, PojoComponentTuplizer.class );
map.put( RepresentationMode.MAP, DynamicMapComponentTuplizer.class );
return map;
}
}

View File

@ -1,40 +0,0 @@
/*
* 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.tuple.component;
import java.util.Map;
import org.hibernate.mapping.Component;
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;
/**
* A {@link ComponentTuplizer} specific to the dynamic-map entity mode.
*
* @author Gavin King
* @author Steve Ebersole
*/
public class DynamicMapComponentTuplizer extends AbstractComponentTuplizer {
public Class getMappedClass() {
return Map.class;
}
public DynamicMapComponentTuplizer(Component component) {
super(component);
}
protected Getter buildGetter(Component component, Property prop) {
return PropertyAccessStrategyMapImpl.INSTANCE.buildPropertyAccess( null, prop.getName() ).getGetter();
}
protected Setter buildSetter(Component component, Property prop) {
return PropertyAccessStrategyMapImpl.INSTANCE.buildPropertyAccess( null, prop.getName() ).getSetter();
}
}

View File

@ -1,146 +0,0 @@
/*
* 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.tuple.component;
import java.lang.reflect.Method;
import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException;
import org.hibernate.bytecode.spi.BasicProxyFactory;
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.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
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;
/**
* A {@link ComponentTuplizer} specific to the pojo entity mode.
*
* @author Gavin King
* @author Steve Ebersole
*/
public class PojoComponentTuplizer extends AbstractComponentTuplizer {
private Class componentClass;
private ReflectionOptimizer optimizer;
private final Getter parentGetter;
private final Setter parentSetter;
public PojoComponentTuplizer(Component component) {
super( component );
String[] getterNames = new String[propertySpan];
String[] setterNames = new String[propertySpan];
Class[] propTypes = new Class[propertySpan];
for ( int i = 0; i < propertySpan; i++ ) {
getterNames[i] = getters[i].getMethodName();
setterNames[i] = setters[i].getMethodName();
propTypes[i] = getters[i].getReturnTypeClass();
}
final String parentPropertyName = component.getParentProperty();
if ( parentPropertyName == null ) {
parentSetter = null;
parentGetter = null;
}
else {
final PropertyAccess propertyAccess = PropertyAccessStrategyBasicImpl.INSTANCE.buildPropertyAccess(
componentClass,
parentPropertyName
);
parentSetter = propertyAccess.getSetter();
parentGetter = propertyAccess.getGetter();
}
if ( hasCustomAccessors || !Environment.useReflectionOptimizer() ) {
optimizer = null;
}
else {
final BytecodeProvider bytecodeProvider = component.getServiceRegistry().getService( BytecodeProvider.class );
optimizer = bytecodeProvider.getReflectionOptimizer(
componentClass, getterNames, setterNames, propTypes
);
}
}
public Class getMappedClass() {
return componentClass;
}
public Object[] getPropertyValues(Object component) throws HibernateException {
if ( component == PropertyAccessStrategyBackRefImpl.UNKNOWN ) {
return new Object[propertySpan];
}
else if ( optimizer != null && optimizer.getAccessOptimizer() != null ) {
return optimizer.getAccessOptimizer().getPropertyValues( component );
}
else {
return super.getPropertyValues( component );
}
}
public void setPropertyValues(Object component, Object[] values) throws HibernateException {
if ( optimizer != null && optimizer.getAccessOptimizer() != null ) {
optimizer.getAccessOptimizer().setPropertyValues( component, values );
}
else {
super.setPropertyValues( component, values );
}
}
public boolean isMethodOf(Method method) {
for ( int i = 0; i < propertySpan; i++ ) {
final Method getterMethod = getters[i].getMethod();
if ( getterMethod != null && getterMethod.equals( method ) ) {
return true;
}
}
return false;
}
protected Getter buildGetter(Component component, Property prop) {
return prop.getGetter( this.componentClass );
}
protected Setter buildSetter(Component component, Property prop) {
return prop.getSetter( this.componentClass );
}
@Override
protected void setComponentClass(Component component) {
this.componentClass = component.getComponentClass();
}
private static class ProxiedInstantiator implements Instantiator {
private final Class proxiedClass;
private final BasicProxyFactory factory;
public ProxiedInstantiator(Class componentClass, ProxyFactoryFactory proxyFactoryFactory) {
proxiedClass = componentClass;
factory = proxyFactoryFactory
.buildBasicProxyFactory( proxiedClass );
}
public Object instantiate(Object id) {
throw new AssertionFailure( "ProxiedInstantiator can only be used to instantiate component" );
}
public Object instantiate() {
return factory.getProxy();
}
public boolean isInstance(Object object) {
return proxiedClass.isInstance( object );
}
}
}

View File

@ -12,6 +12,7 @@ import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.FetchMode;
@ -19,7 +20,10 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.Mapping;
@ -27,18 +31,19 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.metamodel.EmbeddableInstantiator;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.EmbeddableInstantiator;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.tuple.PropertyFactory;
import org.hibernate.tuple.StandardProperty;
import org.hibernate.tuple.ValueGeneration;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.tuple.component.ComponentTuplizer;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.spi.CompositeTypeImplementor;
import org.hibernate.type.spi.TypeConfiguration;
/**
* Handles "component" mappings
@ -46,8 +51,8 @@ import org.hibernate.type.spi.TypeConfiguration;
* @author Gavin King
*/
public class ComponentType extends AbstractType implements CompositeTypeImplementor, ProcedureParameterExtractionAware {
private final Class<?> componentClass;
private final TypeConfiguration typeConfiguration;
private final String[] propertyNames;
private final Type[] propertyTypes;
private final ValueGeneration[] propertyValueGenerationStrategies;
@ -56,18 +61,20 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
protected final int propertySpan;
private final CascadeStyle[] cascade;
private final FetchMode[] joinedFetch;
private final boolean isKey;
private boolean hasNotNullProperty;
private final boolean createEmptyCompositesEnabled;
protected final ComponentTuplizer componentTuplizer;
private EmbeddableValuedModelPart mappingModelPart;
public ComponentType(TypeConfiguration typeConfiguration, ComponentMetamodel metamodel, int[] originalPropertyOrder) {
this.typeConfiguration = typeConfiguration;
// for now, just "re-flatten" the metamodel since this is temporary stuff anyway (HHH-1907)
this.isKey = metamodel.isKey();
this.propertySpan = metamodel.getPropertySpan();
public ComponentType(Component component, int[] originalPropertyOrder, MetadataBuildingContext buildingContext) {
this.componentClass = component.isDynamic()
? Map.class
: component.getComponentClass();
this.isKey = component.isKey();
this.propertySpan = component.getPropertySpan();
this.originalPropertyOrder = originalPropertyOrder;
this.propertyNames = new String[propertySpan];
this.propertyTypes = new Type[propertySpan];
@ -76,8 +83,12 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
this.cascade = new CascadeStyle[propertySpan];
this.joinedFetch = new FetchMode[propertySpan];
for ( int i = 0; i < propertySpan; i++ ) {
StandardProperty prop = metamodel.getProperty( i );
final Iterator<Property> itr = component.getPropertyIterator();
int i = 0;
while ( itr.hasNext() ) {
final Property property = itr.next();
// todo (6.0) : see if we really need to create these
final StandardProperty prop = PropertyFactory.buildStandardProperty( property, false );
this.propertyNames[i] = prop.getName();
this.propertyTypes[i] = prop.getType();
this.propertyNullability[i] = prop.isNullable();
@ -87,10 +98,14 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
hasNotNullProperty = true;
}
this.propertyValueGenerationStrategies[i] = prop.getValueGenerationStrategy();
i++;
}
this.componentTuplizer = metamodel.getComponentTuplizer();
this.createEmptyCompositesEnabled = metamodel.isCreateEmptyCompositesEnabled();
this.createEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
buildingContext.getBootstrapContext().getServiceRegistry().getService( ConfigurationService.class ).getSettings(),
false
);
}
public boolean isKey() {
@ -152,8 +167,8 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return true;
}
public Class getReturnedClass() {
return componentTuplizer.getMappedClass();
public Class<?> getReturnedClass() {
return componentClass;
}
@Override

View File

@ -8,22 +8,41 @@ package org.hibernate.type;
import java.lang.reflect.Method;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Component;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.property.access.spi.Getter;
/**
* @author Gavin King
*/
public class EmbeddedComponentType extends ComponentType {
public EmbeddedComponentType(TypeConfiguration typeScope, ComponentMetamodel metamodel, int[] propertyReordering) {
super( typeScope, metamodel, propertyReordering );
public EmbeddedComponentType(Component component, int[] originalPropertyOrder, MetadataBuildingContext buildingContext) {
super( component, originalPropertyOrder, buildingContext );
}
@Override
public boolean isEmbedded() {
return true;
}
@Override
public boolean isMethodOf(Method method) {
return componentTuplizer.isMethodOf( method );
if ( mappingModelPart() == null ) {
throw new IllegalStateException( "EmbeddableValuedModelPart not known yet" );
}
final EmbeddableMappingType embeddable = mappingModelPart().getEmbeddableTypeDescriptor();
for ( int i = 0; i < embeddable.getAttributeMappings().size(); i++ ) {
final AttributeMapping attributeMapping = embeddable.getAttributeMapping( i );
final Getter getter = attributeMapping.getPropertyAccess().getGetter();
final Method getterMethod = getter.getMethod();
if ( getterMethod != null && getterMethod.equals( method ) ) {
return true;
}
}
return false;
}
}

View File

@ -66,7 +66,6 @@ public class BasicCriteriaUsageTest extends BaseEntityManagerFunctionalTestCase
@Test
@TestForIssue(jiraKey = "HHH-8283")
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
@Ignore( "Missing support for composite user types" )
public void testDateCompositeCustomType() {
Payment payment = new Payment();

View File

@ -17,7 +17,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Table(name = "persons")
@Table(name = "people")
//tag::embeddable-instantiator-class[]
@Entity
public class Person {

View File

@ -20,7 +20,7 @@ import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Table(name = "persons")
@Table(name = "people")
//tag::embeddable-instantiator-property[]
@Entity
public class Person {

View File

@ -28,15 +28,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@DomainModel( annotatedClasses = { Person.class, NameImpl.class } )
@SessionFactory
@FailureExpected( jiraKey = "HHH-14950", reason = "Model has no setters, which is not supported" )
@JiraKey( "HHH-14950" )
public class InstantiationTests {
// these tests fail the build even though they are marked @FailureExpected because the
// failure happens while creating the test "fixtures" (here the boot model) which JUnit
// does not like
// @Test
@Test
public void modelTest(DomainModelScope scope) {
scope.withHierarchy( Person.class, (personMapping) -> {
final Property name = personMapping.getProperty( "name" );
@ -49,7 +43,8 @@ public class InstantiationTests {
});
}
// @Test
@Test
@FailureExpected( jiraKey = "HHH-14950", reason = "Model has no setters, which is not supported" )
public void basicTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final Person mick = new Person( 1, new NameImpl( "Mick", "Jagger" ) );

View File

@ -20,9 +20,8 @@ import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Table(name = "persons")
//tag::embeddable-instantiator-property[]
@Entity
@Table(name = "people")
public class Person {
@Id
public Integer id;
@ -38,8 +37,6 @@ public class Person {
@Access( AccessType.PROPERTY )
public Set<Name> aliases;
//end::embeddable-instantiator-property[]
private Person() {
// for Hibernate use
}
@ -80,6 +77,4 @@ public class Person {
aliases.add( alias );
}
//tag::embeddable-instantiator-property[]
}
//end::embeddable-instantiator-property[]

View File

@ -1,53 +0,0 @@
/*
* 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.orm.test.mapping.embeddable.strategy.instantiator.intf;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Creating a new version of {@link InstantiationTests} that won't fail the build,
* but still allows us to test the behavior.
*
* JUnit does not like when build fixtures (here, the SF) fails
*/
@ServiceRegistry
@FailureExpected( jiraKey = "HHH-14950", reason = "Model has no setters, which is not supported" )
@JiraKey( "HHH-14950" )
public class TempInstantiationTests {
@Test
public void basicTest(ServiceRegistryScope registerScope) {
final MetadataSources metadataSources = new MetadataSources( registerScope.getRegistry() )
.addAnnotatedClass( Person.class )
.addAnnotatedClass( Name.class )
.addAnnotatedClass( NameImpl.class );
final Metadata metadata = metadataSources.buildMetadata();
final PersistentClass personMapping = metadata.getEntityBinding( Person.class.getName() );
final Property name = personMapping.getProperty( "name" );
final Component nameMapping = (Component) name.getValue();
assertThat( nameMapping.getPropertySpan() ).isEqualTo( 2 );
final Property aliases = personMapping.getProperty( "aliases" );
final Component aliasMapping = (Component) ( (Collection) aliases.getValue() ).getElement();
assertThat( aliasMapping.getPropertySpan() ).isEqualTo( 2 );
}
}

View File

@ -14,8 +14,8 @@ import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.DomainModelScope;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.JiraKey;
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.assertj.core.api.Assertions.assertThat;
@ -23,7 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* A "baseline" test for {@link org.hibernate.orm.test.mapping.embeddable.strategy.instantiator.intf.InstantiationTests}.
*
* There we have try to map an interface as an embeddable.
* There, we try to map an interface as an embeddable.
*
* Here we try to map a class that only defines getters to mimic a typical interface.
*
@ -31,15 +31,9 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
@DomainModel( annotatedClasses = { Person.class, Name.class } )
@SessionFactory
@FailureExpected( jiraKey = "HHH-14950", reason = "Model has no setters, which is not supported" )
@JiraKey( "HHH-14950" )
public class InstantiationTests {
// these tests fail the build even though they are marked @FailureExpected because the
// failure happens while creating the test "fixtures" (here the boot model) which JUnit
// does not like
// @Test
@Test
public void modelTest(DomainModelScope scope) {
scope.withHierarchy( Person.class, (personMapping) -> {
final Property name = personMapping.getProperty( "name" );
@ -52,4 +46,28 @@ public class InstantiationTests {
});
}
@Test
@FailureExpected( jiraKey = "HHH-14950", reason = "Model has no setters, which is not supported" )
public void basicTest(SessionFactoryScope scope) {
scope.inTransaction( (session) -> {
final Person mick = new Person( 1, Name.make( "Mick", "Jagger" ) );
session.persist( mick );
final Person john = new Person( 2, Name.make( "John", "Doe" ) );
john.addAlias( Name.make( "Jon", "Doe" ) );
session.persist( john );
} );
scope.inTransaction( (session) -> {
final Person mick = session.createQuery( "from Person where id = 1", Person.class ).uniqueResult();
assertThat( mick.getName().getFirstName() ).isEqualTo( "Mick" );
} );
scope.inTransaction( (session) -> {
final Person john = session.createQuery( "from Person p join fetch p.aliases where p.id = 2", Person.class ).uniqueResult();
assertThat( john.getName().getFirstName() ).isEqualTo( "John" );
assertThat( john.getAliases() ).hasSize( 1 );
final Name alias = john.getAliases().iterator().next();
assertThat( alias.getFirstName() ).isEqualTo( "Jon" );
} );
}
}

View File

@ -10,6 +10,18 @@ package org.hibernate.orm.test.mapping.embeddable.strategy.instantiator.intf2;
* @author Steve Ebersole
*/
public class Name {
private final String first;
private final String last;
public static Name make(String first, String last) {
return new Name( first, last );
}
private Name(String first, String last) {
this.first = first;
this.last = last;
}
String getFirstName() {
return "John";
}

View File

@ -17,7 +17,8 @@ import org.hibernate.metamodel.EmbeddableInstantiator;
public class NameInstantiator implements EmbeddableInstantiator {
@Override
public Object instantiate(Supplier<Object[]> valuesAccess, SessionFactoryImplementor sessionFactory) {
return new Name();
final Object[] values = valuesAccess.get();
return Name.make( (String) values[0], (String) values[1] );
}
@Override

View File

@ -20,9 +20,8 @@ import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Table(name = "persons")
//tag::embeddable-instantiator-property[]
@Entity
@Table(name = "people")
public class Person {
@Id
public Integer id;
@ -38,8 +37,6 @@ public class Person {
@Access( AccessType.PROPERTY )
public Set<Name> aliases;
//end::embeddable-instantiator-property[]
private Person() {
// for Hibernate use
}
@ -80,6 +77,5 @@ public class Person {
aliases.add( alias );
}
//tag::embeddable-instantiator-property[]
}
//end::embeddable-instantiator-property[]

View File

@ -1,52 +0,0 @@
/*
* 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.orm.test.mapping.embeddable.strategy.instantiator.intf2;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Creating a new version of {@link InstantiationTests} that won't fail the build,
* but still allows us to test the behavior.
*
* JUnit does not like when build fixtures (here, the SF) fails
*/
@ServiceRegistry
@FailureExpected( jiraKey = "HHH-14950", reason = "Model has no setters, which is not supported" )
@JiraKey( "HHH-14950" )
public class TempInstantiationTests {
@Test
public void basicTest(ServiceRegistryScope registerScope) {
final MetadataSources metadataSources = new MetadataSources( registerScope.getRegistry() )
.addAnnotatedClass( org.hibernate.orm.test.mapping.embeddable.strategy.instantiator.intf.Person.class )
.addAnnotatedClass( Name.class );
final Metadata metadata = metadataSources.buildMetadata();
final PersistentClass personMapping = metadata.getEntityBinding( Person.class.getName() );
final Property name = personMapping.getProperty( "name" );
final Component nameMapping = (Component) name.getValue();
assertThat( nameMapping.getPropertySpan() ).isEqualTo( 2 );
final Property aliases = personMapping.getProperty( "aliases" );
final Component aliasMapping = (Component) ( (Collection) aliases.getValue() ).getElement();
assertThat( aliasMapping.getPropertySpan() ).isEqualTo( 2 );
}
}

View File

@ -18,7 +18,7 @@ import org.junit.Ignore;
* @author Max Rydahl Andersen
*/
@RequiresDialect( DB2Dialect.class )
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
@Ignore( "Missing support for composite user types" )
public class DB2CustomSQLTest extends CustomStoredProcTestSupport {
public String[] getMappings() {

View File

@ -16,7 +16,7 @@ import org.junit.Ignore;
* @author Andrea Boriero
*/
@RequiresDialect(DerbyDialect.class)
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
@Ignore( "Missing support for composite user types" )
public class DerbyCustomSQLTest extends CustomStoredProcTestSupport {
public String[] getMappings() {

View File

@ -18,7 +18,7 @@ import org.junit.Ignore;
* @author Gavin King
*/
@RequiresDialect( MySQLDialect.class )
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
@Ignore( "Missing support for composite user types" )
public class MySQLCustomSQLTest extends CustomStoredProcTestSupport {
public String[] getMappings() {

View File

@ -18,7 +18,7 @@ import org.junit.Ignore;
* @author Gavin King
*/
@RequiresDialect( OracleDialect.class )
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
@Ignore( "Missing support for composite user types" )
public class OracleCustomSQLTest extends CustomStoredProcTestSupport {
public String[] getMappings() {

View File

@ -18,7 +18,7 @@ import org.junit.Ignore;
* @author Gail Badner
*/
@RequiresDialect( SQLServerDialect.class )
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
@Ignore( "Missing support for composite user types" )
public class SQLServerCustomSQLTest extends CustomStoredProcTestSupport {
public String[] getMappings() {

View File

@ -18,7 +18,7 @@ import org.junit.Ignore;
* @author Gavin King
*/
@RequiresDialect( { SybaseDialect.class })
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
@Ignore( "Missing support for composite user types" )
public class SybaseCustomSQLTest extends CustomStoredProcTestSupport {
public String[] getMappings() {

View File

@ -75,7 +75,7 @@ import static org.junit.jupiter.api.Assertions.fail;
xmlMappings = {"org/hibernate/orm/test/sql/hand/query/NativeSQLQueries.hbm.xml"}
)
@SessionFactory
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom ComponentTuplizer/Instantiator
// todo (6.0): needs a composite user type mechanism e.g. by providing a custom embeddable strategy or istantiator
public class NativeSQLQueriesTest {
protected String getOrganizationFetchJoinEmploymentSQL() {

View File

@ -22,8 +22,6 @@ final class StaticClassLists {
//The CoreMessageLogger is sometimes looked up without it necessarily being a field, so we're
//not processing it the same way as other Logger lookups.
org.hibernate.internal.CoreMessageLogger_$logger.class,
org.hibernate.tuple.component.PojoComponentTuplizer.class,
org.hibernate.tuple.component.DynamicMapComponentTuplizer.class,
org.hibernate.tuple.entity.DynamicMapEntityTuplizer.class,
org.hibernate.persister.collection.OneToManyPersister.class,
org.hibernate.persister.collection.BasicCollectionPersister.class,