HHH-7380 - union subclass support
This commit is contained in:
parent
73f9df1bb5
commit
9b3e6a3714
|
@ -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 );
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -854,15 +854,17 @@ public class Binder {
|
|||
final boolean hasPrimaryKeyJoinColumns = CollectionHelper.isNotEmpty( primaryKeyJoinColumnSources );
|
||||
final List<Column> 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.
|
||||
*
|
||||
|
|
|
@ -195,7 +195,7 @@ public class EntitySourceImpl implements EntitySource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSynchronizedTableNames() {
|
||||
public String[] getSynchronizedTableNames() {
|
||||
return entityClass.getSynchronizedTableNames();
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ public class EntityClass extends ConfiguredClass {
|
|||
|
||||
private final String explicitEntityName;
|
||||
private final String customLoaderQueryName;
|
||||
private final List<String> 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<String> getSynchronizedTableNames() {
|
||||
public String[] getSynchronizedTableNames() {
|
||||
return synchronizedTableNames;
|
||||
}
|
||||
|
||||
|
@ -495,16 +495,15 @@ public class EntityClass extends ConfiguredClass {
|
|||
return customLoader;
|
||||
}
|
||||
|
||||
private List<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> getSynchronizedTableNames() {
|
||||
List<String> tableNames = new ArrayList<String>();
|
||||
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
|
||||
|
|
|
@ -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<String> synchronizedTableNames = new HashSet<String>();
|
||||
private String[] synchronizedTableNames = StringHelper.EMPTY_STRINGS;
|
||||
private Map<String, AttributeBinding> attributeBindingMap = new HashMap<String, AttributeBinding>();
|
||||
|
||||
private List<JpaCallbackSource> jpaCallbackClasses = new ArrayList<JpaCallbackSource>();
|
||||
|
@ -435,12 +437,12 @@ public class EntityBinding extends AbstractAttributeBindingContainer {
|
|||
this.isAbstract = isAbstract;
|
||||
}
|
||||
|
||||
public Set<String> getSynchronizedTableNames() {
|
||||
public String[] getSynchronizedTableNames() {
|
||||
return synchronizedTableNames;
|
||||
}
|
||||
|
||||
public void addSynchronizedTableNames(java.util.Collection<String> synchronizedTableNames) {
|
||||
this.synchronizedTableNames.addAll( synchronizedTableNames );
|
||||
public void addSynchronizedTableNames(String [] synchronizedTableNames) {
|
||||
this.synchronizedTableNames = ArrayHelper.join( this.synchronizedTableNames, synchronizedTableNames );
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -284,6 +284,7 @@ public class Table extends AbstractTableSpecification implements Exportable {
|
|||
*/
|
||||
public Iterable<Column> sortedColumns() {
|
||||
final Set<Column> sortedColumns = new LinkedHashSet<Column>();
|
||||
|
||||
// Adding primary key columns.
|
||||
sortedColumns.addAll( getPrimaryKey().getColumns() );
|
||||
// Adding foreign key columns.
|
||||
|
|
|
@ -41,6 +41,8 @@ public interface ValueContainer {
|
|||
*/
|
||||
public List<Value> 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.
|
||||
|
|
|
@ -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<String> getSynchronizedTableNames();
|
||||
public String[] getSynchronizedTableNames();
|
||||
|
||||
/**
|
||||
* Get the actual discriminator value in case of a single table inheritance
|
||||
|
|
|
@ -504,7 +504,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
|
||||
spaces = ArrayHelper.join(
|
||||
qualifiedTableNames,
|
||||
ArrayHelper.toStringArray( entityBinding.getSynchronizedTableNames() )
|
||||
entityBinding.getSynchronizedTableNames()
|
||||
);
|
||||
|
||||
final boolean lazyAvailable = isInstrumented();
|
||||
|
|
|
@ -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 <union-subclass> 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<EntityBinding> 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<String> subclassTables = new HashSet<String>();
|
||||
Iterable<EntityBinding> 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<String> tableNames = new ArrayList<String>();
|
||||
ArrayList<String[]> keyColumns = new ArrayList<String[]>();
|
||||
if ( !isAbstract() ) {
|
||||
tableNames.add( tableName );
|
||||
keyColumns.add( getIdentifierColumnNames() );
|
||||
}
|
||||
Iterator<EntityBinding> siter = new JoinedIterator<EntityBinding>(
|
||||
new SingletonIterator<EntityBinding>( 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<org.hibernate.metamodel.spi.relational.Column> 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<org.hibernate.metamodel.spi.relational.Column> columns = new LinkedHashSet<org.hibernate.metamodel.spi.relational.Column>();
|
||||
Iterable<EntityBinding> subEntityBindings = entityBinding.getPreOrderSubEntityBindingClosure();
|
||||
Iterator<EntityBinding> siter = new JoinedIterator<EntityBinding>(
|
||||
new SingletonIterator<EntityBinding>(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<EntityBinding>(
|
||||
new SingletonIterator<EntityBinding>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -47,17 +47,26 @@ public class SynchronizeBindingTest extends BaseAnnotationBindingTestCase {
|
|||
@Resources(annotatedClasses = TestEntityWithSynchronizeAnnotation.class)
|
||||
public void testSynchronizeAnnotation() {
|
||||
EntityBinding binding = getEntityBinding( TestEntityWithSynchronizeAnnotation.class );
|
||||
Set<String> 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
|
||||
|
|
Loading…
Reference in New Issue