HHH-7076 - Split 'type resolution' back out into separate class

This commit is contained in:
Steve Ebersole 2012-02-16 17:41:30 -06:00
parent 9adb996520
commit f5679281e0
4 changed files with 595 additions and 418 deletions

View File

@ -23,9 +23,6 @@
*/
package org.hibernate.metamodel.internal.source;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Comparator;
@ -45,12 +42,9 @@
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.beans.BeanInfoHelper;
import org.hibernate.metamodel.internal.source.hbm.Helper;
import org.hibernate.metamodel.spi.binding.AbstractPluralAttributeBinding;
import org.hibernate.metamodel.spi.binding.AbstractPluralAttributeElementBinding;
import org.hibernate.metamodel.spi.binding.AttributeBinding;
import org.hibernate.metamodel.spi.binding.AttributeBindingContainer;
import org.hibernate.metamodel.spi.binding.BasicAttributeBinding;
@ -58,7 +52,6 @@
import org.hibernate.metamodel.spi.binding.CompositionAttributeBinding;
import org.hibernate.metamodel.spi.binding.EntityBinding;
import org.hibernate.metamodel.spi.binding.EntityDiscriminator;
import org.hibernate.metamodel.spi.binding.HibernateTypeDescriptor;
import org.hibernate.metamodel.spi.binding.IdGenerator;
import org.hibernate.metamodel.spi.binding.InheritanceType;
import org.hibernate.metamodel.spi.binding.ManyToOneAttributeBinding;
@ -67,18 +60,15 @@
import org.hibernate.metamodel.spi.binding.RelationalValueBinding;
import org.hibernate.metamodel.spi.binding.SingularAssociationAttributeBinding;
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
import org.hibernate.metamodel.spi.binding.TypeDefinition;
import org.hibernate.metamodel.spi.domain.Attribute;
import org.hibernate.metamodel.spi.domain.Composition;
import org.hibernate.metamodel.spi.domain.Entity;
import org.hibernate.metamodel.spi.domain.PluralAttribute;
import org.hibernate.metamodel.spi.domain.SingularAttribute;
import org.hibernate.metamodel.spi.relational.AbstractValue;
import org.hibernate.metamodel.spi.relational.Column;
import org.hibernate.metamodel.spi.relational.DerivedValue;
import org.hibernate.metamodel.spi.relational.ForeignKey;
import org.hibernate.metamodel.spi.relational.Identifier;
import org.hibernate.metamodel.spi.relational.JdbcDataType;
import org.hibernate.metamodel.spi.relational.Schema;
import org.hibernate.metamodel.spi.relational.Table;
import org.hibernate.metamodel.spi.relational.TableSpecification;
@ -95,7 +85,6 @@
import org.hibernate.metamodel.spi.source.DiscriminatorSource;
import org.hibernate.metamodel.spi.source.EntityHierarchy;
import org.hibernate.metamodel.spi.source.EntitySource;
import org.hibernate.metamodel.spi.source.ExplicitHibernateTypeSource;
import org.hibernate.metamodel.spi.source.LocalBindingContext;
import org.hibernate.metamodel.spi.source.MappingException;
import org.hibernate.metamodel.spi.source.MetaAttributeContext;
@ -123,7 +112,6 @@
import org.hibernate.service.config.spi.ConfigurationService;
import org.hibernate.tuple.entity.EntityTuplizer;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
/**
* The common binder shared between annotations and {@code hbm.xml} processing.
@ -132,6 +120,7 @@
*
* @todo Really need to chop this up. The class is doing so many different things right now.
* @todo And really need to come up with consistent method naming.
* @todo This class really should live in the o.h.metamodel.internal package
*
* @author Steve Ebersole
* @author Hardy Ferentschik
@ -140,6 +129,8 @@ public class Binder {
private final MetadataImplementor metadata;
private final ArrayList<String> processedEntityNames = new ArrayList<String>();
private HibernateTypeHelper typeHelper = new HibernateTypeHelper( this );
private final ObjectNameNormalizer NAME_NORMALIZER = new ObjectNameNormalizer() {
@Override
protected boolean isUseQuotedIdentifiersGlobally() {
@ -161,6 +152,14 @@ public Binder( MetadataImplementor metadata ) {
this.metadata = metadata;
}
MetadataImplementor getMetadata() {
return metadata;
}
LocalBindingContext getCurrentBindingContext() {
return currentBindingContext;
}
/**
* Process an entity hierarchy.
*
@ -537,9 +536,9 @@ private void bindDiscriminator(RootEntitySource entitySource, EntityBinding enti
entityBinding.getHierarchyDetails().setEntityDiscriminator( discriminator );
entityBinding.setDiscriminatorMatchValue( entitySource.getDiscriminatorMatchValue() );
Type resolvedType = determineHibernateTypeFromDescriptor( discriminator.getExplicitHibernateTypeDescriptor() );
Type resolvedType = typeHelper.determineHibernateTypeFromDescriptor( discriminator.getExplicitHibernateTypeDescriptor() );
if ( resolvedType != null ) {
pushHibernateTypeInformationDownIfNeeded( resolvedType, relationalValue );
typeHelper.pushHibernateTypeInformationDown( resolvedType, relationalValue );
}
}
@ -636,7 +635,7 @@ else if ( attributeSource.getNature() == SingularAttributeNature.MANY_TO_ONE ) {
attributeSource.getReferencedEntityAttributeName(),
relationalValueBindings
);
resolveTypeInformation( attributeSource.getTypeInformation(), attributeBinding );
typeHelper.bindSingularAttributeTypeInformation( attributeSource.getTypeInformation(), attributeBinding );
resolveToOneInformation(
attributeSource,
(ManyToOneAttributeBinding) attributeBinding
@ -810,7 +809,7 @@ else if ( attributeSource.isVirtualAttribute() ) {
metaAttributeContext,
attributeSource.getGeneration()
);
resolveTypeInformation( attributeSource.getTypeInformation(), attributeBinding );
typeHelper.bindSingularAttributeTypeInformation( attributeSource.getTypeInformation(), attributeBinding );
return attributeBinding;
}
@ -1138,17 +1137,18 @@ private void bindCollectionElement(
AbstractPluralAttributeBinding pluralAttributeBinding) {
final PluralAttributeElementSource elementSource = attributeSource.getElementSource();
if ( elementSource.getNature() == org.hibernate.metamodel.spi.source.PluralAttributeElementNature.BASIC ) {
final BasicPluralAttributeElementSource basicElementSource = (BasicPluralAttributeElementSource) elementSource;
final BasicPluralAttributeElementBinding basicCollectionElement = (BasicPluralAttributeElementBinding) pluralAttributeBinding.getPluralAttributeElementBinding();
resolveTypeInformation(
basicElementSource.getExplicitHibernateTypeSource(),
pluralAttributeBinding.getAttribute(),
basicCollectionElement
);
final BasicPluralAttributeElementSource basicElementSource =
(BasicPluralAttributeElementSource) elementSource;
final BasicPluralAttributeElementBinding basicCollectionElement =
(BasicPluralAttributeElementBinding) pluralAttributeBinding.getPluralAttributeElementBinding();
bindBasicPluralElementRelationalValues(
basicElementSource,
basicCollectionElement
);
typeHelper.bindPluralAttributeTypeInformation(
attributeSource,
pluralAttributeBinding
);
return;
}
@ -1232,350 +1232,6 @@ private EntityBinding getEntityBinding(String entityName) {
return binding;
}
private final Properties EMPTY_PROPERTIES = new Properties();
private void resolveTypeInformation(ExplicitHibernateTypeSource typeSource, SingularAttributeBinding attributeBinding) {
final Class<?> attributeJavaType = determineJavaType( attributeBinding.getAttribute() );
if ( attributeJavaType != null ) {
attributeBinding.getAttribute()
.resolveType( currentBindingContext.makeJavaType( attributeJavaType.getName() ) );
}
resolveTypeInformation( typeSource, attributeBinding.getHibernateTypeDescriptor(), attributeJavaType );
resolveSingularAttributeTypeInformation( attributeBinding );
}
// perform any needed type resolutions for SingularAttributeBinding
private void resolveSingularAttributeTypeInformation(SingularAttributeBinding attributeBinding) {
if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() != null ) {
return;
}
// we can determine the Hibernate Type if either:
// 1) the user explicitly named a Type in a HibernateTypeDescriptor
// 2) we know the java type of the attribute
Type resolvedType = determineHibernateTypeFromDescriptor( attributeBinding.getHibernateTypeDescriptor() );
if ( resolvedType == null ) {
if ( ! attributeBinding.getAttribute().isSingular() ) {
throw new AssertionFailure( "SingularAttributeBinding object has a plural attribute: " + attributeBinding.getAttribute().getName() );
}
final SingularAttribute singularAttribute = attributeBinding.getAttribute();
if ( singularAttribute.getSingularAttributeType() != null ) {
resolvedType = getHeuristicType(
singularAttribute.getSingularAttributeType().getClassName(),
EMPTY_PROPERTIES
);
}
} else {
attributeBinding.getHibernateTypeDescriptor().setResolvedTypeMapping( resolvedType );
pushHibernateTypeInformationDownIfNeeded( attributeBinding, resolvedType );
}
}
private Type determineHibernateTypeFromDescriptor(HibernateTypeDescriptor hibernateTypeDescriptor) {
if ( hibernateTypeDescriptor.getResolvedTypeMapping() != null ) {
return hibernateTypeDescriptor.getResolvedTypeMapping();
}
String typeName = determineTypeName( hibernateTypeDescriptor );
Properties typeParameters = getTypeParameters( hibernateTypeDescriptor );
Type type = getHeuristicType( typeName, typeParameters );
hibernateTypeDescriptor.setResolvedTypeMapping( type );
return type;
}
private static String determineTypeName(HibernateTypeDescriptor hibernateTypeDescriptor) {
return hibernateTypeDescriptor.getExplicitTypeName() != null
? hibernateTypeDescriptor.getExplicitTypeName()
: hibernateTypeDescriptor.getJavaTypeName();
}
private static Properties getTypeParameters(HibernateTypeDescriptor hibernateTypeDescriptor) {
Properties typeParameters = new Properties( );
if ( hibernateTypeDescriptor.getTypeParameters() != null ) {
typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() );
}
return typeParameters;
}
private void resolvePluralAttributeTypeInformation(AbstractPluralAttributeBinding attributeBinding) {
if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() != null ) {
return;
}
Type resolvedType;
// do NOT look at java type...
//String typeName = determineTypeName( attributeBinding.getHibernateTypeDescriptor() );
String typeName = attributeBinding.getHibernateTypeDescriptor().getExplicitTypeName();
if ( typeName != null ) {
resolvedType =
metadata.getTypeResolver()
.getTypeFactory()
.customCollection(
typeName,
getTypeParameters( attributeBinding.getHibernateTypeDescriptor() ),
attributeBinding.getAttribute().getName(),
attributeBinding.getReferencedPropertyName(),
attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() ==
PluralAttributeElementNature.COMPOSITE
);
}
else {
resolvedType = determineDefaultCollectionInformation( attributeBinding );
}
if ( resolvedType != null ) {
// todo : what exactly is getting pushed down here? and to what/where?
// pushHibernateTypeInformationDownIfNeeded(
// attributeBinding.getHibernateTypeDescriptor(),
// null,
// resolvedType
// );
}
resolveCollectionElementTypeInformation( attributeBinding.getPluralAttributeElementBinding() );
}
private Type determineDefaultCollectionInformation(AbstractPluralAttributeBinding attributeBinding) {
final TypeFactory typeFactory = metadata.getTypeResolver().getTypeFactory();
switch ( attributeBinding.getAttribute().getNature() ) {
case SET: {
return typeFactory.set(
attributeBinding.getAttribute().getName(),
attributeBinding.getReferencedPropertyName(),
attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() == PluralAttributeElementNature.COMPOSITE
);
}
case BAG: {
return typeFactory.bag(
attributeBinding.getAttribute().getName(),
attributeBinding.getReferencedPropertyName(),
attributeBinding.getPluralAttributeElementBinding()
.getPluralAttributeElementNature() == PluralAttributeElementNature.COMPOSITE
);
}
default: {
throw new UnsupportedOperationException(
"Collection type not supported yet:" + attributeBinding.getAttribute().getNature()
);
}
}
}
private void resolveCollectionElementTypeInformation(AbstractPluralAttributeElementBinding pluralAttributeElementBinding) {
switch ( pluralAttributeElementBinding.getPluralAttributeElementNature() ) {
case BASIC: {
resolveBasicCollectionElement( BasicPluralAttributeElementBinding.class.cast(
pluralAttributeElementBinding
) );
break;
}
case COMPOSITE:
case ONE_TO_MANY:
case MANY_TO_MANY:
case MANY_TO_ANY: {
throw new UnsupportedOperationException( "Collection element nature not supported yet: " + pluralAttributeElementBinding
.getPluralAttributeElementNature() );
}
default: {
throw new AssertionFailure( "Unknown collection element nature : " + pluralAttributeElementBinding.getPluralAttributeElementNature() );
}
}
}
private void resolveBasicCollectionElement(BasicPluralAttributeElementBinding basicCollectionElement) {
Type resolvedHibernateType = determineHibernateTypeFromDescriptor( basicCollectionElement.getHibernateTypeDescriptor() );
if ( resolvedHibernateType != null ) {
pushHibernateTypeInformationDownIfNeeded(
basicCollectionElement.getHibernateTypeDescriptor(),
basicCollectionElement.getRelationalValueBindings(),
resolvedHibernateType
);
}
}
private Type getHeuristicType(String typeName, Properties typeParameters) {
if ( typeName != null ) {
try {
return metadata.getTypeResolver().heuristicType( typeName, typeParameters );
}
catch (Exception ignore) {
}
}
return null;
}
private void pushHibernateTypeInformationDownIfNeeded(
SingularAttributeBinding attributeBinding,
Type resolvedHibernateType) {
final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor();
// sql type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( BasicAttributeBinding.class.isInstance( attributeBinding ) ) {
pushHibernateTypeInformationDownIfNeeded(
hibernateTypeDescriptor,
(BasicAttributeBinding) attributeBinding,
resolvedHibernateType
);
}
else if ( CompositionAttributeBinding.class.isInstance( attributeBinding ) ) {
pushHibernateTypeInformationDownIfNeeded(
hibernateTypeDescriptor,
(CompositionAttributeBinding) attributeBinding
);
}
}
private void pushHibernateTypeInformationDownIfNeeded(
HibernateTypeDescriptor hibernateTypeDescriptor,
CompositionAttributeBinding attributeBinding) {
final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() );
if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) {
singularAttribute.resolveType( metadata.makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) );
}
for ( AttributeBinding subAttributeBinding : attributeBinding.attributeBindings() ) {
if ( SingularAttributeBinding.class.isInstance( subAttributeBinding ) ) {
resolveSingularAttributeTypeInformation(
SingularAttributeBinding.class.cast( subAttributeBinding )
);
}
else if ( AbstractPluralAttributeBinding.class.isInstance( subAttributeBinding ) ) {
resolvePluralAttributeTypeInformation(
AbstractPluralAttributeBinding.class.cast( subAttributeBinding )
);
}
else {
throw new AssertionFailure( "Unknown type of AttributeBinding: " + attributeBinding.getClass().getName() );
}
}
}
private void pushHibernateTypeInformationDownIfNeeded(
HibernateTypeDescriptor hibernateTypeDescriptor,
BasicAttributeBinding attributeBinding,
Type resolvedHibernateType) {
final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() );
if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) {
singularAttribute.resolveType( metadata.makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) );
}
pushHibernateTypeInformationDownIfNeeded(
hibernateTypeDescriptor,
attributeBinding.getRelationalValueBindings(),
resolvedHibernateType
);
}
private void pushHibernateTypeInformationDownIfNeeded(
HibernateTypeDescriptor hibernateTypeDescriptor,
List<RelationalValueBinding> relationalValueBindings,
Type resolvedHibernateType) {
if ( resolvedHibernateType == null ) {
return;
}
if ( hibernateTypeDescriptor.getResolvedTypeMapping() == null ) {
hibernateTypeDescriptor.setResolvedTypeMapping( resolvedHibernateType );
}
// java type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( hibernateTypeDescriptor.getJavaTypeName() == null ) {
hibernateTypeDescriptor.setJavaTypeName( resolvedHibernateType.getReturnedClass().getName() );
}
// todo : this can be made a lot smarter, but for now this will suffice. currently we only handle single value bindings
if ( relationalValueBindings.size() > 1 ) {
return;
}
final Value value = relationalValueBindings.get( 0 ).getValue();
pushHibernateTypeInformationDownIfNeeded( resolvedHibernateType, value );
}
private void pushHibernateTypeInformationDownIfNeeded(Type resolvedHibernateType, Value value) {
if ( value.getJdbcDataType() == null ) {
if ( AbstractValue.class.isInstance( value ) ) {
( (AbstractValue) value ).setJdbcDataType(
new JdbcDataType(
resolvedHibernateType.sqlTypes( metadata )[0],
resolvedHibernateType.getName(),
resolvedHibernateType.getReturnedClass()
)
);
}
}
}
private void resolveTypeInformation(
ExplicitHibernateTypeSource typeSource,
PluralAttribute attribute,
BasicPluralAttributeElementBinding collectionElement) {
final Class<?> attributeJavaType = determineJavaType( attribute );
resolveTypeInformation( typeSource, collectionElement.getHibernateTypeDescriptor(), attributeJavaType );
}
private void resolveTypeInformation(
ExplicitHibernateTypeSource typeSource,
HibernateTypeDescriptor hibernateTypeDescriptor,
Class<?> discoveredJavaType) {
if ( discoveredJavaType != null ) {
hibernateTypeDescriptor.setJavaTypeName( discoveredJavaType.getName() );
}
final String explicitTypeName = typeSource.getName();
if ( explicitTypeName != null ) {
final TypeDefinition typeDefinition = currentBindingContext.getMetadataImplementor()
.getTypeDefinition( explicitTypeName );
if ( typeDefinition != null ) {
hibernateTypeDescriptor.setExplicitTypeName( typeDefinition.getTypeImplementorClass().getName() );
hibernateTypeDescriptor.getTypeParameters().putAll( typeDefinition.getParameters() );
}
else {
hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName );
}
final Map<String, String> parameters = typeSource.getParameters();
if ( parameters != null ) {
hibernateTypeDescriptor.getTypeParameters().putAll( parameters );
}
}
else {
if ( discoveredJavaType == null ) {
// we will have problems later determining the Hibernate Type to use. Should we throw an
// exception now? Might be better to get better contextual info
}
}
}
/**
* @param attribute the domain attribute
*
* @return Returns the Java type of the attribute using reflection or {@code null} if the type cannot be discovered
*/
private Class<?> determineJavaType(final SingularAttribute attribute) {
try {
final Class<?> ownerClass = attribute.getAttributeContainer().getClassReference();
return ReflectHelper.reflectedPropertyClass( ownerClass, attribute.getName() );
}
catch ( Exception ignore ) {
// todo : log it?
}
return null;
}
private Class<?> determineJavaType(PluralAttribute attribute) {
try {
final Class<?> ownerClass = attribute.getAttributeContainer().getClassReference();
PluralAttributeJavaTypeDeterminerDelegate delegate = new PluralAttributeJavaTypeDeterminerDelegate(
ownerClass,
attribute.getName()
);
BeanInfoHelper.visitBeanInfo( ownerClass, delegate );
return delegate.javaType;
}
catch ( Exception ignore ) {
// todo : log it?
}
return null;
}
private void resolveToOneInformation(ToOneAttributeSource attributeSource, ManyToOneAttributeBinding attributeBinding) {
final String referencedEntityName = attributeSource.getReferencedEntityName() != null
? attributeSource.getReferencedEntityName()
@ -1827,55 +1483,4 @@ private void processFetchProfiles(EntitySource entitySource, EntityBinding entit
// todo : process the entity-local fetch-profile declaration
}
private class PluralAttributeJavaTypeDeterminerDelegate implements BeanInfoHelper.BeanInfoDelegate {
private final Class<?> ownerClass;
private final String attributeName;
private Class<?> javaType = null;
private PluralAttributeJavaTypeDeterminerDelegate(Class<?> ownerClass, String attributeName) {
this.ownerClass = ownerClass;
this.attributeName = attributeName;
}
@Override
public void processBeanInfo(BeanInfo beanInfo) throws Exception {
java.lang.reflect.Type collectionAttributeType = null;
if ( beanInfo.getPropertyDescriptors() == null || beanInfo.getPropertyDescriptors().length == 0 ) {
// we need to look for the field and look at it...
collectionAttributeType = ownerClass.getField( attributeName ).getGenericType();
}
else {
for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) {
if ( propertyDescriptor.getName().equals( attributeName ) ) {
if ( propertyDescriptor.getReadMethod() != null ) {
collectionAttributeType = propertyDescriptor.getReadMethod().getGenericReturnType();
}
else if ( propertyDescriptor.getWriteMethod() != null ) {
collectionAttributeType = propertyDescriptor.getWriteMethod().getGenericParameterTypes()[0];
}
}
}
}
if ( collectionAttributeType != null ) {
if ( ParameterizedType.class.isInstance( collectionAttributeType ) ) {
final java.lang.reflect.Type[] types = ( (ParameterizedType) collectionAttributeType ).getActualTypeArguments();
if ( types == null ) {
return;
}
else if ( types.length == 1 ) {
javaType = (Class<?>) types[0];
}
else if ( types.length == 2 ) {
// Map<K,V>
javaType = (Class<?>) types[1];
}
}
else {
javaType = (Class<?>) collectionAttributeType;
}
}
}
}
}

