From 9d404168aab4abcf354e2880a75345a03461cf1a Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Tue, 19 Feb 2013 11:44:42 -0800 Subject: [PATCH] HHH-7436 : Add support for many-to-many associations to new metamodel --- .../hibernate/metamodel/internal/Binder.java | 22 +++++++ .../internal/HibernateTypeHelper.java | 2 - ...oManyPluralAttributeElementSourceImpl.java | 16 +++-- ...oManyPluralAttributeElementSourceImpl.java | 7 ++- .../PluralAttributeSourceImpl.java | 63 ++++++++++++++++--- .../AbstractPluralAttributeSourceImpl.java | 30 ++++++++- .../PluralAttributeElementSourceResolver.java | 36 +++++++++++ .../spi/source/PluralAttributeSource.java | 4 +- .../EntityWithUnidirectionalOneToMany.java | 1 + 9 files changed, 164 insertions(+), 17 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeElementSourceResolver.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java index 3126a09fbc..319685ac72 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/Binder.java @@ -136,6 +136,7 @@ import org.hibernate.metamodel.spi.source.IdentifierSource; import org.hibernate.metamodel.spi.source.InLineViewSource; import org.hibernate.metamodel.spi.source.IndexedPluralAttributeSource; import org.hibernate.metamodel.spi.source.JoinedSubclassEntitySource; +import org.hibernate.metamodel.spi.source.PluralAttributeElementSourceResolver; import org.hibernate.metamodel.spi.source.PluralAttributeIndexSource; import org.hibernate.metamodel.spi.source.LocalBindingContext; import org.hibernate.metamodel.spi.source.ManyToManyPluralAttributeElementSource; @@ -1391,6 +1392,16 @@ public class Binder { final AttributeBindingContainer attributeBindingContainer, final PluralAttributeSource attributeSource) { final PluralAttributeSource.Nature nature = attributeSource.getNature(); + if ( attributeSource.getMappedBy() != null ) { + attributeSource.resolvePluralAttributeElementSource( + new PluralAttributeElementSourceResolver.PluralAttributeElementSourceResolutionContext() { + @Override + public AttributeSource resolveAttributeSource(String referencedEntityName, String mappedBy) { + return attributeSource( referencedEntityName, mappedBy ); + } + } + ); + } final PluralAttribute attribute = attributeBindingContainer.getAttributeContainer().locatePluralAttribute( attributeSource.getName() ); final AbstractPluralAttributeBinding attributeBinding; @@ -1890,6 +1901,13 @@ public class Binder { true ) ); + if ( elementSource.isUnique() ) { + for ( RelationalValueBinding relationalValueBinding : elementBinding.getRelationalValueBindings() ) { + if ( ! relationalValueBinding.isDerived() ) { + ( (Column) relationalValueBinding.getValue() ).setUnique( true ); + } + } + } if ( !elementBinding.getPluralAttributeBinding().getPluralAttributeKeyBinding().isInverse() && !elementBinding.hasDerivedValue() ) { locateOrCreateForeignKey( @@ -3040,6 +3058,10 @@ public class Binder { .createSingularAttribute( attributeSource.getName() ); } + private AttributeSource attributeSource(final String entityName, final String attributeName) { + return attributeSourcesByName.get( attributeSourcesByNameKey( entityName, attributeName ) ); + } + private static String attributeSourcesByNameKey( final String entityName, final String attributeName) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java index d929496007..5ef5cdfbc1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/HibernateTypeHelper.java @@ -33,8 +33,6 @@ import java.util.Properties; import org.hibernate.AssertionFailure; import org.hibernate.EntityMode; -import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; -import org.hibernate.boot.registry.classloading.spi.ClassLoadingException; import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ValueHolder; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/ManyToManyPluralAttributeElementSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/ManyToManyPluralAttributeElementSourceImpl.java index a5c3273285..8a8d7c82a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/ManyToManyPluralAttributeElementSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/ManyToManyPluralAttributeElementSourceImpl.java @@ -36,6 +36,7 @@ import org.hibernate.metamodel.internal.source.annotations.util.JPADotNames; import org.hibernate.metamodel.internal.source.annotations.util.JandexHelper; import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.metamodel.spi.relational.Value; +import org.hibernate.metamodel.spi.source.AttributeSource; import org.hibernate.metamodel.spi.source.FilterSource; import org.hibernate.metamodel.spi.source.ForeignKeyContributingSource; import org.hibernate.metamodel.spi.source.ManyToManyPluralAttributeElementSource; @@ -47,18 +48,24 @@ import org.jboss.jandex.AnnotationInstance; * @author Brett Meyer */ public class ManyToManyPluralAttributeElementSourceImpl implements ManyToManyPluralAttributeElementSource { - + + private final AttributeSource ownerAttributeSource; private final PluralAssociationAttribute associationAttribute; private final List relationalValueSources = new ArrayList(); private final Collection referencedColumnNames = new HashSet(); private final Iterable cascadeStyles; + private final boolean isUnique; public ManyToManyPluralAttributeElementSourceImpl( - PluralAssociationAttribute associationAttribute) { + AttributeSource ownerAttributeSource, + PluralAssociationAttribute associationAttribute, + boolean isUnique) { + this.ownerAttributeSource = ownerAttributeSource; this.associationAttribute = associationAttribute; - + this.isUnique = isUnique; + for ( Column column : associationAttribute.getInverseJoinColumnValues() ) { relationalValueSources.add( new ColumnSourceImpl( associationAttribute, null, column ) ); @@ -123,8 +130,7 @@ public class ManyToManyPluralAttributeElementSourceImpl implements ManyToManyPlu @Override public boolean isUnique() { - // TODO - return false; + return isUnique; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/OneToManyPluralAttributeElementSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/OneToManyPluralAttributeElementSourceImpl.java index 612fd277c7..34b1e59c5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/OneToManyPluralAttributeElementSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/OneToManyPluralAttributeElementSourceImpl.java @@ -27,15 +27,20 @@ import org.hibernate.AssertionFailure; import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.metamodel.internal.source.annotations.attribute.PluralAssociationAttribute; import org.hibernate.metamodel.internal.source.annotations.util.EnumConversionHelper; +import org.hibernate.metamodel.spi.source.AttributeSource; import org.hibernate.metamodel.spi.source.OneToManyPluralAttributeElementSource; /** * @author Hardy Ferentschik */ public class OneToManyPluralAttributeElementSourceImpl implements OneToManyPluralAttributeElementSource { + private final AttributeSource ownerAttributeSource; private final PluralAssociationAttribute associationAttribute; - public OneToManyPluralAttributeElementSourceImpl(PluralAssociationAttribute associationAttribute) { + public OneToManyPluralAttributeElementSourceImpl( + AttributeSource ownerAttributeSource, + PluralAssociationAttribute associationAttribute) { + this.ownerAttributeSource = ownerAttributeSource; this.associationAttribute = associationAttribute; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java index 2ef1159557..b08d0756ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/PluralAttributeSourceImpl.java @@ -39,6 +39,7 @@ import org.hibernate.metamodel.internal.source.annotations.util.HibernateDotName import org.hibernate.metamodel.internal.source.annotations.util.JandexHelper; import org.hibernate.metamodel.spi.binding.Caching; import org.hibernate.metamodel.spi.binding.CustomSQL; +import org.hibernate.metamodel.spi.source.AttributeSource; import org.hibernate.metamodel.spi.source.ExplicitHibernateTypeSource; import org.hibernate.metamodel.spi.source.FilterSource; import org.hibernate.metamodel.spi.source.MetaAttributeSource; @@ -48,6 +49,7 @@ import org.hibernate.metamodel.spi.source.PluralAttributeKeySource; import org.hibernate.metamodel.spi.source.PluralAttributeSource; import org.hibernate.metamodel.spi.source.Sortable; import org.hibernate.metamodel.spi.source.TableSpecificationSource; +import org.hibernate.metamodel.spi.source.ToOneAttributeSource; /** * @author Hardy Ferentschik @@ -55,23 +57,31 @@ import org.hibernate.metamodel.spi.source.TableSpecificationSource; public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderable, Sortable { private final PluralAssociationAttribute associationAttribute; + private final ConfiguredClass entityClass; private final Nature nature; private final ExplicitHibernateTypeSource typeSource; private final PluralAttributeKeySource keySource; - private final PluralAttributeElementSource elementSource; private final FilterSource[] filterSources; + + // If it is not the owner side (i.e., mappedBy != null), then the AttributeSource + // for the owner is required to determine elementSource. + private PluralAttributeElementSource elementSource; + public PluralAttributeSourceImpl( final PluralAssociationAttribute associationAttribute, final ConfiguredClass entityClass ) { this.associationAttribute = associationAttribute; + this.entityClass = entityClass; this.keySource = new PluralAttributeKeySourceImpl( associationAttribute ); this.typeSource = new ExplicitHibernateTypeSourceImpl( associationAttribute ); this.nature = associationAttribute.getPluralAttributeNature(); - this.elementSource = determineElementSource( associationAttribute, entityClass ); + if ( associationAttribute.getMappedBy() == null ) { + this.elementSource = determineOwnerElementSource( this, associationAttribute, entityClass ); + } this.filterSources = determineFilterSources(associationAttribute); } - private FilterSource[] determineFilterSources(PluralAssociationAttribute associationAttribute) { + private static FilterSource[] determineFilterSources(PluralAssociationAttribute associationAttribute) { AnnotationInstance filtersAnnotation = JandexHelper.getSingleAnnotation( associationAttribute.annotations(), HibernateDotNames.FILTERS @@ -108,6 +118,9 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab @Override public PluralAttributeElementSource getElementSource() { + if ( elementSource == null ) { + throw new IllegalStateException( "elementSource has not been initialized yet." ); + } return elementSource; } @@ -121,6 +134,23 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab return associationAttribute.getBatchSize(); } + public static boolean usesJoinTable(AttributeSource ownerAttributeSource) { + return ownerAttributeSource.isSingular() ? + ( (ToOneAttributeSource) ownerAttributeSource ).getContainingTableName() != null : + ( (PluralAttributeSource) ownerAttributeSource ).usesJoinTable(); + } + + public boolean usesJoinTable() { + if ( associationAttribute.getMappedBy() != null ) { + throw new IllegalStateException( "Cannot determine if a join table is used because plural attribute is not the owner." ); + } + // By default, a unidirectional one-to-many (i.e., with mappedBy == null) uses a join table, + // unless it has join columns defined. + return associationAttribute.getJoinTableAnnotation() != null || + ( associationAttribute.getJoinTableAnnotation() == null && + associationAttribute.getJoinColumnValues().size() == 0 ); + } + @Override public ValueHolder> getElementClassReference() { // needed for arrays @@ -133,14 +163,19 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab } } - private static PluralAttributeElementSource determineElementSource(PluralAssociationAttribute associationAttribute, ConfiguredClass entityClass) { + private static PluralAttributeElementSource determineOwnerElementSource( + AttributeSource ownerAttributeSource, + PluralAssociationAttribute associationAttribute, + ConfiguredClass entityClass) { switch ( associationAttribute.getNature() ) { case MANY_TO_MANY: - return new ManyToManyPluralAttributeElementSourceImpl( associationAttribute ); + return new ManyToManyPluralAttributeElementSourceImpl( ownerAttributeSource, associationAttribute, false ); case MANY_TO_ANY: return new ManyToAnyPluralAttributeElementSourceImpl( associationAttribute ); case ONE_TO_MANY: - return new OneToManyPluralAttributeElementSourceImpl( associationAttribute ); + return usesJoinTable( ownerAttributeSource ) ? + new ManyToManyPluralAttributeElementSourceImpl( ownerAttributeSource, associationAttribute, true ) : + new OneToManyPluralAttributeElementSourceImpl( ownerAttributeSource, associationAttribute ); case ELEMENT_COLLECTION_BASIC: return new BasicPluralAttributeElementSourceImpl( associationAttribute ); case ELEMENT_COLLECTION_EMBEDDABLE: { @@ -300,7 +335,21 @@ public class PluralAttributeSourceImpl implements PluralAttributeSource, Orderab return associationAttribute.getFetchStyle(); } - + @Override + public PluralAttributeElementSource resolvePluralAttributeElementSource( + PluralAttributeElementSourceResolutionContext context) { + if ( associationAttribute.getMappedBy() == null ) { + return elementSource; + } + else { + AttributeSource ownerAttributeSource = context.resolveAttributeSource( + associationAttribute.getReferencedEntityType(), + associationAttribute.getMappedBy() + ); + elementSource = determineOwnerElementSource( ownerAttributeSource, associationAttribute, entityClass ); + return elementSource; + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java index 40524d2f4e..9ecc38d908 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractPluralAttributeSourceImpl.java @@ -26,12 +26,12 @@ package org.hibernate.metamodel.internal.source.hbm; import java.util.Collections; import java.util.Map; +import org.hibernate.AssertionFailure; import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.ValueHolder; -import org.hibernate.jaxb.spi.hbm.JaxbClassElement; import org.hibernate.jaxb.spi.hbm.JaxbFilterElement; import org.hibernate.jaxb.spi.hbm.PluralAttributeElement; import org.hibernate.metamodel.spi.binding.Caching; @@ -163,6 +163,34 @@ public abstract class AbstractPluralAttributeSourceImpl } } + @Override + public PluralAttributeElementSource resolvePluralAttributeElementSource(PluralAttributeElementSourceResolutionContext context) { + return elementSource; + } + + @Override + public boolean usesJoinTable() { + switch ( elementSource.getNature() ) { + case BASIC: + case AGGREGATE: + case ONE_TO_MANY: + return false; + case MANY_TO_MANY: + return true; + case MANY_TO_ANY: + throw new NotYetImplementedException( + String.format( "%s is not implemented yet.", elementSource.getNature() ) + ); + default: + throw new AssertionFailure( + String.format( + "Unexpected plural attribute element source nature: %s", + elementSource.getNature() + ) + ); + } + } + public PluralAttributeElement getPluralAttributeElement() { return pluralAttributeElement; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeElementSourceResolver.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeElementSourceResolver.java new file mode 100644 index 0000000000..184ffa59c0 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeElementSourceResolver.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2013, 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.spi.source; + +/** + * @author Gail Badner + */ +public interface PluralAttributeElementSourceResolver { + + PluralAttributeElementSource resolvePluralAttributeElementSource(PluralAttributeElementSourceResolutionContext context); + + public static interface PluralAttributeElementSourceResolutionContext { + public AttributeSource resolveAttributeSource(String referencedEntityName, String mappedBy); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeSource.java index 8a85945ec5..085ca831a5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/PluralAttributeSource.java @@ -36,7 +36,7 @@ import org.hibernate.metamodel.spi.binding.CustomSQL; * @author Steve Ebersole */ public interface PluralAttributeSource - extends AttributeSource, FetchableAttributeSource { + extends AttributeSource, FetchableAttributeSource, PluralAttributeElementSourceResolver { public Nature getNature(); public PluralAttributeKeySource getKeySource(); @@ -77,6 +77,8 @@ public interface PluralAttributeSource public int getBatchSize(); + public boolean usesJoinTable(); + /** * Describes the nature of the collection itself as declared by the metadata. * diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/onetomany/EntityWithUnidirectionalOneToMany.java b/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/onetomany/EntityWithUnidirectionalOneToMany.java index d9fec93ee1..0ebfdc1016 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/onetomany/EntityWithUnidirectionalOneToMany.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/onetomany/EntityWithUnidirectionalOneToMany.java @@ -49,6 +49,7 @@ public class EntityWithUnidirectionalOneToMany { } @OneToMany + @JoinColumn public Collection getTheBag() { return theBag; }