From 0671761c1483d170afcf7e0448fb547c6c013658 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Sat, 12 Jan 2013 07:09:58 +0800 Subject: [PATCH] HHH-7736 join subclass support --- .../ddl/SchemaModificationHelper.java | 4 +- .../engine/spi/LoadQueryInfluencers.java | 2 +- .../util/collections/ArrayHelper.java | 22 +- .../internal/builder/AttributeBuilder.java | 2 +- .../hibernate/metamodel/internal/Binder.java | 6 +- .../internal/source/hbm/BindHelper.java | 18 +- .../hbm/PrimaryKeyJoinColumnSourceImpl.java | 53 ++ .../source/hbm/SubclassEntitySourceImpl.java | 54 +- .../AbstractAttributeBindingContainer.java | 2 +- .../metamodel/spi/binding/EntityBinding.java | 396 ++++++----- .../spi/binding/EntityIdentifier.java | 8 + .../spi/relational/DenormalizedTable.java | 3 - .../AbstractCollectionPersister.java | 7 +- .../entity/AbstractEntityPersister.java | 110 +-- .../entity/JoinedSubclassEntityPersister.java | 667 ++++++++++++++---- .../entity/SingleTableEntityPersister.java | 49 +- .../entity/UnionSubclassEntityPersister.java | 74 +- .../tuple/entity/EntityMetamodel.java | 6 +- .../entity/InheritanceBindingTest.java | 103 +-- .../test/annotations/various/IndexTest.java | 2 - .../xml/ejb3/OrmVersion1SupportedTest.java | 2 +- .../xml/hbm/HbmWithIdentityTest.java | 2 - .../OrderCollectionOfJoinedHierarchyTest.java | 2 - .../test/legacy/SQLFunctionsTest.java | 3 +- .../inheritance/InheritedNaturalIdTest.java | 2 - .../test/queryplan/GetHqlQueryPlanTest.java | 4 +- .../org/hibernate/test/legacy/Blobber.hbm.xml | 2 +- .../internal/jpa/CallbackProcessorImpl.java | 69 +- .../internal/jpa/CallbackRegistryImpl.java | 13 +- 29 files changed, 1111 insertions(+), 576 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/PrimaryKeyJoinColumnSourceImpl.java diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ddl/SchemaModificationHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ddl/SchemaModificationHelper.java index c31a2fcb6c..35f8c6dc1c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ddl/SchemaModificationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/beanvalidation/ddl/SchemaModificationHelper.java @@ -56,7 +56,7 @@ public class SchemaModificationHelper { } public static Column getSingleColumn(AttributeBinding attributeBinding) { - if ( !( attributeBinding instanceof SingularAttributeBinding ) ) { + if ( !( attributeBinding.getAttribute().isSingular() ) ) { // TODO verify that's correct (HF) return null; } @@ -65,7 +65,7 @@ public class SchemaModificationHelper { RelationalValueBinding valueBinding = basicAttributeBinding.getRelationalValueBindings().get( 0 ); Value value = valueBinding.getValue(); - if ( !( value instanceof Column ) ) { + if ( valueBinding.isDerived() ) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java index 6be01440da..ac5d9bb213 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java @@ -170,7 +170,7 @@ public class LoadQueryInfluencers implements Serializable { return enabledFetchProfileNames != null && !enabledFetchProfileNames.isEmpty(); } - public Set getEnabledFetchProfileNames() { + public Set getEnabledFetchProfileNames() { return enabledFetchProfileNames; } diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java index f3c96eb6b8..9c036577e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/collections/ArrayHelper.java @@ -163,6 +163,21 @@ public final class ArrayHelper { return list; } + public static T[] reverse(T[] objects) { + return reverse( objects, objects.length ); + } + + public static T[] reverse(T[] objects, int n) { + final int size = objects.length; + final T[] temp = (T[]) Array.newInstance( objects.getClass().getComponentType(), size ); + + for ( int i = 0; i < n; i++ ) { + temp[i] = objects[n - i - 1]; + } + System.arraycopy( objects, n, temp, n, size - n ); + return temp; + } + public static String[] join(String[] x, String[] y) { String[] result = new String[ x.length + y.length ]; System.arraycopy( x, 0, result, 0, x.length ); @@ -181,7 +196,12 @@ public final class ArrayHelper { } return result; } - + public static boolean[] join(boolean[] x, boolean[] y) { + boolean[] result = new boolean[ x.length + y.length ]; + System.arraycopy( x, 0, result, 0, x.length ); + System.arraycopy( y, 0, result, x.length, y.length ); + return result; + } public static int[] join(int[] x, int[] y) { int[] result = new int[ x.length + y.length ]; System.arraycopy( x, 0, result, 0, x.length ); diff --git a/hibernate-core/src/main/java/org/hibernate/jpa/metamodel/internal/builder/AttributeBuilder.java b/hibernate-core/src/main/java/org/hibernate/jpa/metamodel/internal/builder/AttributeBuilder.java index ede442416c..acfbfa76fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/jpa/metamodel/internal/builder/AttributeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/jpa/metamodel/internal/builder/AttributeBuilder.java @@ -457,7 +457,7 @@ public class AttributeBuilder { return null; } - if ( SingularAttributeBinding.class.isInstance( attributeBinding ) ) { + if ( attributeBinding.getAttribute().isSingular() ) { final SingularAttributeBinding singularAttributeBinding = (SingularAttributeBinding) attributeBinding; final PersistentAttributeType jpaAttributeType; 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 5fe058ff18..47a26d73e2 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 @@ -531,7 +531,7 @@ public class Binder { // ensure version is non-nullable for ( RelationalValueBinding valueBinding : version.getVersioningAttributeBinding() .getRelationalValueBindings() ) { - if ( valueBinding.getValue() instanceof Column ) { + if ( !valueBinding.isDerived() ) { ( (Column) valueBinding.getValue() ).setNullable( false ); } } @@ -2283,7 +2283,7 @@ public class Binder { primaryKey.addColumn( foreignKeyColumn ); } for ( final RelationalValueBinding elementValueBinding : elementBinding.getRelationalValueBindings() ) { - if ( elementValueBinding.getValue() instanceof Column && !elementValueBinding.isNullable() ) { + if ( !elementValueBinding.isDerived() && !elementValueBinding.isNullable() ) { primaryKey.addColumn( (Column) elementValueBinding.getValue() ); } } @@ -3023,7 +3023,7 @@ public class Binder { primaryKey.addColumn( foreignKeyColumn ); } for ( RelationalValueBinding relationalValueBinding : indexBinding.getRelationalValueBindings() ) { - if ( relationalValueBinding.getValue() instanceof Column ) { + if ( !relationalValueBinding.isDerived() ) { primaryKey.addColumn( (Column) relationalValueBinding.getValue() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/BindHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/BindHelper.java index a5d071622c..495067db56 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/BindHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/BindHelper.java @@ -367,16 +367,16 @@ public class BindHelper { private static AttributeBinding getRecursiveAttributeBinding( EntityBinding entityBinding, String propertyPath) { - Iterable attributeBindings - = entityBinding.getAttributeBindingClosure(); - StringTokenizer st = new StringTokenizer( propertyPath, "." ); +// Iterable attributeBindings +// = entityBinding.getAttributeBindingClosure(); +// StringTokenizer st = new StringTokenizer( propertyPath, "." ); AttributeBinding attributeBinding = null; - while ( st.hasMoreElements() ) { - String element = st.nextToken(); - for ( AttributeBinding binding : attributeBindings ) { - - } - } +// while ( st.hasMoreElements() ) { +// String element = st.nextToken(); +// for ( AttributeBinding binding : attributeBindings ) { +// +// } +// } return attributeBinding; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/PrimaryKeyJoinColumnSourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/PrimaryKeyJoinColumnSourceImpl.java new file mode 100644 index 0000000000..d90d359957 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/PrimaryKeyJoinColumnSourceImpl.java @@ -0,0 +1,53 @@ +/* + * 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.internal.source.hbm; + +import org.hibernate.metamodel.spi.source.ColumnSource; +import org.hibernate.metamodel.spi.source.PrimaryKeyJoinColumnSource; + +/** + * @author Strong Liu + */ +public class PrimaryKeyJoinColumnSourceImpl implements PrimaryKeyJoinColumnSource { + private final ColumnSource columnSource; + + public PrimaryKeyJoinColumnSourceImpl(ColumnSource relationalValueSource) { + this.columnSource = relationalValueSource; + } + + @Override + public String getColumnName() { + return columnSource.getName(); + } + + @Override + public String getReferencedColumnName() { + return null; + } + + @Override + public String getColumnDefinition() { + return columnSource.getSqlType(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/SubclassEntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/SubclassEntitySourceImpl.java index 9552b058e3..84387a27f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/SubclassEntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/SubclassEntitySourceImpl.java @@ -23,14 +23,19 @@ */ package org.hibernate.metamodel.internal.source.hbm; +import java.util.ArrayList; import java.util.List; import org.hibernate.jaxb.spi.hbm.EntityElement; +import org.hibernate.jaxb.spi.hbm.JaxbColumnElement; import org.hibernate.jaxb.spi.hbm.JaxbJoinedSubclassElement; +import org.hibernate.jaxb.spi.hbm.JaxbKeyElement; import org.hibernate.jaxb.spi.hbm.JaxbSubclassElement; import org.hibernate.jaxb.spi.hbm.TableInformationSource; +import org.hibernate.metamodel.spi.source.ColumnSource; import org.hibernate.metamodel.spi.source.EntitySource; import org.hibernate.metamodel.spi.source.PrimaryKeyJoinColumnSource; +import org.hibernate.metamodel.spi.source.RelationalValueSource; import org.hibernate.metamodel.spi.source.SubclassEntitySource; import org.hibernate.metamodel.spi.source.TableSpecificationSource; @@ -40,7 +45,9 @@ import org.hibernate.metamodel.spi.source.TableSpecificationSource; public class SubclassEntitySourceImpl extends AbstractEntitySourceImpl implements SubclassEntitySource { private final EntitySource container; private final TableSpecificationSource primaryTable; - + private final boolean isJoinedSubclass; + private final JaxbKeyElement key; + private final List primaryKeyJoinColumnSources; protected SubclassEntitySourceImpl( MappingDocument sourceMappingDocument, EntityElement entityElement, @@ -50,6 +57,45 @@ public class SubclassEntitySourceImpl extends AbstractEntitySourceImpl implement this.primaryTable = TableInformationSource.class.isInstance( entityElement ) ? Helper.createTableSource( sourceMappingDocument(), (TableInformationSource) entityElement, this ) : null; + this.isJoinedSubclass = JaxbJoinedSubclassElement.class.isInstance( entityElement ); + this.key = isJoinedSubclass? ( (JaxbJoinedSubclassElement) entityElement() ).getKey() : null; + if ( isJoinedSubclass ) { + List valueSources = Helper.buildValueSources( + sourceMappingDocument(), + new Helper.ValueSourcesAdapter() { + @Override + public boolean isIncludedInInsertByDefault() { + return true; + } + + @Override + public boolean isIncludedInUpdateByDefault() { + return Helper.getValue( key.isUpdate(), true ); + } + + @Override + public String getColumnAttribute() { + return key.getColumnAttribute(); + } + + @Override + public List getColumn() { + return key.getColumn(); + } + + @Override + public boolean isForceNotNull() { + return Helper.getValue( key.isNotNull(), false ); + } + } + ); + this.primaryKeyJoinColumnSources = new ArrayList( valueSources.size() ); + for(final RelationalValueSource valueSource : valueSources){ + primaryKeyJoinColumnSources.add( new PrimaryKeyJoinColumnSourceImpl( ColumnSource.class.cast( valueSource ) ) ); + } + } else { + this.primaryKeyJoinColumnSources = null; + } afterInstantiation(); } @@ -73,14 +119,14 @@ public class SubclassEntitySourceImpl extends AbstractEntitySourceImpl implement @Override public String getJoinedForeignKeyName() { - if ( JaxbJoinedSubclassElement.class.isInstance( entityElement() ) ) { - return ( (JaxbJoinedSubclassElement) entityElement() ).getKey().getForeignKey(); + if ( isJoinedSubclass ) { + return key.getForeignKey(); } return null; } @Override public List getPrimaryKeyJoinColumnSources() { - return null; + return primaryKeyJoinColumnSources; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractAttributeBindingContainer.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractAttributeBindingContainer.java index c952dc3a71..d743dbb56c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractAttributeBindingContainer.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractAttributeBindingContainer.java @@ -49,7 +49,7 @@ public abstract class AbstractAttributeBindingContainer implements AttributeBind @Override public AttributeBinding locateAttributeBinding(List values) { for ( AttributeBinding attributeBinding : attributeBindingMapInternal().values() ) { - if ( !SingularAttributeBinding.class.isInstance( attributeBinding ) ) { + if ( !attributeBinding.getAttribute().isSingular() ) { continue; } SingularAttributeBinding basicAttributeBinding = (SingularAttributeBinding) attributeBinding; 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 2d0dfe05de..d029bd82aa 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 @@ -24,18 +24,16 @@ package org.hibernate.metamodel.spi.binding; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; import org.hibernate.AssertionFailure; import org.hibernate.EntityMode; import org.hibernate.MappingException; -import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.internal.FilterConfiguration; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.ValueHolder; @@ -158,7 +156,7 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements } public boolean isPolymorphic() { - return superEntityBinding != null || + return !isRoot() || hierarchyDetails.getEntityDiscriminator() != null || !subEntityBindings.isEmpty(); } @@ -167,71 +165,7 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements return !subEntityBindings.isEmpty(); } - public int getSubEntityBindingClosureSpan() { - int n = subEntityBindings.size(); - for ( EntityBinding subEntityBinding : subEntityBindings ) { - n += subEntityBinding.getSubEntityBindingClosureSpan(); - } - return n; - } - /* used for testing */ - public Iterable getDirectSubEntityBindings() { - return subEntityBindings; - } - - /** - * Returns sub-EntityBinding objects in a special 'order', most derived subclasses - * first. Specifically, the sub-entity bindings follow a depth-first, - * post-order traversal - * - * Note that the returned value excludes this entity binding. - * - * @return sub-entity bindings ordered by those entity bindings that are most derived. - */ - public Iterable getPostOrderSubEntityBindingClosure() { - // TODO: why this order? - List> subclassIterables = new ArrayList>( subEntityBindings.size() + 1 ); - for ( EntityBinding subEntityBinding : subEntityBindings ) { - Iterable subSubEntityBindings = subEntityBinding.getPostOrderSubEntityBindingClosure(); - if ( subSubEntityBindings.iterator().hasNext() ) { - subclassIterables.add( subSubEntityBindings ); - } - } - if ( !subEntityBindings.isEmpty() ) { - subclassIterables.add( subEntityBindings ); - } - return new JoinedIterable( subclassIterables ); - } - - /** - * Returns sub-EntityBinding ordered as a depth-first, - * pre-order traversal (a subclass precedes its own subclasses). - * - * Note that the returned value specifically excludes this entity binding. - * - * @return sub-entity bindings ordered as a depth-first, - * pre-order traversal - */ - public Iterable getPreOrderSubEntityBindingClosure() { - return getPreOrderSubEntityBindingClosure( false ); - } - - private Iterable getPreOrderSubEntityBindingClosure(boolean includeThis) { - List> iterables = new ArrayList>(); - if ( includeThis ) { - iterables.add( java.util.Collections.singletonList( this ) ); - } - for ( EntityBinding subEntityBinding : subEntityBindings ) { - Iterable subSubEntityBindingClosure = subEntityBinding.getPreOrderSubEntityBindingClosure( - true - ); - if ( subSubEntityBindingClosure.iterator().hasNext() ) { - iterables.add( subSubEntityBindingClosure ); - } - } - return new JoinedIterable( iterables ); - } public Entity getEntity() { return entity; @@ -317,79 +251,8 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements public void addSecondaryTable(SecondaryTable secondaryTable) { secondaryTables.put( secondaryTable.getSecondaryTableReference().getLogicalName().getText(), secondaryTable ); } - - public int getSecondaryTableClosureSpan() { - return superEntityBinding != null ? - superEntityBinding.getSecondaryTableClosureSpan() + secondaryTables.size() : - secondaryTables.size(); - } - - /** - * Gets the attribute bindings defined on this class, including the - * identifier attribute binding and attribute bindings defined - * as part of a join. - * - * @return The attribute bindings. - */ - public Iterable getSecondaryTableClosure() { - Iterable iterable; - if ( superEntityBinding != null ) { - List> iterables = new ArrayList>( 2 ); - iterables.add( superEntityBinding.getSecondaryTableClosure() ); - iterables.add( secondaryTables.values() ); - iterable = new JoinedIterable( iterables ); - } - else { - iterable = secondaryTables.values(); - } - return iterable; - } - - private Iterable getSubclassSecondaryTables() { - List> iterables = new ArrayList>( subEntityBindings.size() ); - for ( EntityBinding subEntityBinding : subEntityBindings ) { - iterables.add( subEntityBinding.secondaryTables.values() ); - if ( ! subEntityBinding.subEntityBindings.isEmpty() ) { - iterables.add( subEntityBinding.getSubclassSecondaryTables() ); - } - } - return new JoinedIterable( iterables ); - } - - public Iterable getSubclassSecondaryTableClosure() { - Iterable iterable; - if ( ! subEntityBindings.isEmpty() ) { - List> iterables = new ArrayList>( 2 ); - iterables.add( getSecondaryTableClosure() ); - iterables.add( getSubclassSecondaryTables() ); - iterable = new JoinedIterable( iterables ); - } - else { - iterable = getSecondaryTableClosure(); - } - return iterable; - } - - public boolean isClassOrSuperclassSecondaryTable(SecondaryTable secondaryTable) { - String secondaryTableName = secondaryTable.getSecondaryTableReference().getLogicalName().getText(); - return secondaryTables.containsKey( secondaryTableName ) || - ( superEntityBinding != null && superEntityBinding.isClassOrSuperclassSecondaryTable( secondaryTable ) ); - } - - public int getSecondaryTableNumber(SingularAttributeBinding attributeBinding) { - if ( attributeBinding.getRelationalValueBindings().isEmpty() ) { - return 0; - } - int result=1; - Value value = attributeBinding.getRelationalValueBindings().get( 0 ).getValue(); - TableSpecification table = value.getTable(); - for ( SecondaryTable secondaryTable : getSubclassSecondaryTableClosure() ) { - if ( secondaryTable.getSecondaryTableReference() == table ) { - return result; - } - result++; - } - return 0; + public Map getSecondaryTables() { + return secondaryTables; } @@ -669,28 +532,57 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements return binding; } - public AttributeBinding locateAttributeBindingByPath(String path) { - if ( path == null ) { - throw new IllegalArgumentException( "path must be non-null." ); - } - final String pathDelimiter = "\\."; - String[] tokens = path.split( pathDelimiter ); - AttributeBinding attributeBinding = locateAttributeBinding( tokens[ 0 ] ); - if ( attributeBinding == null ) { - return superEntityBinding == null ? null : superEntityBinding.locateAttributeBindingByPath( path ); - } - for ( int i = 1 ; i < tokens.length && attributeBinding != null ; i++ ) { - if ( ! attributeBinding.getAttribute().isSingular() || - ! ( (SingularAttribute) attributeBinding.getAttribute() ).getSingularAttributeType().isAggregate() ) { - // TODO: improve this message!!! - throw new MappingException( "improve this!!!" ); - } - AttributeBindingContainer attributeBindingContainer = (AttributeBindingContainer) attributeBinding; - attributeBinding = attributeBindingContainer.locateAttributeBinding( tokens[ i ] ); - } - return attributeBinding; + + public void setJpaCallbackClasses(List jpaCallbackClasses) { + this.jpaCallbackClasses = jpaCallbackClasses; } + public Iterable getJpaCallbackClasses() { + return jpaCallbackClasses; + } + //-------------------------- + //meta methods for persister , to improve performance, these methods below should really be replaced as ValueHolder + //and only be called in persister -- after build MetadataImpl + + + public TableSpecification[] getTableClosure() { + if ( isRoot() ) { + return new TableSpecification[] { getPrimaryTable() }; + } + return ArrayHelper.join( superEntityBinding.getTableClosure(), getPrimaryTable() ); + } + + public EntityBinding[] getEntityBindingClosure() { + if ( isRoot() ) { + return new EntityBinding[] { this }; + } + return ArrayHelper.join( superEntityBinding.getEntityBindingClosure(), this ); + } + + public int getSecondaryTableClosureSpan() { + return isRoot() ? secondaryTables.size() : superEntityBinding.getSecondaryTableClosureSpan() + secondaryTables.size(); + } + + public SecondaryTable[] getSecondaryTableClosure() { + if ( isRoot() ) { + return secondaryTables.values().toArray( new SecondaryTable[secondaryTables.size()] ); + } + else { + return ArrayHelper.join( + superEntityBinding.getSecondaryTableClosure(), + secondaryTables.values().toArray( new SecondaryTable[secondaryTables.size()] ) + ); + } + } + + public String[] getSynchronizedTableNameClosure() { + if ( isRoot() ) { + return getSynchronizedTableNames(); + } + return ArrayHelper.join( superEntityBinding.getSynchronizedTableNameClosure(), getSynchronizedTableNames() ); + } + + /** * Gets the number of attribute bindings defined on this class, including the * identifier attribute binding and attribute bindings defined @@ -700,9 +592,8 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements */ public int getAttributeBindingClosureSpan() { // TODO: update account for join attribute bindings - return superEntityBinding != null ? - superEntityBinding.getAttributeBindingClosureSpan() + attributeBindingMap.size() : - attributeBindingMap.size(); + return isRoot() ? getNonIdAttributeBindingClosure().length : + superEntityBinding.getAttributeBindingClosureSpan() + getNonIdAttributeBindingClosure().length; } /** @@ -712,19 +603,127 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements * * @return The attribute bindings. */ - public Iterable getAttributeBindingClosure() { + public AttributeBinding[] getAttributeBindingClosure() { // TODO: update size to account for joins - Iterable iterable; - if ( superEntityBinding != null ) { - List> iterables = new ArrayList>( 2 ); - iterables.add( superEntityBinding.getAttributeBindingClosure() ); - iterables.add( attributeBindings() ); - iterable = new JoinedIterable( iterables ); + if ( isRoot() ) { + return getNonIdAttributeBindingClosure(); } else { - iterable = attributeBindings(); + return ArrayHelper.join( + superEntityBinding.getAttributeBindingClosure(), + getNonIdAttributeBindingClosure() + ); } - return iterable; + } + + private AttributeBinding[] getNonIdAttributeBindingClosure() { + List list = new ArrayList(); + attributeBindings(); + for ( final AttributeBinding ab : attributeBindings() ) { + if(ab instanceof CompositeAttributeBinding){ + + } + boolean isId = getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( ab ); + if ( !isId ) { + list.add( ab ); + } + } + return list.toArray( new AttributeBinding[list.size()] ); + + } + + /* used for testing */ + public List getDirectSubEntityBindings() { + return subEntityBindings; + } + + /** + * Returns sub-EntityBinding objects in a special 'order', most derived subclasses + * first. Specifically, the sub-entity bindings follow a depth-first, + * post-order traversal + * + * Note that the returned value excludes this entity binding. + * + * @return sub-entity bindings ordered by those entity bindings that are most derived. + */ + public EntityBinding[] getPostOrderSubEntityBindingClosure() { + EntityBinding[] results = new EntityBinding[0]; + if ( subEntityBindings.isEmpty() ) { + return results; + } + for ( EntityBinding subEntityBinding : subEntityBindings ) { + EntityBinding[] subSubEntityBindings = subEntityBinding.getPostOrderSubEntityBindingClosure(); + results = ArrayHelper.join( results, subSubEntityBindings ); + } + if ( !subEntityBindings.isEmpty() ) { + results = ArrayHelper.join( results, subEntityBindings.toArray( new EntityBinding[subEntityBindings.size()] ) ); + } + return results; + } + + /** + * Returns sub-EntityBinding ordered as a depth-first, + * pre-order traversal (a subclass precedes its own subclasses). + * + * Note that the returned value specifically excludes this entity binding. + * + * @return sub-entity bindings ordered as a depth-first, + * pre-order traversal + */ + public EntityBinding[] getPreOrderSubEntityBindingClosure() { + return getPreOrderSubEntityBindingClosure( false, new EntityBinding[0] ); + } + + private EntityBinding[] getPreOrderSubEntityBindingClosure(boolean includeThis, EntityBinding[] results) { + if ( includeThis ) { + results = ArrayHelper.join( results, this ); + } + for ( EntityBinding subEntityBinding : subEntityBindings ) { + results = subEntityBinding.getPreOrderSubEntityBindingClosure( + true, results + ); + } + return results; + } + + public TableSpecification[] getPreOrderSubTableClosure(){ + EntityBinding[] subEntityBindings = getPreOrderSubEntityBindingClosure(); + TableSpecification [] tables = new TableSpecification[subEntityBindings.length]; + for(int i=0;i sts = eb.getSecondaryTables().values(); + int size = sts.size(); + if ( size == 0 ) { + continue; + } + results = ArrayHelper.join( results, sts.toArray( new SecondaryTable[size] ) ); + } + return results; + } + + + public SecondaryTable[] getEntitiesSecondaryTableClosure() { + if ( ! subEntityBindings.isEmpty() ) { + return ArrayHelper.join( getSecondaryTableClosure(), getSubEntitySecondaryTables() ); + } + else { + return getSecondaryTableClosure(); + } + } + + public int getSubEntityBindingClosureSpan() { + int n = subEntityBindings.size(); + for ( final EntityBinding seb : subEntityBindings ) { + n += seb.getSubEntityBindingClosureSpan(); + } + return n; } /** @@ -732,37 +731,42 @@ public class EntityBinding extends AbstractAttributeBindingContainer implements * sub-EntityBinding, starting from the root of the hierarchy; includes * the identifier and attribute bindings defined as part of a join. */ - public Iterable getSubEntityAttributeBindingClosure() { - List> iterables = new ArrayList>(); - iterables.add( getAttributeBindingClosure() ); + public AttributeBinding[] getEntitiesAttributeBindingClosure() { + AttributeBinding[] results = getAttributeBindingClosure(); + for ( EntityBinding subEntityBinding : getPreOrderSubEntityBindingClosure() ) { // only add attribute bindings declared for the subEntityBinding - iterables.add( subEntityBinding.attributeBindings() ); + + results = ArrayHelper.join( + results, + subEntityBinding.getNonIdAttributeBindingClosure() + ); // TODO: if EntityBinding.attributeBindings() excludes joined attributes, then they need to be added here } - return new JoinedIterable( iterables ); + return results; } - public Iterator getTableClosureIterator() { - if ( superEntityBinding == null ) { - return new SingletonIterator( getPrimaryTable() ); + + public boolean isClassOrSuperclassSecondaryTable(SecondaryTable secondaryTable) { + String secondaryTableName = secondaryTable.getSecondaryTableReference().getLogicalName().getText(); + return secondaryTables.containsKey( secondaryTableName ) || + ( superEntityBinding != null && superEntityBinding.isClassOrSuperclassSecondaryTable( secondaryTable ) ); + } + + public int getSecondaryTableNumber(SingularAttributeBinding attributeBinding) { + if ( attributeBinding.getRelationalValueBindings().isEmpty() ) { + return 0; } - else { - return new JoinedIterator( - superEntityBinding.getTableClosureIterator(), - new SingletonIterator( getPrimaryTable() ) - ); + int result=1; + Value value = attributeBinding.getRelationalValueBindings().get( 0 ).getValue(); + TableSpecification table = value.getTable(); + for ( SecondaryTable secondaryTable : getEntitiesSecondaryTableClosure() ) { + if ( secondaryTable.getSecondaryTableReference() == table ) { + return result; + } + result++; } - } - public Iterator getKeyClosureIterator(){ - return null; + return 0; } - public void setJpaCallbackClasses(List jpaCallbackClasses) { - this.jpaCallbackClasses = jpaCallbackClasses; - } - - public Iterable getJpaCallbackClasses() { - return jpaCallbackClasses; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityIdentifier.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityIdentifier.java index f0a4637d89..3ab1bb2905 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityIdentifier.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/EntityIdentifier.java @@ -119,6 +119,14 @@ public class EntityIdentifier { return entityIdentifierBinding.isIdentifierAttributeBinding( attributeBinding ); } + public boolean isCascadeDeleteEnabled() { + if ( getAttributeBinding() instanceof Cascadeable ) { + Cascadeable cascadeable = Cascadeable.class.cast( getAttributeBinding() ); + cascadeable.getCascadeStyle();//todo + } + return false; + } + public String getUnsavedValue() { ensureBound(); return entityIdentifierBinding.getUnsavedValue(); 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 8da10dc96e..5ab29645dc 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 @@ -25,14 +25,11 @@ 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; - /** * @author Strong Liu */ diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 2c9e5e4337..dcd03e4b6b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -702,7 +702,7 @@ public abstract class AbstractCollectionPersister // isSorted = collection.isSorted(); isArray = collectionType.isArrayType(); isPrimitiveArray = - collectionType.isArrayType() && + isArray && PrimitiveType.class.isInstance( collection.getPluralAttributeElementBinding() .getHibernateTypeDescriptor() @@ -840,9 +840,10 @@ public abstract class AbstractCollectionPersister indexColumnIsSettable = new boolean[indexSpan]; indexColumnAliases = new String[indexSpan]; for ( int i = 0 ; i < indexSpan ; i++ ) { - final Value value = indexRelationalValueBindings.get( i ).getValue(); + final RelationalValueBinding rb = indexRelationalValueBindings.get( i ); + final Value value = rb.getValue(); indexColumnAliases[ i ] = value.getAlias( dialect, null ); - if ( value instanceof Column ) { + if ( !rb.isDerived() ) { indexColumnIsSettable[ i ] = true; Column column = ( Column ) value; indexColumnNames[ i ] = column.getColumnName().getText( dialect ); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index c1e6da48a1..b419af166a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -73,7 +73,6 @@ import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; -import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.PersistenceContext.NaturalIdHelper; @@ -86,7 +85,6 @@ import org.hibernate.id.PostInsertIdentityPersister; import org.hibernate.id.insert.Binder; import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate; import org.hibernate.internal.CoreMessageLogger; -import org.hibernate.internal.FilterConfiguration; import org.hibernate.internal.FilterHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.ValueHolder; @@ -94,7 +92,6 @@ import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.jdbc.Expectation; import org.hibernate.jdbc.Expectations; import org.hibernate.jdbc.TooManyRowsAffectedException; -import org.hibernate.loader.entity.BatchingEntityLoader; import org.hibernate.loader.entity.BatchingEntityLoaderBuilder; import org.hibernate.loader.entity.CascadeEntityLoader; import org.hibernate.loader.entity.EntityLoader; @@ -109,15 +106,15 @@ import org.hibernate.metamodel.spi.binding.Cascadeable; import org.hibernate.metamodel.spi.binding.CompositeAttributeBinding; import org.hibernate.metamodel.spi.binding.AttributeBinding; import org.hibernate.metamodel.spi.binding.BasicAttributeBinding; +import org.hibernate.metamodel.spi.binding.CustomSQL; import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.metamodel.spi.binding.Fetchable; -import org.hibernate.metamodel.spi.binding.PluralAttributeAssociationElementBinding; import org.hibernate.metamodel.spi.binding.PluralAttributeBinding; import org.hibernate.metamodel.spi.binding.PluralAttributeElementBinding; 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.relational.DerivedValue; +import org.hibernate.metamodel.spi.relational.PrimaryKey; import org.hibernate.metamodel.spi.relational.Value; import org.hibernate.pretty.MessageHelper; import org.hibernate.property.BackrefPropertyAccessor; @@ -169,6 +166,7 @@ public abstract class AbstractEntityPersister private final String[] rootTableKeyColumnReaders; private final String[] rootTableKeyColumnReaderTemplates; private final String[] identifierAliases; + //how many columns are mapped by the identifier? private final int identifierColumnSpan; private final String versionColumnName; private final boolean hasFormulaProperties; @@ -237,8 +235,8 @@ public abstract class AbstractEntityPersister private final Set affectingFetchProfileNames = new HashSet(); - private final Map uniqueKeyLoaders = new HashMap(); - private final Map lockers = new HashMap(); + private final Map uniqueKeyLoaders = new HashMap(); + private final Map lockers = new HashMap(); private final Map loaders = new HashMap(); // SQL strings @@ -280,8 +278,8 @@ public abstract class AbstractEntityPersister private final String temporaryIdTableName; private final String temporaryIdTableDDL; - private final Map subclassPropertyAliases = new HashMap(); - private final Map subclassPropertyColumnNames = new HashMap(); + private final Map subclassPropertyAliases = new HashMap(); + private final Map subclassPropertyColumnNames = new HashMap(); protected final BasicEntityPropertyMapping propertyMapping; @@ -390,6 +388,19 @@ public abstract class AbstractEntityPersister } return result; } + protected static void initializeCustomSql( + CustomSQL customSql, + int i, + String[] sqlStrings, + boolean[] callable, + ExecuteUpdateResultCheckStyle[] checkStyles) { + sqlStrings[i] = customSql != null ? customSql.getSql(): null; + callable[i] = customSql != null && customSql.isCallable(); + checkStyles[i] = customSql != null && customSql.getCheckStyle() != null ? + customSql.getCheckStyle() : + ExecuteUpdateResultCheckStyle.determineDefault( sqlStrings[i], callable[i] ); + } + protected String getSQLSnapshotSelectString() { return sqlSnapshotSelectString; @@ -866,9 +877,11 @@ public abstract class AbstractEntityPersister rootTableKeyColumnReaderTemplates = new String[identifierColumnSpan]; identifierAliases = new String[identifierColumnSpan]; - - int i = 0; - for ( org.hibernate.metamodel.spi.relational.Column col : entityBinding.getPrimaryTable().getPrimaryKey().getColumns() ) { + for ( int i = 0; i < identifierColumnSpan; i++ ) { + org.hibernate.metamodel.spi.relational.Column col = entityBinding.getPrimaryTable() + .getPrimaryKey() + .getColumns() + .get( i ); rootTableKeyColumnNames[i] = col.getColumnName().getText( factory.getDialect() ); if ( col.getReadFragment() == null ) { rootTableKeyColumnReaders[i] = rootTableKeyColumnNames[i]; @@ -878,8 +891,7 @@ public abstract class AbstractEntityPersister rootTableKeyColumnReaders[i] = col.getReadFragment(); rootTableKeyColumnReaderTemplates[i] = getTemplateFromString( col.getReadFragment(), factory ); } - identifierAliases[i] = col.getAlias( factory.getDialect(), entityBinding.getPrimaryTable() ); - i++; + identifierAliases[i] = col.getAlias( factory.getDialect(), entityBinding.getHierarchyDetails().getRootEntityBinding().getPrimaryTable() ); } // VERSION @@ -937,7 +949,7 @@ public abstract class AbstractEntityPersister List lazyColAliases = new ArrayList(); - i = 0; + int i = 0; boolean foundFormula = false; for ( AttributeBinding attributeBinding : entityBinding.getAttributeBindingClosure() ) { if ( entityBinding.getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( attributeBinding ) ) { @@ -1042,7 +1054,7 @@ public abstract class AbstractEntityPersister List columnSelectables = new ArrayList(); List propNullables = new ArrayList(); - for ( AttributeBinding attributeBinding : entityBinding.getSubEntityAttributeBindingClosure() ) { + for ( AttributeBinding attributeBinding : entityBinding.getEntitiesAttributeBindingClosure() ) { if ( entityBinding.getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( attributeBinding ) ) { // entity identifier is not considered a "normal" property continue; @@ -1835,7 +1847,7 @@ public abstract class AbstractEntityPersister } private LockingStrategy getLocker(LockMode lockMode) { - return ( LockingStrategy ) lockers.get( lockMode ); + return lockers.get( lockMode ); } public void lock( @@ -2085,7 +2097,7 @@ public abstract class AbstractEntityPersister } public String[] getSubclassPropertyColumnAliases(String propertyName, String suffix) { - String rawAliases[] = ( String[] ) subclassPropertyAliases.get( propertyName ); + String rawAliases[] = subclassPropertyAliases.get( propertyName ); if ( rawAliases == null ) { return null; @@ -2097,10 +2109,10 @@ public abstract class AbstractEntityPersister } return result; } - + @Override public String[] getSubclassPropertyColumnNames(String propertyName) { //TODO: should we allow suffixes on these ? - return ( String[] ) subclassPropertyColumnNames.get( propertyName ); + return subclassPropertyColumnNames.get( propertyName ); } @@ -2179,7 +2191,7 @@ public abstract class AbstractEntityPersister // ALIASES // TODO: Fix when subclasses are working (HHH-6337) - internalInitSubclassPropertyAliasesMap( null, entityBinding.getSubEntityAttributeBindingClosure() ); + internalInitSubclassPropertyAliasesMap( null, entityBinding.getEntitiesAttributeBindingClosure() ); // aliases for identifier ( alias.id ); skip if the entity defines a non-id property named 'id' if ( ! entityMetamodel.hasNonIdentifierPropertyNamedId() ) { @@ -2266,10 +2278,12 @@ public abstract class AbstractEntityPersister } } - - private void internalInitSubclassPropertyAliasesMap(String path, Iterable attributeBindings) { + protected static boolean isIdentifierAttributeBinding(final AttributeBinding prop){ + return prop.getContainer().seekEntityBinding().getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( prop ); + } + private void internalInitSubclassPropertyAliasesMap(String path, AttributeBinding[] attributeBindings) { for ( AttributeBinding prop : attributeBindings ) { - if ( prop.getContainer().seekEntityBinding().getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( prop ) ) { + if ( isIdentifierAttributeBinding( prop ) ) { // ID propertie aliases are dealt with elsewhere. continue; } @@ -2281,7 +2295,12 @@ public abstract class AbstractEntityPersister String propname = path == null ? prop.getAttribute().getName() : path + "." + prop.getAttribute().getName(); if ( prop instanceof CompositeAttributeBinding ) { CompositeAttributeBinding component = (CompositeAttributeBinding) prop; - internalInitSubclassPropertyAliasesMap( propname, component.attributeBindings() ); + AttributeBinding[] abs = new AttributeBinding[component.attributeBindingSpan()]; + int i=0; + for(AttributeBinding ab : component.attributeBindings()){ + abs[i++] = ab; + } + internalInitSubclassPropertyAliasesMap( propname, abs ); } else { int span = singularProp.getRelationalValueBindings().size(); @@ -2324,7 +2343,7 @@ public abstract class AbstractEntityPersister && propertyName.indexOf('.')<0; //ugly little workaround for fact that createUniqueKeyLoaders() does not handle component properties if ( useStaticLoader ) { - return ( EntityLoader ) uniqueKeyLoaders.get( propertyName ); + return uniqueKeyLoaders.get( propertyName ); } else { return createUniqueKeyLoader( @@ -3898,10 +3917,11 @@ public abstract class AbstractEntityPersister } private boolean isAffectedByEnabledFetchProfiles(SessionImplementor session) { - Iterator itr = session.getLoadQueryInfluencers().getEnabledFetchProfileNames().iterator(); - while ( itr.hasNext() ) { - if ( affectingFetchProfileNames.contains( itr.next() ) ) { - return true; + if ( session.getLoadQueryInfluencers().hasEnabledFetchProfiles() ) { + for ( String name : session.getLoadQueryInfluencers().getEnabledFetchProfileNames() ) { + if ( affectingFetchProfileNames.contains( name ) ) { + return true; + } } } return false; @@ -3962,8 +3982,7 @@ public abstract class AbstractEntityPersister protected final boolean[] getPropertiesToUpdate(final int[] dirtyProperties, final boolean hasDirtyCollection) { final boolean[] propsToUpdate = new boolean[ entityMetamodel.getPropertySpan() ]; final boolean[] updateability = getPropertyUpdateability(); //no need to check laziness, dirty checking handles that - for ( int j = 0; j < dirtyProperties.length; j++ ) { - int property = dirtyProperties[j]; + for ( int property : dirtyProperties ) { if ( updateability[property] ) { propsToUpdate[property] = true; } @@ -4058,8 +4077,8 @@ public abstract class AbstractEntityPersister private void logDirtyProperties(int[] props) { if ( LOG.isTraceEnabled() ) { - for ( int i = 0; i < props.length; i++ ) { - String propertyName = entityMetamodel.getProperties()[ props[i] ].getName(); + for ( int prop : props ) { + String propertyName = entityMetamodel.getProperties()[prop].getName(); LOG.trace( StringHelper.qualify( getEntityName(), propertyName ) + " is dirty" ); } } @@ -4220,13 +4239,7 @@ public abstract class AbstractEntityPersister } public Boolean isTransient(Object entity, SessionImplementor session) throws HibernateException { - final Serializable id; - if ( canExtractIdOutOfEntity() ) { - id = getIdentifier( entity, session ); - } - else { - id = null; - } + final Serializable id = canExtractIdOutOfEntity() ? getIdentifier( entity, session ) : null; // we *always* assume an instance with a null // identifier or no identifier property is unsaved! if ( id == null ) { @@ -4276,7 +4289,6 @@ public abstract class AbstractEntityPersister } private boolean isModifiableEntity(EntityEntry entry) { - return ( entry == null ? isMutable() : entry.isModifiableEntity() ); } @@ -4566,6 +4578,20 @@ public abstract class AbstractEntityPersister return temporaryIdTableDDL; } + /** + * Here, we want to know how many properties of this persister know about. + *

+ * The properties including: + *

    + *
  • properties belongs to the current entity
  • + *
  • properties belongs to the join entity
  • + *
  • parent's properties, recursively
  • + *
+ * + * note: id property is not included here + * + * @return + */ protected int getPropertySpan() { return entityMetamodel.getPropertySpan(); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index e5c8bc9a35..eb2e7bfc35 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -27,6 +27,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import org.hibernate.AssertionFailure; @@ -35,6 +36,7 @@ import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; +import org.hibernate.engine.FetchStyle; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.Mapping; @@ -50,9 +52,12 @@ import org.hibernate.mapping.Property; import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Table; +import org.hibernate.metamodel.spi.binding.AttributeBinding; import org.hibernate.metamodel.spi.binding.EntityBinding; -import org.hibernate.metamodel.spi.binding.EntityDiscriminator; -import org.hibernate.metamodel.spi.relational.DerivedValue; +import org.hibernate.metamodel.spi.binding.PluralAttributeBinding; +import org.hibernate.metamodel.spi.binding.RelationalValueBinding; +import org.hibernate.metamodel.spi.binding.SecondaryTable; +import org.hibernate.metamodel.spi.binding.SingularAttributeBinding; import org.hibernate.metamodel.spi.relational.PrimaryKey; import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.sql.CaseFragment; @@ -67,32 +72,192 @@ import org.hibernate.type.*; */ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { - // the class hierarchy structure + /** + * Tables' count, which tables are mapped directly by the current entity ( and its parent entity, recursively, if any ), + * including the joined tables, but sub-entities of the current entity are not included. + */ private final int tableSpan; - private final String[] tableNames; + /** + * Span of the tables directly mapped by this entity and super-classes, if any. + *

+ * so, coreTableSpan = tableSpan - join tables span + */ + private final int coreTableSpan; + + /** + * Tables' name, the tables' scope is same as {@code tableSpan}. + *

+ * + * This array ( the table names ) is order by the following roles: + *

    + *
  • Table mapped by the root entity of current one if any
  • + *
  • Table mapped by sub entity of the root one, recursively, till the current entity in the hierarchy level
  • + *
  • Joined tables, also, in the top-down order of the hierarchy
  • + *
+ * + *

+ * + * Suppose an entity Client extends Person, mapped to the tables CLIENT and PERSON respectively. + * For the Client entity: + * naturalOrderTableNames -> PERSON, CLIENT; this reflects the sequence in which the tables are + * added to the meta-data when the annotated entities are processed. + * However, in some instances, for example when generating joins, the CLIENT table needs to be + * the first table as it will the driving table. + * tableNames -> CLIENT, PERSON + * + *

+ * The length of this array is tableSpan + *

+ */ private final String[] naturalOrderTableNames; - private final String[][] tableKeyColumns; - private final String[][] tableKeyColumnReaders; - private final String[][] tableKeyColumnReaderTemplates; + + /** + * This contains same elements as naturalOrderTableNames, but in different order. + *

+ * + * As said above, elements in this array are actually composited by two parts: + * + *

    + *
  • Table names mapped directly by the entities of the hierarchy
  • + *
  • Table names mapped by "join"
  • + *
+ * + * In the first part of elements, the naturalOrderTableNames follows "root" -> "sub-entities" -> "current entity" + * + * here, we have a reversed order, so "current entity" -> "parent entity" -> "root entity" + * + *

+ * + * The order of the second part is same. + * + *

+ * The length of this array is tableSpan + *

+ * + */ + private final String[] tableNames; + + /** + * These two follow same role as above, but for the primary key columns + * + *

+ * The first dimension length of this array is tableSpan + *

+ */ private final String[][] naturalOrderTableKeyColumns; + private final String[][] tableKeyColumns; + + /** + * Same as above, just for different column metadata. + */ + private final String[][] tableKeyColumnReaders; private final String[][] naturalOrderTableKeyColumnReaders; + + /** + * Same as above, just for different column metadata. + */ + private final String[][] tableKeyColumnReaderTemplates; private final String[][] naturalOrderTableKeyColumnReaderTemplates; + + /** + * If the identifier is cascade delete enabled. + * Array is ordered by the natural way ( {@see naturalOrderTableNames} ) + * + *

+ * The length of this array is tableSpan + *

+ */ private final boolean[] naturalOrderCascadeDeleteEnabled; + /** + * tableNames + * synchronized tables in the entity hierarchy from top down to the current entity level, sub-entities are not included + * + *

+ * The length of this array is tableSpan + sync table count + *

+ */ private final String[] spaces; - + /** + * This array contains all the sub-entities' name of the current entity, and + * the last element of this array is the current entity name. + * + * Sub-entities' name is ordered in the most derived subclasses first, from bottom to top, till the current one. + * + *

+ * The length of this array is the count of all the sub entities (recursively) of the current one + 1 + *

+ */ private final String[] subclassClosure; - + /** + * This is kind of same as {@see tableNames}, but it contains all the tables' name mapped by the current entity's super entities AND + * sub entities, and joined tables mapped by the entities. + * + * Actually, due to the order operation, the first coreTableSpan elements are same as tableNames. + * (table names mapped by the current entity and its super entities, joined tables are not included) + * + * So, this property is kind of weird to me, it has: + * + * 1. "current entity" -> "parent entity" -> "root entity" -> "first sub-entity" -> "second sub-entity" .... "last sub-entity" + * 2. "root entity join table" -> ..."last entity join table" + * + * + *

+ * Though this property is named with "subclassTable" perfix, but its length is actually: + * tableSpan + all sub-entities mapped tables' count + all sub-entities joined tables' count + *

+ */ private final String[] subclassTableNameClosure; + /** + * table's primary key columns, in the same order as above + * + *

+ * The length is same as subclassTableNameClosure + *

+ */ private final String[][] subclassTableKeyColumnClosure; + + /** + * If the table, in the order of subclassTableNameClosure is concreted. + * By "concreted", here means if the table is one of current entity or its super entity mapped table + * + *

+ * The length is same as subclassTableNameClosure + *

+ */ private final boolean[] isClassOrSuperclassTable; - // properties of this class, including inherited properties + /** + * The element in this array is the index of the {@see naturalOrderTableNames}, which table that the column is belonged to. + * + * So, this is all about the columns ( except PK ) mapped by the properties in the current entity and its parent entity, in a top down order. + * + *

+ * The length is the count of columns, mapped by the properties in the current entity and from its super entity, and joined columns. + *

+ */ private final int[] naturalOrderPropertyTableNumbers; + + /** + * Same as above, but here is the index of {@see tableNames} + * + *

+ * The length is same as above. + *

+ */ private final int[] propertyTableNumbers; - // the closure of all properties in the entire hierarchy including - // subclasses and superclasses of this class + /** + * the closure of all properties in the entire hierarchy including + * subclasses and superclasses of this class + * + * The element is the index of {@see subclassTableNameClosure}, which table that the property's relational value belongs to. + * + *

+ * The length is all the properties count in the entire hierarchy. + *

+ * + */ private final int[] subclassPropertyTableNumberClosure; // the closure of all columns used by the entire hierarchy including @@ -117,11 +282,16 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { private final Object discriminatorValue; private final String discriminatorSQLString; - // Span of the tables directly mapped by this entity and super-classes, if any - private final int coreTableSpan; + // only contains values for SecondaryTables, ie. not tables part of the "coreTableSpan" private final boolean[] isNullableTable; + + private void assertOptimisticLockStyle() { + if ( optimisticLockStyle() == OptimisticLockStyle.ALL || optimisticLockStyle() == OptimisticLockStyle.DIRTY ) { + throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" ); + } + } //INITIALIZATION: @SuppressWarnings( {"UnusedDeclaration"}) public JoinedSubclassEntityPersister( @@ -149,14 +319,11 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { discriminatorSQLString = null; } - if ( optimisticLockStyle() == OptimisticLockStyle.ALL || optimisticLockStyle() == OptimisticLockStyle.DIRTY ) { - throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" ); - } + assertOptimisticLockStyle(); //MULTITABLES final int idColumnSpan = getIdentifierColumnSpan(); - ArrayList tables = new ArrayList(); ArrayList keyColumns = new ArrayList(); ArrayList keyColumnReaders = new ArrayList(); @@ -191,7 +358,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { //Span of the tables directly mapped by this entity and super-classes, if any coreTableSpan = tables.size(); - isNullableTable = new boolean[persistentClass.getJoinClosureSpan()]; int tableIndex = 0; @@ -241,7 +407,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { ArrayList isConcretes = new ArrayList(); ArrayList isDeferreds = new ArrayList(); ArrayList isLazies = new ArrayList(); - keyColumns = new ArrayList(); titer = persistentClass.getSubclassTableClosureIterator(); while ( titer.hasNext() ) { @@ -262,7 +427,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { } keyColumns.add( key ); } - //Add joins joinIter = persistentClass.getSubclassJoinClosureIterator(); while ( joinIter.hasNext() ) { @@ -293,7 +457,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { isClassOrSuperclassTable = ArrayHelper.toBooleanArray( isConcretes ); subclassTableSequentialSelect = ArrayHelper.toBooleanArray( isDeferreds ); subclassTableIsLazyClosure = ArrayHelper.toBooleanArray( isLazies ); - constraintOrderedTableNames = new String[naturalOrderSubclassTableNameClosure.length]; constraintOrderedKeyColumnNames = new String[naturalOrderSubclassTableNameClosure.length][]; int currentPosition = 0; @@ -305,9 +468,9 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { /** * Suppose an entity Client extends Person, mapped to the tables CLIENT and PERSON respectively. * For the Client entity: - * naturalOrderTableNames -> PERSON, CLIENT; this reflects the sequence in which the tables are + * naturalOrderTableNames -> PERSON, CLIENT; this reflects the sequence in which the tables are * added to the meta-data when the annotated entities are processed. - * However, in some instances, for example when generating joins, the CLIENT table needs to be + * However, in some instances, for example when generating joins, the CLIENT table needs to be * the first table as it will the driving table. * tableNames -> CLIENT, PERSON */ @@ -319,12 +482,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { tableKeyColumnReaderTemplates = reverse( naturalOrderTableKeyColumnReaderTemplates, coreTableSpan ); subclassTableNameClosure = reverse( naturalOrderSubclassTableNameClosure, coreTableSpan ); subclassTableKeyColumnClosure = reverse( naturalOrderSubclassTableKeyColumnClosure, coreTableSpan ); - spaces = ArrayHelper.join( tableNames, ArrayHelper.toStringArray( persistentClass.getSynchronizedTables() ) ); - // Custom sql customSQLInsert = new String[tableSpan]; customSQLUpdate = new String[tableSpan]; @@ -363,7 +524,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { if ( jk != -1 ) { throw new AssertionFailure( "Tablespan does not match height of joined-subclass hiearchy." ); } - joinIter = persistentClass.getJoinClosureIterator(); int j = coreTableSpan; while ( joinIter.hasNext() ) { @@ -387,6 +547,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { j++; } + // PROPERTIES int hydrateSpan = getPropertySpan(); naturalOrderPropertyTableNumbers = new int[hydrateSpan]; @@ -412,7 +573,6 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { ArrayList columnTableNumbers = new ArrayList(); ArrayList formulaTableNumbers = new ArrayList(); ArrayList propTableNumbers = new ArrayList(); - iter = persistentClass.getSubclassPropertyClosureIterator(); while ( iter.hasNext() ) { Property prop = (Property) iter.next(); @@ -445,8 +605,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { // SUBCLASSES int subclassSpan = persistentClass.getSubclassSpan() + 1; - subclassClosure = new String[subclassSpan]; - subclassClosure[subclassSpan - 1] = getEntityName(); + if ( persistentClass.isPolymorphic() ) { subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() ); discriminatorValues = new String[subclassSpan]; @@ -470,6 +629,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { notNullColumnNames = null; } + subclassClosure = new String[subclassSpan]; + subclassClosure[subclassSpan - 1] = getEntityName(); iter = persistentClass.getSubclassIterator(); int k = 0; while ( iter.hasNext() ) { @@ -508,6 +669,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { postConstruct( mapping ); } + + @SuppressWarnings( {"UnusedDeclaration"}) public JoinedSubclassEntityPersister( final EntityBinding entityBinding, @@ -517,104 +680,358 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { final Mapping mapping) throws HibernateException { super( entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory ); - // DISCRIMINATOR - if ( entityBinding.isPolymorphic() ) { - try{ - discriminatorValue = entityBinding.getSubEntityBindingId(); - discriminatorSQLString = discriminatorValue.toString(); - } catch ( Exception e ){ - throw new MappingException( "Could not format discriminator value to SQL string", e ); + + assertOptimisticLockStyle(); + + final boolean isCascadeDeleteDefault = factory.getDialect().supportsCascadeDelete(); + + final EntityBinding[] entityBindings = entityBinding.getEntityBindingClosure(); + final TableSpecification[] tables = entityBinding.getTableClosure(); + final SecondaryTable[] secondaryTables = entityBinding.getSecondaryTableClosure(); + final String[] synchronizedTableNames = entityBinding.getSynchronizedTableNameClosure(); + final AttributeBinding[] attributeBindings = entityBinding.getAttributeBindingClosure(); + //todo the count of these two are not equal, which they should be + final EntityBinding[] preOrderSubEntityBindings = entityBinding.getPreOrderSubEntityBindingClosure(); + final EntityBinding[] postOrderSubEntityBindings = entityBinding.getPostOrderSubEntityBindingClosure(); + final TableSpecification[] subTables = entityBinding.getPreOrderSubTableClosure(); + final SecondaryTable[] subSecondaryTables = entityBinding.getSubEntitySecondaryTables(); + final AttributeBinding[] allAttributeBindings = entityBinding.getEntitiesAttributeBindingClosure(); + + final int idColumnSpan = getIdentifierColumnSpan(); + coreTableSpan = tables.length; + final int secondaryTableSpan = secondaryTables.length; + tableSpan = coreTableSpan + secondaryTableSpan; + final int subclassSpan = postOrderSubEntityBindings.length; + final int subclassSecondaryTableSpan = subSecondaryTables.length; + final int subTableSpan = subclassSpan + subclassSecondaryTableSpan; + final int allTableSpan = tableSpan + subTableSpan; + final int hydrateSpan = getPropertySpan(); + isClassOrSuperclassTable = new boolean[allTableSpan]; + subclassTableSequentialSelect = new boolean[allTableSpan]; + subclassTableIsLazyClosure = new boolean[allTableSpan]; + naturalOrderTableNames = new String[tableSpan]; + naturalOrderCascadeDeleteEnabled = new boolean[tableSpan]; + + naturalOrderTableKeyColumns = new String[tableSpan][]; + naturalOrderTableKeyColumnReaders = new String[tableSpan][]; + naturalOrderTableKeyColumnReaderTemplates = new String[tableSpan][]; + //custom sql + customSQLInsert = new String[tableSpan]; + customSQLUpdate = new String[tableSpan]; + customSQLDelete = new String[tableSpan]; + insertCallable = new boolean[tableSpan]; + updateCallable = new boolean[tableSpan]; + deleteCallable = new boolean[tableSpan]; + insertResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan]; + updateResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan]; + deleteResultCheckStyles = new ExecuteUpdateResultCheckStyle[tableSpan]; + subclassClosure = new String[subclassSpan+1]; + subclassClosure[subclassSpan] = getEntityName(); + isNullableTable = new boolean[secondaryTableSpan]; + naturalOrderPropertyTableNumbers = new int[hydrateSpan]; + propertyTableNumbers = new int[hydrateSpan]; + constraintOrderedTableNames = new String[allTableSpan]; + constraintOrderedKeyColumnNames = new String[allTableSpan][]; + /** + * 1. core table names + * 2. direct sub entity table names + * 3. core joined table names + * 4. direct sub entity joined table names + */ + final String[] naturalOrderSubclassTableNameClosure = new String[allTableSpan]; + final String[][] naturalOrderSubclassTableKeyColumnClosure = new String[allTableSpan][]; + + + int tableIndex = 0; + int allTableIndex =0; + //first, process tables / entitybindings mapped directly by the current entitybinding and its super entitybindings + for ( int i = 0; i < coreTableSpan; i++, tableIndex++, allTableIndex++ ) { + final TableSpecification table = tables[i]; + naturalOrderTableNames[tableIndex] = table.getQualifiedName( factory.getDialect() ); + naturalOrderCascadeDeleteEnabled[tableIndex] = false; //todo fix me @OnDelete + naturalOrderTableKeyColumns[tableIndex] = new String[idColumnSpan]; + naturalOrderTableKeyColumnReaders[tableIndex] = new String[idColumnSpan]; + naturalOrderTableKeyColumnReaderTemplates[tableIndex] = new String[idColumnSpan]; + PrimaryKey primaryKey = table.getPrimaryKey(); + resolvePkColumnNames( + factory, + primaryKey, + naturalOrderTableKeyColumns[tableIndex], + naturalOrderTableKeyColumnReaders[tableIndex], + naturalOrderTableKeyColumnReaderTemplates[tableIndex] + ); + final EntityBinding eb = entityBindings[i]; + //Custom SQL + initializeCustomSql( eb.getCustomInsert(), tableIndex, customSQLInsert, insertCallable, insertResultCheckStyles ); + initializeCustomSql( eb.getCustomUpdate(), tableIndex, customSQLUpdate, updateCallable, updateResultCheckStyles ); + initializeCustomSql( eb.getCustomDelete(), tableIndex, customSQLDelete, deleteCallable, deleteResultCheckStyles ); + isClassOrSuperclassTable[allTableIndex] = true;//EntityBindingHelper.isClassOrSuperclassTable( entityBinding, table ); + subclassTableSequentialSelect[allTableIndex] = false; + subclassTableIsLazyClosure[allTableIndex] = false; + } + + //#1 + System.arraycopy( naturalOrderTableNames, 0, naturalOrderSubclassTableNameClosure, 0, coreTableSpan ); + System.arraycopy( naturalOrderTableKeyColumns, 0, naturalOrderSubclassTableKeyColumnClosure, 0, coreTableSpan ); + //--------------------------------- directly sub entities + final String[] naturalOrderSubTableNames = new String[subclassSpan]; + final String[][] naturalOrderSubTableKeyColumns = new String[subclassSpan][idColumnSpan]; + + for ( int i = 0; i < subclassSpan; i++, allTableIndex++ ) { + final EntityBinding subEntityBinding = preOrderSubEntityBindings[i]; //todo post order?? + final TableSpecification table = subEntityBinding.getPrimaryTable(); + naturalOrderSubTableNames[i] = table.getQualifiedName( factory.getDialect() ); + + final PrimaryKey pk = table.getPrimaryKey(); + for(int j=0;j= 0; i--, currentPosition++ ) { + constraintOrderedTableNames[currentPosition] = naturalOrderSubclassTableNameClosure[i]; + constraintOrderedKeyColumnNames[currentPosition] = naturalOrderSubclassTableKeyColumnClosure[i]; + } + subclassTableNameClosure = reverse( naturalOrderSubclassTableNameClosure, coreTableSpan ); + subclassTableKeyColumnClosure = reverse( naturalOrderSubclassTableKeyColumnClosure, coreTableSpan ); + + // PROPERTIES + + + ArrayList columnTableNumbers = new ArrayList(); + ArrayList formulaTableNumbers = new ArrayList(); + ArrayList propTableNumbers = new ArrayList(); + for(int i=0;i valueBindings; + if ( SingularAttributeBinding.class.isInstance( attributeBinding ) ) { + SingularAttributeBinding singularAttributeBinding = SingularAttributeBinding.class.cast( + attributeBinding + ); + valueBindings = singularAttributeBinding.getRelationalValueBindings(); + } + else { + PluralAttributeBinding pluralAttributeBinding = PluralAttributeBinding.class.cast( attributeBinding ); + valueBindings = pluralAttributeBinding.getPluralAttributeElementBinding().getRelationalValueBindings(); + } + RelationalValueBinding valueBinding = valueBindings.get( 0 ); + TableSpecification table = valueBinding.getValue().getTable(); + final String tableName = table.getQualifiedName( factory.getDialect() ); + if ( i < hydrateSpan ) { + propertyTableNumbers[i] = getTableId( tableName, tableNames ); + naturalOrderPropertyTableNumbers[i] = getTableId( tableName, naturalOrderTableNames ); + } + final int tableNumberInSubclass = getTableId( tableName, subclassTableNameClosure ); + propTableNumbers.add( tableNumberInSubclass ); + for ( RelationalValueBinding vb : valueBindings ) { + if ( vb.isDerived() ) { + formulaTableNumbers.add( tableNumberInSubclass ); + } + else { + columnTableNumbers.add( tableNumberInSubclass ); + } } } + + + + subclassColumnTableNumberClosure = ArrayHelper.toIntArray( columnTableNumbers ); + subclassPropertyTableNumberClosure = ArrayHelper.toIntArray( propTableNumbers ); + subclassFormulaTableNumberClosure = ArrayHelper.toIntArray( formulaTableNumbers ); + // SUBCLASSES + + // DISCRIMINATOR + + if ( entityBinding.isPolymorphic() ) { + try { + discriminatorValue = entityBinding.getSubEntityBindingId(); + discriminatorSQLString = discriminatorValue.toString(); + } + catch ( Exception e ) { + throw new MappingException( "Could not format discriminator value to SQL string", e ); + } + subclassesByDiscriminatorValue.put( discriminatorValue, getEntityName() ); + discriminatorValues = new String[subclassSpan+1]; + discriminatorValues[subclassSpan] = discriminatorSQLString; + notNullColumnTableNumbers = new int[subclassSpan+1]; + final int id = getTableId( + tableNames[0], //the current entitybinding's primary table name + subclassTableNameClosure + ); + notNullColumnTableNumbers[subclassSpan] = id; + notNullColumnNames = new String[subclassSpan+1]; + notNullColumnNames[subclassSpan] = subclassTableKeyColumnClosure[id][0]; + } else { + discriminatorValues = null; + notNullColumnTableNumbers = null; + notNullColumnNames = null; discriminatorValue = null; discriminatorSQLString = null; } - - if ( optimisticLockStyle() == OptimisticLockStyle.ALL || optimisticLockStyle() == OptimisticLockStyle.DIRTY ) { - throw new MappingException( "optimistic-lock=all|dirty not supported for joined-subclass mappings [" + getEntityName() + "]" ); - } - - final int idColumnSpan = getIdentifierColumnSpan(); - - ArrayList tables = new ArrayList(); - ArrayList keyColumns = new ArrayList(); - ArrayList keyColumnReaders = new ArrayList(); - ArrayList keyColumnReaderTemplates = new ArrayList(); - ArrayList cascadeDeletes = new ArrayList(); - Iterator titer = entityBinding.getTableClosureIterator(); - while ( titer.hasNext() ){ - TableSpecification table = titer.next(); - String tableName = table.getLogicalName().getText(factory.getDialect()); - tables.add( tableName ); - String[] keyCols = new String[idColumnSpan]; - String[] keyColReaders = new String[idColumnSpan]; - String[] keyColReaderTemplates = new String[idColumnSpan]; - PrimaryKey primaryKey= table.getPrimaryKey(); - for ( int k = 0; k < idColumnSpan; k++ ) { - org.hibernate.metamodel.spi.relational.Column column = primaryKey.getColumns().get( k ); - keyCols[k] = column.getColumnName().getText(factory.getDialect()); - keyColReaders[k] = column.getReadExpr( factory.getDialect() ); - keyColReaderTemplates[k] = column.getTemplate( factory.getDialect(), factory.getSqlFunctionRegistry() ); + for ( int k = 0; k < postOrderSubEntityBindings.length; k++ ) { + final EntityBinding eb = postOrderSubEntityBindings[k]; + subclassClosure[k] = eb.getEntityName(); + try { + if ( eb.isPolymorphic() ) { + // we now use subclass ids that are consistent across all + // persisters for a class hierarchy, so that the use of + // "foo.class = Bar" works in HQL + Integer subclassId = eb.getSubEntityBindingId(); + subclassesByDiscriminatorValue.put( subclassId, eb.getEntityName() ); + discriminatorValues[k] = subclassId.toString(); + int id = getTableId( + eb.getPrimaryTable().getQualifiedName( factory.getDialect() ), + subclassTableNameClosure + ); + notNullColumnTableNumbers[k] = id; + notNullColumnNames[k] = subclassTableKeyColumnClosure[id][0]; //( (Column) sc.getTable().getPrimaryKey().getColumnIterator().next() ).getName(); + if(notNullColumnNames[k] == null){ + System.out.println(); + } + } + } + catch ( Exception e ) { + throw new MappingException( "Error parsing discriminator value", e ); } - keyColumns.add( keyCols ); - keyColumnReaders.add( keyColReaders ); - keyColumnReaderTemplates.add( keyColReaderTemplates ); - cascadeDeletes.add( false && factory.getDialect().supportsCascadeDelete() ); //todo add @OnDelete support } - //Span of the tables directly mapped by this entity and super-classes, if any - coreTableSpan = tables.size(); - //todo secondary table - isNullableTable = new boolean[]{true}; - naturalOrderTableNames = ArrayHelper.toStringArray( tables ); - naturalOrderTableKeyColumns = ArrayHelper.to2DStringArray( keyColumns ); - naturalOrderTableKeyColumnReaders = ArrayHelper.to2DStringArray( keyColumnReaders ); - naturalOrderTableKeyColumnReaderTemplates = ArrayHelper.to2DStringArray( keyColumnReaderTemplates ); - naturalOrderCascadeDeleteEnabled = ArrayHelper.toBooleanArray( cascadeDeletes ); - ArrayList subtables = new ArrayList(); - ArrayList isConcretes = new ArrayList(); - ArrayList isDeferreds = new ArrayList(); - ArrayList isLazies = new ArrayList(); - keyColumns = new ArrayList(); - //todo add sub class tables here - //---------------------------------------------- - - tableSpan = -1; - tableNames = null; - tableKeyColumns = null; - tableKeyColumnReaders = null; - tableKeyColumnReaderTemplates = null; - spaces = null; - subclassClosure = null; - subclassTableNameClosure = null; - subclassTableKeyColumnClosure = null; - isClassOrSuperclassTable = null; - naturalOrderPropertyTableNumbers = null; - propertyTableNumbers = null; - subclassPropertyTableNumberClosure = null; - subclassColumnTableNumberClosure = null; - subclassFormulaTableNumberClosure = null; - subclassTableSequentialSelect = null; - subclassTableIsLazyClosure = null; - discriminatorValues = null; - notNullColumnNames = null; - notNullColumnTableNumbers = null; - constraintOrderedTableNames = null; - constraintOrderedKeyColumnNames = null; - //----------------------------- initLockers(); initSubclassPropertyAliasesMap( entityBinding ); postConstruct( mapping ); } - protected boolean isNullableTable(int j) { - if ( j < coreTableSpan ) { - return false; + + + private void resolvePkColumnNames(SessionFactoryImplementor factory, PrimaryKey primaryKey, String[] columns, String[] readers, String[] templates) { + for ( int k = 0; k < primaryKey.getColumnSpan(); k++ ) { + org.hibernate.metamodel.spi.relational.Column column = primaryKey.getColumns().get( k ); + columns[k] = column.getColumnName().getText( factory.getDialect() ); + readers[k] = column.getReadExpr( factory.getDialect() ); + templates[k] = column.getTemplate( + factory.getDialect(), + factory.getSqlFunctionRegistry() + ); } - return isNullableTable[j - coreTableSpan]; + } + + protected boolean isNullableTable(int j) { + return j >= coreTableSpan && isNullableTable[j - coreTableSpan]; } protected boolean isSubclassTableSequentialSelect(int j) { @@ -693,14 +1110,12 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { throw new JDBCException( "could not load by id: " + MessageHelper.infoString(this, id), sqle ); } }*/ - private static final void reverse(Object[] objects, int len) { + private static void reverse(Object[] objects, int len) { Object[] temp = new Object[len]; for ( int i = 0; i < len; i++ ) { temp[i] = objects[len - i - 1]; } - for ( int i = 0; i < len; i++ ) { - objects[i] = temp[i]; - } + System.arraycopy( temp, 0, objects, 0, len ); } @@ -714,20 +1129,26 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { */ private static String[] reverse(String[] objects, int n) { - int size = objects.length; - String[] temp = new String[size]; + final int size = objects.length; + final String[] temp = new String[size]; for ( int i = 0; i < n; i++ ) { temp[i] = objects[n - i - 1]; } - for ( int i = n; i < size; i++ ) { - temp[i] = objects[i]; - } + System.arraycopy( objects, n, temp, n, size - n ); return temp; } + public static void main(String[] args) { + String [] array = {"a", "b", "c", "d", "e"}; + array = reverse( array, 3 ); + for(String s : array){ + System.out.println(s); + } + } + /** * Reverse the first n elements of the incoming array * @@ -743,9 +1164,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { temp[i] = objects[n - i - 1]; } - for ( int i = n; i < size; i++ ) { - temp[i] = objects[i]; - } + System.arraycopy( objects, n, temp, n, size - n ); return temp; } @@ -870,7 +1289,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { if ( index == null ) { return null; } - return tableNames[propertyTableNumbers[index.intValue()]]; + return tableNames[propertyTableNumbers[index]]; } public String[] getConstraintOrderedTableNameClosure() { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index fb753afaf7..7b6cff825b 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -35,7 +35,6 @@ import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.cache.spi.access.EntityRegionAccessStrategy; import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy; -import org.hibernate.cfg.NotYetImplementedException; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.engine.spi.Mapping; @@ -129,9 +128,9 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { private final String[][] constraintOrderedKeyColumnNames; //private final Map propertyTableNumbersByName = new HashMap(); - private final Map propertyTableNumbersByNameAndSubclass = new HashMap(); + private final Map propertyTableNumbersByNameAndSubclass = new HashMap(); - private final Map sequentialSelectStringsByEntityName = new HashMap(); + private final Map sequentialSelectStringsByEntityName = new HashMap(); private static final Object NULL_DISCRIMINATOR = new MarkerObject(""); private static final Object NOT_NULL_DISCRIMINATOR = new MarkerObject(""); @@ -546,7 +545,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { isNullables.add(Boolean.FALSE); isLazies.add(Boolean.FALSE); - for ( SecondaryTable join : entityBinding.getSubclassSecondaryTableClosure() ) { + for ( SecondaryTable join : entityBinding.getEntitiesSecondaryTableClosure() ) { final boolean isConcrete = entityBinding.isClassOrSuperclassSecondaryTable( join ); isConcretes.add( isConcrete ); boolean isDeferred = join.getFetchStyle() != FetchStyle.JOIN; @@ -672,11 +671,11 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { //TODO: code duplication with JoinedSubclassEntityPersister - ArrayList columnJoinNumbers = new ArrayList(); - ArrayList formulaJoinedNumbers = new ArrayList(); - ArrayList propertyJoinNumbers = new ArrayList(); + ArrayList columnJoinNumbers = new ArrayList(); + ArrayList formulaJoinedNumbers = new ArrayList(); + ArrayList propertyJoinNumbers = new ArrayList(); - for ( AttributeBinding attributeBinding : entityBinding.getSubEntityAttributeBindingClosure() ) { + for ( AttributeBinding attributeBinding : entityBinding.getEntitiesAttributeBindingClosure() ) { if ( entityBinding.getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( attributeBinding ) ) { continue; // skip identifier binding } @@ -753,18 +752,6 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { postConstruct( mapping ); } - private static void initializeCustomSql( - CustomSQL customSql, - int i, - String[] sqlStrings, - boolean[] callable, - ExecuteUpdateResultCheckStyle[] checkStyles) { - sqlStrings[i] = customSql != null ? customSql.getSql(): null; - callable[i] = sqlStrings[i] != null && customSql.isCallable(); - checkStyles[i] = customSql != null && customSql.getCheckStyle() != null ? - customSql.getCheckStyle() : - ExecuteUpdateResultCheckStyle.determineDefault( sqlStrings[i], callable[i] ); - } protected boolean isInverseTable(int j) { return isInverseTable[j]; @@ -889,9 +876,11 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { } String[] subclasses = getSubclassClosure(); - for ( int i=0; i tableNumbers = new HashSet(); String[] props = subclassPersister.getPropertyNames(); String[] classes = subclassPersister.getPropertySubclassNames(); for ( int i=0; i columnNumbers = new ArrayList(); final int[] columnTableNumbers = getSubclassColumnTableNumberClosure(); for ( int i=0; i formulaNumbers = new ArrayList(); final int[] formulaTableNumbers = getSubclassColumnTableNumberClosure(); for ( int i=0; i iter = entityBinding.getPreOrderSubEntityBindingClosure(); int k=1; - for(EntityBinding subEntityBinding : iter){ + for(EntityBinding subEntityBinding : entityBinding.getPreOrderSubEntityBindingClosure()){ subclassClosure[k++] = subEntityBinding.getEntityName(); subclassByDiscriminatorValue.put( subEntityBinding.getSubEntityBindingId(), subEntityBinding.getEntityName() ); } @@ -351,8 +319,8 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { } HashSet subclassTables = new HashSet(); - Iterable iter = entityBinding.getPreOrderSubEntityBindingClosure(); - for ( EntityBinding subEntityBinding : iter ) { + final EntityBinding[] subEntityBindings = entityBinding.getPreOrderSubEntityBindingClosure(); + for ( EntityBinding subEntityBinding : entityBinding.getPreOrderSubEntityBindingClosure() ) { subclassTables.add( subEntityBinding.getPrimaryTable().getQualifiedName( factory.getDialect() ) ); } subclassSpaces = ArrayHelper.toStringArray( subclassTables ); @@ -367,12 +335,8 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { tableNames.add( tableName ); keyColumns.add( getIdentifierColumnNames() ); } - Iterator siter = new JoinedIterator( - new SingletonIterator( entityBinding ), - iter.iterator() - ); - while ( siter.hasNext() ) { - EntityBinding eb = siter.next(); + EntityBinding[] ebs = ArrayHelper.join( new EntityBinding[]{entityBinding}, subEntityBindings ); + for(final EntityBinding eb : ebs){ TableSpecification tab = eb.getPrimaryTable(); if ( isNotAbstractUnionTable( eb ) ) { String tableName = tab.getQualifiedName( factory.getDialect() ); diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 13e01e9c56..092ce89fd4 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -412,7 +412,9 @@ public class EntityMetamodel implements Serializable { else { identifierAttributeBindingSpan = 1; } - propertySpan = entityBinding.getAttributeBindingClosureSpan() - identifierAttributeBindingSpan; + + final AttributeBinding [] attributeBindings = entityBinding.getAttributeBindingClosure(); + propertySpan = attributeBindings.length; properties = new StandardProperty[propertySpan]; List naturalIdNumbers = new ArrayList(); @@ -442,7 +444,7 @@ public class EntityMetamodel implements Serializable { boolean foundUpdateGeneratedValue = false; boolean foundUpdateableNaturalIdProperty = false; - for ( AttributeBinding attributeBinding : entityBinding.getAttributeBindingClosure() ) { + for ( AttributeBinding attributeBinding : attributeBindings ) { if ( entityBinding.getHierarchyDetails().getEntityIdentifier().isIdentifierAttributeBinding( attributeBinding ) ) { // skip the identifier attribute binding continue; diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/InheritanceBindingTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/InheritanceBindingTest.java index 0169757934..c78c8e7d21 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/InheritanceBindingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/InheritanceBindingTest.java @@ -124,8 +124,8 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertFalse( noInheritanceEntityBinding.isPolymorphic() ); assertFalse( noInheritanceEntityBinding.hasSubEntityBindings() ); assertEquals( 0, noInheritanceEntityBinding.getSubEntityBindingClosureSpan() ); - assertFalse( noInheritanceEntityBinding.getPostOrderSubEntityBindingClosure().iterator().hasNext() ); - assertFalse( noInheritanceEntityBinding.getPreOrderSubEntityBindingClosure().iterator().hasNext() ); + assertEquals( 0, noInheritanceEntityBinding.getPostOrderSubEntityBindingClosure().length ); + assertEquals( 0, noInheritanceEntityBinding.getPreOrderSubEntityBindingClosure().length ); Set directAttributeBindings = new HashSet(); for ( AttributeBinding attributeBinding : noInheritanceEntityBinding.attributeBindings() ) { assertTrue( directAttributeBindings.add( attributeBinding ) ); @@ -140,14 +140,17 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( iterator.hasNext() ); assertSame( noInheritanceEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(), iterator.next() ); assertFalse( iterator.hasNext() ); - iterator = noInheritanceEntityBinding.getAttributeBindingClosure().iterator(); - assertTrue( iterator.hasNext() ); - assertSame( noInheritanceEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(), iterator.next() ); - assertFalse( iterator.hasNext() ); - iterator = noInheritanceEntityBinding.getSubEntityAttributeBindingClosure().iterator(); - assertTrue( iterator.hasNext() ); - assertSame( noInheritanceEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(), iterator.next() ); - assertFalse( iterator.hasNext() ); + + AttributeBinding[] attributeBindings = noInheritanceEntityBinding.getAttributeBindingClosure(); + assertTrue( attributeBindings.length > 0 ); + int index =0; + assertSame( noInheritanceEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(), attributeBindings[index++] ); + assertFalse( index < attributeBindings.length ); + attributeBindings = noInheritanceEntityBinding.getEntitiesAttributeBindingClosure(); + index = 0; + assertTrue( attributeBindings.length > 0 ); + assertSame( noInheritanceEntityBinding.getHierarchyDetails().getEntityIdentifier().getAttributeBinding(), attributeBindings[index++] ); + assertFalse( index < attributeBindings.length ); } @Test @@ -198,7 +201,7 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertEquals( 1, attributeBindingClosure.size() ); assertTrue( attributeBindingClosure.contains( rootEntityBinding.locateAttributeBinding( "id" ) ) ); Set subAttributeBindings = new HashSet(); - for ( AttributeBinding subAttributeBinding : rootEntityBinding.getSubEntityAttributeBindingClosure() ) { + for ( AttributeBinding subAttributeBinding : rootEntityBinding.getEntitiesAttributeBindingClosure() ) { assertTrue( subAttributeBindings.add( subAttributeBinding ) ); } assertEquals( 4, subAttributeBindings.size() ); @@ -225,23 +228,24 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { Iterator directEntityBindingIterator = rootEntityBinding.getDirectSubEntityBindings().iterator(); boolean isSubclassEntityBindingFirst = subclassEntityBinding == directEntityBindingIterator.next(); assertEquals( 3, rootEntityBinding.getSubEntityBindingClosureSpan() ); - Iterator subEntityBindingIterator = rootEntityBinding.getPreOrderSubEntityBindingClosure().iterator(); - assertTrue( subEntityBindingIterator.hasNext() ); + EntityBinding[] subEntityBindingIterator = rootEntityBinding.getPreOrderSubEntityBindingClosure(); + assertTrue( subEntityBindingIterator.length > 0 ); + int i = 0; if ( isSubclassEntityBindingFirst ) { - assertSame( subclassEntityBinding, subEntityBindingIterator.next() ); - assertTrue( subEntityBindingIterator.hasNext() ); - assertSame( subclassOfSubclassEntityBinding, subEntityBindingIterator.next() ); - assertTrue( subEntityBindingIterator.hasNext() ); - assertSame( otherSubclassEntityBinding, subEntityBindingIterator.next() ); + assertSame( subclassEntityBinding, subEntityBindingIterator[i++] ); + assertTrue( i directEntityBindingIterator = rootEntityBinding.getDirectSubEntityBindings().iterator(); boolean isSubclassEntityBindingFirst = subclassEntityBinding == directEntityBindingIterator.next(); assertEquals( 3, rootEntityBinding.getSubEntityBindingClosureSpan() ); - Iterator subEntityBindingIterator = rootEntityBinding.getPostOrderSubEntityBindingClosure().iterator(); - assertTrue( subEntityBindingIterator.hasNext() ); + EntityBinding[] subEntityBindingIterator = rootEntityBinding.getPostOrderSubEntityBindingClosure(); + int i =0; + assertTrue( subEntityBindingIterator.length > 0 ); if ( isSubclassEntityBindingFirst ) { - assertSame( subclassOfSubclassEntityBinding, subEntityBindingIterator.next() ); - assertSame( subclassEntityBinding, subEntityBindingIterator.next() ); - assertSame( otherSubclassEntityBinding, subEntityBindingIterator.next() ); + assertSame( subclassOfSubclassEntityBinding, subEntityBindingIterator[i++] ); + assertSame( subclassEntityBinding, subEntityBindingIterator[i++] ); + assertSame( otherSubclassEntityBinding, subEntityBindingIterator[i++] ); } else { - assertSame( subclassOfSubclassEntityBinding, subEntityBindingIterator.next() ); - assertSame( otherSubclassEntityBinding, subEntityBindingIterator.next() ); - assertSame( subclassEntityBinding, subEntityBindingIterator.next() ); + assertSame( subclassOfSubclassEntityBinding, subEntityBindingIterator[i++] ); + assertSame( otherSubclassEntityBinding, subEntityBindingIterator[i++] ); + assertSame( subclassEntityBinding, subEntityBindingIterator[i++] ); } - assertFalse( subEntityBindingIterator.hasNext() ); + assertFalse( i < subEntityBindingIterator.length ); } @Test @@ -297,8 +302,8 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( otherSubclassEntityBinding.isPolymorphic() ); assertFalse( otherSubclassEntityBinding.hasSubEntityBindings() ); assertEquals( 0, otherSubclassEntityBinding.getSubEntityBindingClosureSpan() ); - assertFalse( otherSubclassEntityBinding.getPostOrderSubEntityBindingClosure().iterator().hasNext() ); - assertFalse( otherSubclassEntityBinding.getPreOrderSubEntityBindingClosure().iterator().hasNext() ); + assertFalse( otherSubclassEntityBinding.getPostOrderSubEntityBindingClosure().length > 0 ); + assertFalse( otherSubclassEntityBinding.getPreOrderSubEntityBindingClosure().length > 0 ); Set directAttributeBindings = new HashSet(); for ( AttributeBinding attributeBinding : otherSubclassEntityBinding.attributeBindings() ) { assertTrue( directAttributeBindings.add( attributeBinding ) ); @@ -314,7 +319,7 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( attributeBindingClosure.contains( rootEntityBinding.locateAttributeBinding( "id" ) ) ); assertTrue( attributeBindingClosure.contains( otherSubclassEntityBinding.locateAttributeBinding( "otherName" ) ) ); Set subAttributeBindings = new HashSet(); - for ( AttributeBinding subAttributeBinding : otherSubclassEntityBinding.getSubEntityAttributeBindingClosure() ) { + for ( AttributeBinding subAttributeBinding : otherSubclassEntityBinding.getEntitiesAttributeBindingClosure() ) { assertTrue( subAttributeBindings.add( subAttributeBinding ) ); } assertEquals( 2, subAttributeBindings.size() ); @@ -343,14 +348,16 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( subclassEntityBinding.isPolymorphic() ); assertTrue( subclassEntityBinding.hasSubEntityBindings() ); assertEquals( 1, subclassEntityBinding.getSubEntityBindingClosureSpan() ); - Iterator itSubEntityBindings = subclassEntityBinding.getPostOrderSubEntityBindingClosure().iterator(); - assertTrue( itSubEntityBindings.hasNext() ); - assertSame( subclassOfSubclassEntityBinding, itSubEntityBindings.next() ); - assertFalse( itSubEntityBindings.hasNext() ); - itSubEntityBindings = subclassEntityBinding.getPreOrderSubEntityBindingClosure().iterator(); - assertTrue( itSubEntityBindings.hasNext() ); - assertSame( subclassOfSubclassEntityBinding, itSubEntityBindings.next() ); - assertFalse( itSubEntityBindings.hasNext() ); + EntityBinding[] itSubEntityBindings = subclassEntityBinding.getPostOrderSubEntityBindingClosure(); + int i = 0; + assertTrue( i < itSubEntityBindings.length ); + assertSame( subclassOfSubclassEntityBinding, itSubEntityBindings[i++] ); + assertFalse( i < itSubEntityBindings.length ); + itSubEntityBindings = subclassEntityBinding.getPreOrderSubEntityBindingClosure(); + i = 0; + assertTrue( i < itSubEntityBindings.length ); + assertSame( subclassOfSubclassEntityBinding, itSubEntityBindings[i++] ); + assertFalse( i < itSubEntityBindings.length ); Set directAttributeBindings = new HashSet(); for ( AttributeBinding attributeBinding : subclassEntityBinding.attributeBindings() ) { assertTrue( directAttributeBindings.add( attributeBinding ) ); @@ -366,7 +373,7 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( attributeBindingClosure.contains( rootEntityBinding.locateAttributeBinding( "id" ) ) ); assertTrue( attributeBindingClosure.contains( subclassEntityBinding.locateAttributeBinding( "name" ) ) ); Set subAttributeBindings = new HashSet(); - for ( AttributeBinding subAttributeBinding : subclassEntityBinding.getSubEntityAttributeBindingClosure() ) { + for ( AttributeBinding subAttributeBinding : subclassEntityBinding.getEntitiesAttributeBindingClosure() ) { assertTrue( subAttributeBindings.add( subAttributeBinding ) ); } assertEquals( 3, subAttributeBindings.size() ); @@ -396,8 +403,8 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( subclassOfSubclassEntityBinding.isPolymorphic() ); assertFalse( subclassOfSubclassEntityBinding.hasSubEntityBindings() ); assertEquals( 0, subclassOfSubclassEntityBinding.getSubEntityBindingClosureSpan() ); - assertFalse( subclassOfSubclassEntityBinding.getPostOrderSubEntityBindingClosure().iterator().hasNext() ); - assertFalse( subclassOfSubclassEntityBinding.getPreOrderSubEntityBindingClosure().iterator().hasNext() ); + assertFalse( subclassOfSubclassEntityBinding.getPostOrderSubEntityBindingClosure().length > 0 ); + assertFalse( subclassOfSubclassEntityBinding.getPreOrderSubEntityBindingClosure().length > 0 ); Set directAttributeBindings = new HashSet(); for ( AttributeBinding attributeBinding : subclassOfSubclassEntityBinding.attributeBindings() ) { assertTrue( directAttributeBindings.add( attributeBinding ) ); @@ -414,7 +421,7 @@ public class InheritanceBindingTest extends BaseAnnotationBindingTestCase { assertTrue( attributeBindingClosure.contains( subclassEntityBinding.locateAttributeBinding( "name" ) ) ); assertTrue( attributeBindingClosure.contains( subclassOfSubclassEntityBinding.locateAttributeBinding( "otherOtherName" ) ) ); Set subAttributeBindings = new HashSet(); - for ( AttributeBinding subAttributeBinding : subclassOfSubclassEntityBinding.getSubEntityAttributeBindingClosure() ) { + for ( AttributeBinding subAttributeBinding : subclassOfSubclassEntityBinding.getEntitiesAttributeBindingClosure() ) { assertTrue( subAttributeBindings.add( subAttributeBinding ) ); } assertEquals( 3, subAttributeBindings.size() ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/various/IndexTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/various/IndexTest.java index 436dc2be2a..b6b0f1dd3c 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/various/IndexTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/various/IndexTest.java @@ -28,13 +28,11 @@ import java.util.Date; import org.junit.Test; import org.hibernate.Session; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; /** * @author Emmanuel Bernard */ -@FailureExpectedWithNewMetamodel public class IndexTest extends BaseCoreFunctionalTestCase { @Test public void testIndexManyToOne() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java index 1a40ef56c7..d88ba734ce 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/ejb3/OrmVersion1SupportedTest.java @@ -58,7 +58,7 @@ public class OrmVersion1SupportedTest extends BaseCoreFunctionalTestCase { @FailureExpectedWithNewMetamodel // This doesn't actually work since this test class requires BMUnitRunner instead of // CustomRunner. Thus, the if block below skips the test if the new metamodel is being used. public void testOrm1Support() { - if ( Boolean.getBoolean( USE_NEW_METADATA_MAPPINGS ) ) { + if ( isMetadataUsed ) { return; } // need to call buildSessionFactory, because this test is not using org.hibernate.testing.junit4.CustomRunner diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/HbmWithIdentityTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/HbmWithIdentityTest.java index f2359c641b..9da7cd5634 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/HbmWithIdentityTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/xml/hbm/HbmWithIdentityTest.java @@ -27,7 +27,6 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.testing.DialectChecks; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -35,7 +34,6 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; * @author Emmanuel Bernard */ @RequiresDialectFeature(DialectChecks.SupportsIdentityColumns.class) -@FailureExpectedWithNewMetamodel public class HbmWithIdentityTest extends BaseCoreFunctionalTestCase { @Test public void testManyToOneAndInterface() throws Exception { diff --git a/hibernate-core/src/test/java/org/hibernate/test/collection/ordered/joinedInheritence/OrderCollectionOfJoinedHierarchyTest.java b/hibernate-core/src/test/java/org/hibernate/test/collection/ordered/joinedInheritence/OrderCollectionOfJoinedHierarchyTest.java index 919fe4f7e5..5ba37c0374 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/collection/ordered/joinedInheritence/OrderCollectionOfJoinedHierarchyTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/collection/ordered/joinedInheritence/OrderCollectionOfJoinedHierarchyTest.java @@ -26,13 +26,11 @@ package org.hibernate.test.collection.ordered.joinedInheritence; import org.junit.Test; import org.hibernate.Session; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; /** * @author Steve Ebersole */ -@FailureExpectedWithNewMetamodel public class OrderCollectionOfJoinedHierarchyTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java index e2c87aae8d..f7919b044e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/SQLFunctionsTest.java @@ -59,7 +59,7 @@ import static org.junit.Assert.assertTrue; @SuppressWarnings( {"UnnecessaryUnboxing", "UnnecessaryBoxing"}) -@FailureExpectedWithNewMetamodel +//@FailureExpectedWithNewMetamodel public class SQLFunctionsTest extends LegacyTestCase { private static final Logger log = Logger.getLogger( SQLFunctionsTest.class ); @@ -217,6 +217,7 @@ public class SQLFunctionsTest extends LegacyTestCase { } @Test + @FailureExpectedWithNewMetamodel public void testBroken() throws Exception { Session s = openSession(); Transaction t = s.beginTransaction(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/naturalid/inheritance/InheritedNaturalIdTest.java b/hibernate-core/src/test/java/org/hibernate/test/naturalid/inheritance/InheritedNaturalIdTest.java index 20cc6ddc05..a32e2bcfd0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/naturalid/inheritance/InheritedNaturalIdTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/naturalid/inheritance/InheritedNaturalIdTest.java @@ -26,7 +26,6 @@ package org.hibernate.test.naturalid.inheritance; import org.junit.Test; import org.hibernate.Session; -import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertNotNull; @@ -36,7 +35,6 @@ import static org.junit.Assert.assertSame; /** * @author Steve Ebersole */ -@FailureExpectedWithNewMetamodel public class InheritedNaturalIdTest extends BaseCoreFunctionalTestCase { @Override protected Class[] getAnnotatedClasses() { diff --git a/hibernate-core/src/test/java/org/hibernate/test/queryplan/GetHqlQueryPlanTest.java b/hibernate-core/src/test/java/org/hibernate/test/queryplan/GetHqlQueryPlanTest.java index cf1d39d1a0..5134df127f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/queryplan/GetHqlQueryPlanTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/queryplan/GetHqlQueryPlanTest.java @@ -43,7 +43,7 @@ import static org.junit.Assert.assertTrue; * * @author Gail Badner */ -@FailureExpectedWithNewMetamodel + public class GetHqlQueryPlanTest extends BaseCoreFunctionalTestCase { @Override public String[] getMappings() { @@ -82,7 +82,7 @@ public class GetHqlQueryPlanTest extends BaseCoreFunctionalTestCase { s.close(); } - + @FailureExpectedWithNewMetamodel @Test @SuppressWarnings( {"UnnecessaryBoxing"}) public void testHqlQueryPlanWithEnabledFilter() { diff --git a/hibernate-core/src/test/resources/org/hibernate/test/legacy/Blobber.hbm.xml b/hibernate-core/src/test/resources/org/hibernate/test/legacy/Blobber.hbm.xml index 9a2e21ddea..66b368d717 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/legacy/Blobber.hbm.xml +++ b/hibernate-core/src/test/resources/org/hibernate/test/legacy/Blobber.hbm.xml @@ -5,7 +5,7 @@ - + diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java index ebe31e5305..95bd4c5716 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java @@ -29,6 +29,7 @@ import java.util.List; import org.jboss.logging.Logger; +import org.hibernate.EntityMode; import org.hibernate.MappingException; import org.hibernate.jpa.event.spi.jpa.Callback; import org.hibernate.jpa.event.spi.jpa.ListenerFactory; @@ -62,29 +63,29 @@ public class CallbackProcessorImpl implements CallbackProcessor { @Override public void processCallbacksForEntity(Object entityObject, CallbackRegistryImpl callbackRegistry) { final EntityBinding entityBinding = (EntityBinding) entityObject; - final String entityClassName = entityBinding.getEntity().getClassName(); - if ( entityClassName == null ) { + if ( entityBinding.getHierarchyDetails().getEntityMode() != EntityMode.POJO ) { return; } - - try { - final Class entityClass = classLoaderService.classForName( entityClassName ); - for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) { - callbackRegistry.addEntityCallbacks( - entityClass, - annotationClass, - collectCallbacks( entityBinding, entityClass, annotationClass ) - ); - } - } - catch (ClassLoadingException e) { - throw new MappingException( "entity class not found: " + entityClassName, e ); + final Class entityClass = entityBinding.getEntity().getClassReference(); + for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) { + callbackRegistry.addEntityCallbacks( + entityClass, + annotationClass, + collectCallbacks( entityBinding, entityClass, annotationClass ) + ); } } + private final static Callback[] EMPTY_CALLBACK = new Callback[0]; + private Callback[] collectCallbacks(EntityBinding entityBinding, Class entityClass, Class annotationClass) { - final List callbacks = new ArrayList(); - for ( JpaCallbackSource jpaCallbackClass : entityBinding.getJpaCallbackClasses() ) { + if ( entityBinding.getJpaCallbackClasses() == null || entityBinding.getJpaCallbackClasses().isEmpty() ) { + return EMPTY_CALLBACK; + } + final int size = entityBinding.getJpaCallbackClasses().size(); + final Callback[] result = new Callback[size]; + for ( int i = 0; i < size; i++ ) { + final JpaCallbackSource jpaCallbackClass = entityBinding.getJpaCallbackClasses().get( i ); final Class listenerClass = classLoaderService.classForName( jpaCallbackClass.getName() ); final String methodName = jpaCallbackClass.getCallbackMethod( annotationClass ); @@ -100,9 +101,9 @@ public class CallbackProcessorImpl implements CallbackProcessor { ? createListenerCallback( listenerClass, entityClass, methodName ) : createBeanCallback( listenerClass, methodName ); assert callback != null; - callbacks.add(callback); + result[i] = callback; } - return callbacks.toArray(new Callback[callbacks.size()]); + return result; } private Callback createListenerCallback( @@ -141,24 +142,32 @@ public class CallbackProcessorImpl implements CallbackProcessor { return null; } - private Callback createBeanCallback( Class callbackClass, - String methodName ) { + private Callback createBeanCallback(Class callbackClass, + String methodName) { Class callbackSuperclass = callbackClass.getSuperclass(); - if (callbackSuperclass != null) { - Callback callback = createBeanCallback(callbackSuperclass, methodName); - if (callback != null) return callback; + if ( callbackSuperclass != null ) { + Callback callback = createBeanCallback( callbackSuperclass, methodName ); + if ( callback != null ) { + return callback; + } } - for (Method method : callbackClass.getDeclaredMethods()) { - if (!method.getName().equals(methodName)) continue; - if (method.getParameterTypes().length != 0) continue; - if (!method.isAccessible()) method.setAccessible(true); - return new EntityCallback(method); + for ( Method method : callbackClass.getDeclaredMethods() ) { + if ( !method.getName().equals( methodName ) ) { + continue; + } + if ( method.getParameterTypes().length != 0 ) { + continue; + } + if ( !method.isAccessible() ) { + method.setAccessible( true ); + } + return new EntityCallback( method ); } return null; } @Override public void release() { - //To change body of implemented methods use File | Settings | File Templates. + // N/A } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java index 10e40f33e7..dc27353aa0 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java @@ -33,6 +33,7 @@ import javax.persistence.PreRemove; import javax.persistence.PreUpdate; import java.util.HashMap; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.jpa.event.spi.jpa.Callback; import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; @@ -59,11 +60,7 @@ public class CallbackRegistryImpl implements CallbackRegistry { @Override public boolean hasPostCreateCallbacks(Class entityClass) { - return notEmpty( preCreates.get( entityClass ) ); - } - - private boolean notEmpty(Callback[] callbacks) { - return callbacks != null && callbacks.length > 0; + return CollectionHelper.isNotEmpty( preCreates.get( entityClass ) ); } @Override @@ -78,7 +75,7 @@ public class CallbackRegistryImpl implements CallbackRegistry { @Override public boolean hasPostUpdateCallbacks(Class entityClass) { - return notEmpty( postUpdates.get( entityClass ) ); + return CollectionHelper.isNotEmpty( postUpdates.get( entityClass ) ); } @Override @@ -93,7 +90,7 @@ public class CallbackRegistryImpl implements CallbackRegistry { @Override public boolean hasPostRemoveCallbacks(Class entityClass) { - return notEmpty( postRemoves.get( entityClass ) ); + return CollectionHelper.isNotEmpty( postRemoves.get( entityClass ) ); } @Override @@ -107,7 +104,7 @@ public class CallbackRegistryImpl implements CallbackRegistry { } private boolean callback(Callback[] callbacks, Object bean) { - if ( callbacks != null && callbacks.length != 0 ) { + if ( CollectionHelper.isNotEmpty( callbacks ) ) { for ( Callback callback : callbacks ) { callback.performCallback( bean ); }