HHH-15932 allow @XxxxToOne associations to target a secondary table
This commit is contained in:
parent
c7bad70073
commit
014847f41b
|
@ -8,7 +8,6 @@ package org.hibernate.boot.internal;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -84,7 +83,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.generator.Generator;
|
import org.hibernate.generator.Generator;
|
||||||
import org.hibernate.internal.CoreLogging;
|
import org.hibernate.internal.CoreLogging;
|
||||||
import org.hibernate.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
|
||||||
import org.hibernate.mapping.Collection;
|
import org.hibernate.mapping.Collection;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.mapping.Component;
|
import org.hibernate.mapping.Component;
|
||||||
|
@ -117,6 +115,9 @@ import jakarta.persistence.Embeddable;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.MapsId;
|
import jakarta.persistence.MapsId;
|
||||||
|
|
||||||
|
import static java.util.Collections.emptyList;
|
||||||
|
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The implementation of the {@linkplain InFlightMetadataCollector in-flight
|
* The implementation of the {@linkplain InFlightMetadataCollector in-flight
|
||||||
* metadata collector contract}.
|
* metadata collector contract}.
|
||||||
|
@ -1957,10 +1958,8 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void secondPassCompileForeignKeys(
|
protected void secondPassCompileForeignKeys(Table table, Set<ForeignKey> done, MetadataBuildingContext buildingContext)
|
||||||
final Table table,
|
throws MappingException {
|
||||||
Set<ForeignKey> done,
|
|
||||||
final MetadataBuildingContext buildingContext) throws MappingException {
|
|
||||||
table.createForeignKeys();
|
table.createForeignKeys();
|
||||||
|
|
||||||
for ( ForeignKey foreignKey : table.getForeignKeys().values() ) {
|
for ( ForeignKey foreignKey : table.getForeignKeys().values() ) {
|
||||||
|
@ -1968,34 +1967,27 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
||||||
done.add( foreignKey );
|
done.add( foreignKey );
|
||||||
final String referencedEntityName = foreignKey.getReferencedEntityName();
|
final String referencedEntityName = foreignKey.getReferencedEntityName();
|
||||||
if ( referencedEntityName == null ) {
|
if ( referencedEntityName == null ) {
|
||||||
throw new MappingException(
|
throw new MappingException( "An association from the table '" + foreignKey.getTable().getName() +
|
||||||
"An association from the table " +
|
"' does not specify the referenced entity" );
|
||||||
foreignKey.getTable().getName() +
|
|
||||||
" does not specify the referenced entity"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debugf( "Resolving reference to class: %s", referencedEntityName );
|
log.debugf( "Resolving reference to class: %s", referencedEntityName );
|
||||||
final PersistentClass referencedClass = getEntityBinding( referencedEntityName );
|
final PersistentClass referencedClass = getEntityBinding( referencedEntityName );
|
||||||
if ( referencedClass == null ) {
|
if ( referencedClass == null ) {
|
||||||
throw new MappingException(
|
throw new MappingException( "An association from the table '" + foreignKey.getTable().getName() +
|
||||||
"An association from the table " +
|
"' refers to an unmapped class '" + referencedEntityName + "'" );
|
||||||
foreignKey.getTable().getName() +
|
|
||||||
" refers to an unmapped class: " +
|
|
||||||
referencedEntityName
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if ( referencedClass.isJoinedSubclass() ) {
|
if ( referencedClass.isJoinedSubclass() ) {
|
||||||
secondPassCompileForeignKeys( referencedClass.getSuperclass().getTable(), done, buildingContext );
|
secondPassCompileForeignKeys( referencedClass.getSuperclass().getTable(), done, buildingContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
foreignKey.setReferencedTable( referencedClass.getTable() );
|
// the ForeignKeys created in the first pass did not have their referenced table initialized
|
||||||
|
if ( foreignKey.getReferencedTable() == null ) {
|
||||||
|
foreignKey.setReferencedTable( referencedClass.getTable() );
|
||||||
|
}
|
||||||
|
|
||||||
Identifier nameIdentifier;
|
final Identifier nameIdentifier = getMetadataBuildingOptions().getImplicitNamingStrategy()
|
||||||
|
|
||||||
nameIdentifier = getMetadataBuildingOptions().getImplicitNamingStrategy()
|
|
||||||
.determineForeignKeyName( new ForeignKeyNameSource( foreignKey, table, buildingContext ) );
|
.determineForeignKeyName( new ForeignKeyNameSource( foreignKey, table, buildingContext ) );
|
||||||
|
|
||||||
foreignKey.setName( nameIdentifier.render( getDatabase().getJdbcEnvironment().getDialect() ) );
|
foreignKey.setName( nameIdentifier.render( getDatabase().getJdbcEnvironment().getDialect() ) );
|
||||||
|
|
||||||
foreignKey.alignColumns();
|
foreignKey.alignColumns();
|
||||||
|
@ -2005,10 +1997,10 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
||||||
|
|
||||||
private List<Identifier> toIdentifiers(String[] names) {
|
private List<Identifier> toIdentifiers(String[] names) {
|
||||||
if ( names == null ) {
|
if ( names == null ) {
|
||||||
return Collections.emptyList();
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Identifier> columnNames = CollectionHelper.arrayList( names.length );
|
final List<Identifier> columnNames = arrayList( names.length );
|
||||||
for ( String name : names ) {
|
for ( String name : names ) {
|
||||||
columnNames.add( getDatabase().toIdentifier( name ) );
|
columnNames.add( getDatabase().toIdentifier( name ) );
|
||||||
}
|
}
|
||||||
|
@ -2018,10 +2010,10 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private List<Identifier> extractColumnNames(List columns) {
|
private List<Identifier> extractColumnNames(List columns) {
|
||||||
if ( columns == null || columns.isEmpty() ) {
|
if ( columns == null || columns.isEmpty() ) {
|
||||||
return Collections.emptyList();
|
return emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Identifier> columnNames = CollectionHelper.arrayList( columns.size() );
|
final List<Identifier> columnNames = arrayList( columns.size() );
|
||||||
for ( Column column : (List<Column>) columns ) {
|
for ( Column column : (List<Column>) columns ) {
|
||||||
columnNames.add( getDatabase().toIdentifier( column.getQuotedName() ) );
|
columnNames.add( getDatabase().toIdentifier( column.getQuotedName() ) );
|
||||||
}
|
}
|
||||||
|
|
|
@ -284,8 +284,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
|
||||||
* the primary key of the given {@link PersistentClass}, or whether they reference
|
* the primary key of the given {@link PersistentClass}, or whether they reference
|
||||||
* some other combination of mapped columns.
|
* some other combination of mapped columns.
|
||||||
*/
|
*/
|
||||||
public ForeignKeyType getReferencedColumnsType(
|
public ForeignKeyType getReferencedColumnsType(PersistentClass referencedEntity) {
|
||||||
PersistentClass referencedEntity) {
|
|
||||||
if ( columns.isEmpty() ) {
|
if ( columns.isEmpty() ) {
|
||||||
return ForeignKeyType.IMPLICIT_PRIMARY_KEY_REFERENCE; //shortcut
|
return ForeignKeyType.IMPLICIT_PRIMARY_KEY_REFERENCE; //shortcut
|
||||||
}
|
}
|
||||||
|
@ -298,7 +297,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
|
||||||
+ firstColumn.getReferencedColumn() + "' but the target entity '"
|
+ firstColumn.getReferencedColumn() + "' but the target entity '"
|
||||||
+ referencedEntity.getEntityName() + "' has no property which maps to this column" );
|
+ referencedEntity.getEntityName() + "' has no property which maps to this column" );
|
||||||
}
|
}
|
||||||
catch (MappingException me) {
|
catch ( MappingException me ) {
|
||||||
// we throw a recoverable exception here in case this
|
// we throw a recoverable exception here in case this
|
||||||
// is merely an ordering issue, so that the SecondPass
|
// is merely an ordering issue, so that the SecondPass
|
||||||
// will get reprocessed later
|
// will get reprocessed later
|
||||||
|
@ -306,7 +305,10 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Table table = table( columnOwner );
|
final Table table = table( columnOwner );
|
||||||
final List<Selectable> keyColumns = referencedEntity.getKey().getSelectables();
|
// final List<Selectable> keyColumns = referencedEntity.getKey().getSelectables();
|
||||||
|
final List<? extends Selectable> keyColumns = table.getPrimaryKey() == null
|
||||||
|
? referencedEntity.getKey().getSelectables()
|
||||||
|
: table.getPrimaryKey().getColumns();
|
||||||
boolean explicitColumnReference = false;
|
boolean explicitColumnReference = false;
|
||||||
for ( AnnotatedJoinColumn column : columns ) {
|
for ( AnnotatedJoinColumn column : columns ) {
|
||||||
if ( !column.isReferenceImplicit() ) {
|
if ( !column.isReferenceImplicit() ) {
|
||||||
|
@ -342,7 +344,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
|
||||||
return new Column( context.getMetadataCollector()
|
return new Column( context.getMetadataCollector()
|
||||||
.getPhysicalColumnName( table, logicalReferencedColumnName ) );
|
.getPhysicalColumnName( table, logicalReferencedColumnName ) );
|
||||||
}
|
}
|
||||||
catch (MappingException me) {
|
catch (MappingException me ) {
|
||||||
throw new MappingException( "No column with logical name '" + logicalReferencedColumnName
|
throw new MappingException( "No column with logical name '" + logicalReferencedColumnName
|
||||||
+ "' in table '" + table.getName() + "'" );
|
+ "' in table '" + table.getName() + "'" );
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,11 +336,11 @@ public class BinderHelper {
|
||||||
}
|
}
|
||||||
embeddedComponent.sortProperties();
|
embeddedComponent.sortProperties();
|
||||||
final Property result = new SyntheticProperty();
|
final Property result = new SyntheticProperty();
|
||||||
result.setName(syntheticPropertyName);
|
result.setName( syntheticPropertyName );
|
||||||
result.setPersistentClass(ownerEntity);
|
result.setPersistentClass( ownerEntity );
|
||||||
result.setUpdateable( false );
|
result.setUpdateable( false );
|
||||||
result.setInsertable( false );
|
result.setInsertable( false );
|
||||||
result.setValue(embeddedComponent);
|
result.setValue( embeddedComponent );
|
||||||
result.setPropertyAccessorName( "embedded" );
|
result.setPropertyAccessorName( "embedded" );
|
||||||
ownerEntity.addProperty( result );
|
ownerEntity.addProperty( result );
|
||||||
embeddedComponent.createUniqueKey(); //make it unique
|
embeddedComponent.createUniqueKey(); //make it unique
|
||||||
|
|
|
@ -2551,7 +2551,7 @@ public abstract class CollectionBinder {
|
||||||
|
|
||||||
private static void checkFilterConditions(Collection collection) {
|
private static void checkFilterConditions(Collection collection) {
|
||||||
//for now it can't happen, but sometime soon...
|
//for now it can't happen, but sometime soon...
|
||||||
if ( ( collection.getFilters().size() != 0 || isNotEmpty( collection.getWhere() ) )
|
if ( ( !collection.getFilters().isEmpty() || isNotEmpty( collection.getWhere() ) )
|
||||||
&& collection.getFetchMode() == FetchMode.JOIN
|
&& collection.getFetchMode() == FetchMode.JOIN
|
||||||
&& !( collection.getElement() instanceof SimpleValue ) //SimpleValue (CollectionOfElements) are always SELECT but it does not matter
|
&& !( collection.getElement() instanceof SimpleValue ) //SimpleValue (CollectionOfElements) are always SELECT but it does not matter
|
||||||
&& collection.getElement().getFetchMode() != FetchMode.JOIN ) {
|
&& collection.getElement().getFetchMode() != FetchMode.JOIN ) {
|
||||||
|
@ -2623,7 +2623,7 @@ public abstract class CollectionBinder {
|
||||||
AnnotatedJoinColumns joinColumns,
|
AnnotatedJoinColumns joinColumns,
|
||||||
SimpleValue value,
|
SimpleValue value,
|
||||||
boolean unique) {
|
boolean unique) {
|
||||||
if (isUnownedCollection()) {
|
if ( isUnownedCollection() ) {
|
||||||
bindUnownedManyToManyInverseForeignKey( targetEntity, joinColumns, value );
|
bindUnownedManyToManyInverseForeignKey( targetEntity, joinColumns, value );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2663,7 +2663,7 @@ public abstract class CollectionBinder {
|
||||||
AnnotatedJoinColumns joinColumns,
|
AnnotatedJoinColumns joinColumns,
|
||||||
SimpleValue value) {
|
SimpleValue value) {
|
||||||
final Property property = targetEntity.getRecursiveProperty( mappedBy );
|
final Property property = targetEntity.getRecursiveProperty( mappedBy );
|
||||||
final List<Selectable> mappedByColumns = mappedByColumns(targetEntity, property );
|
final List<Selectable> mappedByColumns = mappedByColumns( targetEntity, property );
|
||||||
final AnnotatedJoinColumn firstColumn = joinColumns.getJoinColumns().get(0);
|
final AnnotatedJoinColumn firstColumn = joinColumns.getJoinColumns().get(0);
|
||||||
for ( Selectable selectable: mappedByColumns ) {
|
for ( Selectable selectable: mappedByColumns ) {
|
||||||
firstColumn.linkValueUsingAColumnCopy( (Column) selectable, value);
|
firstColumn.linkValueUsingAColumnCopy( (Column) selectable, value);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.AnnotationException;
|
import org.hibernate.AnnotationException;
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.boot.model.naming.EntityNaming;
|
import org.hibernate.boot.model.naming.EntityNaming;
|
||||||
import org.hibernate.boot.model.naming.Identifier;
|
import org.hibernate.boot.model.naming.Identifier;
|
||||||
import org.hibernate.boot.model.naming.ImplicitCollectionTableNameSource;
|
import org.hibernate.boot.model.naming.ImplicitCollectionTableNameSource;
|
||||||
|
@ -27,6 +28,7 @@ import org.hibernate.mapping.Collection;
|
||||||
import org.hibernate.mapping.Column;
|
import org.hibernate.mapping.Column;
|
||||||
import org.hibernate.mapping.Component;
|
import org.hibernate.mapping.Component;
|
||||||
import org.hibernate.mapping.DependantValue;
|
import org.hibernate.mapping.DependantValue;
|
||||||
|
import org.hibernate.mapping.Join;
|
||||||
import org.hibernate.mapping.JoinedSubclass;
|
import org.hibernate.mapping.JoinedSubclass;
|
||||||
import org.hibernate.mapping.KeyValue;
|
import org.hibernate.mapping.KeyValue;
|
||||||
import org.hibernate.mapping.PersistentClass;
|
import org.hibernate.mapping.PersistentClass;
|
||||||
|
@ -64,7 +66,6 @@ public class TableBinder {
|
||||||
private String name;
|
private String name;
|
||||||
private boolean isAbstract;
|
private boolean isAbstract;
|
||||||
private List<UniqueConstraintHolder> uniqueConstraints;
|
private List<UniqueConstraintHolder> uniqueConstraints;
|
||||||
// private List<String[]> uniqueConstraints;
|
|
||||||
String constraints;
|
String constraints;
|
||||||
private String ownerEntityTable;
|
private String ownerEntityTable;
|
||||||
private String associatedEntityTable;
|
private String associatedEntityTable;
|
||||||
|
@ -552,7 +553,7 @@ public class TableBinder {
|
||||||
else {
|
else {
|
||||||
bindExplicitColumns( referencedEntity, joinColumns, value, buildingContext, associatedClass );
|
bindExplicitColumns( referencedEntity, joinColumns, value, buildingContext, associatedClass );
|
||||||
}
|
}
|
||||||
value.createForeignKey();
|
value.createForeignKey( referencedEntity, joinColumns );
|
||||||
if ( unique ) {
|
if ( unique ) {
|
||||||
value.createUniqueKey();
|
value.createUniqueKey();
|
||||||
}
|
}
|
||||||
|
@ -616,35 +617,15 @@ public class TableBinder {
|
||||||
( (Component) key).sortProperties();
|
( (Component) key).sortProperties();
|
||||||
}
|
}
|
||||||
// works because the pk has to be on the primary table
|
// works because the pk has to be on the primary table
|
||||||
final Dialect dialect = buildingContext.getMetadataCollector().getDatabase()
|
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
|
||||||
.getJdbcEnvironment().getDialect();
|
final Dialect dialect = metadataCollector.getDatabase().getJdbcEnvironment().getDialect();
|
||||||
for ( Column column: key.getColumns() ) {
|
for ( int j = 0; j < key.getColumnSpan(); j++ ) {
|
||||||
boolean match = false;
|
if ( !matchUpJoinColumnsWithKeyColumns( referencedEntity, joinColumns, value, metadataCollector, dialect, j ) ) {
|
||||||
// for each PK column, find the associated FK column.
|
|
||||||
final String quotedName = column.getQuotedName( dialect );
|
|
||||||
for ( AnnotatedJoinColumn joinColumn : joinColumns.getJoinColumns() ) {
|
|
||||||
final String referencedColumn = buildingContext.getMetadataCollector()
|
|
||||||
.getPhysicalColumnName( referencedEntity.getTable(), joinColumn.getReferencedColumn() );
|
|
||||||
// in JPA 2 referencedColumnName is case-insensitive
|
|
||||||
if ( referencedColumn.equalsIgnoreCase( quotedName ) ) {
|
|
||||||
// correct join column
|
|
||||||
if ( joinColumn.isNameDeferred() ) {
|
|
||||||
joinColumn.linkValueUsingDefaultColumnNaming( column, referencedEntity, value );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
joinColumn.linkWithValue( value );
|
|
||||||
}
|
|
||||||
joinColumn.overrideFromReferencedColumnIfNecessary( column );
|
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( !match ) {
|
|
||||||
// we can only get here if there's a dupe PK column in the @JoinColumns
|
// we can only get here if there's a dupe PK column in the @JoinColumns
|
||||||
throw new AnnotationException(
|
throw new AnnotationException(
|
||||||
"An association that targets entity '" + referencedEntity.getEntityName()
|
"An association that targets entity '" + referencedEntity.getEntityName()
|
||||||
+ "' from entity '" + associatedClass.getEntityName()
|
+ "' from entity '" + associatedClass.getEntityName()
|
||||||
+ "' has no '@JoinColumn' referencing column '"+ column.getName()
|
+ "' has no '@JoinColumn' referencing column '" + key.getColumns().get(j).getName() + "'"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,6 +634,56 @@ public class TableBinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean matchUpJoinColumnsWithKeyColumns(
|
||||||
|
PersistentClass referencedEntity,
|
||||||
|
AnnotatedJoinColumns joinColumns,
|
||||||
|
SimpleValue value,
|
||||||
|
InFlightMetadataCollector metadataCollector,
|
||||||
|
Dialect dialect,
|
||||||
|
int index) {
|
||||||
|
// for each PK column, find the associated FK column.
|
||||||
|
for ( AnnotatedJoinColumn joinColumn : joinColumns.getJoinColumns() ) {
|
||||||
|
final String referencedNamed = joinColumn.getReferencedColumn();
|
||||||
|
String referencedColumn = null;
|
||||||
|
List<Column> columns = null;
|
||||||
|
try {
|
||||||
|
final Table referencedTable = referencedEntity.getTable();
|
||||||
|
referencedColumn = metadataCollector.getPhysicalColumnName( referencedTable, referencedNamed );
|
||||||
|
columns = referencedEntity.getKey().getColumns();
|
||||||
|
}
|
||||||
|
catch ( MappingException me ) {
|
||||||
|
for ( Join join : referencedEntity.getJoins() ) {
|
||||||
|
try {
|
||||||
|
final Table referencedTable = join.getTable();
|
||||||
|
referencedColumn = metadataCollector.getPhysicalColumnName( referencedTable, referencedNamed );
|
||||||
|
columns = referencedTable.getPrimaryKey().getColumns();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch ( MappingException i ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( referencedColumn == null ) {
|
||||||
|
throw me;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final Column column = columns.get( index );
|
||||||
|
final String quotedName = column.getQuotedName( dialect );
|
||||||
|
// in JPA 2 referencedColumnName is case-insensitive
|
||||||
|
if ( referencedColumn.equalsIgnoreCase( quotedName ) ) {
|
||||||
|
// correct join column
|
||||||
|
if ( joinColumn.isNameDeferred() ) {
|
||||||
|
joinColumn.linkValueUsingDefaultColumnNaming( column, referencedEntity, value );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
joinColumn.linkWithValue( value );
|
||||||
|
}
|
||||||
|
joinColumn.overrideFromReferencedColumnIfNecessary( column );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static void bindNonPrimaryKeyReference(
|
private static void bindNonPrimaryKeyReference(
|
||||||
PersistentClass referencedEntity,
|
PersistentClass referencedEntity,
|
||||||
AnnotatedJoinColumns joinColumns,
|
AnnotatedJoinColumns joinColumns,
|
||||||
|
|
|
@ -17,7 +17,6 @@ import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.Remove;
|
|
||||||
import org.hibernate.boot.model.relational.Exportable;
|
import org.hibernate.boot.model.relational.Exportable;
|
||||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
|
@ -165,7 +164,7 @@ public abstract class Constraint implements Exportable, Serializable {
|
||||||
/**
|
/**
|
||||||
* @deprecated this method is no longer called
|
* @deprecated this method is no longer called
|
||||||
*/
|
*/
|
||||||
@Deprecated(since="6.2") @Remove
|
@Deprecated(since="6.2", forRemoval = true)
|
||||||
public abstract String sqlConstraintString(
|
public abstract String sqlConstraintString(
|
||||||
SqlStringGenerationContext context,
|
SqlStringGenerationContext context,
|
||||||
String constraintName,
|
String constraintName,
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class ForeignKey extends Constraint {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @Deprecated(since="6.2")
|
@Override @Deprecated(since="6.2", forRemoval = true)
|
||||||
public String sqlConstraintString(
|
public String sqlConstraintString(
|
||||||
SqlStringGenerationContext context,
|
SqlStringGenerationContext context,
|
||||||
String constraintName,
|
String constraintName,
|
||||||
|
@ -76,17 +76,12 @@ public class ForeignKey extends Constraint {
|
||||||
referencedColumnNames[i] = referencedColumns.get(i).getQuotedName( dialect );
|
referencedColumnNames[i] = referencedColumns.get(i).getQuotedName( dialect );
|
||||||
}
|
}
|
||||||
|
|
||||||
final String result = keyDefinition != null ?
|
final String result = keyDefinition != null
|
||||||
dialect.getAddForeignKeyConstraintString(
|
? dialect.getAddForeignKeyConstraintString( constraintName, keyDefinition )
|
||||||
constraintName,
|
: dialect.getAddForeignKeyConstraintString(
|
||||||
keyDefinition
|
|
||||||
) :
|
|
||||||
dialect.getAddForeignKeyConstraintString(
|
|
||||||
constraintName,
|
constraintName,
|
||||||
columnNames,
|
columnNames,
|
||||||
referencedTable.getQualifiedName(
|
referencedTable.getQualifiedName( context ),
|
||||||
context
|
|
||||||
),
|
|
||||||
referencedColumnNames,
|
referencedColumnNames,
|
||||||
isReferenceToPrimaryKey()
|
isReferenceToPrimaryKey()
|
||||||
);
|
);
|
||||||
|
@ -111,8 +106,6 @@ public class ForeignKey extends Constraint {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReferencedTable(Table referencedTable) throws MappingException {
|
public void setReferencedTable(Table referencedTable) throws MappingException {
|
||||||
//if( isReferenceToPrimaryKey() ) alignColumns(referencedTable); // TODO: possibly remove to allow more piecemal building of a foreignkey.
|
|
||||||
|
|
||||||
this.referencedTable = referencedTable;
|
this.referencedTable = referencedTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,15 +59,6 @@ public class ManyToOne extends ToOne {
|
||||||
return resolvedType;
|
return resolvedType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void createForeignKey() {
|
|
||||||
// Ensure properties are sorted before we create a foreign key
|
|
||||||
sortProperties();
|
|
||||||
// the case of a foreign key to something other than the pk is handled in createPropertyRefConstraints
|
|
||||||
if ( isForeignKeyEnabled() && referencedPropertyName==null && !hasFormula() ) {
|
|
||||||
createForeignKeyOfEntity( ( (EntityType) getType() ).getAssociatedEntityName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createUniqueKey() {
|
public void createUniqueKey() {
|
||||||
if ( !hasFormula() ) {
|
if ( !hasFormula() ) {
|
||||||
|
@ -75,21 +66,32 @@ public class ManyToOne extends ToOne {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@linkplain ForeignKey foreign key constraint} in the
|
||||||
|
* case that the foreign key of this association does not reference
|
||||||
|
* the primary key of the referenced table, but instead some other
|
||||||
|
* unique key.
|
||||||
|
* <p>
|
||||||
|
* We depend here on having a property of the referenced entity
|
||||||
|
* that does hold the referenced unique key. We might have created
|
||||||
|
* a "synthetic" composite property for this purpose.
|
||||||
|
*/
|
||||||
public void createPropertyRefConstraints(Map<String, PersistentClass> persistentClasses) {
|
public void createPropertyRefConstraints(Map<String, PersistentClass> persistentClasses) {
|
||||||
if ( referencedPropertyName != null ) {
|
if ( referencedPropertyName != null ) {
|
||||||
// Ensure properties are sorted before we create a foreign key
|
// Ensure properties are sorted before we create a foreign key
|
||||||
sortProperties();
|
sortProperties();
|
||||||
PersistentClass pc = persistentClasses.get( getReferencedEntityName() );
|
|
||||||
|
final String referencedEntityName = getReferencedEntityName();
|
||||||
Property property = pc.getReferencedProperty( getReferencedPropertyName() );
|
final String referencedPropertyName = getReferencedPropertyName();
|
||||||
|
final PersistentClass referencedClass = persistentClasses.get( referencedEntityName );
|
||||||
if (property==null) {
|
if ( referencedClass == null ) {
|
||||||
throw new MappingException(
|
throw new MappingException( "Referenced entity '" + referencedEntityName + "' does not exist" );
|
||||||
"Could not find property " +
|
|
||||||
getReferencedPropertyName() +
|
}
|
||||||
" on " +
|
final Property property = referencedClass.getReferencedProperty( referencedPropertyName );
|
||||||
getReferencedEntityName()
|
if ( property==null ) {
|
||||||
);
|
throw new MappingException( "Referenced entity '" + referencedEntityName
|
||||||
|
+ "' has no property named '" + referencedPropertyName + "'" );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Make sure synthetic properties are sorted
|
// Make sure synthetic properties are sorted
|
||||||
|
@ -98,14 +100,16 @@ public class ManyToOne extends ToOne {
|
||||||
}
|
}
|
||||||
// todo : if "none" another option is to create the ForeignKey object still but to set its #disableCreation flag
|
// todo : if "none" another option is to create the ForeignKey object still but to set its #disableCreation flag
|
||||||
if ( isForeignKeyEnabled() && !hasFormula() ) {
|
if ( isForeignKeyEnabled() && !hasFormula() ) {
|
||||||
ForeignKey fk = getTable().createForeignKey(
|
final ForeignKey foreignKey = getTable().createForeignKey(
|
||||||
getForeignKeyName(),
|
getForeignKeyName(),
|
||||||
getConstraintColumns(),
|
getConstraintColumns(),
|
||||||
( (EntityType) getType() ).getAssociatedEntityName(),
|
( (EntityType) getType() ).getAssociatedEntityName(),
|
||||||
getForeignKeyDefinition(),
|
getForeignKeyDefinition(),
|
||||||
new ArrayList<>( property.getColumns() )
|
new ArrayList<>( property.getColumns() )
|
||||||
);
|
);
|
||||||
fk.setOnDeleteAction( getOnDeleteAction() );
|
//TODO: if the property belongs to a secondary table,
|
||||||
|
// need to call foreignKey.setReferencedTable(table)
|
||||||
|
foreignKey.setOnDeleteAction( getOnDeleteAction() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ import java.util.Objects;
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.type.EntityType;
|
|
||||||
import org.hibernate.type.ForeignKeyDirection;
|
import org.hibernate.type.ForeignKeyDirection;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
@ -94,16 +93,6 @@ public class OneToOne extends ToOne {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void createForeignKey() throws MappingException {
|
|
||||||
// Ensure properties are sorted before we create a foreign key
|
|
||||||
sortProperties();
|
|
||||||
if ( isForeignKeyEnabled() && constrained && referencedPropertyName==null) {
|
|
||||||
//TODO: handle the case of a foreign key to something other than the pk
|
|
||||||
createForeignKeyOfEntity( ( (EntityType) getType() ).getAssociatedEntityName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void createUniqueKey() {
|
public void createUniqueKey() {
|
||||||
if ( !hasFormula() && getColumnSpan()>0 ) {
|
if ( !hasFormula() && getColumnSpan()>0 ) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class PrimaryKey extends Constraint {
|
||||||
return buf.append(')').toString();
|
return buf.append(')').toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override @Deprecated(since="6.2")
|
@Override @Deprecated(since="6.2", forRemoval = true)
|
||||||
public String sqlConstraintString(SqlStringGenerationContext context, String constraintName, String defaultCatalog, String defaultSchema) {
|
public String sqlConstraintString(SqlStringGenerationContext context, String constraintName, String defaultCatalog, String defaultSchema) {
|
||||||
Dialect dialect = context.getDialect();
|
Dialect dialect = context.getDialect();
|
||||||
StringBuilder buf = new StringBuilder();
|
StringBuilder buf = new StringBuilder();
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.hibernate.annotations.common.reflection.XProperty;
|
||||||
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
|
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
|
||||||
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
|
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
|
||||||
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
|
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
|
||||||
|
import org.hibernate.boot.model.internal.AnnotatedJoinColumns;
|
||||||
import org.hibernate.boot.model.relational.Database;
|
import org.hibernate.boot.model.relational.Database;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||||
|
@ -342,12 +343,19 @@ public abstract class SimpleValue implements KeyValue {
|
||||||
@Override
|
@Override
|
||||||
public void createForeignKey() throws MappingException {}
|
public void createForeignKey() throws MappingException {}
|
||||||
|
|
||||||
|
public void createForeignKey(PersistentClass referencedEntity, AnnotatedJoinColumns joinColumns) throws MappingException {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ForeignKey createForeignKeyOfEntity(String entityName) {
|
public ForeignKey createForeignKeyOfEntity(String entityName) {
|
||||||
if ( isConstrained() ) {
|
if ( isConstrained() ) {
|
||||||
final ForeignKey fk = table.createForeignKey( getForeignKeyName(), getConstraintColumns(), entityName, getForeignKeyDefinition() );
|
final ForeignKey foreignKey = table.createForeignKey(
|
||||||
fk.setOnDeleteAction( onDeleteAction );
|
getForeignKeyName(),
|
||||||
return fk;
|
getConstraintColumns(),
|
||||||
|
entityName,
|
||||||
|
getForeignKeyDefinition()
|
||||||
|
);
|
||||||
|
foreignKey.setOnDeleteAction( onDeleteAction );
|
||||||
|
return foreignKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -554,7 +554,7 @@ public class Table implements Serializable, ContributableDatabaseObject {
|
||||||
foreignKey.setTable( this );
|
foreignKey.setTable( this );
|
||||||
foreignKey.setReferencedEntityName( referencedEntityName );
|
foreignKey.setReferencedEntityName( referencedEntityName );
|
||||||
foreignKey.setKeyDefinition( keyDefinition );
|
foreignKey.setKeyDefinition( keyDefinition );
|
||||||
for (Column keyColumn : keyColumns) {
|
for ( Column keyColumn : keyColumns ) {
|
||||||
foreignKey.addColumn( keyColumn );
|
foreignKey.addColumn( keyColumn );
|
||||||
}
|
}
|
||||||
if ( referencedColumns != null ) {
|
if ( referencedColumns != null ) {
|
||||||
|
|
|
@ -8,13 +8,18 @@ package org.hibernate.mapping;
|
||||||
|
|
||||||
import org.hibernate.FetchMode;
|
import org.hibernate.FetchMode;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.boot.model.internal.AnnotatedJoinColumn;
|
||||||
|
import org.hibernate.boot.model.internal.AnnotatedJoinColumns;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.spi.MetadataBuildingContext;
|
import org.hibernate.boot.spi.MetadataBuildingContext;
|
||||||
import org.hibernate.engine.spi.Mapping;
|
import org.hibernate.engine.spi.Mapping;
|
||||||
import org.hibernate.internal.util.ReflectHelper;
|
import org.hibernate.internal.util.ReflectHelper;
|
||||||
|
import org.hibernate.type.EntityType;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static org.hibernate.boot.model.internal.BinderHelper.findReferencedColumnOwner;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping model object representing an association where the target side has cardinality one.
|
* A mapping model object representing an association where the target side has cardinality one.
|
||||||
*
|
*
|
||||||
|
@ -165,29 +170,72 @@ public abstract class ToOne extends SimpleValue implements Fetchable, SortableVa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] sortProperties() {
|
public int[] sortProperties() {
|
||||||
final PersistentClass entityBinding = getMetadata().getEntityBinding( getReferencedEntityName() );
|
final PersistentClass entityBinding = getMetadata().getEntityBinding( referencedEntityName );
|
||||||
if ( entityBinding == null ) {
|
if ( entityBinding != null ) {
|
||||||
return null;
|
final Value value = referencedPropertyName == null
|
||||||
}
|
? entityBinding.getIdentifier()
|
||||||
final Value value;
|
: entityBinding.getRecursiveProperty( referencedPropertyName ).getValue();
|
||||||
if ( getReferencedPropertyName() == null ) {
|
if ( value instanceof Component ) {
|
||||||
value = entityBinding.getIdentifier();
|
final Component component = (Component) value;
|
||||||
}
|
final int[] originalPropertyOrder = component.sortProperties();
|
||||||
else {
|
if ( !sorted ) {
|
||||||
value = entityBinding.getRecursiveProperty( getReferencedPropertyName() ).getValue();
|
if ( originalPropertyOrder != null ) {
|
||||||
}
|
sortColumns( originalPropertyOrder );
|
||||||
if ( value instanceof Component ) {
|
}
|
||||||
final Component component = (Component) value;
|
sorted = true;
|
||||||
final int[] originalPropertyOrder = component.sortProperties();
|
|
||||||
if ( !sorted ) {
|
|
||||||
sorted = true;
|
|
||||||
if ( originalPropertyOrder != null ) {
|
|
||||||
sortColumns( originalPropertyOrder );
|
|
||||||
}
|
}
|
||||||
|
return originalPropertyOrder;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sorted = true;
|
||||||
}
|
}
|
||||||
return originalPropertyOrder;
|
|
||||||
}
|
}
|
||||||
sorted = true;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createForeignKey(PersistentClass referencedEntity, AnnotatedJoinColumns joinColumns) {
|
||||||
|
// Ensure properties are sorted before we create a foreign key
|
||||||
|
sortProperties();
|
||||||
|
if ( isForeignKeyEnabled() && referencedPropertyName==null && !hasFormula() ) {
|
||||||
|
if ( isConstrained() ) {
|
||||||
|
final AnnotatedJoinColumn firstColumn = joinColumns.getJoinColumns().get(0);
|
||||||
|
final Object owner = findReferencedColumnOwner( referencedEntity, firstColumn, getBuildingContext() );
|
||||||
|
if ( owner instanceof Join ) {
|
||||||
|
// Here we handle the case of a foreign key that refers to the
|
||||||
|
// primary key of a secondary table of the referenced entity
|
||||||
|
final Join join = (Join) owner;
|
||||||
|
final ForeignKey foreignKey = getTable().createForeignKey(
|
||||||
|
getForeignKeyName(),
|
||||||
|
getConstraintColumns(),
|
||||||
|
referencedEntity.getEntityName(),
|
||||||
|
getForeignKeyDefinition(),
|
||||||
|
join.getKey().getColumns()
|
||||||
|
);
|
||||||
|
foreignKey.setOnDeleteAction( getOnDeleteAction() );
|
||||||
|
foreignKey.setReferencedTable( join.getTable() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// it's just a reference to the primary key of the main table
|
||||||
|
createForeignKeyOfEntity( referencedEntity.getEntityName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createForeignKey() {
|
||||||
|
// Ensure properties are sorted before we create a foreign key
|
||||||
|
sortProperties();
|
||||||
|
// A non-null referencedPropertyName tells us that the foreign key
|
||||||
|
// does not reference the primary key, but some other unique key of
|
||||||
|
// the referenced table. We do not handle this case here:
|
||||||
|
// - For ManyToOne, the case of a foreign key to something other than
|
||||||
|
// the primary key is handled in createPropertyRefConstraints()
|
||||||
|
// - For OneToOne, we still need to add some similar logic somewhere
|
||||||
|
// (for now, no foreign key constraint is created)
|
||||||
|
if ( isForeignKeyEnabled() && referencedPropertyName==null && !hasFormula() ) {
|
||||||
|
createForeignKeyOfEntity( ( (EntityType) getType() ).getAssociatedEntityName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ public class UniqueKey extends Constraint {
|
||||||
private final Map<Column, String> columnOrderMap = new HashMap<>();
|
private final Map<Column, String> columnOrderMap = new HashMap<>();
|
||||||
private boolean nameExplicit; // true when the constraint name was explicitly specified by @UniqueConstraint annotation
|
private boolean nameExplicit; // true when the constraint name was explicitly specified by @UniqueConstraint annotation
|
||||||
|
|
||||||
@Override @Deprecated(since="6.2")
|
@Override @Deprecated(since="6.2", forRemoval = true)
|
||||||
public String sqlConstraintString(
|
public String sqlConstraintString(
|
||||||
SqlStringGenerationContext context,
|
SqlStringGenerationContext context,
|
||||||
String constraintName,
|
String constraintName,
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
|
||||||
MappingModelCreationProcess creationProcess) {
|
MappingModelCreationProcess creationProcess) {
|
||||||
super( entityDescriptor, incomingDiscriminatorType, valueMappings, creationProcess );
|
super( entityDescriptor, incomingDiscriminatorType, valueMappings, creationProcess );
|
||||||
|
|
||||||
for ( int i = 0; i < discriminatorValues.length; i++ ) {
|
for ( int i = 0; i < discriminatorValues.length; i++ ) {
|
||||||
if ( !discriminatorAbstract[i] ) {
|
if ( !discriminatorAbstract[i] ) {
|
||||||
final String tableName = tableNames[notNullColumnTableNumbers[i]];
|
final String tableName = tableNames[notNullColumnTableNumbers[i]];
|
||||||
final String subEntityName = subEntityNameByTableName.get( tableName );
|
final String subEntityName = subEntityNameByTableName.get( tableName );
|
||||||
|
|
|
@ -9,7 +9,6 @@ package org.hibernate.persister.entity;
|
||||||
import org.hibernate.Internal;
|
import org.hibernate.Internal;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|
||||||
import org.hibernate.internal.util.MarkerObject;
|
import org.hibernate.internal.util.MarkerObject;
|
||||||
import org.hibernate.mapping.PersistentClass;
|
import org.hibernate.mapping.PersistentClass;
|
||||||
import org.hibernate.sql.InFragment;
|
import org.hibernate.sql.InFragment;
|
||||||
|
|
|
@ -754,6 +754,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected boolean needsDiscriminator() {
|
protected boolean needsDiscriminator() {
|
||||||
return forceDiscriminator;
|
return forceDiscriminator;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,12 +63,12 @@ public class StandardForeignKeyExporter implements Exporter<ForeignKey> {
|
||||||
|
|
||||||
final StringBuilder buffer = new StringBuilder( dialect.getAlterTableString( sourceTableName ) )
|
final StringBuilder buffer = new StringBuilder( dialect.getAlterTableString( sourceTableName ) )
|
||||||
.append(
|
.append(
|
||||||
foreignKey.getKeyDefinition() != null ?
|
foreignKey.getKeyDefinition() != null
|
||||||
dialect.getAddForeignKeyConstraintString(
|
? dialect.getAddForeignKeyConstraintString(
|
||||||
foreignKey.getName(),
|
foreignKey.getName(),
|
||||||
foreignKey.getKeyDefinition()
|
foreignKey.getKeyDefinition()
|
||||||
) :
|
)
|
||||||
dialect.getAddForeignKeyConstraintString(
|
: dialect.getAddForeignKeyConstraintString(
|
||||||
foreignKey.getName(),
|
foreignKey.getName(),
|
||||||
columnNames,
|
columnNames,
|
||||||
targetTableName,
|
targetTableName,
|
||||||
|
|
|
@ -0,0 +1,232 @@
|
||||||
|
package org.hibernate.orm.test.inheritance.repeatedtable;
|
||||||
|
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.DiscriminatorColumn;
|
||||||
|
import jakarta.persistence.DiscriminatorValue;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Inheritance;
|
||||||
|
import jakarta.persistence.JoinColumn;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.persistence.OneToMany;
|
||||||
|
import jakarta.persistence.PrimaryKeyJoinColumn;
|
||||||
|
import jakarta.persistence.SecondaryTable;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
import org.hibernate.annotations.SecondaryRow;
|
||||||
|
import org.hibernate.cfg.Configuration;
|
||||||
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static jakarta.persistence.CascadeType.ALL;
|
||||||
|
import static jakarta.persistence.InheritanceType.SINGLE_TABLE;
|
||||||
|
import static junit.framework.TestCase.assertEquals;
|
||||||
|
import static junit.framework.TestCase.assertNotNull;
|
||||||
|
import static junit.framework.TestCase.assertNull;
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.FORMAT_SQL;
|
||||||
|
import static org.hibernate.cfg.AvailableSettings.SHOW_SQL;
|
||||||
|
|
||||||
|
public class AlternativeToRepeatedTableTest extends BaseCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[]{
|
||||||
|
DataType.class,
|
||||||
|
ObjectType.class,
|
||||||
|
SimpleType.class,
|
||||||
|
Prop.class
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configure(Configuration configuration) {
|
||||||
|
super.configure(configuration);
|
||||||
|
configuration.setProperty(SHOW_SQL, Boolean.toString(true));
|
||||||
|
configuration.setProperty(FORMAT_SQL, Boolean.toString(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_append_properties() {
|
||||||
|
Long id;
|
||||||
|
Long sId;
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
Transaction tx = sess.beginTransaction();
|
||||||
|
|
||||||
|
SimpleType simpleType = new SimpleType();
|
||||||
|
simpleType.setName("simple");
|
||||||
|
simpleType.setCount(69);
|
||||||
|
sess.persist(simpleType);
|
||||||
|
sId = simpleType.getId();
|
||||||
|
|
||||||
|
ObjectType objectType = new ObjectType();
|
||||||
|
objectType.setName("name");
|
||||||
|
sess.persist(objectType);
|
||||||
|
id = objectType.getId();
|
||||||
|
|
||||||
|
tx.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
Transaction tx = sess.beginTransaction();
|
||||||
|
ObjectType objectType = sess.find(ObjectType.class, id);
|
||||||
|
Prop property = new Prop();
|
||||||
|
property.setName("Prop1");
|
||||||
|
property.setObjectType(objectType);
|
||||||
|
objectType.getProperties().add(property);
|
||||||
|
tx.commit();
|
||||||
|
}
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
Transaction tx = sess.beginTransaction();
|
||||||
|
ObjectType objectType = sess.find(ObjectType.class, id);
|
||||||
|
assertEquals(1, objectType.getProperties().size());
|
||||||
|
tx.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
DataType dataType1 = sess.find(DataType.class, sId);
|
||||||
|
assertTrue( dataType1 instanceof SimpleType );
|
||||||
|
DataType dataType2 = sess.find(DataType.class, id);
|
||||||
|
assertTrue( dataType2 instanceof ObjectType );
|
||||||
|
}
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
SimpleType simpleType = sess.find(SimpleType.class, sId);
|
||||||
|
assertNotNull( simpleType );
|
||||||
|
SimpleType wrongType = sess.find(SimpleType.class, id);
|
||||||
|
assertNull( wrongType );
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
assertEquals( "Prop1",
|
||||||
|
sess.createQuery("select p.name from AlternativeToRepeatedTableTest$ObjectType ot join ot.properties p")
|
||||||
|
.getSingleResult() );
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Session sess = openSession()) {
|
||||||
|
assertEquals( 2, sess.createQuery("from AlternativeToRepeatedTableTest$DataType").getResultList().size() );
|
||||||
|
assertEquals( 1, sess.createQuery("from AlternativeToRepeatedTableTest$ObjectType").getResultList().size() );
|
||||||
|
assertEquals( 1, sess.createQuery("from AlternativeToRepeatedTableTest$SimpleType").getResultList().size() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "DATA_TYPE")
|
||||||
|
@Inheritance(strategy = SINGLE_TABLE)
|
||||||
|
@DiscriminatorColumn(name = "supertype_id")
|
||||||
|
public static abstract class DataType {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "ID")
|
||||||
|
@GeneratedValue
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@DiscriminatorValue("8")
|
||||||
|
@SecondaryTable(name = "OBJ_TYPE",
|
||||||
|
pkJoinColumns = @PrimaryKeyJoinColumn(name = "TYPE_ID", referencedColumnName = "ID"))
|
||||||
|
@SecondaryRow(optional = false)
|
||||||
|
public static class ObjectType extends DataType {
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
private List<Prop> properties;
|
||||||
|
|
||||||
|
@Column(name = "descr", table = "OBJ_TYPE")
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "objectType", cascade = ALL, orphanRemoval = true)
|
||||||
|
public List<Prop> getProperties() {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProperties(List<Prop> properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "PROP")
|
||||||
|
public static class Prop {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private ObjectType objectType;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "ID")
|
||||||
|
@GeneratedValue
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Column(name = "name")
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @JoinColumn(name = "OBJ_TYPE_ID")
|
||||||
|
// @JoinColumn(name = "OBJ_TYPE_ID", referencedColumnName = "ID")
|
||||||
|
@JoinColumn(name = "OBJ_TYPE_ID", referencedColumnName = "TYPE_ID")
|
||||||
|
@ManyToOne
|
||||||
|
public ObjectType getObjectType() {
|
||||||
|
return objectType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObjectType(ObjectType objectType) {
|
||||||
|
this.objectType = objectType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@DiscriminatorValue("2")
|
||||||
|
public static class SimpleType extends DataType {
|
||||||
|
Integer count;
|
||||||
|
|
||||||
|
@Column(name = "counter")
|
||||||
|
public Integer getCount() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCount(Integer count) {
|
||||||
|
this.count = count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -136,7 +136,7 @@ public class RepeatedTableTest extends BaseCoreFunctionalTestCase {
|
||||||
private String description;
|
private String description;
|
||||||
private List<Prop> properties;
|
private List<Prop> properties;
|
||||||
|
|
||||||
@Column(name = "desc")
|
@Column(name = "descr")
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
@ -200,6 +200,7 @@ public class RepeatedTableTest extends BaseCoreFunctionalTestCase {
|
||||||
public static class SimpleType extends DataType {
|
public static class SimpleType extends DataType {
|
||||||
Integer count;
|
Integer count;
|
||||||
|
|
||||||
|
@Column(name = "counter")
|
||||||
public Integer getCount() {
|
public Integer getCount() {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue