From 9a462a7b31bf88d26c4b822cda5664c3e3dea5c5 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Fri, 14 Dec 2012 22:58:37 +0800 Subject: [PATCH] HHH-7380 - union subclass support --- .../main/java/org/hibernate/TruthValue.java | 12 ++- .../hibernate/metamodel/internal/Binder.java | 60 +++++++++------ .../metamodel/internal/MetadataImpl.java | 2 +- .../hbm/AbstractToOneAttributeSourceImpl.java | 10 ++- .../metamodel/spi/binding/EntityBinding.java | 11 +++ .../AbstractTableSpecification.java | 12 ++- .../spi/relational/DenormalizedTable.java | 77 +++++++++++++++++-- .../spi/source/MetaAttributeContext.java | 2 +- .../entity/UnionSubclassEntityPersister.java | 2 +- .../inheritance/union/SubclassTest.java | 2 - .../polymorphism/PolymorphismTest.java | 2 - .../test/legacy/CustomPersister.java | 12 +++ .../org/hibernate/test/legacy/IJ2Test.java | 2 +- .../unionsubclass2/UnionSubclassTest.java | 2 +- .../org/hibernate/test/legacy/IJ2.hbm.xml | 4 +- .../inheritence/union/Person.hbm.xml | 8 +- 16 files changed, 169 insertions(+), 51 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/TruthValue.java b/hibernate-core/src/main/java/org/hibernate/TruthValue.java index 36ad60fbcc..cdb8946af7 100644 --- a/hibernate-core/src/main/java/org/hibernate/TruthValue.java +++ b/hibernate-core/src/main/java/org/hibernate/TruthValue.java @@ -34,5 +34,15 @@ package org.hibernate; public enum TruthValue { TRUE, FALSE, - UNKNOWN + UNKNOWN; + + public static boolean toBoolean(TruthValue value, boolean defaultValue) { + if ( value == TruthValue.TRUE ) { + return true; + } + if ( value == TruthValue.FALSE ) { + return false; + } + return defaultValue; + } } 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 85b784c380..28b41e0188 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 @@ -244,16 +244,6 @@ public class Binder { return unsavedValue; } - private static boolean toBoolean( final TruthValue truthValue, final boolean truthValueDefault ) { - if ( truthValue == TruthValue.TRUE ) { - return true; - } - if ( truthValue == TruthValue.FALSE ) { - return false; - } - return truthValueDefault; - } - private final MetadataImplementor metadata; private final IdentifierGeneratorFactory identifierGeneratorFactory; private final ObjectNameNormalizer nameNormalizer; @@ -2030,8 +2020,10 @@ public class Binder { // table per class and non-abstract entity else { Table includedTable = null; - if(superEntityBinding!=null && inheritanceType == InheritanceType.TABLE_PER_CLASS && Table.class.isInstance( superEntityBinding.getPrimaryTable() )){ - includedTable = Table.class.cast( superEntityBinding.getPrimaryTable()); + if ( superEntityBinding != null + && inheritanceType == InheritanceType.TABLE_PER_CLASS + && Table.class.isInstance( superEntityBinding.getPrimaryTable() ) ) { + includedTable = Table.class.cast( superEntityBinding.getPrimaryTable() ); } table = createTable( entitySource.getPrimaryTable(), new DefaultNamingStrategy() { @@ -2289,11 +2281,11 @@ public class Binder { if ( valueSource.getNature() == RelationalValueSource.Nature.COLUMN ) { final ColumnSource columnSource = ( ColumnSource ) valueSource; final boolean isIncludedInInsert = - toBoolean( + TruthValue.toBoolean( columnSource.isIncludedInInsert(), valueSourceContainer.areValuesIncludedInInsertByDefault() ); final boolean isIncludedInUpdate = - toBoolean( + TruthValue.toBoolean( columnSource.isIncludedInUpdate(), valueSourceContainer.areValuesIncludedInUpdateByDefault() ); Column column = createColumn( @@ -2452,7 +2444,7 @@ public class Binder { else { // if the column is already non-nullable, leave it alone if ( column.isNullable() ) { - column.setNullable( toBoolean( columnSource.isNullable(), isNullableByDefault ) ); + column.setNullable( TruthValue.toBoolean( columnSource.isNullable(), isNullableByDefault ) ); } } column.setDefaultValue( columnSource.getDefaultValue() ); @@ -2598,8 +2590,9 @@ public class Binder { final LocalBindingContext bindingContext = bindingContexts.peek(); final MappingDefaults mappingDefaults = bindingContext.getMappingDefaults(); - final String explicitCatalogName = tableSpecSource == null ? null : tableSpecSource.getExplicitCatalogName(); - final String explicitSchemaName = tableSpecSource == null ? null : tableSpecSource.getExplicitSchemaName(); + final boolean isTableSourceNull = tableSpecSource == null; + final String explicitCatalogName = isTableSourceNull ? null : tableSpecSource.getExplicitCatalogName(); + final String explicitSchemaName = isTableSourceNull ? null : tableSpecSource.getExplicitSchemaName(); final Schema.Name schemaName = new Schema.Name( createIdentifier( explicitCatalogName, mappingDefaults.getCatalogName() ), @@ -2608,7 +2601,7 @@ public class Binder { final Schema schema = metadata.getDatabase().locateSchema( schemaName ); TableSpecification tableSpec = null; - if ( tableSpecSource == null ) { + if ( isTableSourceNull ) { if ( defaultNamingStrategy == null ) { throw bindingContext().makeMappingException( "An explicit name must be specified for the table" ); } @@ -2723,7 +2716,7 @@ public class Binder { return values; } final AttributeBinding referencedAttributeBinding = - entityBinding.locateAttributeBinding( attributeName ); + entityBinding.locateAttributeBinding( attributeName, true ); if ( referencedAttributeBinding == null ) { throw bindingContext().makeMappingException( String.format( @@ -2831,9 +2824,12 @@ public class Binder { } final String explicitName = resolutionDelegate.getReferencedAttributeName(); - final AttributeBinding referencedAttributeBinding = explicitName != null - ? referencedEntityBinding.locateAttributeBinding( explicitName ) - :referencedEntityBinding.locateAttributeBinding( resolutionDelegate.getJoinColumns( resolutionContext ) ); + final AttributeBinding referencedAttributeBinding = locateAttributeBinding( + resolutionDelegate, + resolutionContext, + referencedEntityBinding, + explicitName + ); if ( !referencedAttributeBinding.getAttribute().isSingular() ) { throw bindingContext().makeMappingException( String.format( @@ -2843,6 +2839,26 @@ public class Binder { return (SingularAttributeBinding) referencedAttributeBinding; } + private AttributeBinding locateAttributeBinding(JoinColumnResolutionDelegate resolutionDelegate, + JoinColumnResolutionContext resolutionContext, + EntityBinding referencedEntityBinding, + String explicitName) { + AttributeBinding attributeBinding = explicitName != null + ? referencedEntityBinding.locateAttributeBinding( explicitName ) + : referencedEntityBinding.locateAttributeBinding( resolutionDelegate.getJoinColumns( resolutionContext ) ); + if ( attributeBinding == null && referencedEntityBinding.getSuperEntityBinding() != null ) { + return locateAttributeBinding( + resolutionDelegate, + resolutionContext, + referencedEntityBinding.getSuperEntityBinding(), + explicitName + ); + } else { + return attributeBinding; + } + } + + private EntityBinding findOrBindEntityBinding(ValueHolder< Class< ? >> entityJavaTypeValue, String explicitEntityName) { final String referencedEntityName = bindingContext().qualifyClassName( explicitEntityName != null diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java index 8f54089063..0d04891046 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataImpl.java @@ -707,7 +707,7 @@ public class MetadataImpl implements MetadataImplementor, Serializable { throw new MappingException( "Entity binding not known: " + entityName ); } // TODO: should this call EntityBinding.getReferencedAttributeBindingString), which does not exist yet? - AttributeBinding attributeBinding = entityBinding.locateAttributeBinding( propertyName ); + AttributeBinding attributeBinding = entityBinding.locateAttributeBinding( propertyName, true ); if ( attributeBinding == null ) { throw new MappingException( "unknown property: " + entityName + '.' + propertyName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractToOneAttributeSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractToOneAttributeSourceImpl.java index 8c95b2d3cd..91928691fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractToOneAttributeSourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractToOneAttributeSourceImpl.java @@ -187,10 +187,16 @@ public abstract class AbstractToOneAttributeSourceImpl extends AbstractHbmSource public JoinColumnResolutionDelegate getForeignKeyTargetColumnResolutionDelegate() { return propertyRef == null ? null - : new JoinColumnResolutionDelegateImpl(); + : new JoinColumnResolutionDelegateImpl( propertyRef ); } - public class JoinColumnResolutionDelegateImpl implements JoinColumnResolutionDelegate { + public static class JoinColumnResolutionDelegateImpl implements JoinColumnResolutionDelegate { + private final String propertyRef; + + public JoinColumnResolutionDelegateImpl(String propertyRef) { + this.propertyRef = propertyRef; + } + @Override public String getReferencedAttributeName() { return propertyRef; diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityBinding.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityBinding.java index 00505d5c3a..c2abcc521f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityBinding.java @@ -264,6 +264,17 @@ public class EntityBinding extends AbstractAttributeBindingContainer { return secondaryTable.getSecondaryTableReference(); } + public AttributeBinding locateAttributeBinding(String name, boolean searchParent) { + AttributeBinding attributeBinding = locateAttributeBinding( name ); + if ( attributeBinding == null && searchParent && getSuperEntityBinding() != null ) { + return getSuperEntityBinding().locateAttributeBinding( name, searchParent ); + } + else { + return attributeBinding; + } + } + + public String getPrimaryTableName() { return primaryTableName; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java index 2df6e865f7..b3314201a1 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java @@ -173,15 +173,19 @@ public abstract class AbstractTableSpecification implements TableSpecification { public PrimaryKey getPrimaryKey() { return primaryKey; } - + @Override public int generateColumnListId(Iterable columns) { int result = getLogicalName().hashCode(); for ( Column column : columns ) { - if ( !this.equals( column.getTable() ) ) { - throw new IllegalArgumentException( "All columns must be from this table." ); - } + sameTableCheck( column ); result = 31 * result + column.getColumnName().hashCode(); } return result; } + + protected void sameTableCheck(Column column) { + if ( !this.equals( column.getTable() ) ) { + throw new IllegalArgumentException( "All columns must be from this table." ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/DenormalizedTable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/DenormalizedTable.java index 7a3b5d4a81..8da10dc96e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/DenormalizedTable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/DenormalizedTable.java @@ -1,10 +1,35 @@ +/* + * 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.spi.relational; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.hibernate.internal.util.collections.JoinedIterable; @@ -46,15 +71,44 @@ public class DenormalizedTable extends Table { DerivedValue value = includedTable.locateDerivedValue( fragment ); return value != null ? value : super.locateDerivedValue( fragment ); } - + //todo other constraints other than fk + //we have to copy all FKs defined in the parent table to this sub table, can this be doing only once? like using ValueHolder? @Override public Iterable getForeignKeys() { - return new JoinedIterable( - Arrays.asList( - includedTable.getForeignKeys(), - super.getForeignKeys() - ) - ); + copyFKsFromParentTable(); + return super.getForeignKeys(); + } + private Set alreadyCopiedNonNameParentFK = new HashSet( ); + private void copyFKsFromParentTable() { + Iterable fksInSuperTable = includedTable.getForeignKeys(); + final String fkNamePostfix = Integer.toHexString( getTableName().hashCode() ); + for ( ForeignKey fk : fksInSuperTable ) { + + String name = fk.getName(); + if ( name == null ) { + if(!alreadyCopiedNonNameParentFK.contains( fk )){ + copyFK( fk, name ); + alreadyCopiedNonNameParentFK.add( fk ); + } + } + else { + String fkName = name + fkNamePostfix; + ForeignKey copiedFK = super.locateForeignKey( fkName ); + if ( copiedFK == null ) { + copyFK( fk, fkName ); + } + } + } + } + + private void copyFK(ForeignKey fk, String fkName) { + ForeignKey copiedFK = createForeignKey( fk.getTargetTable(), fkName ); + copiedFK.setDeleteRule( fk.getDeleteRule() ); + copiedFK.setUpdateRule( fk.getUpdateRule() ); + Iterable columnMappings = fk.getColumnMappings(); + for ( ForeignKey.ColumnMapping cm : columnMappings ) { + copiedFK.addColumnMapping( cm.getSourceColumn(), cm.getTargetColumn() ); + } } @Override @@ -73,4 +127,13 @@ public class DenormalizedTable extends Table { public PrimaryKey getPrimaryKey() { return includedTable.getPrimaryKey(); } + + @Override + protected void sameTableCheck(Column column) { + try{ + super.sameTableCheck( column ); + } catch ( IllegalArgumentException e ){ + includedTable.sameTableCheck( column ); + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/MetaAttributeContext.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/MetaAttributeContext.java index 9f6e0f796f..f9488cffe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/MetaAttributeContext.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/MetaAttributeContext.java @@ -67,7 +67,7 @@ public class MetaAttributeContext { public MetaAttribute getMetaAttribute(String key) { MetaAttribute value = getLocalMetaAttribute( key ); - if ( value == null ) { + if ( value == null && parentContext != null ) { // recursive call value = parentContext.getMetaAttribute( key ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index db94330736..197f9df24c 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -608,7 +608,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { final StringBuilder buf = new StringBuilder() .append("( "); - visitSubEntityBindings( entityBinding, new Callback() { + visitEntityHierarchy( entityBinding, new Callback() { @Override public void execute(EntityBinding eb) { TableSpecification table = eb.getPrimaryTable(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/inheritance/union/SubclassTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/inheritance/union/SubclassTest.java index 07414d5148..72f430a15f 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/inheritance/union/SubclassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/inheritance/union/SubclassTest.java @@ -29,7 +29,6 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.Transaction; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; @@ -42,7 +41,6 @@ import static org.junit.Assert.fail; /** * @author Emmanuel Bernard */ -@FailureExpectedWithNewMetamodel public class SubclassTest extends BaseCoreFunctionalTestCase { @Test public void testDefault() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/polymorphism/PolymorphismTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/polymorphism/PolymorphismTest.java index 1e3295fb34..9e0d7c7756 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/polymorphism/PolymorphismTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/polymorphism/PolymorphismTest.java @@ -27,7 +27,6 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.Transaction; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; @@ -35,7 +34,6 @@ import static org.junit.Assert.assertEquals; /** * @author Emmanuel Bernard */ -@FailureExpectedWithNewMetamodel public class PolymorphismTest extends BaseCoreFunctionalTestCase { @Test public void testPolymorphism() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 3289c7a75d..1f67a14ca6 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -31,6 +31,7 @@ import org.hibernate.internal.StaticFilterAliasGenerator; import org.hibernate.internal.util.compare.EqualsHelper; import org.hibernate.mapping.PersistentClass; import org.hibernate.metadata.ClassMetadata; +import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityTuplizer; @@ -55,6 +56,17 @@ public class CustomPersister implements EntityPersister { this.factory = factory; } + + public CustomPersister( + final EntityBinding entityBinding, + final EntityRegionAccessStrategy cacheAccessStrategy, + final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy, + final SessionFactoryImplementor factory, + final Mapping mapping) throws HibernateException { + this.factory = factory; + } + + public boolean hasLazyProperties() { return false; } diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/IJ2Test.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/IJ2Test.java index 1904b7e67f..1332b5f07e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/IJ2Test.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/IJ2Test.java @@ -35,7 +35,7 @@ import static org.junit.Assert.assertTrue; /** * @author Gavin King */ -@FailureExpectedWithNewMetamodel +//@FailureExpectedWithNewMetamodel public class IJ2Test extends LegacyTestCase { @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java b/hibernate-core/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java index a4e9121624..ce6fa87633 100755 --- a/hibernate-core/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/unionsubclass2/UnionSubclassTest.java @@ -46,7 +46,6 @@ import static org.junit.Assert.assertTrue; * @author Gavin King */ @SuppressWarnings( {"UnnecessaryBoxing"}) -@FailureExpectedWithNewMetamodel public class UnionSubclassTest extends BaseCoreFunctionalTestCase { @Override protected String[] getMappings() { @@ -54,6 +53,7 @@ public class UnionSubclassTest extends BaseCoreFunctionalTestCase { } @Test + @FailureExpectedWithNewMetamodel public void testUnionSubclass() { Session s = openSession(); Transaction t = s.beginTransaction(); diff --git a/hibernate-core/src/test/resources/org/hibernate/test/legacy/IJ2.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/test/legacy/IJ2.hbm.xml index 17067e433c..e2c421cd1f 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/legacy/IJ2.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/test/legacy/IJ2.hbm.xml @@ -8,7 +8,7 @@ - + @@ -20,7 +20,7 @@ - + diff --git a/hibernate-core/src/test/resources/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml index 2df1413f32..0548a466e8 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/test/propertyref/inheritence/union/Person.hbm.xml @@ -5,19 +5,19 @@ - + - + - + - +