From 9b3e6a3714c2238a54d2835e6f1b728a01642fe9 Mon Sep 17 00:00:00 2001 From: Strong Liu Date: Wed, 28 Nov 2012 18:12:57 +0800 Subject: [PATCH] HHH-7380 - union subclass support --- .../DefaultIdentifierGeneratorFactory.java | 2 +- .../hibernate/internal/util/StringHelper.java | 1 + .../util/collections/ArrayHelper.java | 8 + .../hibernate/metamodel/internal/Binder.java | 28 +- .../source/annotations/EntitySourceImpl.java | 2 +- .../annotations/entity/EntityClass.java | 11 +- .../source/hbm/AbstractEntitySourceImpl.java | 17 +- .../metamodel/spi/binding/EntityBinding.java | 10 +- .../AbstractTableSpecification.java | 16 +- .../metamodel/spi/relational/Table.java | 1 + .../spi/relational/ValueContainer.java | 2 + .../metamodel/spi/source/EntitySource.java | 4 +- .../entity/SingleTableEntityPersister.java | 2 +- .../entity/UnionSubclassEntityPersister.java | 241 +++++++++++++++++- .../internal/source/AssertSourcesTest.java | 4 +- .../entity/SynchronizeBindingTest.java | 19 +- 16 files changed, 316 insertions(+), 52 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java index b4f5cd2c2a..fdcb237e0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/id/factory/internal/DefaultIdentifierGeneratorFactory.java @@ -88,7 +88,7 @@ public class DefaultIdentifierGeneratorFactory implements MutableIdentifierGener register( "enhanced-sequence", SequenceStyleGenerator.class ); register( "enhanced-table", TableGenerator.class ); } - + @Override public void register(String strategy, Class generatorClass) { LOG.debugf( "Registering IdentifierGenerator strategy [%s] -> [%s]", strategy, generatorClass.getName() ); final Class previous = generatorStrategyToClassNameMap.put( strategy, generatorClass ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 19f3e64833..7d8a3fa23a 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -37,6 +37,7 @@ public final class StringHelper { private static final int ALIAS_TRUNCATE_LENGTH = 10; public static final String WHITESPACE = " \n\r\f\t"; + public static final String [] EMPTY_STRINGS = new String[0]; private StringHelper() { /* static methods only - hide constructor */ } 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 f43e35b6b4..f3c96eb6b8 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 @@ -347,6 +347,14 @@ public final class ArrayHelper { return true; } + public static boolean isEmpty(final Object[] arrays) { + return arrays == null || arrays.length == 0; + } + + public static boolean isNotEmpty(final Object[] arrays) { + return !isEmpty( arrays ); + } + /** * Compare 2 arrays only at the first level */ 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 f3781a27b5..431f82f205 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 @@ -854,15 +854,17 @@ public class Binder { final boolean hasPrimaryKeyJoinColumns = CollectionHelper.isNotEmpty( primaryKeyJoinColumnSources ); final List superEntityBindingPrimaryKeyColumns = superEntityBinding.getPrimaryTable().getPrimaryKey().getColumns(); - for ( int i = 0; i < superEntityBindingPrimaryKeyColumns.size(); i++ ) { + for ( int i = 0, size = superEntityBindingPrimaryKeyColumns.size(); i < size; i++ ) { Column superEntityBindingPrimaryKeyColumn = superEntityBindingPrimaryKeyColumns.get( i ); PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource = hasPrimaryKeyJoinColumns && i < primaryKeyJoinColumnSources .size() ? primaryKeyJoinColumnSources.get( i ) : null; final String columnName; if ( primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( primaryKeyJoinColumnSource.getColumnName() ) ) { - columnName = bindingContext().getNamingStrategy().columnName( primaryKeyJoinColumnSource.getColumnName() ); - } else { - columnName = superEntityBindingPrimaryKeyColumn.getColumnName().getText(); + columnName = bindingContext().getNamingStrategy() + .columnName( primaryKeyJoinColumnSource.getColumnName() ); + } + else { + columnName = superEntityBindingPrimaryKeyColumn.getColumnName().getText(); } Column column = entityBinding.getPrimaryTable().locateOrCreateColumn( columnName ); column.setCheckCondition( superEntityBindingPrimaryKeyColumn.getCheckCondition() ); @@ -873,12 +875,10 @@ public class Binder { column.setReadFragment( superEntityBindingPrimaryKeyColumn.getReadFragment() ); column.setWriteFragment( superEntityBindingPrimaryKeyColumn.getWriteFragment() ); column.setUnique( superEntityBindingPrimaryKeyColumn.isUnique() ); - final String sqlType; - if(primaryKeyJoinColumnSource!=null && StringHelper.isNotEmpty( primaryKeyJoinColumnSource.getColumnDefinition() )){ - sqlType = primaryKeyJoinColumnSource.getColumnDefinition(); - } else { - sqlType = superEntityBindingPrimaryKeyColumn.getSqlType(); - } + final String sqlType = getSqlTypeFromPrimaryKeyJoinColumnSourceIfExist( + superEntityBindingPrimaryKeyColumn, + primaryKeyJoinColumnSource + ); column.setSqlType( sqlType ); column.setSize( superEntityBindingPrimaryKeyColumn.getSize() ); column.setJdbcDataType( superEntityBindingPrimaryKeyColumn.getJdbcDataType() ); @@ -889,6 +889,14 @@ public class Binder { } } + private String getSqlTypeFromPrimaryKeyJoinColumnSourceIfExist(Column superEntityBindingPrimaryKeyColumn, PrimaryKeyJoinColumnSource primaryKeyJoinColumnSource) { + final boolean isColumnDefOverrided = primaryKeyJoinColumnSource != null && StringHelper.isNotEmpty( + primaryKeyJoinColumnSource.getColumnDefinition() + ); + return isColumnDefOverrided ? primaryKeyJoinColumnSource.getColumnDefinition() : superEntityBindingPrimaryKeyColumn + .getSqlType(); + } + /** * Binding a single entity hierarchy. * diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java index afde46978f..38f8849a9e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/EntitySourceImpl.java @@ -195,7 +195,7 @@ public class EntitySourceImpl implements EntitySource { } @Override - public List getSynchronizedTableNames() { + public String[] getSynchronizedTableNames() { return entityClass.getSynchronizedTableNames(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/entity/EntityClass.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/entity/EntityClass.java index e8142eccbf..9e48deb27e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/entity/EntityClass.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/entity/EntityClass.java @@ -81,7 +81,7 @@ public class EntityClass extends ConfiguredClass { private final String explicitEntityName; private final String customLoaderQueryName; - private final List synchronizedTableNames; + private final String[] synchronizedTableNames; private final int batchSize; private boolean isImmutable; @@ -215,7 +215,7 @@ public class EntityClass extends ConfiguredClass { return customDelete; } - public List getSynchronizedTableNames() { + public String[] getSynchronizedTableNames() { return synchronizedTableNames; } @@ -495,16 +495,15 @@ public class EntityClass extends ConfiguredClass { return customLoader; } - private List determineSynchronizedTableNames() { + private String[] determineSynchronizedTableNames() { final AnnotationInstance synchronizeAnnotation = JandexHelper.getSingleAnnotation( getClassInfo(), HibernateDotNames.SYNCHRONIZE ); if ( synchronizeAnnotation != null ) { - final String[] tableNames = synchronizeAnnotation.value().asStringArray(); - return Arrays.asList( tableNames ); + return synchronizeAnnotation.value().asStringArray(); } else { - return Collections.emptyList(); + return StringHelper.EMPTY_STRINGS; } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractEntitySourceImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractEntitySourceImpl.java index 86326fdb2f..007b6501b0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractEntitySourceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/AbstractEntitySourceImpl.java @@ -31,6 +31,7 @@ import java.util.Set; import org.hibernate.EntityMode; import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.jaxb.spi.Origin; import org.hibernate.jaxb.spi.hbm.EntityElement; import org.hibernate.jaxb.spi.hbm.JaxbAnyElement; @@ -444,12 +445,18 @@ public abstract class AbstractEntitySourceImpl } @Override - public List getSynchronizedTableNames() { - List tableNames = new ArrayList(); - for ( JaxbSynchronizeElement synchronizeElement : entityElement.getSynchronize() ) { - tableNames.add( synchronizeElement.getTable() ); + public String[] getSynchronizedTableNames() { + if ( CollectionHelper.isEmpty( entityElement.getSynchronize() ) ) { + return StringHelper.EMPTY_STRINGS; + } + else { + final int size = entityElement.getSynchronize().size(); + final String[] synchronizedTableNames = new String[size]; + for ( int i = 0; i < size; i++ ) { + synchronizedTableNames[i] = entityElement.getSynchronize().get( i ).getTable(); + } + return synchronizedTableNames; } - return tableNames; } @Override 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 3b91bc4e22..00505d5c3a 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 @@ -34,7 +34,9 @@ import java.util.Set; import org.hibernate.AssertionFailure; import org.hibernate.EntityMode; import org.hibernate.engine.spi.FilterDefinition; +import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.ValueHolder; +import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.JoinedIterable; import org.hibernate.internal.util.collections.JoinedIterator; import org.hibernate.internal.util.collections.SingletonIterator; @@ -102,7 +104,7 @@ public class EntityBinding extends AbstractAttributeBindingContainer { private CustomSQL customUpdate; private CustomSQL customDelete; - private Set synchronizedTableNames = new HashSet(); + private String[] synchronizedTableNames = StringHelper.EMPTY_STRINGS; private Map attributeBindingMap = new HashMap(); private List jpaCallbackClasses = new ArrayList(); @@ -435,12 +437,12 @@ public class EntityBinding extends AbstractAttributeBindingContainer { this.isAbstract = isAbstract; } - public Set getSynchronizedTableNames() { + public String[] getSynchronizedTableNames() { return synchronizedTableNames; } - public void addSynchronizedTableNames(java.util.Collection synchronizedTableNames) { - this.synchronizedTableNames.addAll( synchronizedTableNames ); + public void addSynchronizedTableNames(String [] synchronizedTableNames) { + this.synchronizedTableNames = ArrayHelper.join( this.synchronizedTableNames, synchronizedTableNames ); } public String getEntityName() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java index 2aad0d7bb1..dfec35674c 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/AbstractTableSpecification.java @@ -72,11 +72,20 @@ public abstract class AbstractTableSpecification implements TableSpecification { public Column locateColumn(String name) { final Identifier identifier = Identifier.toIdentifier( name ); if ( valueMap.containsKey( identifier ) ) { - return (Column) valueMap.get( identifier ); + Value value = valueMap.get( identifier ); + return Column.class.isInstance( value ) ? Column.class.cast( value ) : null; } return null; } + @Override + public boolean hasValue(Value value) { + //shortcut + if ( this != value.getTable() ) { + return false; + } + return valueMap.containsValue( value ); + } @Override public Column createColumn(String name) { @@ -95,7 +104,10 @@ public abstract class AbstractTableSpecification implements TableSpecification { public DerivedValue locateOrCreateDerivedValue(String fragment) { final Identifier identifier = Identifier.toIdentifier( fragment ); if ( valueMap.containsKey( identifier ) ) { - return (DerivedValue) valueMap.get( identifier ); + Value value = valueMap.get( identifier ); + if ( DerivedValue.class.isInstance( value ) ) { + return DerivedValue.class.cast( value ); + } } final DerivedValue value = new DerivedValue( this, valueList.size(), fragment ); valueMap.put( identifier, value ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/Table.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/Table.java index 3e1e675da1..22130cc132 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/Table.java @@ -284,6 +284,7 @@ public class Table extends AbstractTableSpecification implements Exportable { */ public Iterable sortedColumns() { final Set sortedColumns = new LinkedHashSet(); + // Adding primary key columns. sortedColumns.addAll( getPrimaryKey().getColumns() ); // Adding foreign key columns. diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/ValueContainer.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/ValueContainer.java index 0fc5473d5d..301f7e14c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/ValueContainer.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/relational/ValueContainer.java @@ -41,6 +41,8 @@ public interface ValueContainer { */ public List values(); + public boolean hasValue(Value value); + /** * Get a qualifier which can be used to qualify {@link Value values} belonging to this container in * their logging. diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/EntitySource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/EntitySource.java index f91425eab4..03e0604002 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/EntitySource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/source/EntitySource.java @@ -184,9 +184,9 @@ public interface EntitySource extends SubclassEntityContainer, AttributeSourceCo /** * Obtain any additional table names on which to synchronize (auto flushing) this entity. * - * @return Additional synchronized table names. + * @return Additional synchronized table names or 0 sized String array, never return null. */ - public List getSynchronizedTableNames(); + public String[] getSynchronizedTableNames(); /** * Get the actual discriminator value in case of a single table inheritance 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 58eb8ba64d..ab11efcaa7 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 @@ -504,7 +504,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { spaces = ArrayHelper.join( qualifiedTableNames, - ArrayHelper.toStringArray( entityBinding.getSynchronizedTableNames() ) + entityBinding.getSynchronizedTableNames() ); final boolean lazyAvailable = isInstrumented(); diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index 8ae23845b9..87a9db30f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import org.hibernate.AssertionFailure; @@ -52,7 +53,11 @@ import org.hibernate.mapping.Column; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Table; +import org.hibernate.metamodel.spi.binding.CustomSQL; import org.hibernate.metamodel.spi.binding.EntityBinding; +import org.hibernate.metamodel.spi.binding.InheritanceType; +import org.hibernate.metamodel.spi.relational.TableSpecification; +import org.hibernate.metamodel.spi.relational.Value; import org.hibernate.sql.SelectFragment; import org.hibernate.sql.SimpleSelect; import org.hibernate.type.StandardBasicTypes; @@ -241,6 +246,30 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { postConstruct(mapping); } + + private static class CustomSQLMetadata { + final boolean[] callable; + final String[] sqls; + final ExecuteUpdateResultCheckStyle[] checkStyles; + + CustomSQLMetadata(String sql, boolean callable, ExecuteUpdateResultCheckStyle checkStyle) { + this.callable = new boolean[] { callable }; + this.sqls = new String[] { sql }; + this.checkStyles = new ExecuteUpdateResultCheckStyle[] { checkStyle }; + } + } + + private CustomSQLMetadata parse(CustomSQL customSql) { + final String sql = customSql.getSql(); + final boolean callable = sql != null && customSql.isCallable(); + final ExecuteUpdateResultCheckStyle checkStyle = sql == null + ? ExecuteUpdateResultCheckStyle.COUNT + : customSql.getCheckStyle() == null + ? ExecuteUpdateResultCheckStyle.determineDefault( sql, callable ) + : customSql.getCheckStyle(); + return new CustomSQLMetadata( sql, callable, checkStyle ); + } + @SuppressWarnings( {"UnusedDeclaration"}) public UnionSubclassEntityPersister( final EntityBinding entityBinding, @@ -249,16 +278,122 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { final SessionFactoryImplementor factory, final Mapping mapping) throws HibernateException { super(entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory ); - // TODO: implement!!! initializing final fields to null to make compiler happy. - subquery = null; + + if ( getIdentifierGenerator() instanceof IdentityGenerator ) { + throw new MappingException( + "Cannot use identity column key generation with mapping for: " + + getEntityName() + ); + } tableName = entityBinding.getPrimaryTable().getQualifiedName( factory.getDialect() ); - subclassClosure = null; - spaces = null; - subclassSpaces = null; - discriminatorValue = null; - discriminatorSQLValue = null; - constraintOrderedTableNames = null; - constraintOrderedKeyColumnNames = null; + + + //Custom SQL + { + CustomSQLMetadata customSQLMetadata; + { + customSQLMetadata = parse( entityBinding.getCustomInsert() ); + customSQLInsert = customSQLMetadata.sqls; + insertCallable = customSQLMetadata.callable; + insertResultCheckStyles = customSQLMetadata.checkStyles; + } + { + customSQLMetadata = parse( entityBinding.getCustomUpdate() ); + customSQLUpdate = customSQLMetadata.sqls; + updateCallable = customSQLMetadata.callable; + updateResultCheckStyles = customSQLMetadata.checkStyles; + } + { + customSQLMetadata = parse( entityBinding.getCustomDelete() ); + customSQLDelete = customSQLMetadata.sqls; + deleteCallable = customSQLMetadata.callable; + deleteResultCheckStyles = customSQLMetadata.checkStyles; + } + } + //discriminator + { + discriminatorValue = entityBinding.getSubEntityBindingId(); + discriminatorSQLValue = String.valueOf( discriminatorValue ); + } + + // PROPERTIES + + int subclassSpan = entityBinding.getSubEntityBindingClosureSpan() + 1; + subclassClosure = new String[subclassSpan]; + subclassClosure[0] = getEntityName(); + + // SUBCLASSES + subclassByDiscriminatorValue.put( + entityBinding.getSubEntityBindingId(), + entityBinding.getEntityName() + ); + if ( entityBinding.isPolymorphic() ) { + Iterable iter = entityBinding.getPreOrderSubEntityBindingClosure(); + int k=1; + for(EntityBinding subEntityBinding : iter){ + subclassClosure[k++] = subEntityBinding.getEntityName(); + subclassByDiscriminatorValue.put( subEntityBinding.getSubEntityBindingId(), subEntityBinding.getEntityName() ); + } + } + + + //SPACES + //TODO: i'm not sure, but perhaps we should exclude + // abstract denormalized tables? + + int spacesSize = 1 + entityBinding.getSynchronizedTableNames().length; + spaces = new String[spacesSize]; + spaces[0] = tableName; + String[] synchronizedTableNames = entityBinding.getSynchronizedTableNames(); + System.arraycopy( synchronizedTableNames, 0, spaces, 1, spacesSize ); + + HashSet subclassTables = new HashSet(); + Iterable iter = entityBinding.getPreOrderSubEntityBindingClosure(); + for ( EntityBinding subEntityBinding : iter ) { + subclassTables.add( subEntityBinding.getPrimaryTable().getQualifiedName( factory.getDialect() ) ); + } + subclassSpaces = ArrayHelper.toStringArray( subclassTables ); + + subquery = generateSubquery( entityBinding ); + + if ( isMultiTable() ) { + int idColumnSpan = getIdentifierColumnSpan(); + ArrayList tableNames = new ArrayList(); + ArrayList keyColumns = new ArrayList(); + if ( !isAbstract() ) { + tableNames.add( tableName ); + keyColumns.add( getIdentifierColumnNames() ); + } + Iterator siter = new JoinedIterator( + new SingletonIterator( entityBinding ), + iter.iterator() + ); + while ( siter.hasNext() ) { + EntityBinding eb = siter.next(); + TableSpecification tab = eb.getPrimaryTable(); + if ( isNotAbstractUnionTable( eb ) ) { + String tableName = tab.getQualifiedName( factory.getDialect() ); + tableNames.add( tableName ); + String[] key = new String[idColumnSpan]; + List columns = tab.getPrimaryKey().getColumns(); + for ( int k = 0; k < idColumnSpan; k++ ) { + key[k] = columns.get( k ).getColumnName().getText( factory.getDialect() ); + } + keyColumns.add( key ); + } + } + + constraintOrderedTableNames = ArrayHelper.toStringArray( tableNames ); + constraintOrderedKeyColumnNames = ArrayHelper.to2DStringArray( keyColumns ); + } + else { + constraintOrderedTableNames = new String[] { tableName }; + constraintOrderedKeyColumnNames = new String[][] { getIdentifierColumnNames() }; + } + initLockers(); + initSubclassPropertyAliasesMap(entityBinding); + postConstruct(mapping); + } public Serializable[] getQuerySpaces() { @@ -407,6 +542,78 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { return new int[ getPropertySpan() ]; } + private boolean isNotAbstractUnionTable(EntityBinding entityBinding) { + return !entityBinding.isAbstract() && entityBinding.getHierarchyDetails() + .getInheritanceType() != InheritanceType.TABLE_PER_CLASS; + } + + protected String generateSubquery(EntityBinding entityBinding){ + Dialect dialect = getFactory().getDialect(); + Settings settings = getFactory().getSettings(); + + if ( !entityBinding.hasSubEntityBindings() ) { + return entityBinding.getPrimaryTable().getQualifiedName( dialect ); + } + + HashSet columns = new LinkedHashSet(); + Iterable subEntityBindings = entityBinding.getPreOrderSubEntityBindingClosure(); + Iterator siter = new JoinedIterator( + new SingletonIterator(entityBinding), + subEntityBindings.iterator() + ); + while ( siter.hasNext() ) { + EntityBinding eb = siter.next(); + if ( isNotAbstractUnionTable( eb ) ) { + TableSpecification table = entityBinding.getPrimaryTable(); + for ( Value v : table.values() ) { + if ( org.hibernate.metamodel.spi.relational.Column.class.isInstance( v ) ) { + columns.add( org.hibernate.metamodel.spi.relational.Column.class.cast( v ) ); + } + } + } + } + + StringBuilder buf = new StringBuilder() + .append("( "); + + siter = new JoinedIterator( + new SingletonIterator(entityBinding), + subEntityBindings.iterator() + ); + + while ( siter.hasNext() ) { + EntityBinding eb = siter.next(); + TableSpecification table = eb.getPrimaryTable(); + if ( isNotAbstractUnionTable( eb )) { + //TODO: move to .sql package!! + buf.append("select "); + for(org.hibernate.metamodel.spi.relational.Column column : columns){ + if(!table.hasValue( column )){ + buf.append( dialect.getSelectClauseNullString(column.getJdbcDataType().getTypeCode()) ) + .append(" as "); + } + buf.append( column.getColumnName().getText( dialect ) ); + buf.append( ", " ); + } + buf.append( eb.getSubEntityBindingId() ) + .append( " as clazz_" ); + buf.append(" from ") + .append( table.getQualifiedName( dialect )); + buf.append(" union "); + if ( dialect.supportsUnionAll() ) { + buf.append("all "); + } + } + } + + if ( buf.length() > 2 ) { + //chop the last union (all) + buf.setLength( buf.length() - ( dialect.supportsUnionAll() ? 11 : 7 ) ); + } + + return buf.append(" )").toString(); + } + protected String generateSubquery(PersistentClass model, Mapping mapping) { Dialect dialect = getFactory().getDialect(); @@ -426,7 +633,9 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { Table table = (Table) titer.next(); if ( !table.isAbstractUnionTable() ) { Iterator citer = table.getColumnIterator(); - while ( citer.hasNext() ) columns.add( citer.next() ); + while ( citer.hasNext() ) { + columns.add( citer.next() ); + } } } @@ -479,12 +688,16 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { } protected String[] getSubclassTableKeyColumns(int j) { - if (j!=0) throw new AssertionFailure("only one table"); + if ( j != 0 ) { + throw new AssertionFailure( "only one table" ); + } return getIdentifierColumnNames(); } public String getSubclassTableName(int j) { - if (j!=0) throw new AssertionFailure("only one table"); + if ( j != 0 ) { + throw new AssertionFailure( "only one table" ); + } return tableName; } @@ -493,7 +706,9 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { } protected boolean isClassOrSuperclassTable(int j) { - if (j!=0) throw new AssertionFailure("only one table"); + if ( j != 0 ) { + throw new AssertionFailure( "only one table" ); + } return true; } diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/AssertSourcesTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/AssertSourcesTest.java index 94bac56701..29a6f26bd0 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/AssertSourcesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/AssertSourcesTest.java @@ -32,6 +32,7 @@ import org.hibernate.boot.registry.StandardServiceRegistry; import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.id.EntityIdentifierNature; import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.mapping.PropertyGeneration; import org.hibernate.metamodel.MetadataSources; import org.hibernate.metamodel.internal.MetadataBuilderImpl; @@ -119,8 +120,7 @@ public class AssertSourcesTest extends BaseUnitTestCase { assertFalse( entitySource.getSecondaryTables().iterator().hasNext() ); assertTrue( - entitySource.getSynchronizedTableNames() == null - || entitySource.getSynchronizedTableNames().isEmpty() + ArrayHelper.isEmpty( entitySource.getSynchronizedTableNames() ) ); IdentifierSource identifierSource = entitySource.getIdentifierSource(); diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/SynchronizeBindingTest.java b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/SynchronizeBindingTest.java index a4289fcc9f..c299c1c06b 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/SynchronizeBindingTest.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/internal/source/annotations/entity/SynchronizeBindingTest.java @@ -47,17 +47,26 @@ public class SynchronizeBindingTest extends BaseAnnotationBindingTestCase { @Resources(annotatedClasses = TestEntityWithSynchronizeAnnotation.class) public void testSynchronizeAnnotation() { EntityBinding binding = getEntityBinding( TestEntityWithSynchronizeAnnotation.class ); - Set synchronizedTableNames = binding.getSynchronizedTableNames(); - assertEquals( "Wrong number of synced tables", 2, synchronizedTableNames.size() ); - assertTrue( "Table name missing", synchronizedTableNames.contains( "Foo" ) ); - assertTrue( "Table name missing", synchronizedTableNames.contains( "Bar" ) ); + String [] synchronizedTableNames = binding.getSynchronizedTableNames(); + assertEquals( "Wrong number of synced tables", 2, synchronizedTableNames.length ); + assertTrue( "Table name missing", contains( synchronizedTableNames, "Foo" ) ); + assertTrue( "Table name missing", contains( synchronizedTableNames, "Bar" ) ); + } + + private boolean contains(String[] arrays, String key) { + for ( String s : arrays ) { + if ( key.equals( s ) ) { + return true; + } + } + return false; } @Test @Resources(annotatedClasses = TestEntity.class) public void testNoSynchronizeAnnotation() { EntityBinding binding = getEntityBinding( TestEntity.class ); - assertTrue( "There should be no cache binding", binding.getSynchronizedTableNames().size() == 0 ); + assertTrue( "There should be no cache binding", binding.getSynchronizedTableNames().length == 0 ); } @Entity