View File

@ -0,0 +1,569 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.metamodel.internal.source;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.beans.BeanInfoHelper;
import org.hibernate.metamodel.spi.binding.AbstractPluralAttributeBinding;
import org.hibernate.metamodel.spi.binding.AttributeBinding;
import org.hibernate.metamodel.spi.binding.BasicAttributeBinding;
import org.hibernate.metamodel.spi.binding.BasicPluralAttributeElementBinding;
import org.hibernate.metamodel.spi.binding.CompositionAttributeBinding;
import org.hibernate.metamodel.spi.binding.HibernateTypeDescriptor;
import org.hibernate.metamodel.spi.binding.IndexedPluralAttributeBinding;
import org.hibernate.metamodel.spi.binding.PluralAttributeBinding;
import org.hibernate.metamodel.spi.binding.PluralAttributeElementBinding;
import org.hibernate.metamodel.spi.binding.PluralAttributeElementNature;
import org.hibernate.metamodel.spi.binding.PluralAttributeIndexBinding;
import org.hibernate.metamodel.spi.binding.RelationalValueBinding;
import org.hibernate.metamodel.spi.binding.SingularAttributeBinding;
import org.hibernate.metamodel.spi.binding.TypeDefinition;
import org.hibernate.metamodel.spi.domain.PluralAttribute;
import org.hibernate.metamodel.spi.domain.SingularAttribute;
import org.hibernate.metamodel.spi.relational.AbstractValue;
import org.hibernate.metamodel.spi.relational.JdbcDataType;
import org.hibernate.metamodel.spi.relational.Value;
import org.hibernate.metamodel.spi.source.ExplicitHibernateTypeSource;
import org.hibernate.metamodel.spi.source.MetadataImplementor;
import org.hibernate.metamodel.spi.source.PluralAttributeSource;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
/**
* Serves 2 roles:<ol>
* <li>
* Takes information about an attribute mapping and determines the appropriate Hibernate
* {@link org.hibernate.type.Type} to use, if possible.
* </li>
* <li>
* Given a Hibernate {@link org.hibernate.type.Type}, it pushes the jdbc and java type information
* reported the {@link org.hibernate.type.Type} into parts of the metamodel that may be missing it
* </li>
* </ol>
* <p/>
* Methods intended as entry points are:<ul>
* <li>{@link #bindSingularAttributeTypeInformation}</li>
* <li>{@link #bindPluralAttributeTypeInformation}</li>
* </ul>
* <p/>
* Currently the following methods are also required to be non-private because of handling discriminators which
* are currently not modeled using attributes:<ul>
* <li>{@link #determineHibernateTypeFromDescriptor}</li>
* <li>{@link #pushHibernateTypeInformationDown(org.hibernate.type.Type, org.hibernate.metamodel.spi.relational.Value)}</li>
* </ul>
*
* @author Steve Ebersole
*/
public class HibernateTypeHelper {
private static final Logger log = Logger.getLogger( HibernateTypeHelper.class );
private final Binder binder;
public HibernateTypeHelper(Binder binder) {
this.binder = binder;
}
private org.hibernate.metamodel.spi.domain.Type makeJavaType(String name) {
return binder.getCurrentBindingContext().makeJavaType( name );
}
private MetadataImplementor metadata() {
return binder.getMetadata();
}
public void bindSingularAttributeTypeInformation(
ExplicitHibernateTypeSource typeSource,
SingularAttributeBinding attributeBinding) {
final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor();
final Class<?> attributeJavaType = determineJavaType( attributeBinding.getAttribute() );
if ( attributeJavaType != null ) {
attributeBinding.getAttribute().resolveType( makeJavaType( attributeJavaType.getName() ) );
if ( hibernateTypeDescriptor.getJavaTypeName() == null ) {
hibernateTypeDescriptor.setJavaTypeName( attributeJavaType.getName() );
}
}
bindHibernateTypeInformation( typeSource, hibernateTypeDescriptor );
processSingularAttributeTypeInformation( attributeBinding );
}
public void bindPluralAttributeTypeInformation(
PluralAttributeSource attributeSource,
PluralAttributeBinding attributeBinding) {
final ReflectedCollectionJavaTypes reflectedCollectionJavaTypes = determineJavaType( attributeBinding.getAttribute() );
if ( reflectedCollectionJavaTypes != null ) {
if ( reflectedCollectionJavaTypes.collectionType != null ) {
if ( attributeBinding.getHibernateTypeDescriptor().getJavaTypeName() == null ) {
attributeBinding.getHibernateTypeDescriptor().setJavaTypeName(
reflectedCollectionJavaTypes.collectionType.getName()
);
}
}
if ( reflectedCollectionJavaTypes.collectionElementType != null ) {
if ( attributeBinding.getPluralAttributeElementBinding().getHibernateTypeDescriptor().getJavaTypeName() == null ) {
attributeBinding.getPluralAttributeElementBinding().getHibernateTypeDescriptor().setJavaTypeName(
reflectedCollectionJavaTypes.collectionElementType.getName()
);
}
}
if ( reflectedCollectionJavaTypes.collectionIndexType != null
&& IndexedPluralAttributeBinding.class.isInstance( attributeBinding ) ) {
final PluralAttributeIndexBinding indexBinding
= ( (IndexedPluralAttributeBinding) attributeBinding ).getPluralAttributeIndexBinding();
if ( indexBinding.getHibernateTypeDescriptor().getJavaTypeName() == null ) {
indexBinding.getHibernateTypeDescriptor().setJavaTypeName(
reflectedCollectionJavaTypes.collectionIndexType.getName()
);
}
}
}
//todo : we need to support type descriptors at multiple levels
bindHibernateTypeInformation( attributeSource.getTypeInformation(), attributeBinding.getHibernateTypeDescriptor() );
processPluralAttributeTypeInformation( attributeBinding );
}
private Class<?> determineJavaType(final SingularAttribute attribute) {
try {
final Class<?> ownerClass = attribute.getAttributeContainer().getClassReference();
return ReflectHelper.reflectedPropertyClass( ownerClass, attribute.getName() );
}
catch ( Exception ignore ) {
log.debugf(
"Unable to locate attribute [%s] on class [%s]",
attribute.getName(),
attribute.getAttributeContainer().getClassName()
);
}
return null;
}
private ReflectedCollectionJavaTypes determineJavaType(PluralAttribute attribute) {
try {
final Class<?> ownerClass = attribute.getAttributeContainer().getClassReference();
PluralAttributeJavaTypeDeterminerDelegate delegate = new PluralAttributeJavaTypeDeterminerDelegate(
ownerClass,
attribute.getName()
);
BeanInfoHelper.visitBeanInfo( ownerClass, delegate );
return delegate.collectionJavaTypes;
}
catch ( Exception ignore ) {
log.debugf(
"Unable to locate attribute [%s] on class [%s]",
attribute.getName(),
attribute.getAttributeContainer().getClassName()
);
}
return null;
}
/**
* Takes explicit source type information and applies it to the binding model.
*
* @param typeSource The source (user supplied) hibernate type information
* @param hibernateTypeDescriptor The binding model hibernate type information
*/
private void bindHibernateTypeInformation(
ExplicitHibernateTypeSource typeSource,
HibernateTypeDescriptor hibernateTypeDescriptor) {
final String explicitTypeName = typeSource.getName();
if ( explicitTypeName != null ) {
final TypeDefinition typeDefinition = metadata().getTypeDefinition( explicitTypeName );
if ( typeDefinition != null ) {
hibernateTypeDescriptor.setExplicitTypeName( typeDefinition.getTypeImplementorClass().getName() );
hibernateTypeDescriptor.getTypeParameters().putAll( typeDefinition.getParameters() );
}
else {
hibernateTypeDescriptor.setExplicitTypeName( explicitTypeName );
}
final Map<String, String> parameters = typeSource.getParameters();
if ( parameters != null ) {
hibernateTypeDescriptor.getTypeParameters().putAll( parameters );
}
}
}
/**
* Given an attribute, process all of its type information. This includes resolving the actual
* {@link Type} instance and pushing JDBC/java information from that type down.
*
* @param attributeBinding The attribute.
*/
private void processSingularAttributeTypeInformation(SingularAttributeBinding attributeBinding) {
Type resolvedType = attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping();
if ( resolvedType == null ) {
// we can determine the Hibernate Type if either:
// 1) the user explicitly named a Type in a HibernateTypeDescriptor
// 2) we know the java type of the attribute
resolvedType = determineHibernateTypeFromDescriptor( attributeBinding.getHibernateTypeDescriptor() );
if ( resolvedType == null ) {
resolvedType = determineHibernateTypeFromAttributeJavaType( attributeBinding.getAttribute() );
}
}
if ( resolvedType != null ) {
pushHibernateTypeInformationDown( attributeBinding, resolvedType );
}
}
public Type determineHibernateTypeFromDescriptor(HibernateTypeDescriptor hibernateTypeDescriptor) {
if ( hibernateTypeDescriptor.getResolvedTypeMapping() != null ) {
return hibernateTypeDescriptor.getResolvedTypeMapping();
}
String typeName = determineTypeName( hibernateTypeDescriptor );
Properties typeParameters = getTypeParameters( hibernateTypeDescriptor );
Type type = getHeuristicType( typeName, typeParameters );
hibernateTypeDescriptor.setResolvedTypeMapping( type );
return type;
}
private Type getHeuristicType(String typeName, Properties typeParameters) {
if ( typeName != null ) {
try {
return metadata().getTypeResolver().heuristicType( typeName, typeParameters );
}
catch (Exception ignore) {
}
}
return null;
}
private final Properties EMPTY_PROPERTIES = new Properties();
private Type determineHibernateTypeFromAttributeJavaType(SingularAttribute singularAttribute) {
if ( singularAttribute.getSingularAttributeType() != null ) {
return getHeuristicType(
singularAttribute.getSingularAttributeType().getClassName(),
EMPTY_PROPERTIES
);
}
return null;
}
private static String determineTypeName(HibernateTypeDescriptor hibernateTypeDescriptor) {
return hibernateTypeDescriptor.getExplicitTypeName() != null
? hibernateTypeDescriptor.getExplicitTypeName()
: hibernateTypeDescriptor.getJavaTypeName();
}
private static Properties getTypeParameters(HibernateTypeDescriptor hibernateTypeDescriptor) {
Properties typeParameters = new Properties( );
if ( hibernateTypeDescriptor.getTypeParameters() != null ) {
typeParameters.putAll( hibernateTypeDescriptor.getTypeParameters() );
}
return typeParameters;
}
private void pushHibernateTypeInformationDown(
SingularAttributeBinding attributeBinding,
Type resolvedHibernateType) {
// sql type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( BasicAttributeBinding.class.isInstance( attributeBinding ) ) {
pushHibernateTypeInformationDown(
(BasicAttributeBinding) attributeBinding,
resolvedHibernateType
);
}
else if ( CompositionAttributeBinding.class.isInstance( attributeBinding ) ) {
pushHibernateTypeInformationDown(
(CompositionAttributeBinding) attributeBinding,
resolvedHibernateType
);
}
}
private void pushHibernateTypeInformationDown(
BasicAttributeBinding attributeBinding,
Type resolvedHibernateType) {
final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor();
final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() );
if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) {
singularAttribute.resolveType( makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) );
}
pushHibernateTypeInformationDown(
hibernateTypeDescriptor,
attributeBinding.getRelationalValueBindings(),
resolvedHibernateType
);
}
@SuppressWarnings( {"UnusedParameters"})
private void pushHibernateTypeInformationDown(
CompositionAttributeBinding attributeBinding,
Type resolvedHibernateType) {
final HibernateTypeDescriptor hibernateTypeDescriptor = attributeBinding.getHibernateTypeDescriptor();
final SingularAttribute singularAttribute = SingularAttribute.class.cast( attributeBinding.getAttribute() );
if ( ! singularAttribute.isTypeResolved() && hibernateTypeDescriptor.getJavaTypeName() != null ) {
singularAttribute.resolveType( makeJavaType( hibernateTypeDescriptor.getJavaTypeName() ) );
}
for ( AttributeBinding subAttributeBinding : attributeBinding.attributeBindings() ) {
if ( SingularAttributeBinding.class.isInstance( subAttributeBinding ) ) {
processSingularAttributeTypeInformation(
SingularAttributeBinding.class.cast( subAttributeBinding )
);
}
else if ( AbstractPluralAttributeBinding.class.isInstance( subAttributeBinding ) ) {
processPluralAttributeTypeInformation(
AbstractPluralAttributeBinding.class.cast( subAttributeBinding )
);
}
else {
throw new AssertionFailure( "Unknown type of AttributeBinding: " + attributeBinding.getClass().getName() );
}
}
}
private void pushHibernateTypeInformationDown(
HibernateTypeDescriptor hibernateTypeDescriptor,
List<RelationalValueBinding> relationalValueBindings,
Type resolvedHibernateType) {
if ( resolvedHibernateType == null ) {
return;
}
if ( hibernateTypeDescriptor.getResolvedTypeMapping() == null ) {
hibernateTypeDescriptor.setResolvedTypeMapping( resolvedHibernateType );
}
// java type information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( hibernateTypeDescriptor.getJavaTypeName() == null ) {
hibernateTypeDescriptor.setJavaTypeName( resolvedHibernateType.getReturnedClass().getName() );
}
// todo : this can be made a lot smarter, but for now this will suffice. currently we only handle single value bindings
if ( relationalValueBindings.size() > 1 ) {
return;
}
final Value value = relationalValueBindings.get( 0 ).getValue();
pushHibernateTypeInformationDown( resolvedHibernateType, value );
}
public void pushHibernateTypeInformationDown(Type resolvedHibernateType, Value value) {
if ( value.getJdbcDataType() == null ) {
if ( AbstractValue.class.isInstance( value ) ) {
( (AbstractValue) value ).setJdbcDataType(
new JdbcDataType(
resolvedHibernateType.sqlTypes( metadata() )[0],
resolvedHibernateType.getName(),
resolvedHibernateType.getReturnedClass()
)
);
}
}
}
private void processPluralAttributeTypeInformation(PluralAttributeBinding attributeBinding) {
if ( attributeBinding.getHibernateTypeDescriptor().getResolvedTypeMapping() != null ) {
return;
}
Type resolvedType;
// do NOT look at java type...
//String typeName = determineTypeName( attributeBinding.getHibernateTypeDescriptor() );
String typeName = attributeBinding.getHibernateTypeDescriptor().getExplicitTypeName();
if ( typeName != null ) {
resolvedType =
metadata().getTypeResolver()
.getTypeFactory()
.customCollection(
typeName,
getTypeParameters( attributeBinding.getHibernateTypeDescriptor() ),
attributeBinding.getAttribute().getName(),
attributeBinding.getReferencedPropertyName(),
attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() ==
PluralAttributeElementNature.COMPOSITE
);
}
else {
resolvedType = determineHibernateTypeFromCollectionType( attributeBinding );
}
if ( resolvedType != null ) {
// todo : what exactly is getting pushed down here? and to what/where?
// pushHibernateTypeInformationDownIfNeeded(
// attributeBinding.getHibernateTypeDescriptor(),
// null,
// resolvedType
// );
}
bindCollectionElementTypeInformation( attributeBinding.getPluralAttributeElementBinding() );
}
private Type determineHibernateTypeFromCollectionType(PluralAttributeBinding attributeBinding) {
final TypeFactory typeFactory = metadata().getTypeResolver().getTypeFactory();
switch ( attributeBinding.getAttribute().getNature() ) {
case SET: {
return typeFactory.set(
attributeBinding.getAttribute().getName(),
attributeBinding.getReferencedPropertyName(),
attributeBinding.getPluralAttributeElementBinding().getPluralAttributeElementNature() == PluralAttributeElementNature.COMPOSITE
);
}
case BAG: {
return typeFactory.bag(
attributeBinding.getAttribute().getName(),
attributeBinding.getReferencedPropertyName(),
attributeBinding.getPluralAttributeElementBinding()
.getPluralAttributeElementNature() == PluralAttributeElementNature.COMPOSITE
);
}
default: {
throw new UnsupportedOperationException(
"Collection type not supported yet:" + attributeBinding.getAttribute().getNature()
);
}
}
}
private void bindCollectionElementTypeInformation(PluralAttributeElementBinding pluralAttributeElementBinding) {
switch ( pluralAttributeElementBinding.getPluralAttributeElementNature() ) {
case BASIC: {
bindBasicCollectionElementTypeInformation(
BasicPluralAttributeElementBinding.class.cast(
pluralAttributeElementBinding
)
);
break;
}
case COMPOSITE:
case ONE_TO_MANY:
case MANY_TO_MANY:
case MANY_TO_ANY: {
throw new UnsupportedOperationException( "Collection element nature not supported yet: " + pluralAttributeElementBinding
.getPluralAttributeElementNature() );
}
default: {
throw new AssertionFailure( "Unknown collection element nature : " + pluralAttributeElementBinding.getPluralAttributeElementNature() );
}
}
}
private void bindBasicCollectionElementTypeInformation(BasicPluralAttributeElementBinding basicCollectionElement) {
Type resolvedHibernateType = determineHibernateTypeFromDescriptor( basicCollectionElement.getHibernateTypeDescriptor() );
if ( resolvedHibernateType != null ) {
pushHibernateTypeInformationDown(
basicCollectionElement.getHibernateTypeDescriptor(),
basicCollectionElement.getRelationalValueBindings(),
resolvedHibernateType
);
}
}
private static class ReflectedCollectionJavaTypes {
private final Class<?> collectionType;
private final Class<?> collectionElementType;
private final Class<?> collectionIndexType;
private ReflectedCollectionJavaTypes(
Class<?> collectionType,
Class<?> collectionElementType,
Class<?> collectionIndexType) {
this.collectionType = collectionType;
this.collectionElementType = collectionElementType;
this.collectionIndexType = collectionIndexType;
}
}
/**
* @see HibernateTypeHelper#determineJavaType(PluralAttribute)
*/
private class PluralAttributeJavaTypeDeterminerDelegate implements BeanInfoHelper.BeanInfoDelegate {
private final Class<?> ownerClass;
private final String attributeName;
private ReflectedCollectionJavaTypes collectionJavaTypes;
private PluralAttributeJavaTypeDeterminerDelegate(Class<?> ownerClass, String attributeName) {
this.ownerClass = ownerClass;
this.attributeName = attributeName;
}
@Override
public void processBeanInfo(BeanInfo beanInfo) throws Exception {
Class<?> collectionType = null;
Class<?> elementJavaType = null;
Class<?> indexJavaType = null;
java.lang.reflect.Type collectionAttributeType = null;
if ( beanInfo.getPropertyDescriptors() == null || beanInfo.getPropertyDescriptors().length == 0 ) {
// we need to look for the field and look at it...
collectionAttributeType = ownerClass.getField( attributeName ).getGenericType();
}
else {
for ( PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors() ) {
if ( propertyDescriptor.getName().equals( attributeName ) ) {
if ( propertyDescriptor.getReadMethod() != null ) {
collectionType = propertyDescriptor.getReadMethod().getReturnType();
collectionAttributeType = propertyDescriptor.getReadMethod().getGenericReturnType();
}
else if ( propertyDescriptor.getWriteMethod() != null ) {
collectionType = propertyDescriptor.getWriteMethod().getParameterTypes()[0];
collectionAttributeType = propertyDescriptor.getWriteMethod().getGenericParameterTypes()[0];
}
}
}
}
if ( collectionAttributeType != null ) {
if ( ParameterizedType.class.isInstance( collectionAttributeType ) ) {
final java.lang.reflect.Type[] types = ( (ParameterizedType) collectionAttributeType ).getActualTypeArguments();
if ( types == null ) {
}
else if ( types.length == 1 ) {
elementJavaType = (Class<?>) types[0];
}
else if ( types.length == 2 ) {
// Map<K,V>
indexJavaType = (Class<?>) types[0];
elementJavaType = (Class<?>) types[1];
}
}
else {
}
}
collectionJavaTypes = new ReflectedCollectionJavaTypes( collectionType, elementJavaType, indexJavaType );
}
}
}

View File

@ -289,6 +289,7 @@ public void setBatchSize(int batchSize) {
@Override
public String getReferencedPropertyName() {
return referencedPropertyName;
}

View File

@ -86,4 +86,6 @@ public interface PluralAttributeBinding extends AttributeBinding, Fetchable {
java.util.Map getFilterMap();
String getOrderBy();
String getReferencedPropertyName();
}