/*
 * 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.mapping;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Objects;
import java.util.Map;

import org.hibernate.EntityMode;
import org.hibernate.MappingException;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.ExportableProducer;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.CompositeNestedGeneratedValueGenerator;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.collections.JoinedIterator;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.tuple.component.ComponentMetamodel;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;

The mapping for a component, composite element, composite identifier, etc.
Author:Gavin King, Steve Ebersole
/** * The mapping for a component, composite element, * composite identifier, etc. * * @author Gavin King * @author Steve Ebersole */
public class Component extends SimpleValue implements MetaAttributable { private ArrayList<Property> properties = new ArrayList<>(); private String componentClassName; private boolean embedded; private String parentProperty; private PersistentClass owner; private boolean dynamic; private Map metaAttributes; private boolean isKey; private String roleName; private java.util.Map<EntityMode,String> tuplizerImpls;
Deprecated:User Component(MetadataBuildingContext, PersistentClass) instead.
/** * @deprecated User {@link Component#Component(MetadataBuildingContext, PersistentClass)} instead. */
@Deprecated public Component(MetadataImplementor metadata, PersistentClass owner) throws MappingException { this( metadata, owner.getTable(), owner ); }
Deprecated:User Component(MetadataBuildingContext, Component) instead.
/** * @deprecated User {@link Component#Component(MetadataBuildingContext, Component)} instead. */
@Deprecated public Component(MetadataImplementor metadata, Component component) throws MappingException { this( metadata, component.getTable(), component.getOwner() ); }
Deprecated:User Component(MetadataBuildingContext, Join) instead.
/** * @deprecated User {@link Component#Component(MetadataBuildingContext, Join)} instead. */
@Deprecated public Component(MetadataImplementor metadata, Join join) throws MappingException { this( metadata, join.getTable(), join.getPersistentClass() ); }
Deprecated:User Component(MetadataBuildingContext, Collection) instead.
/** * @deprecated User {@link Component#Component(MetadataBuildingContext, Collection)} instead. */
@Deprecated public Component(MetadataImplementor metadata, Collection collection) throws MappingException { this( metadata, collection.getCollectionTable(), collection.getOwner() ); }
Deprecated:User Component(MetadataBuildingContext, Table, PersistentClass) instead.
/** * @deprecated User {@link Component#Component(MetadataBuildingContext, Table, PersistentClass)} instead. */
@Deprecated public Component(MetadataImplementor metadata, Table table, PersistentClass owner) throws MappingException { super( metadata, table ); this.owner = owner; } public Component(MetadataBuildingContext metadata, PersistentClass owner) throws MappingException { this( metadata, owner.getTable(), owner ); } public Component(MetadataBuildingContext metadata, Component component) throws MappingException { this( metadata, component.getTable(), component.getOwner() ); } public Component(MetadataBuildingContext metadata, Join join) throws MappingException { this( metadata, join.getTable(), join.getPersistentClass() ); } public Component(MetadataBuildingContext metadata, Collection collection) throws MappingException { this( metadata, collection.getCollectionTable(), collection.getOwner() ); } public Component(MetadataBuildingContext metadata, Table table, PersistentClass owner) throws MappingException { super( metadata, table ); this.owner = owner; } public int getPropertySpan() { return properties.size(); } public Iterator getPropertyIterator() { return properties.iterator(); } public void addProperty(Property p) { properties.add( p ); } @Override public void addColumn(Column column) { throw new UnsupportedOperationException("Cant add a column to a component"); } @Override public int getColumnSpan() { int n=0; Iterator iter = getPropertyIterator(); while ( iter.hasNext() ) { Property p = (Property) iter.next(); n+= p.getColumnSpan(); } return n; } @Override @SuppressWarnings("unchecked") public Iterator<Selectable> getColumnIterator() { Iterator[] iters = new Iterator[ getPropertySpan() ]; Iterator iter = getPropertyIterator(); int i=0; while ( iter.hasNext() ) { iters[i++] = ( (Property) iter.next() ).getColumnIterator(); } return new JoinedIterator( iters ); } public boolean isEmbedded() { return embedded; } public String getComponentClassName() { return componentClassName; } public Class getComponentClass() throws MappingException { 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); } } public PersistentClass getOwner() { return owner; } public String getParentProperty() { return parentProperty; } public void setComponentClassName(String componentClass) { this.componentClassName = componentClass; } public void setEmbedded(boolean embedded) { this.embedded = embedded; } public void setOwner(PersistentClass owner) { this.owner = owner; } public void setParentProperty(String parentProperty) { this.parentProperty = parentProperty; } public boolean isDynamic() { return dynamic; } public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } @Override public Type getType() throws MappingException { // TODO : temporary initial step towards HHH-1907 final ComponentMetamodel metamodel = new ComponentMetamodel( this, getMetadata().getMetadataBuildingOptions() ); final TypeFactory factory = getMetadata().getTypeConfiguration().getTypeResolver().getTypeFactory(); return isEmbedded() ? factory.embeddedComponent( metamodel ) : factory.component( metamodel ); } @Override public void setTypeUsingReflection(String className, String propertyName) throws MappingException { } @Override public java.util.Map getMetaAttributes() { return metaAttributes; } @Override public MetaAttribute getMetaAttribute(String attributeName) { return metaAttributes==null?null:(MetaAttribute) metaAttributes.get(attributeName); } @Override public void setMetaAttributes(java.util.Map metas) { this.metaAttributes = metas; } @Override public Object accept(ValueVisitor visitor) { return visitor.accept(this); } @Override public boolean isSame(SimpleValue other) { return other instanceof Component && isSame( (Component) other ); } public boolean isSame(Component other) { return super.isSame( other ) && Objects.equals( properties, other.properties ) && Objects.equals( componentClassName, other.componentClassName ) && embedded == other.embedded && Objects.equals( parentProperty, other.parentProperty ) && Objects.equals( metaAttributes, other.metaAttributes ); } @Override public boolean[] getColumnInsertability() { boolean[] result = new boolean[ getColumnSpan() ]; Iterator iter = getPropertyIterator(); int i=0; while ( iter.hasNext() ) { Property prop = (Property) iter.next(); boolean[] chunk = prop.getValue().getColumnInsertability(); if ( prop.isInsertable() ) { System.arraycopy(chunk, 0, result, i, chunk.length); } i+=chunk.length; } return result; } @Override public boolean[] getColumnUpdateability() { boolean[] result = new boolean[ getColumnSpan() ]; Iterator iter = getPropertyIterator(); int i=0; while ( iter.hasNext() ) { Property prop = (Property) iter.next(); boolean[] chunk = prop.getValue().getColumnUpdateability(); if ( prop.isUpdateable() ) { System.arraycopy(chunk, 0, result, i, chunk.length); } i+=chunk.length; } return result; } public boolean isKey() { return isKey; } public void setKey(boolean isKey) { this.isKey = isKey; } public boolean hasPojoRepresentation() { return componentClassName!=null; } public void addTuplizer(EntityMode entityMode, String implClassName) { if ( tuplizerImpls == null ) { tuplizerImpls = new HashMap<>(); } tuplizerImpls.put( entityMode, implClassName ); } public String getTuplizerImplClassName(EntityMode 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 ); } public Property getProperty(String propertyName) throws MappingException { Iterator iter = getPropertyIterator(); while ( iter.hasNext() ) { Property prop = (Property) iter.next(); if ( prop.getName().equals(propertyName) ) { return prop; } } throw new MappingException("component property not found: " + propertyName); } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } @Override public String toString() { return getClass().getName() + '(' + properties.toString() + ')'; } private IdentifierGenerator builtIdentifierGenerator; @Override public IdentifierGenerator createIdentifierGenerator( IdentifierGeneratorFactory identifierGeneratorFactory, Dialect dialect, String defaultCatalog, String defaultSchema, RootClass rootClass) throws MappingException { if ( builtIdentifierGenerator == null ) { builtIdentifierGenerator = buildIdentifierGenerator( identifierGeneratorFactory, dialect, defaultCatalog, defaultSchema, rootClass ); } return builtIdentifierGenerator; } private IdentifierGenerator buildIdentifierGenerator( IdentifierGeneratorFactory identifierGeneratorFactory, Dialect dialect, String defaultCatalog, String defaultSchema, RootClass rootClass) throws MappingException { final boolean hasCustomGenerator = ! DEFAULT_ID_GEN_STRATEGY.equals( getIdentifierGeneratorStrategy() ); if ( hasCustomGenerator ) { return super.createIdentifierGenerator( identifierGeneratorFactory, dialect, defaultCatalog, defaultSchema, rootClass ); } final Class entityClass = rootClass.getMappedClass(); final Class attributeDeclarer; // what class is the declarer of the composite pk attributes CompositeNestedGeneratedValueGenerator.GenerationContextLocator locator; // IMPL NOTE : See the javadoc discussion on CompositeNestedGeneratedValueGenerator wrt the // various scenarios for which we need to account here if ( rootClass.getIdentifierMapper() != null ) { // we have the @IdClass / <composite-id mapped="true"/> case attributeDeclarer = resolveComponentClass(); } else if ( rootClass.getIdentifierProperty() != null ) { // we have the "@EmbeddedId" / <composite-id name="idName"/> case attributeDeclarer = resolveComponentClass(); } else { // we have the "straight up" embedded (again the hibernate term) component identifier attributeDeclarer = entityClass; } locator = new StandardGenerationContextLocator( rootClass.getEntityName() ); final CompositeNestedGeneratedValueGenerator generator = new CompositeNestedGeneratedValueGenerator( locator ); Iterator itr = getPropertyIterator(); while ( itr.hasNext() ) { final Property property = (Property) itr.next(); if ( property.getValue().isSimpleValue() ) { final SimpleValue value = (SimpleValue) property.getValue(); if ( DEFAULT_ID_GEN_STRATEGY.equals( value.getIdentifierGeneratorStrategy() ) ) { // skip any 'assigned' generators, they would have been handled by // the StandardGenerationContextLocator continue; } final IdentifierGenerator valueGenerator = value.createIdentifierGenerator( identifierGeneratorFactory, dialect, defaultCatalog, defaultSchema, rootClass ); generator.addGeneratedValuePlan( new ValueGenerationPlan( valueGenerator, injector( property, attributeDeclarer ) ) ); } } return generator; } private Setter injector(Property property, Class attributeDeclarer) { return property.getPropertyAccessStrategy( attributeDeclarer ) .buildPropertyAccess( attributeDeclarer, property.getName() ) .getSetter(); } private Class resolveComponentClass() { try { return getComponentClass(); } catch ( Exception e ) { return null; } } public static class StandardGenerationContextLocator implements CompositeNestedGeneratedValueGenerator.GenerationContextLocator { private final String entityName; public StandardGenerationContextLocator(String entityName) { this.entityName = entityName; } @Override public Serializable locateGenerationContext(SharedSessionContractImplementor session, Object incomingObject) { return session.getEntityPersister( entityName, incomingObject ).getIdentifier( incomingObject, session ); } } public static class ValueGenerationPlan implements CompositeNestedGeneratedValueGenerator.GenerationPlan { private final IdentifierGenerator subGenerator; private final Setter injector; public ValueGenerationPlan( IdentifierGenerator subGenerator, Setter injector) { this.subGenerator = subGenerator; this.injector = injector; } @Override public void execute(SharedSessionContractImplementor session, Object incomingObject, Object injectionContext) { final Object generatedValue = subGenerator.generate( session, incomingObject ); injector.set( injectionContext, generatedValue, session.getFactory() ); } @Override public void registerExportables(Database database) { if ( ExportableProducer.class.isInstance( subGenerator ) ) { ( (ExportableProducer) subGenerator ).registerExportables( database ); } } } }