introduce AnnotatedJoinColumns and remove deprecated stuff

This commit is contained in:
Gavin King 2022-10-30 10:45:12 +01:00
parent ce12d4a586
commit 653bf987bd
23 changed files with 1245 additions and 992 deletions

View File

@ -2824,9 +2824,8 @@ public class ModelBinder {
}
String typeName = typeSource.getName();
Map<String,String> typeParameters = new HashMap<>();
final TypeDefinition typeDefinition = sourceDocument.getMetadataCollector().getTypeDefinition( typeName );
final Map<String,String> typeParameters = new HashMap<>();
if ( typeDefinition != null ) {
// the explicit name referred to a type-def
typeName = typeDefinition.getTypeImplementorClass().getName();
@ -3302,13 +3301,10 @@ public class ModelBinder {
final String propRef = keySource.getReferencedPropertyName();
getCollectionBinding().setReferencedPropertyName( propRef );
final KeyValue keyVal;
if ( propRef == null ) {
keyVal = getCollectionBinding().getOwner().getIdentifier();
}
else {
keyVal = (KeyValue) getCollectionBinding().getOwner().getRecursiveProperty( propRef ).getValue();
}
final PersistentClass owner = getCollectionBinding().getOwner();
final KeyValue keyVal = propRef == null
? owner.getIdentifier()
: (KeyValue) owner.getRecursiveProperty( propRef ).getValue();
final DependantValue key = new DependantValue(
mappingDocument,
getCollectionBinding().getCollectionTable(),

View File

@ -44,7 +44,17 @@ import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
/**
* A {@link jakarta.persistence.Column} annotation
* A mapping to a column, logically representing a
* {@link jakarta.persistence.Column} annotation, but not
* every instance corresponds to an explicit annotation in
* the Java code.
* <p>
* This class holds a representation that is intermediate
* between the annotation of the Java source code, and the
* mapping model object {@link Column}. It's used only by
* the {@link AnnotationBinder} while parsing annotations,
* and does not survive into later stages of the startup
* process.
*
* @author Emmanuel Bernard
*/
@ -66,7 +76,7 @@ public class AnnotatedColumn {
private Integer precision;
private Integer scale;
private String logicalColumnName;
private String propertyName;
private String propertyName; // this is really a .-separated property path
private boolean unique;
private boolean nullable = true;
private String formulaString;
@ -184,6 +194,9 @@ public class AnnotatedColumn {
this.propertyName = propertyName;
}
/**
* A property path relative to the {@link #getPropertyHolder() PropertyHolder}.
*/
public String getPropertyName() {
return propertyName;
}
@ -328,7 +341,7 @@ public class AnnotatedColumn {
private String processColumnName(String columnName, boolean applyNamingStrategy) {
if ( applyNamingStrategy ) {
Database database = context.getMetadataCollector().getDatabase();
final Database database = context.getMetadataCollector().getDatabase();
return context.getBuildingOptions().getPhysicalNamingStrategy()
.toPhysicalColumnName( database.toIdentifier( columnName ), database.getJdbcEnvironment() )
.render( database.getDialect() );

View File

@ -7,7 +7,6 @@
package org.hibernate.cfg;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.PrimaryKeyJoinColumn;
@ -16,26 +15,15 @@ import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.model.naming.EntityNaming;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitJoinColumnNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitPrimaryKeyJoinColumnNameSource;
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
@ -47,99 +35,35 @@ import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue;
import static org.hibernate.cfg.BinderHelper.isEmptyOrNullAnnotationValue;
import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.internal.util.StringHelper.isQuoted;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.unquote;
/**
* A {@link jakarta.persistence.JoinColumn} annotation
* An element of a join condition, logically representing a
* {@link jakarta.persistence.JoinColumn} annotation, but not
* every instance corresponds to an explicit annotation in the
* Java code.
* <p>
* There's no exact analog of this class in the mapping model,
* so some information is lost when it's transformed into a
* {@link Column}.
*
* @author Emmanuel Bernard
*/
public class AnnotatedJoinColumn extends AnnotatedColumn {
private String referencedColumn;
@Deprecated private String mappedBy;
@Deprecated private String mappedByPropertyName; //property name on the owning side if any
@Deprecated private String mappedByTableName; //table name on the mapped by side if any
@Deprecated private String mappedByEntityName;
@Deprecated private boolean JPA2ElementCollection;
@Deprecated private String manyToManyOwnerSideEntityName;
private AnnotatedJoinColumns parent;
// due to @AnnotationOverride overriding rules,
// we don't want the constructor to be public
private AnnotatedJoinColumn() {
mappedBy = "";
}
/**
* @deprecated this is not a column-level setting, so it's better to
* hang this information somewhere else
*/
@Deprecated
public void setJPA2ElementCollection(boolean JPA2ElementCollection) {
this.JPA2ElementCollection = JPA2ElementCollection;
}
/**
* @deprecated this is not a column-level setting, so it's better to
* get this information from somewhere else
*/
@Deprecated
public String getManyToManyOwnerSideEntityName() {
return manyToManyOwnerSideEntityName;
}
/**
* @deprecated this is not a column-level setting, so it's better to
* get this information from somewhere else
*/
@Deprecated
public void setManyToManyOwnerSideEntityName(String manyToManyOwnerSideEntityName) {
this.manyToManyOwnerSideEntityName = manyToManyOwnerSideEntityName;
}
private AnnotatedJoinColumn() {}
public void setReferencedColumn(String referencedColumn) {
this.referencedColumn = referencedColumn;
}
/**
* @deprecated this is not a column-level setting, so it's better to
* get this information from somewhere else
*/
@Deprecated
public String getMappedBy() {
return mappedBy;
}
/**
* @deprecated this is not column-level information, so it's better to
* hang all this information somewhere else
*/
public void setMappedBy(String entityName, String logicalTableName, String mappedByProperty) {
mappedByEntityName = entityName;
mappedByTableName = logicalTableName;
mappedByPropertyName = mappedByProperty;
}
/**
* @deprecated this is not a column-level setting, so it's better to
* hang this information somewhere else
*/
@Deprecated
public void setMappedBy(String mappedBy) {
this.mappedBy = mappedBy;
}
/**
* @return true if the association mapping annotation did specify
* {@link jakarta.persistence.OneToMany#mappedBy() mappedBy},
* meaning that this {@code @JoinColumn} mapping belongs to an
* unowned many-valued association.
*/
public boolean hasMappedBy() {
return !isEmptyOrNullAnnotationValue( mappedBy );
}
/**
* The {@link JoinColumn#referencedColumnName() referencedColumnName}.
*/
@ -155,138 +79,58 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
return isEmptyOrNullAnnotationValue( referencedColumn );
}
public static AnnotatedJoinColumn[] buildJoinColumnsOrFormulas(
JoinColumnOrFormula[] joinColumnOrFormulas,
static AnnotatedJoinColumn buildJoinColumn(
JoinColumn joinColumn,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
final AnnotatedJoinColumn[] joinColumns = new AnnotatedJoinColumn[joinColumnOrFormulas.length];
for (int i = 0; i < joinColumnOrFormulas.length; i++) {
JoinColumnOrFormula columnOrFormula = joinColumnOrFormulas[i];
JoinFormula formula = columnOrFormula.formula();
if ( formula.value() != null && !formula.value().isEmpty() ) {
joinColumns[i] = buildJoinFormula( formula, joins, propertyHolder, propertyName, buildingContext );
}
else {
joinColumns[i] = buildJoinColumn( mappedBy, joins, propertyHolder, propertyName, buildingContext, columnOrFormula );
}
final String path = qualify( propertyHolder.getPath(), propertyName );
final JoinColumn[] overriddes = propertyHolder.getOverriddenJoinColumn( path );
if ( overriddes != null ) {
//TODO: relax this restriction
throw new AnnotationException("Property '" + path
+ "' overrides mapping specified using '@JoinColumnOrFormula'");
}
return joinColumns;
}
private static AnnotatedJoinColumn buildJoinColumn(
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName, MetadataBuildingContext buildingContext,
JoinColumnOrFormula join) {
return buildJoinColumns(
new JoinColumn[]{ join.column() },
return buildJoinColumn(
joinColumn,
null,
mappedBy,
joins,
propertyHolder,
propertyName,
buildingContext
)[0];
}
/**
* build join formula
*/
public static AnnotatedJoinColumn buildJoinFormula(
JoinFormula joinFormula,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
AnnotatedJoinColumn formulaColumn = new AnnotatedJoinColumn();
formulaColumn.setFormula( joinFormula.value() );
formulaColumn.setReferencedColumn(joinFormula.referencedColumnName());
formulaColumn.setBuildingContext( buildingContext );
formulaColumn.setPropertyHolder( propertyHolder );
formulaColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
formulaColumn.setJoins( joins );
formulaColumn.bind();
return formulaColumn;
}
public static AnnotatedJoinColumn[] buildJoinColumns(
JoinColumn[] joinColumns,
Comment comment,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
return buildJoinColumnsWithDefaultColumnSuffix(
joinColumns,
comment,
mappedBy,
joins,
propertyHolder,
propertyName,
"",
buildingContext
);
}
public static AnnotatedJoinColumn[] buildJoinColumnsWithDefaultColumnSuffix(
JoinColumn[] joinColumns,
public static AnnotatedJoinColumn buildJoinFormula(
JoinFormula joinFormula,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
final AnnotatedJoinColumn formulaColumn = new AnnotatedJoinColumn();
formulaColumn.setFormula( joinFormula.value() );
formulaColumn.setReferencedColumn( joinFormula.referencedColumnName() );
formulaColumn.setBuildingContext( buildingContext );
formulaColumn.setPropertyHolder( propertyHolder );
formulaColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
formulaColumn.setJoins( joins );
formulaColumn.bind();
return formulaColumn;
}
static AnnotatedJoinColumn buildJoinColumn(
JoinColumn joinColumn,
Comment comment,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String suffixForDefaultColumnName,
MetadataBuildingContext buildingContext) {
JoinColumn[] overriddes = propertyHolder.getOverriddenJoinColumn( qualify( propertyHolder.getPath(), propertyName ) );
JoinColumn[] actualColumns = overriddes == null ? joinColumns : overriddes;
if ( actualColumns == null || actualColumns.length == 0 ) {
return new AnnotatedJoinColumn[] {
buildJoinColumn(
null,
comment,
mappedBy,
joins,
propertyHolder,
propertyName,
suffixForDefaultColumnName,
buildingContext
)
};
}
else {
final AnnotatedJoinColumn[] result = new AnnotatedJoinColumn[actualColumns.length];
for (int index = 0; index < actualColumns.length; index++ ) {
result[index] = buildJoinColumn(
actualColumns[index],
comment,
mappedBy,
joins,
propertyHolder,
propertyName,
suffixForDefaultColumnName,
buildingContext
);
}
return result;
}
}
/**
* build join column for SecondaryTables
*/
private static AnnotatedJoinColumn buildJoinColumn(
JoinColumn joinColumn,
Comment comment,
String mappedBy, Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String suffixForDefaultColumnName,
MetadataBuildingContext buildingContext) {
String defaultColumnSuffix,
MetadataBuildingContext context) {
if ( joinColumn != null ) {
if ( !isEmptyOrNullAnnotationValue( mappedBy ) ) {
throw new AnnotationException(
@ -294,40 +138,59 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
+ "' is 'mappedBy' a different entity and may not explicitly specify the '@JoinColumn'"
);
}
AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setComment( comment != null ? comment.value() : null );
column.setBuildingContext( buildingContext );
column.setJoinAnnotation( joinColumn, null );
if ( isEmpty( column.getLogicalColumnName() ) && isNotEmpty( suffixForDefaultColumnName ) ) {
column.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
}
column.setJoins( joins );
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
column.setImplicit( false );
column.bind();
return column;
return explicitJoinColumn( joinColumn, comment, joins, propertyHolder, propertyName, defaultColumnSuffix, context );
}
else {
AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setMappedBy( mappedBy );
column.setJoins( joins );
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
// property name + suffix is an "explicit" column name
if ( isNotEmpty( suffixForDefaultColumnName ) ) {
column.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
column.setImplicit( false );
}
else {
column.setImplicit( true );
}
column.setBuildingContext( buildingContext );
column.bind();
return column;
return implicitJoinColumn( joins, propertyHolder, propertyName, defaultColumnSuffix, context );
}
}
private static AnnotatedJoinColumn explicitJoinColumn(
JoinColumn joinColumn,
Comment comment,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String defaultColumnSuffix,
MetadataBuildingContext context) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setComment( comment != null ? comment.value() : null );
column.setBuildingContext( context );
column.setJoinAnnotation(joinColumn, null );
if ( isEmpty( column.getLogicalColumnName() ) && isNotEmpty( defaultColumnSuffix ) ) {
column.setLogicalColumnName( propertyName + defaultColumnSuffix );
}
column.setJoins( joins );
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
column.setImplicit( false );
column.bind();
return column;
}
private static AnnotatedJoinColumn implicitJoinColumn(
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String defaultColumnSuffix,
MetadataBuildingContext context) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setJoins( joins );
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
// property name + suffix is an "explicit" column name
if ( isNotEmpty( defaultColumnSuffix ) ) {
column.setLogicalColumnName( propertyName + defaultColumnSuffix );
column.setImplicit( false );
}
else {
column.setImplicit( true );
}
column.setBuildingContext( context );
column.bind();
return column;
}
// TODO default name still useful in association table
public void setJoinAnnotation(JoinColumn joinColumn, String defaultName) {
@ -337,7 +200,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
else {
setImplicit( false );
if ( !isEmptyAnnotationValue( joinColumn.columnDefinition() ) ) {
setSqlType( getBuildingContext().getObjectNameNormalizer().applyGlobalQuoting( joinColumn.columnDefinition() ) );
setSqlType( getBuildingContext().getObjectNameNormalizer()
.applyGlobalQuoting( joinColumn.columnDefinition() ) );
}
if ( !isEmptyAnnotationValue( joinColumn.name() ) ) {
setLogicalColumnName( joinColumn.name() );
@ -372,72 +236,67 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
Map<String, Join> joins,
PropertyHolder propertyHolder,
MetadataBuildingContext context) {
final String defaultColumnName = context.getMetadataCollector().getLogicalColumnName(
identifier.getTable(),
identifier.getColumns().get(0).getQuotedName()
);
final ObjectNameNormalizer normalizer = context.getObjectNameNormalizer();
if ( primaryKeyJoinColumn != null || joinColumn != null ) {
final String columnName;
final String columnDefinition;
final String referencedColumnName;
if ( primaryKeyJoinColumn != null ) {
columnName = primaryKeyJoinColumn.name();
columnDefinition = primaryKeyJoinColumn.columnDefinition();
referencedColumnName = primaryKeyJoinColumn.referencedColumnName();
}
else {
columnName = joinColumn.name();
columnDefinition = joinColumn.columnDefinition();
referencedColumnName = joinColumn.referencedColumnName();
}
final String columnDef = columnDefinition.isEmpty() ? null
: normalizer.toDatabaseIdentifierText( columnDefinition );
final String logicalColumnName = columnName != null && columnName.isEmpty()
? normalizer.normalizeIdentifierQuotingAsString( defaultColumnName )
: normalizer.normalizeIdentifierQuotingAsString( columnName );
AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setSqlType( columnDef );
column.setLogicalColumnName( logicalColumnName );
column.setReferencedColumn( referencedColumnName );
column.setPropertyHolder( propertyHolder );
column.setJoins( joins );
column.setBuildingContext( context );
column.setImplicit( false );
column.setNullable( false );
column.bind();
return column;
}
else {
AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setLogicalColumnName( normalizer.normalizeIdentifierQuotingAsString( defaultColumnName ) );
column.setPropertyHolder( propertyHolder );
column.setJoins( joins );
column.setBuildingContext( context );
column.setImplicit( true );
column.setNullable( false );
column.bind();
return column;
}
final String defaultColumnName = context.getMetadataCollector()
.getLogicalColumnName( identifier.getTable(), identifier.getColumns().get(0).getQuotedName() );
return primaryKeyJoinColumn != null || joinColumn != null
? explicitJoinColumn( primaryKeyJoinColumn, joinColumn, joins, propertyHolder, context, defaultColumnName )
: implicitJoinColumn( joins, propertyHolder, context, defaultColumnName );
}
/**
* Override persistent class on oneToMany Cases for late settings
* Must only be used on second level pass binding
*/
public void setPersistentClass(
PersistentClass persistentClass,
private static AnnotatedJoinColumn explicitJoinColumn(
PrimaryKeyJoinColumn primaryKeyJoinColumn,
JoinColumn joinColumn,
Map<String, Join> joins,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
// TODO shouldn't we deduce the classname from the persistentClass?
this.propertyHolder = PropertyHolderBuilder.buildPropertyHolder(
persistentClass,
joins,
getBuildingContext(),
inheritanceStatePerClass
);
PropertyHolder propertyHolder,
MetadataBuildingContext context,
String defaultColumnName) {
final String columnName;
final String columnDefinition;
final String referencedColumnName;
if ( primaryKeyJoinColumn != null ) {
columnName = primaryKeyJoinColumn.name();
columnDefinition = primaryKeyJoinColumn.columnDefinition();
referencedColumnName = primaryKeyJoinColumn.referencedColumnName();
}
else {
columnName = joinColumn.name();
columnDefinition = joinColumn.columnDefinition();
referencedColumnName = joinColumn.referencedColumnName();
}
final ObjectNameNormalizer normalizer = context.getObjectNameNormalizer();
final String columnDef = columnDefinition.isEmpty() ? null
: normalizer.toDatabaseIdentifierText( columnDefinition );
final String logicalColumnName = columnName != null && columnName.isEmpty()
? normalizer.normalizeIdentifierQuotingAsString(defaultColumnName)
: normalizer.normalizeIdentifierQuotingAsString( columnName );
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setSqlType( columnDef );
column.setLogicalColumnName( logicalColumnName );
column.setReferencedColumn( referencedColumnName );
column.setPropertyHolder(propertyHolder);
column.setJoins(joins);
column.setBuildingContext(context);
column.setImplicit( false );
column.setNullable( false );
column.bind();
return column;
}
private static AnnotatedJoinColumn implicitJoinColumn(
Map<String, Join> joins,
PropertyHolder propertyHolder,
MetadataBuildingContext context,
String defaultColumnName ) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
final ObjectNameNormalizer normalizer = context.getObjectNameNormalizer();
column.setLogicalColumnName( normalizer.normalizeIdentifierQuotingAsString(defaultColumnName) );
column.setPropertyHolder(propertyHolder);
column.setJoins(joins);
column.setBuildingContext(context);
column.setImplicit( true );
column.setNullable( false );
column.bind();
return column;
}
public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) {
@ -467,281 +326,32 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
Column referencedColumn,
PersistentClass referencedEntity,
SimpleValue value) {
String logicalReferencedColumn = getBuildingContext().getMetadataCollector().getLogicalColumnName(
referencedEntity.getTable(),
referencedColumn.getQuotedName()
);
String columnName = buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
final String logicalReferencedColumn = getBuildingContext().getMetadataCollector()
.getLogicalColumnName( referencedEntity.getTable(), referencedColumn.getQuotedName() );
final String columnName = parent.buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
//yuk side effect on an implicit column
setLogicalColumnName( columnName );
setReferencedColumn( logicalReferencedColumn );
final Column mappingColumn = getMappingColumn();
initMappingColumn(
columnName,
null, referencedColumn.getLength(),
referencedColumn.getPrecision(),
referencedColumn.getScale(),
getMappingColumn() != null && getMappingColumn().isNullable(),
mappingColumn != null && mappingColumn.isNullable(),
referencedColumn.getSqlType(),
getMappingColumn() != null && getMappingColumn().isUnique(),
mappingColumn != null && mappingColumn.isUnique(),
false
);
linkWithValue( value );
}
public void addDefaultJoinColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
final String columnName = buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
final String columnName = parent.buildDefaultColumnName( referencedEntity, logicalReferencedColumn );
getMappingColumn().setName( columnName );
setLogicalColumnName( columnName );
}
private String buildDefaultColumnName(final PersistentClass referencedEntity, final String logicalReferencedColumn) {
final InFlightMetadataCollector collector = getBuildingContext().getMetadataCollector();
final Database database = collector.getDatabase();
final MetadataBuildingOptions options = getBuildingContext().getBuildingOptions();
final ImplicitNamingStrategy implicitNamingStrategy = options.getImplicitNamingStrategy();
final PhysicalNamingStrategy physicalNamingStrategy = options.getPhysicalNamingStrategy();
boolean mappedBySide = mappedByTableName != null || mappedByPropertyName != null;
boolean ownerSide = getPropertyName() != null;
boolean isRefColumnQuoted = StringHelper.isQuoted( logicalReferencedColumn );
Identifier columnIdentifier;
if ( mappedBySide ) {
// NOTE : While it is completely misleading here to allow for the combination
// of a "JPA ElementCollection" to be mappedBy, the code that uses this
// class relies on this behavior for handling the inverse side of
// many-to-many mappings
columnIdentifier = implicitNamingStrategy.determineJoinColumnName(
new ImplicitJoinColumnNameSource() {
final AttributePath attributePath = AttributePath.parse( mappedByPropertyName );
final ImplicitJoinColumnNameSource.Nature implicitNamingNature = getImplicitNature();
private final EntityNaming entityNaming = new EntityNaming() {
@Override
public String getClassName() {
return referencedEntity.getClassName();
}
@Override
public String getEntityName() {
return referencedEntity.getEntityName();
}
@Override
public String getJpaEntityName() {
return referencedEntity.getJpaEntityName();
}
};
private final Identifier referencedTableName = database.toIdentifier( mappedByTableName );
@Override
public Nature getNature() {
return implicitNamingNature;
}
@Override
public EntityNaming getEntityNaming() {
return entityNaming;
}
@Override
public AttributePath getAttributePath() {
return attributePath;
}
@Override
public Identifier getReferencedTableName() {
return referencedTableName;
}
@Override
public Identifier getReferencedColumnName() {
if ( logicalReferencedColumn != null ) {
return database.toIdentifier( logicalReferencedColumn );
}
if ( mappedByEntityName == null || mappedByPropertyName == null ) {
return null;
}
final Property mappedByProperty = collector.getEntityBinding( mappedByEntityName )
.getProperty( mappedByPropertyName );
final SimpleValue value = (SimpleValue) mappedByProperty.getValue();
if ( value.getSelectables().isEmpty() ) {
throw new AnnotationException(
String.format(
Locale.ENGLISH,
"Association '%s' is 'mappedBy' a property '%s' of entity '%s' with no columns",
propertyHolder.getPath(),
mappedByPropertyName,
mappedByEntityName
)
);
}
final Selectable selectable = value.getSelectables().get(0);
if ( !(selectable instanceof Column) ) {
throw new AnnotationException(
String.format(
Locale.ENGLISH,
"Association '%s' is 'mappedBy' a property '%s' of entity '%s' which maps to a formula",
propertyHolder.getPath(),
mappedByPropertyName,
propertyHolder.getPath()
)
);
}
if ( value.getSelectables().size()>1 ) {
throw new AnnotationException(
String.format(
Locale.ENGLISH,
"Association '%s' is 'mappedBy' a property '%s' of entity '%s' with multiple columns",
propertyHolder.getPath(),
mappedByPropertyName,
propertyHolder.getPath()
)
);
}
return database.toIdentifier( ( (Column) selectable ).getQuotedName() );
}
@Override
public MetadataBuildingContext getBuildingContext() {
return AnnotatedJoinColumn.this.getBuildingContext();
}
}
);
//one element was quoted so we quote
if ( isRefColumnQuoted || StringHelper.isQuoted( mappedByTableName ) ) {
columnIdentifier = Identifier.quote( columnIdentifier );
}
}
else if ( ownerSide ) {
final String logicalTableName = collector.getLogicalTableName( referencedEntity.getTable() );
columnIdentifier = implicitNamingStrategy.determineJoinColumnName(
new ImplicitJoinColumnNameSource() {
final ImplicitJoinColumnNameSource.Nature implicitNamingNature = getImplicitNature();
private final EntityNaming entityNaming = new EntityNaming() {
@Override
public String getClassName() {
return referencedEntity.getClassName();
}
@Override
public String getEntityName() {
return referencedEntity.getEntityName();
}
@Override
public String getJpaEntityName() {
return referencedEntity.getJpaEntityName();
}
};
private final AttributePath attributePath = AttributePath.parse( getPropertyName() );
private final Identifier referencedTableName = database.toIdentifier( logicalTableName );
private final Identifier referencedColumnName = database.toIdentifier( logicalReferencedColumn );
@Override
public Nature getNature() {
return implicitNamingNature;
}
@Override
public EntityNaming getEntityNaming() {
return entityNaming;
}
@Override
public AttributePath getAttributePath() {
return attributePath;
}
@Override
public Identifier getReferencedTableName() {
return referencedTableName;
}
@Override
public Identifier getReferencedColumnName() {
return referencedColumnName;
}
@Override
public MetadataBuildingContext getBuildingContext() {
return AnnotatedJoinColumn.this.getBuildingContext();
}
}
);
// HHH-11826 magic. See Ejb3Column and the HHH-6005 comments
if ( columnIdentifier.getText().contains( "_collection&&element_" ) ) {
columnIdentifier = Identifier.toIdentifier(
columnIdentifier.getText().replace( "_collection&&element_", "_" ),
columnIdentifier.isQuoted()
);
}
//one element was quoted so we quote
if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
columnIdentifier = Identifier.quote( columnIdentifier );
}
}
else {
final Identifier logicalTableName = database.toIdentifier(
collector.getLogicalTableName( referencedEntity.getTable() )
);
// is an intra-entity hierarchy table join so copy the name by default
columnIdentifier = implicitNamingStrategy.determinePrimaryKeyJoinColumnName(
new ImplicitPrimaryKeyJoinColumnNameSource() {
@Override
public MetadataBuildingContext getBuildingContext() {
return AnnotatedJoinColumn.this.getBuildingContext();
}
@Override
public Identifier getReferencedTableName() {
return logicalTableName;
}
@Override
public Identifier getReferencedPrimaryKeyColumnName() {
return database.toIdentifier( logicalReferencedColumn );
}
}
);
if ( !columnIdentifier.isQuoted() && ( isRefColumnQuoted || logicalTableName.isQuoted() ) ) {
columnIdentifier = Identifier.quote( columnIdentifier );
}
}
return physicalNamingStrategy.toPhysicalColumnName( columnIdentifier, database.getJdbcEnvironment() )
.render( database.getJdbcEnvironment().getDialect() );
}
/**
* @deprecated this is not a column-level setting, so it's better to
* do this work somewhere else
*/
@Deprecated
private ImplicitJoinColumnNameSource.Nature getImplicitNature() {
if ( getPropertyHolder().isEntity() ) {
return ImplicitJoinColumnNameSource.Nature.ENTITY;
}
else if ( JPA2ElementCollection ) {
return ImplicitJoinColumnNameSource.Nature.ELEMENT_COLLECTION;
}
else {
return ImplicitJoinColumnNameSource.Nature.ENTITY_COLLECTION;
}
}
/**
* used for mappedBy cases
*/
@ -749,7 +359,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
initMappingColumn(
//column.getName(),
column.getQuotedName(),
null, column.getLength(),
null,
column.getLength(),
column.getPrecision(),
column.getScale(),
getMappingColumn().isNullable(),
@ -762,15 +373,15 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
@Override
protected void addColumnBinding(SimpleValue value) {
if ( isEmpty( mappedBy ) ) {
if ( isEmpty( parent.getMappedBy() ) ) {
// was the column explicitly quoted in the mapping/annotation
// TODO: in metamodel, we need to better split global quoting and explicit quoting w/ respect to logical names
boolean isLogicalColumnQuoted = StringHelper.isQuoted( getLogicalColumnName() );
final ObjectNameNormalizer nameNormalizer = getBuildingContext().getObjectNameNormalizer();
final String logicalColumnName = nameNormalizer.normalizeIdentifierQuotingAsString( getLogicalColumnName() );
final String referencedColumn = nameNormalizer.normalizeIdentifierQuotingAsString( getReferencedColumn() );
final String unquotedLogColName = StringHelper.unquote( logicalColumnName );
final String unquotedRefColumn = StringHelper.unquote( referencedColumn );
boolean isLogicalColumnQuoted = isQuoted( getLogicalColumnName() );
final ObjectNameNormalizer normalizer = getBuildingContext().getObjectNameNormalizer();
final String logicalColumnName = normalizer.normalizeIdentifierQuotingAsString( getLogicalColumnName() );
final String referencedColumn = normalizer.normalizeIdentifierQuotingAsString( getReferencedColumn() );
final String unquotedLogColName = unquote( logicalColumnName );
final String unquotedRefColumn = unquote( referencedColumn );
final String collectionColName = isNotEmpty( unquotedLogColName )
? unquotedLogColName
: getPropertyName() + '_' + unquotedRefColumn;
@ -793,18 +404,20 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
public static final int NON_PK_REFERENCE = 2;
public static int checkReferencedColumnsType(
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
PersistentClass referencedEntity,
MetadataBuildingContext context) {
if ( joinColumns.length == 0 ) {
final AnnotatedJoinColumn[] columns = joinColumns.getColumns();
if ( columns.length == 0 ) {
return NO_REFERENCE; //shortcut
}
final Object columnOwner = findReferencedColumnOwner( referencedEntity, joinColumns[0], context );
final AnnotatedJoinColumn firstColumn = columns[0];
final Object columnOwner = findReferencedColumnOwner( referencedEntity, firstColumn, context );
if ( columnOwner == null ) {
try {
throw new MappingException( "A '@JoinColumn' references a column named '"
+ joinColumns[0].getReferencedColumn() + "' but the target entity '"
+ firstColumn.getReferencedColumn() + "' but the target entity '"
+ referencedEntity.getEntityName() + "' has no property which maps to this column" );
}
catch (MappingException me) {
@ -817,7 +430,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
final Table table = getTable( columnOwner );
final List<Selectable> keyColumns = referencedEntity.getKey().getSelectables();
boolean explicitColumnReference = false;
for ( AnnotatedJoinColumn column : joinColumns ) {
for ( AnnotatedJoinColumn column : columns ) {
if ( !column.isReferenceImplicit() ) {
explicitColumnReference = true;
if ( !keyColumns.contains( column( context, table, column.getReferencedColumn() ) ) ) {
@ -828,7 +441,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
}
if ( explicitColumnReference ) {
// if we got to here, all the columns belong to the PK
return keyColumns.size() == joinColumns.length
return keyColumns.size() == columns.length
// we have all the PK columns
? PK_REFERENCE
// we have a subset of the PK columns
@ -862,7 +475,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
* @param column the referenced column.
*/
public void overrideFromReferencedColumnIfNecessary(Column column) {
Column mappingColumn = getMappingColumn();
final Column mappingColumn = getMappingColumn();
if ( mappingColumn != null ) {
// columnDefinition can also be specified using @JoinColumn, hence we have to check
// whether it is set or not
@ -883,50 +496,44 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
super.redefineColumnName( columnName, null, applyNamingStrategy );
}
public static AnnotatedJoinColumn[] buildJoinTableJoinColumns(
JoinColumn[] joinColumns,
static AnnotatedJoinColumn buildImplicitJoinTableJoinColumn(
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
String propertyName,
String mappedBy,
MetadataBuildingContext buildingContext) {
if ( joinColumns == null ) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setImplicit( true );
column.setNullable( false ); //I break the spec, but it's for good
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
column.setJoins( secondaryTables );
column.setBuildingContext( buildingContext );
column.setMappedBy( mappedBy );
column.bind();
return new AnnotatedJoinColumn[] { column };
}
else {
final AnnotatedJoinColumn[] columns = new AnnotatedJoinColumn[joinColumns.length];
int length = joinColumns.length;
for (int index = 0; index < length; index++) {
final JoinColumn joinColumn = joinColumns[index];
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setImplicit( true );
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
column.setJoins( secondaryTables );
column.setBuildingContext( buildingContext );
column.setMappedBy( mappedBy );
column.setJoinAnnotation( joinColumn, propertyName );
column.setNullable( false ); //I break the spec, but it's for good
//done after the annotation to override it
column.bind();
columns[index] = column;
}
return columns;
}
MetadataBuildingContext context) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setImplicit( true );
column.setNullable( false ); //I break the spec, but it's for good
column.setPropertyHolder( propertyHolder );
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
column.setJoins( secondaryTables );
column.setBuildingContext( context );
column.bind();
return column;
}
static AnnotatedJoinColumn buildExplicitJoinTableJoinColumn(
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext context,
JoinColumn joinColumn) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setImplicit( true );
column.setPropertyHolder(propertyHolder);
column.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
column.setJoins( secondaryTables );
column.setBuildingContext( context );
column.setJoinAnnotation( joinColumn, propertyName );
column.setNullable( false ); //I break the spec, but it's for good
//done after the annotation to override it
column.bind();
return column;
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
final StringBuilder string = new StringBuilder();
string.append( getClass().getSimpleName() ).append( "(" );
if ( isNotEmpty( getLogicalColumnName() ) ) {
string.append( "column='" ).append( getLogicalColumnName() ).append( "'," );
@ -940,4 +547,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
string.append( ")" );
return string.toString();
}
public void setParent(AnnotatedJoinColumns parent) {
this.parent = parent;
}
}

View File

@ -0,0 +1,581 @@
package org.hibernate.cfg;
import jakarta.persistence.JoinColumn;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.model.naming.EntityNaming;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitJoinColumnNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitPrimaryKeyJoinColumnNameSource;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue;
import java.util.Locale;
import java.util.Map;
import static org.hibernate.cfg.BinderHelper.getRelativePath;
import static org.hibernate.cfg.BinderHelper.isEmptyOrNullAnnotationValue;
import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder;
import static org.hibernate.internal.util.StringHelper.isQuoted;
import static org.hibernate.internal.util.StringHelper.qualify;
/**
* A list of {@link jakarta.persistence.JoinColumn}s that form a single join
* condition, similar in concept to {@link jakarta.persistence.JoinColumns},
* but not every instance of this class corresponds to an explicit annotation
* in the Java code.
* <p>
* There's no exact analog of this class in the mapping model, so some
* information is lost when it's transformed into a list of {@link Column}s.
*
* @author Gavin King
*/
public class AnnotatedJoinColumns {
private AnnotatedJoinColumn[] columns;
private PropertyHolder propertyHolder;
private String propertyName; // this is really a .-separated property path
private MetadataBuildingContext buildingContext;
//TODO: do we really need to hang so many strings off this class?
private String mappedBy;
private String mappedByPropertyName; //property name on the owning side if any
private String mappedByTableName; //table name on the mapped by side if any
private String mappedByEntityName;
private boolean elementCollection;
private String manyToManyOwnerSideEntityName;
public AnnotatedJoinColumns() {}
public static AnnotatedJoinColumns buildJoinColumnsOrFormulas(
JoinColumnOrFormula[] joinColumnOrFormulas,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext context) {
final AnnotatedJoinColumn[] columns = new AnnotatedJoinColumn[joinColumnOrFormulas.length];
for ( int i = 0; i < joinColumnOrFormulas.length; i++ ) {
final JoinColumnOrFormula columnOrFormula = joinColumnOrFormulas[i];
final JoinFormula formula = columnOrFormula.formula();
final JoinColumn column = columnOrFormula.column();
columns[i] = formula.value() != null && !formula.value().isEmpty()
? AnnotatedJoinColumn.buildJoinFormula( formula, joins, propertyHolder, propertyName, context )
: AnnotatedJoinColumn.buildJoinColumn( column, mappedBy, joins, propertyHolder, propertyName, context );
}
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( context );
joinColumns.setPropertyHolder( propertyHolder );
joinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
joinColumns.setColumns( columns );
joinColumns.setMappedBy( mappedBy );
return joinColumns;
}
public static AnnotatedJoinColumns buildJoinColumns(
JoinColumn[] joinColumns,
Comment comment,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
return buildJoinColumnsWithDefaultColumnSuffix(
joinColumns,
comment,
mappedBy,
joins,
propertyHolder,
propertyName,
"",
buildingContext
);
}
public static AnnotatedJoinColumns buildJoinColumnsWithDefaultColumnSuffix(
JoinColumn[] joinColumns,
Comment comment,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
String propertyName,
String defaultColumnSuffix,
MetadataBuildingContext context) {
final String path = qualify( propertyHolder.getPath(), propertyName );
final JoinColumn[] overriddes = propertyHolder.getOverriddenJoinColumn( path );
final JoinColumn[] actualColumns = overriddes == null ? joinColumns : overriddes;
if ( actualColumns == null || actualColumns.length == 0 ) {
final AnnotatedJoinColumn joinColumn = AnnotatedJoinColumn.buildJoinColumn(
null,
comment,
mappedBy,
joins,
propertyHolder,
propertyName,
defaultColumnSuffix,
context
);
final AnnotatedJoinColumns annotatedJoinColumns = new AnnotatedJoinColumns();
annotatedJoinColumns.setBuildingContext( context );
annotatedJoinColumns.setPropertyHolder( propertyHolder );
annotatedJoinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
annotatedJoinColumns.setColumns( new AnnotatedJoinColumn[] { joinColumn } );
annotatedJoinColumns.setMappedBy( mappedBy );
return annotatedJoinColumns;
}
else {
final AnnotatedJoinColumn[] result = new AnnotatedJoinColumn[actualColumns.length];
for ( int index = 0; index < actualColumns.length; index++ ) {
result[index] = AnnotatedJoinColumn.buildJoinColumn(
actualColumns[index],
comment,
mappedBy,
joins,
propertyHolder,
propertyName,
defaultColumnSuffix,
context
);
}
final AnnotatedJoinColumns annotatedJoinColumns = new AnnotatedJoinColumns();
annotatedJoinColumns.setBuildingContext( context );
annotatedJoinColumns.setPropertyHolder( propertyHolder );
annotatedJoinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
annotatedJoinColumns.setColumns( result );
annotatedJoinColumns.setMappedBy( mappedBy );
return annotatedJoinColumns;
}
}
public static AnnotatedJoinColumns buildJoinTableJoinColumns(
JoinColumn[] joinColumns,
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
String propertyName,
String mappedBy,
MetadataBuildingContext context) {
final AnnotatedJoinColumn[] columns;
if ( joinColumns == null ) {
columns = new AnnotatedJoinColumn[] { AnnotatedJoinColumn.buildImplicitJoinTableJoinColumn(
secondaryTables,
propertyHolder,
propertyName,
context
) };
}
else {
columns = new AnnotatedJoinColumn[joinColumns.length];
int length = joinColumns.length;
for (int index = 0; index < length; index++) {
columns[index] = AnnotatedJoinColumn.buildExplicitJoinTableJoinColumn(
secondaryTables,
propertyHolder,
propertyName,
context,
joinColumns[index]
);
}
}
final AnnotatedJoinColumns annotatedJoinColumns = new AnnotatedJoinColumns();
annotatedJoinColumns.setBuildingContext( context );
annotatedJoinColumns.setPropertyHolder( propertyHolder );
annotatedJoinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
annotatedJoinColumns.setColumns( columns );
annotatedJoinColumns.setMappedBy( mappedBy );
return annotatedJoinColumns;
}
public AnnotatedJoinColumn[] getColumns() {
return columns;
}
public void setColumns(AnnotatedJoinColumn[] columns) {
this.columns = columns;
if ( columns != null ) {
for ( AnnotatedJoinColumn column : columns ) {
column.setParent( this );
}
}
}
public String getMappedBy() {
return mappedBy;
}
public void setMappedBy(String mappedBy) {
this.mappedBy = mappedBy;
}
/**
* @return true if the association mapping annotation did specify
* {@link jakarta.persistence.OneToMany#mappedBy() mappedBy},
* meaning that this {@code @JoinColumn} mapping belongs to an
* unowned many-valued association.
*/
public boolean hasMappedBy() {
return !isEmptyOrNullAnnotationValue( getMappedBy() );
}
public String getMappedByEntityName() {
return mappedByEntityName;
}
public String getMappedByPropertyName() {
return mappedByPropertyName;
}
public String getMappedByTableName() {
return mappedByTableName;
}
public PropertyHolder getPropertyHolder() {
return propertyHolder;
}
public void setPropertyHolder(PropertyHolder propertyHolder) {
this.propertyHolder = propertyHolder;
}
/**
* Override persistent class on oneToMany Cases for late settings
* Must only be used on second level pass binding
*/
public void setPersistentClass(
PersistentClass persistentClass,
Map<String, Join> joins,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
// TODO shouldn't we deduce the class name from the persistentClass?
propertyHolder = buildPropertyHolder(
persistentClass,
joins,
buildingContext,
inheritanceStatePerClass
);
for ( AnnotatedJoinColumn column : columns ) {
column.setPropertyHolder( propertyHolder );
}
}
public void setBuildingContext(MetadataBuildingContext buildingContext) {
this.buildingContext = buildingContext;
}
public boolean isElementCollection() {
return elementCollection;
}
public void setElementCollection(boolean elementCollection) {
this.elementCollection = elementCollection;
}
public void setManyToManyOwnerSideEntityName(String entityName) {
manyToManyOwnerSideEntityName = entityName;
}
public String getManyToManyOwnerSideEntityName() {
return manyToManyOwnerSideEntityName;
}
public void setMappedBy(String entityName, String logicalTableName, String mappedByProperty) {
mappedByEntityName = entityName;
mappedByTableName = logicalTableName;
mappedByPropertyName = mappedByProperty;
}
String buildDefaultColumnName(PersistentClass referencedEntity, String logicalReferencedColumn) {
final MetadataBuildingOptions options = buildingContext.getBuildingOptions();
final ImplicitNamingStrategy implicitNamingStrategy = options.getImplicitNamingStrategy();
final PhysicalNamingStrategy physicalNamingStrategy = options.getPhysicalNamingStrategy();
boolean mappedBySide = getMappedByTableName() != null || getMappedByPropertyName() != null;
boolean ownerSide = getPropertyName() != null;
boolean isRefColumnQuoted = isQuoted( logicalReferencedColumn );
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
final Database database = collector.getDatabase();
Identifier columnIdentifier;
if ( mappedBySide ) {
// NOTE : While it is completely misleading here to allow for the combination
// of a "JPA ElementCollection" to be mappedBy, the code that uses this
// class relies on this behavior for handling the inverse side of
// many-to-many mappings
columnIdentifier = implicitNamingStrategy.determineJoinColumnName(
new UnownedImplicitJoinColumnNameSource( referencedEntity, logicalReferencedColumn )
);
//one element was quoted so we quote
if ( isRefColumnQuoted || isQuoted( getMappedByTableName() ) ) {
columnIdentifier = Identifier.quote( columnIdentifier );
}
}
else if ( ownerSide ) {
final String logicalTableName = collector.getLogicalTableName( referencedEntity.getTable() );
columnIdentifier = implicitNamingStrategy.determineJoinColumnName(
new OwnedImplicitJoinColumnNameSource( referencedEntity, logicalTableName, logicalReferencedColumn )
);
// HHH-11826 magic. See Ejb3Column and the HHH-6005 comments
if ( columnIdentifier.getText().contains( "_collection&&element_" ) ) {
columnIdentifier = Identifier.toIdentifier(
columnIdentifier.getText().replace( "_collection&&element_", "_" ),
columnIdentifier.isQuoted()
);
}
//one element was quoted so we quote
if ( isRefColumnQuoted || isQuoted( logicalTableName ) ) {
columnIdentifier = Identifier.quote( columnIdentifier );
}
}
else {
final Identifier logicalTableName = database.toIdentifier(
collector.getLogicalTableName( referencedEntity.getTable() )
);
// is an intra-entity hierarchy table join so copy the name by default
columnIdentifier = implicitNamingStrategy.determinePrimaryKeyJoinColumnName(
new ImplicitPrimaryKeyJoinColumnNameSource() {
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
@Override
public Identifier getReferencedTableName() {
return logicalTableName;
}
@Override
public Identifier getReferencedPrimaryKeyColumnName() {
return database.toIdentifier( logicalReferencedColumn );
}
}
);
if ( !columnIdentifier.isQuoted() && ( isRefColumnQuoted || logicalTableName.isQuoted() ) ) {
columnIdentifier = Identifier.quote( columnIdentifier );
}
}
final JdbcEnvironment jdbcEnvironment = database.getJdbcEnvironment();
return physicalNamingStrategy.toPhysicalColumnName( columnIdentifier, jdbcEnvironment )
.render( jdbcEnvironment.getDialect() );
}
/**
* A property path relative to the {@link #getPropertyHolder() PropertyHolder}.
*/
public String getPropertyName() {
return propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
private ImplicitJoinColumnNameSource.Nature getImplicitNature() {
if ( getPropertyHolder().isEntity() ) {
return ImplicitJoinColumnNameSource.Nature.ENTITY;
}
else if ( isElementCollection() ) {
return ImplicitJoinColumnNameSource.Nature.ELEMENT_COLLECTION;
}
else {
return ImplicitJoinColumnNameSource.Nature.ENTITY_COLLECTION;
}
}
private class UnownedImplicitJoinColumnNameSource implements ImplicitJoinColumnNameSource {
final AttributePath attributePath;
final Nature implicitNamingNature;
private final EntityNaming entityNaming;
private final Identifier referencedTableName;
private final String logicalReferencedColumn;
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
final Database database = collector.getDatabase();
public UnownedImplicitJoinColumnNameSource(PersistentClass referencedEntity, String logicalReferencedColumn) {
this.logicalReferencedColumn = logicalReferencedColumn;
attributePath = AttributePath.parse( getMappedByPropertyName() );
implicitNamingNature = getImplicitNature();
entityNaming = new EntityNaming() {
@Override
public String getClassName() {
return referencedEntity.getClassName();
}
@Override
public String getEntityName() {
return referencedEntity.getEntityName();
}
@Override
public String getJpaEntityName() {
return referencedEntity.getJpaEntityName();
}
};
referencedTableName = database.toIdentifier( getMappedByTableName() );
}
@Override
public Nature getNature() {
return implicitNamingNature;
}
@Override
public EntityNaming getEntityNaming() {
return entityNaming;
}
@Override
public AttributePath getAttributePath() {
return attributePath;
}
@Override
public Identifier getReferencedTableName() {
return referencedTableName;
}
@Override
public Identifier getReferencedColumnName() {
if ( logicalReferencedColumn != null ) {
return database.toIdentifier(logicalReferencedColumn);
}
if ( getMappedByEntityName() == null || getMappedByPropertyName() == null ) {
return null;
}
final Property mappedByProperty = collector.getEntityBinding( getMappedByEntityName() )
.getProperty( getMappedByPropertyName() );
final SimpleValue value = (SimpleValue) mappedByProperty.getValue();
if ( value.getSelectables().isEmpty() ) {
throw new AnnotationException(
String.format(
Locale.ENGLISH,
"Association '%s' is 'mappedBy' a property '%s' of entity '%s' with no columns",
propertyHolder.getPath(),
getMappedByPropertyName(),
getMappedByEntityName()
)
);
}
final Selectable selectable = value.getSelectables().get(0);
if ( !(selectable instanceof Column) ) {
throw new AnnotationException(
String.format(
Locale.ENGLISH,
"Association '%s' is 'mappedBy' a property '%s' of entity '%s' which maps to a formula",
propertyHolder.getPath(),
getMappedByPropertyName(),
propertyHolder.getPath()
)
);
}
if ( value.getSelectables().size()>1 ) {
throw new AnnotationException(
String.format(
Locale.ENGLISH,
"Association '%s' is 'mappedBy' a property '%s' of entity '%s' with multiple columns",
propertyHolder.getPath(),
getMappedByPropertyName(),
propertyHolder.getPath()
)
);
}
return database.toIdentifier( ( (Column) selectable ).getQuotedName() );
}
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
}
private class OwnedImplicitJoinColumnNameSource implements ImplicitJoinColumnNameSource {
final Nature implicitNamingNature;
private final EntityNaming entityNaming;
private final AttributePath attributePath;
private final Identifier referencedTableName;
private final Identifier referencedColumnName;
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
final Database database = collector.getDatabase();
public OwnedImplicitJoinColumnNameSource(PersistentClass referencedEntity, String logicalTableName, String logicalReferencedColumn) {
implicitNamingNature = getImplicitNature();
entityNaming = new EntityNaming() {
@Override
public String getClassName() {
return referencedEntity.getClassName();
}
@Override
public String getEntityName() {
return referencedEntity.getEntityName();
}
@Override
public String getJpaEntityName() {
return referencedEntity.getJpaEntityName();
}
};
attributePath = AttributePath.parse( getPropertyName() );
referencedTableName = database.toIdentifier( logicalTableName );
referencedColumnName = database.toIdentifier( logicalReferencedColumn );
}
@Override
public Nature getNature() {
return implicitNamingNature;
}
@Override
public EntityNaming getEntityNaming() {
return entityNaming;
}
@Override
public AttributePath getAttributePath() {
return attributePath;
}
@Override
public Identifier getReferencedTableName() {
return referencedTableName;
}
@Override
public Identifier getReferencedColumnName() {
return referencedColumnName;
}
@Override
public MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
}
}

View File

@ -152,6 +152,7 @@ import static org.hibernate.cfg.BinderHelper.getMappedSuperclassOrNull;
import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getPath;
import static org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.cfg.BinderHelper.getRelativePath;
import static org.hibernate.cfg.BinderHelper.hasToOneAnnotation;
import static org.hibernate.cfg.BinderHelper.makeIdGenerator;
import static org.hibernate.cfg.InheritanceState.getInheritanceStateOfSuperEntity;
@ -1118,7 +1119,7 @@ public final class AnnotationBinder {
context
).extractMetadata();
AnnotatedColumn[] columns = columnsBuilder.getColumns();
AnnotatedJoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
AnnotatedJoinColumns joinColumns = columnsBuilder.getJoinColumns();
final PropertyBinder propertyBinder = new PropertyBinder();
propertyBinder.setName( inferredData.getPropertyName() );
@ -1505,7 +1506,7 @@ public final class AnnotationBinder {
Class<? extends CompositeUserType<?>> compositeUserType) {
final String referencedEntityName;
final String propertyName;
final AnnotatedJoinColumn[] actualColumns;
final AnnotatedJoinColumns actualColumns;
if ( isOverridden ) {
// careful: not always a @MapsId property, sometimes it's from an @IdClass
PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId(
@ -1513,7 +1514,12 @@ public final class AnnotationBinder {
);
referencedEntityName = mapsIdProperty.getClassOrElementName();
propertyName = mapsIdProperty.getPropertyName();
actualColumns = (AnnotatedJoinColumn[]) columns;
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( context );
joinColumns.setPropertyHolder( propertyHolder );
joinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
joinColumns.setColumns( (AnnotatedJoinColumn[]) columns );
actualColumns = joinColumns;
}
else {
referencedEntityName = null;
@ -1553,7 +1559,7 @@ public final class AnnotationBinder {
boolean isIdentifierMapper,
MetadataBuildingContext context,
XProperty property,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
boolean forcePersist) {
//check validity
@ -1573,12 +1579,12 @@ public final class AnnotationBinder {
JoinTable assocTable = propertyHolder.getJoinTable(property);
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( AnnotatedJoinColumn joinColumn : joinColumns) {
for ( AnnotatedJoinColumn joinColumn : joinColumns.getColumns() ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
bindAny(
BinderHelper.getCascadeStrategy( null, hibernateCascade, false, forcePersist),
BinderHelper.getCascadeStrategy( null, hibernateCascade, false, forcePersist ),
//@Any has not cascade attribute
joinColumns,
onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action(),
@ -1595,20 +1601,20 @@ public final class AnnotationBinder {
boolean inSecondPass,
XProperty property,
AnnotatedColumn[] columns,
AnnotatedJoinColumn[] joinColumns) {
AnnotatedJoinColumns joinColumns) {
//process indexes after everything: in second pass, many to one has to be done before indexes
Index index = property.getAnnotation( Index.class );
final Index index = property.getAnnotation( Index.class );
if ( index != null ) {
if ( joinColumns != null ) {
for ( AnnotatedColumn column : joinColumns) {
for ( AnnotatedColumn column : joinColumns.getColumns() ) {
column.addIndex( index, inSecondPass);
}
}
else {
if ( columns != null ) {
for ( AnnotatedColumn column : columns) {
column.addIndex( index, inSecondPass);
for ( AnnotatedColumn column : columns ) {
column.addIndex( index, inSecondPass );
}
}
}
@ -1619,23 +1625,23 @@ public final class AnnotationBinder {
boolean inSecondPass,
XProperty property,
AnnotatedColumn[] columns,
AnnotatedJoinColumn[] joinColumns) {
AnnotatedJoinColumns joinColumns) {
// Natural ID columns must reside in one single UniqueKey within the Table.
// For now, simply ensure consistent naming.
// TODO: AFAIK, there really isn't a reason for these UKs to be created
// on the secondPass. This whole area should go away...
NaturalId naturalIdAnn = property.getAnnotation( NaturalId.class );
// on the SecondPass. This whole area should go away...
final NaturalId naturalIdAnn = property.getAnnotation( NaturalId.class );
if ( naturalIdAnn != null ) {
if ( joinColumns != null ) {
for ( AnnotatedColumn column : joinColumns) {
for ( AnnotatedColumn column : joinColumns.getColumns() ) {
String keyName = "UK_" + Constraint.hashedName( column.getTable().getName() + "_NaturalID" );
column.addUniqueKey( keyName, inSecondPass);
column.addUniqueKey( keyName, inSecondPass );
}
}
else {
for ( AnnotatedColumn column : columns) {
String keyName = "UK_" + Constraint.hashedName( column.getTable().getName() + "_NaturalID" );
column.addUniqueKey( keyName, inSecondPass);
column.addUniqueKey( keyName, inSecondPass );
}
}
}
@ -1854,7 +1860,7 @@ public final class AnnotationBinder {
String propertyName,
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
AnnotatedJoinColumn[] columns) {
AnnotatedJoinColumns columns) {
final Component component;
if ( referencedEntityName != null ) {
component = createComponent(
@ -2201,7 +2207,7 @@ public final class AnnotationBinder {
private static void bindAny(
String cascadeStrategy,
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns columns,
boolean cascadeOnDelete,
Nullability nullability,
PropertyHolder propertyHolder,
@ -2241,14 +2247,15 @@ public final class AnnotationBinder {
binder.setUpdatable( false );
}
else {
binder.setInsertable( columns[0].isInsertable() );
binder.setUpdatable( columns[0].isUpdatable() );
final AnnotatedJoinColumn firstColumn = columns.getColumns()[0];
binder.setInsertable( firstColumn.isInsertable() );
binder.setUpdatable( firstColumn.isUpdatable() );
}
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setCascade( cascadeStrategy );
Property prop = binder.makeProperty();
//composite FK columns are in the same table so its OK
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
propertyHolder.addProperty( prop, columns.getColumns(), inferredData.getDeclaringClass() );
}
public static HashMap<String, IdentifierGeneratorDefinition> buildGenerators(

View File

@ -52,7 +52,6 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Collection;
@ -84,6 +83,7 @@ import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.cfg.AnnotatedJoinColumn.NON_PK_REFERENCE;
import static org.hibernate.cfg.AnnotatedJoinColumn.checkReferencedColumnsType;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.NOOP;
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.interpret;
@ -162,7 +162,7 @@ public class BinderHelper {
* the synthetic {@link Component}.
*/
public static void createSyntheticPropertyReference(
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns joinColumns,
// the target entity of the association, to which the columns belong
PersistentClass targetEntity,
// the entity which declares the association (used for exception message)
@ -172,15 +172,16 @@ public class BinderHelper {
// true when we do the reverse side of a @ManyToMany
boolean inverse,
MetadataBuildingContext context) {
final AnnotatedJoinColumn[] columns = joinColumns.getColumns();
// this work is not necessary for a primary key reference
if ( checkReferencedColumnsType( columns, targetEntity, context ) == NON_PK_REFERENCE ) { // && !firstColumn.isImplicit()
if ( checkReferencedColumnsType( joinColumns, targetEntity, context ) == NON_PK_REFERENCE ) { // && !firstColumn.isImplicit()
// all the columns have to belong to the same table;
// figure out which table has the columns by looking
// for a PersistentClass or Join in the hierarchy of
// the target entity which has the first column
final Object columnOwner = findReferencedColumnOwner( targetEntity, columns[0], context );
checkColumnInSameTable( columns, targetEntity, associatedEntity, context, columnOwner );
checkColumnInSameTable( joinColumns, targetEntity, associatedEntity, context, columnOwner );
// find all properties mapped to each column
final List<Property> properties = findPropertiesByColumns( columnOwner, columns, associatedEntity, context );
// create a Property along with the new synthetic
@ -214,28 +215,28 @@ public class BinderHelper {
* must return the same value for all of them.
*/
private static void checkColumnInSameTable(
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns joinColumns,
PersistentClass targetEntity,
PersistentClass associatedEntity,
MetadataBuildingContext context,
Object columnOwner) {
final AnnotatedJoinColumn firstColumn = columns[0];
for ( AnnotatedJoinColumn column: columns) {
if ( column.hasMappedBy() ) {
// we should only get called for owning side of association
throw new AssertionFailure("no need to create synthetic properties for unowned collections");
}
if ( joinColumns.hasMappedBy() ) {
// we should only get called for owning side of association
throw new AssertionFailure("no need to create synthetic properties for unowned collections");
}
for ( AnnotatedJoinColumn column: joinColumns.getColumns() ) {
final Object owner = findReferencedColumnOwner( targetEntity, column, context );
if ( owner == null ) {
throw new AnnotationException( "A '@JoinColumn' for association "
+ associationMessage( associatedEntity, firstColumn )
+ associationMessage( associatedEntity, column )
+ " references a column named '" + column.getReferencedColumn()
+ "' which is not mapped by the target entity '"
+ targetEntity.getEntityName() + "'" );
}
if ( owner != columnOwner) {
final AnnotatedJoinColumn firstColumn = joinColumns.getColumns()[0];
throw new AnnotationException( "The '@JoinColumn's for association "
+ associationMessage( associatedEntity, firstColumn )
+ associationMessage( associatedEntity, column )
+ " reference columns of different tables mapped by the target entity '"
+ targetEntity.getEntityName() + "' ('" + column.getReferencedColumn() +
"' belongs to a different table to '" + firstColumn.getReferencedColumn() + "'" );
@ -483,7 +484,7 @@ public class BinderHelper {
// each column, but this means we will reject some mappings
// that could be made to work for a different choice of
// properties (it's also not very deterministic)
List<Property> orderedProperties = new ArrayList<>();
final List<Property> orderedProperties = new ArrayList<>();
int lastPropertyColumnIndex = 0;
Property currentProperty = null;
for ( Column column : orderedColumns ) {
@ -694,13 +695,12 @@ public class BinderHelper {
if ( propertyHolder == null ) {
return propertyName;
}
final String path = propertyHolder.getPath();
final String entityName = propertyHolder.getPersistentClass().getEntityName();
if ( path.length() == entityName.length() ) {
return propertyName;
}
else {
return StringHelper.qualify( path.substring( entityName.length() + 1 ), propertyName );
final String path = propertyHolder.getPath();
final String entityName = propertyHolder.getPersistentClass().getEntityName();
return path.length() == entityName.length()
? propertyName
: qualify( path.substring(entityName.length() + 1), propertyName );
}
}
@ -708,12 +708,9 @@ public class BinderHelper {
PersistentClass persistentClass,
AnnotatedJoinColumn joinColumn,
MetadataBuildingContext context) {
if ( joinColumn.isImplicit() || joinColumn.isReferenceImplicit() ) {
return persistentClass;
}
else {
return findColumnOwner( persistentClass, joinColumn.getReferencedColumn(), context );
}
return joinColumn.isImplicit() || joinColumn.isReferenceImplicit()
? persistentClass
: findColumnOwner( persistentClass, joinColumn.getReferencedColumn(), context );
}
/**
@ -1043,7 +1040,7 @@ public class BinderHelper {
public static Any buildAnyValue(
jakarta.persistence.Column discriminatorColumn,
Formula discriminatorFormula,
AnnotatedJoinColumn[] keyColumns,
AnnotatedJoinColumns keyColumns,
PropertyData inferredData,
boolean cascadeOnDelete,
boolean lazy,
@ -1053,8 +1050,9 @@ public class BinderHelper {
boolean optional,
MetadataBuildingContext context) {
final XProperty xProperty = inferredData.getProperty();
final AnnotatedJoinColumn[] columns = keyColumns.getColumns();
final Any value = new Any( context, keyColumns[0].getTable(), true );
final Any value = new Any( context, columns[0].getTable(), true );
value.setLazy( lazy );
value.setCascadeDeleteEnabled( cascadeOnDelete );
@ -1090,7 +1088,7 @@ public class BinderHelper {
final Map<Object,Class<?>> discriminatorValueMappings = new HashMap<>();
processAnyDiscriminatorValues(
inferredData.getProperty(),
(valueMapping) -> discriminatorValueMappings.put(
valueMapping -> discriminatorValueMappings.put(
discriminatorJavaType.wrap( valueMapping.discriminator(), null ),
valueMapping.entity()
)
@ -1098,12 +1096,12 @@ public class BinderHelper {
value.setDiscriminatorValueMappings( discriminatorValueMappings );
BasicValueBinder keyValueBinder = new BasicValueBinder( BasicValueBinder.Kind.ANY_KEY, context );
assert keyColumns.length == 1;
keyColumns[0].setTable( value.getTable() );
keyValueBinder.setColumns( keyColumns );
assert columns.length == 1;
columns[0].setTable( value.getTable() );
keyValueBinder.setColumns(columns);
if ( !optional ) {
for (AnnotatedJoinColumn column : keyColumns) {
for ( AnnotatedJoinColumn column : columns) {
column.setNullable( false );
}
}
@ -1112,10 +1110,10 @@ public class BinderHelper {
value.setKey( keyDescriptor );
keyValueBinder.fillSimpleValue();
AnnotatedColumn.checkPropertyConsistency(
keyColumns,
columns,
propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
);
keyColumns[0].linkWithValue( keyDescriptor );
columns[0].linkWithValue( keyDescriptor );
return value;
}
@ -1168,7 +1166,7 @@ public class BinderHelper {
}
public static String getPath(PropertyHolder holder, PropertyData property) {
return StringHelper.qualify( holder.getPath(), property.getPropertyName() );
return qualify( holder.getPath(), property.getPropertyName() );
}
static PropertyData getPropertyOverriddenByMapperOrMapsId(

View File

@ -26,7 +26,6 @@ import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.internal.util.StringHelper;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromNoAnnotation;
@ -35,6 +34,8 @@ import static org.hibernate.cfg.AnnotatedColumn.buildFormulaFromAnnotation;
import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getPath;
import static org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.cfg.BinderHelper.getRelativePath;
import static org.hibernate.internal.util.StringHelper.isEmpty;
/**
* Do the initial discovery of columns metadata and apply defaults.
@ -52,7 +53,7 @@ class ColumnsBuilder {
private final EntityBinder entityBinder;
private final MetadataBuildingContext buildingContext;
private AnnotatedColumn[] columns;
private AnnotatedJoinColumn[] joinColumns;
private AnnotatedJoinColumns joinColumns;
public ColumnsBuilder(
PropertyHolder propertyHolder,
@ -73,7 +74,7 @@ class ColumnsBuilder {
return columns;
}
public AnnotatedJoinColumn[] getJoinColumns() {
public AnnotatedJoinColumns getJoinColumns() {
return joinColumns;
}
@ -122,14 +123,14 @@ class ColumnsBuilder {
( property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class ) )
) {
joinColumns = buildDefaultJoinColumnsForXToOne( property, inferredData );
joinColumns = buildDefaultJoinColumnsForToOne( property, inferredData );
}
else if ( joinColumns == null &&
( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( ElementCollection.class )
) ) {
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
joinColumns = AnnotatedJoinColumn.buildJoinColumns(
joinColumns = AnnotatedJoinColumns.buildJoinColumns(
null,
comment,
oneToMany != null ? oneToMany.mappedBy() : "",
@ -164,12 +165,18 @@ class ColumnsBuilder {
return this;
}
private AnnotatedJoinColumn[] buildDefaultJoinColumnsForXToOne(XProperty property, PropertyData inferredData) {
AnnotatedJoinColumn[] joinColumns;
JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
Comment comment = property.getAnnotation(Comment.class);
private AnnotatedJoinColumns buildDefaultJoinColumnsForToOne(XProperty property, PropertyData inferredData) {
final JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
final Comment comment = property.getAnnotation(Comment.class);
if ( joinTableAnn != null ) {
joinColumns = AnnotatedJoinColumn.buildJoinColumns(
if ( isEmpty( joinTableAnn.name() ) ) {
//TODO: I don't see why this restriction makes sense (use the same defaulting rule as for many-valued)
throw new AnnotationException(
"Single-valued association " + getPath( propertyHolder, inferredData )
+ " has a '@JoinTable' annotation with no explicit 'name'"
);
}
return AnnotatedJoinColumns.buildJoinColumns(
joinTableAnn.inverseJoinColumns(),
comment,
null,
@ -178,17 +185,10 @@ class ColumnsBuilder {
inferredData.getPropertyName(),
buildingContext
);
if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
//TODO: I don't see why this restriction makes sense (use the same defaulting rule as for many-valued)
throw new AnnotationException(
"Single-valued association " + getPath( propertyHolder, inferredData )
+ " has a '@JoinTable' annotation with no explicit 'name'"
);
}
}
else {
OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
joinColumns = AnnotatedJoinColumn.buildJoinColumns(
return AnnotatedJoinColumns.buildJoinColumns(
null,
comment,
oneToOneAnn != null ? oneToOneAnn.mappedBy() : null,
@ -198,53 +198,63 @@ class ColumnsBuilder {
buildingContext
);
}
return joinColumns;
}
private AnnotatedJoinColumn[] buildExplicitJoinColumns(XProperty property, PropertyData inferredData) {
private AnnotatedJoinColumns buildExplicitJoinColumns(XProperty property, PropertyData inferredData) {
// process @JoinColumns before @Columns to handle collection of entities properly
final JoinColumn[] joinColumnAnnotations = getJoinColumnAnnotations(property, inferredData);
final String propertyName = inferredData.getPropertyName();
final JoinColumn[] joinColumnAnnotations = getJoinColumnAnnotations( property, inferredData );
if ( joinColumnAnnotations != null ) {
return AnnotatedJoinColumn.buildJoinColumns(
return AnnotatedJoinColumns.buildJoinColumns(
joinColumnAnnotations,
property.getAnnotation( Comment.class ),
null,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
propertyName,
buildingContext
);
}
final JoinColumnOrFormula[] joinColumnOrFormulaAnnotations = joinColumnOrFormulaAnnotations( property, inferredData );
if ( joinColumnOrFormulaAnnotations != null ) {
return AnnotatedJoinColumn.buildJoinColumnsOrFormulas(
return AnnotatedJoinColumns.buildJoinColumnsOrFormulas(
joinColumnOrFormulaAnnotations,
null,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
propertyName,
buildingContext
);
}
if ( property.isAnnotationPresent( JoinFormula.class) ) {
final JoinFormula joinFormula = getOverridableAnnotation( property, JoinFormula.class, buildingContext );
final AnnotatedJoinColumn[] annotatedJoinColumns = new AnnotatedJoinColumn[1];
annotatedJoinColumns[0] = AnnotatedJoinColumn.buildJoinFormula(
joinFormula,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
buildingContext
);
return annotatedJoinColumns;
return buildJoinColumnsWithFormula( propertyName, joinFormula );
}
return null;
}
private AnnotatedJoinColumns buildJoinColumnsWithFormula(String propertyName, JoinFormula joinFormula) {
final AnnotatedJoinColumn[] columns = new AnnotatedJoinColumn[1];
columns[0] = AnnotatedJoinColumn.buildJoinFormula(
joinFormula,
entityBinder.getSecondaryTables(),
propertyHolder,
propertyName,
buildingContext
);
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( buildingContext );
joinColumns.setPropertyHolder( propertyHolder );
joinColumns.setPropertyName( getRelativePath( propertyHolder, propertyName) );
joinColumns.setColumns( columns );
return joinColumns;
}
private JoinColumnOrFormula[] joinColumnOrFormulaAnnotations(XProperty property, PropertyData inferredData) {
if ( property.isAnnotationPresent( JoinColumnOrFormula.class ) ) {
return new JoinColumnOrFormula[] { property.getAnnotation( JoinColumnOrFormula.class ) };
@ -292,10 +302,13 @@ class ColumnsBuilder {
}
/**
* useful to override a column either by @MapsId or by @IdClass
* Useful to override a column either by {@code @MapsId} or by {@code @IdClass}
*/
//TODO: should we introduce an AnnotatedColumns type and return that here?
private AnnotatedColumn[] buildExplicitOrDefaultJoinColumn(PropertyData overridingProperty) {
final AnnotatedColumn[] columns = buildExplicitJoinColumns( overridingProperty.getProperty(), overridingProperty );
return columns == null ? buildDefaultJoinColumnsForXToOne( overridingProperty.getProperty(), overridingProperty ) : columns;
final AnnotatedJoinColumns columns = buildExplicitJoinColumns( overridingProperty.getProperty(), overridingProperty );
return columns == null
? buildDefaultJoinColumnsForToOne( overridingProperty.getProperty(), overridingProperty ).getColumns()
: columns.getColumns();
}
}

View File

@ -39,12 +39,12 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
private final String propertyName;
private final Component component;
private final MetadataBuildingContext buildingContext;
private final AnnotatedJoinColumn[] joinColumns;
private final AnnotatedJoinColumns joinColumns;
public CopyIdentifierComponentSecondPass(
Component comp,
String referencedEntityName, String propertyName,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
MetadataBuildingContext buildingContext) {
super( comp, joinColumns );
this.component = comp;
@ -91,8 +91,9 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
//prepare column name structure
boolean isExplicitReference = true;
final Map<String, AnnotatedJoinColumn> columnByReferencedName = mapOfSize( joinColumns.length );
for ( AnnotatedJoinColumn joinColumn : joinColumns ) {
final AnnotatedJoinColumn[] cols = joinColumns.getColumns();
final Map<String, AnnotatedJoinColumn> columnByReferencedName = mapOfSize( cols.length );
for ( AnnotatedJoinColumn joinColumn : cols) {
if ( !joinColumn.isReferenceImplicit() ) {
//JPA 2 requires referencedColumnNames to be case-insensitive
columnByReferencedName.put( joinColumn.getReferencedColumn().toLowerCase(Locale.ROOT), joinColumn );
@ -101,8 +102,8 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
//try default column orientation
if ( columnByReferencedName.isEmpty() ) {
isExplicitReference = false;
for ( int i = 0; i < joinColumns.length; i++ ) {
columnByReferencedName.put( String.valueOf( i ), joinColumns[i] );
for (int i = 0; i < cols.length; i++ ) {
columnByReferencedName.put( String.valueOf( i ), cols[i] );
}
}
@ -184,8 +185,9 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
final SimpleValue referencedValue = (SimpleValue) referencedProperty.getValue();
value.copyTypeFrom( referencedValue );
if ( joinColumns[0].isNameDeferred() ) {
joinColumns[0].copyReferencedStructureAndCreateDefaultJoinColumns(
final AnnotatedJoinColumn firstColumn = joinColumns.getColumns()[0];
if ( firstColumn.isNameDeferred() ) {
firstColumn.copyReferencedStructureAndCreateDefaultJoinColumns(
referencedPersistentClass,
referencedValue,
value
@ -200,17 +202,18 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
}
final Column column = (Column) selectable;
final AnnotatedJoinColumn joinColumn;
String logicalColumnName = null;
final String logicalColumnName;
if ( isExplicitReference ) {
logicalColumnName = column.getName();
//JPA 2 requires referencedColumnNames to be case-insensitive
joinColumn = columnByReferencedName.get( logicalColumnName.toLowerCase(Locale.ROOT ) );
}
else {
logicalColumnName = null;
joinColumn = columnByReferencedName.get( String.valueOf( index.get() ) );
index.getAndIncrement();
}
if ( joinColumn == null && !joinColumns[0].isNameDeferred() ) {
if ( joinColumn == null && !firstColumn.isNameDeferred() ) {
throw new AnnotationException(
"Property '" + propertyName
+ "' of entity '" + component.getOwner().getEntityName()
@ -225,7 +228,7 @@ public class CopyIdentifierComponentSecondPass extends FkSecondPass {
final Database database = buildingContext.getMetadataCollector().getDatabase();
final PhysicalNamingStrategy physicalNamingStrategy = buildingContext.getBuildingOptions().getPhysicalNamingStrategy();
final Identifier explicitName = database.toIdentifier( columnName );
final Identifier physicalName = physicalNamingStrategy.toPhysicalColumnName( explicitName, database.getJdbcEnvironment() );
final Identifier physicalName = physicalNamingStrategy.toPhysicalColumnName( explicitName, database.getJdbcEnvironment() );
value.addColumn( new Column( physicalName.render( database.getDialect() ) ) );
if ( joinColumn != null ) {
applyComponentColumnSizeValueToJoinColumn( column, joinColumn );

View File

@ -15,7 +15,7 @@ import org.hibernate.mapping.Value;
*/
public abstract class FkSecondPass implements SecondPass {
protected SimpleValue value;
protected AnnotatedJoinColumn[] columns;
protected AnnotatedJoinColumns columns;
/**
* unique counter is needed to differentiate 2 instances of FKSecondPass
* as they are compared.
@ -25,7 +25,7 @@ public abstract class FkSecondPass implements SecondPass {
private final int uniqueCounter;
private static final AtomicInteger globalCounter = new AtomicInteger();
public FkSecondPass(SimpleValue value, AnnotatedJoinColumn[] columns) {
public FkSecondPass(SimpleValue value, AnnotatedJoinColumns columns) {
this.value = value;
this.columns = columns;
this.uniqueCounter = globalCounter.getAndIncrement();

View File

@ -24,7 +24,7 @@ public class JoinedSubclassFkSecondPass extends FkSecondPass {
public JoinedSubclassFkSecondPass(
JoinedSubclass entity,
AnnotatedJoinColumn[] inheritanceJoinedColumns,
AnnotatedJoinColumns inheritanceJoinedColumns,
SimpleValue key,
MetadataBuildingContext buildingContext) {
super( key, inheritanceJoinedColumns );

View File

@ -53,7 +53,7 @@ public class OneToOneSecondPass implements SecondPass {
private final boolean cascadeOnDelete;
private final boolean optional;
private final String cascadeStrategy;
private final AnnotatedJoinColumn[] joinColumns;
private final AnnotatedJoinColumns joinColumns;
//that sucks, we should read that from the property mainly
public OneToOneSecondPass(
@ -67,7 +67,7 @@ public class OneToOneSecondPass implements SecondPass {
boolean cascadeOnDelete,
boolean optional,
String cascadeStrategy,
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns columns,
MetadataBuildingContext buildingContext) {
this.ownerEntity = ownerEntity;
this.ownerProperty = ownerProperty;

View File

@ -18,10 +18,10 @@ import org.hibernate.mapping.SimpleValue;
*/
public class PkDrivenByDefaultMapsIdSecondPass extends FkSecondPass {
private final String referencedEntityName;
private final AnnotatedJoinColumn[] columns;
private final AnnotatedJoinColumns columns;
private final SimpleValue value;
public PkDrivenByDefaultMapsIdSecondPass(String referencedEntityName, AnnotatedJoinColumn[] columns, SimpleValue value) {
public PkDrivenByDefaultMapsIdSecondPass(String referencedEntityName, AnnotatedJoinColumns columns, SimpleValue value) {
super( value, columns );
this.referencedEntityName = referencedEntityName;
this.columns = columns;

View File

@ -69,7 +69,7 @@ public class ToOneBinder {
boolean inSecondPass,
MetadataBuildingContext context,
XProperty property,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
PropertyBinder propertyBinder,
boolean forcePersist) {
final ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
@ -91,7 +91,7 @@ public class ToOneBinder {
final JoinTable joinTable = propertyHolder.getJoinTable( property );
if ( joinTable != null ) {
final Join join = propertyHolder.addJoin( joinTable, false );
for ( AnnotatedJoinColumn joinColumn : joinColumns ) {
for ( AnnotatedJoinColumn joinColumn : joinColumns.getColumns() ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
@ -128,7 +128,7 @@ public class ToOneBinder {
private static void bindManyToOne(
String cascadeStrategy,
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns joinColumns,
boolean optional,
NotFoundAction notFoundAction,
boolean cascadeOnDelete,
@ -142,7 +142,7 @@ public class ToOneBinder {
MetadataBuildingContext context) {
// All FK columns should be in the same table
final org.hibernate.mapping.ManyToOne value =
new org.hibernate.mapping.ManyToOne( context, columns[0].getTable() );
new org.hibernate.mapping.ManyToOne( context, joinColumns.getColumns()[0].getTable() );
if ( unique ) {
// This is a @OneToOne mapped to a physical o.h.mapping.ManyToOne
value.markAsLogicalOneToOne();
@ -155,20 +155,20 @@ public class ToOneBinder {
value.setCascadeDeleteEnabled( cascadeOnDelete );
//value.setLazy( fetchMode != FetchMode.JOIN );
if ( !optional ) {
for ( AnnotatedJoinColumn column : columns ) {
for ( AnnotatedJoinColumn column : joinColumns.getColumns() ) {
column.setNullable( false );
}
}
if ( property.isAnnotationPresent( MapsId.class ) ) {
//read only
for ( AnnotatedJoinColumn column : columns ) {
for ( AnnotatedJoinColumn column : joinColumns.getColumns() ) {
column.setInsertable( false );
column.setUpdatable( false );
}
}
boolean hasSpecjManyToOne = handleSpecjSyntax( columns, inferredData, context, property );
boolean hasSpecjManyToOne = handleSpecjSyntax( joinColumns, inferredData, context, property );
value.setTypeName( inferredData.getClassOrElementName() );
final String propertyName = inferredData.getPropertyName();
value.setTypeUsingReflection( propertyHolder.getClassName(), propertyName );
@ -179,7 +179,7 @@ public class ToOneBinder {
final FkSecondPass secondPass = new ToOneFkSecondPass(
value,
columns,
joinColumns,
!optional && unique, //cannot have nullable and unique on certain DBs like Derby
propertyHolder.getPersistentClass(),
fullPath,
@ -194,7 +194,7 @@ public class ToOneBinder {
processManyToOneProperty(
cascadeStrategy,
columns,
joinColumns,
optional,
propertyHolder,
inferredData,
@ -208,7 +208,7 @@ public class ToOneBinder {
}
private static boolean handleSpecjSyntax(
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns columns,
PropertyData inferredData,
MetadataBuildingContext context,
XProperty property) {
@ -228,7 +228,7 @@ public class ToOneBinder {
&& joinColumn.name().equals( columnName )
&& !property.isAnnotationPresent( MapsId.class ) ) {
hasSpecjManyToOne = true;
for ( AnnotatedJoinColumn column : columns) {
for ( AnnotatedJoinColumn column : columns.getColumns() ) {
column.setInsertable( false );
column.setUpdatable( false );
}
@ -240,7 +240,7 @@ public class ToOneBinder {
private static void processManyToOneProperty(
String cascadeStrategy,
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns columns,
boolean optional,
PropertyHolder propertyHolder,
PropertyData inferredData,
@ -250,7 +250,7 @@ public class ToOneBinder {
boolean hasSpecjManyToOne,
String propertyName) {
checkPropertyConsistency( columns, qualify( propertyHolder.getEntityName(), propertyName ) );
checkPropertyConsistency( columns.getColumns(), qualify( propertyHolder.getEntityName(), propertyName ) );
//PropertyBinder binder = new PropertyBinder();
propertyBinder.setName( propertyName );
@ -265,10 +265,11 @@ public class ToOneBinder {
propertyBinder.setUpdatable( false );
}
else {
propertyBinder.setInsertable( columns[0].isInsertable() );
propertyBinder.setUpdatable( columns[0].isUpdatable() );
final AnnotatedJoinColumn firstColumn = columns.getColumns()[0];
propertyBinder.setInsertable( firstColumn.isInsertable() );
propertyBinder.setUpdatable( firstColumn.isUpdatable() );
}
propertyBinder.setColumns( columns );
propertyBinder.setColumns( columns.getColumns() );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setCascade( cascadeStrategy );
propertyBinder.setProperty( property );
@ -361,7 +362,7 @@ public class ToOneBinder {
boolean inSecondPass,
MetadataBuildingContext context,
XProperty property,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
PropertyBinder propertyBinder,
boolean forcePersist) {
final OneToOne oneToOne = property.getAnnotation( OneToOne.class );
@ -392,7 +393,7 @@ public class ToOneBinder {
if ( notFoundAction != null ) {
join.disableForeignKeyCreation();
}
for ( AnnotatedJoinColumn joinColumn : joinColumns) {
for ( AnnotatedJoinColumn joinColumn : joinColumns.getColumns() ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
@ -417,7 +418,7 @@ public class ToOneBinder {
private static void bindOneToOne(
String cascadeStrategy,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
boolean optional,
FetchMode fetchMode,
NotFoundAction notFoundAction,
@ -478,7 +479,7 @@ public class ToOneBinder {
}
}
private static boolean isMapToPK(AnnotatedJoinColumn[] joinColumns, PropertyHolder propertyHolder, boolean trueOneToOne) {
private static boolean isMapToPK(AnnotatedJoinColumns joinColumns, PropertyHolder propertyHolder, boolean trueOneToOne) {
if ( trueOneToOne ) {
return true;
}
@ -491,15 +492,16 @@ public class ToOneBinder {
return false;
}
else {
List<String> idColumnNames = new ArrayList<>();
if ( identifier.getColumnSpan() != joinColumns.length ) {
final List<String> idColumnNames = new ArrayList<>();
final AnnotatedJoinColumn[] columns = joinColumns.getColumns();
if ( identifier.getColumnSpan() != columns.length ) {
return false;
}
else {
for ( org.hibernate.mapping.Column currentColumn: identifier.getColumns() ) {
idColumnNames.add( currentColumn.getName() );
}
for ( AnnotatedJoinColumn col: joinColumns) {
for ( AnnotatedJoinColumn col: columns ) {
if ( !idColumnNames.contains( col.getMappingColumn().getName() ) ) {
return false;
}

View File

@ -38,7 +38,7 @@ public class ToOneFkSecondPass extends FkSecondPass {
public ToOneFkSecondPass(
ToOne value,
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns columns,
boolean unique,
PersistentClass persistentClass,
String path,
@ -113,7 +113,9 @@ public class ToOneFkSecondPass extends FkSecondPass {
);
TableBinder.bindForeignKey( targetEntity, persistentClass, columns, manyToOne, unique, buildingContext );
// HbmMetadataSourceProcessorImpl does this only when property-ref != null, but IMO, it makes sense event if it is null
if ( !manyToOne.isIgnoreNotFound() ) manyToOne.createPropertyRefConstraints( persistentClasses );
if ( !manyToOne.isIgnoreNotFound() ) {
manyToOne.createPropertyRefConstraints( persistentClasses );
}
}
else if ( value instanceof OneToOne ) {
value.createForeignKey();

View File

@ -51,10 +51,12 @@ import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.TypeDefinition;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotatedColumn;
import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.AnnotatedJoinColumns;
import org.hibernate.cfg.PkDrivenByDefaultMapsIdSecondPass;
import org.hibernate.cfg.SetBasicValueTypeSecondPass;
import org.hibernate.dialect.Dialect;
@ -1122,11 +1124,15 @@ public class BasicValueBinder implements JdbcTypeIndicators {
}
public void linkWithValue() {
if ( columns[0].isNameDeferred() && !buildingContext.getMetadataCollector().isInSecondPass() && referencedEntityName != null ) {
buildingContext.getMetadataCollector().addSecondPass(
new PkDrivenByDefaultMapsIdSecondPass(
referencedEntityName, (AnnotatedJoinColumn[]) columns, basicValue
)
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
if ( !collector.isInSecondPass() && columns[0].isNameDeferred() && referencedEntityName != null ) {
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( buildingContext );
joinColumns.setPropertyHolder( columns[0].getPropertyHolder() );
joinColumns.setPropertyName( columns[0].getPropertyName() );
joinColumns.setColumns( (AnnotatedJoinColumn[]) columns );
collector.addSecondPass(
new PkDrivenByDefaultMapsIdSecondPass( referencedEntityName, joinColumns, basicValue )
);
}
else {
@ -1139,13 +1145,9 @@ public class BasicValueBinder implements JdbcTypeIndicators {
public void fillSimpleValue() {
LOG.debugf( "Starting `BasicValueBinder#fillSimpleValue` for %s", propertyName );
final String explicitBasicTypeName;
if ( this.explicitBasicTypeName != null ) {
explicitBasicTypeName = this.explicitBasicTypeName;
}
else {
explicitBasicTypeName = this.timeStampVersionType;
}
final String explicitBasicTypeName = this.explicitBasicTypeName != null
? this.explicitBasicTypeName
: this.timeStampVersionType;
basicValue.setExplicitTypeName( explicitBasicTypeName );
basicValue.setExplicitTypeParams( explicitLocalTypeParams );
@ -1188,10 +1190,9 @@ public class BasicValueBinder implements JdbcTypeIndicators {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
if ( ( explicitCustomType != null && DynamicParameterizedType.class.isAssignableFrom( explicitCustomType ) )
|| ( typeClass != null && DynamicParameterizedType.class.isAssignableFrom( typeClass ) ) ) {
final Map<String, Object> parameters = createDynamicParameterizedTypeParameters();
basicValue.setTypeParameters( (Map) parameters );
if ( explicitCustomType != null && DynamicParameterizedType.class.isAssignableFrom( explicitCustomType )
|| typeClass != null && DynamicParameterizedType.class.isAssignableFrom( typeClass ) ) {
basicValue.setTypeParameters( createDynamicParameterizedTypeParameters() );
}
if ( converterDescriptor != null ) {

View File

@ -101,6 +101,7 @@ import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotatedClassType;
import org.hibernate.cfg.AnnotatedColumn;
import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.AnnotatedJoinColumns;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.CollectionPropertyHolder;
@ -135,6 +136,7 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.persister.collection.CollectionPersister;
@ -152,8 +154,8 @@ import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromNoAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnsFromAnnotations;
import static org.hibernate.cfg.AnnotatedColumn.buildFormulaFromAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.checkPropertyConsistency;
import static org.hibernate.cfg.AnnotatedJoinColumn.buildJoinColumnsWithDefaultColumnSuffix;
import static org.hibernate.cfg.AnnotatedJoinColumn.buildJoinTableJoinColumns;
import static org.hibernate.cfg.AnnotatedJoinColumns.buildJoinColumnsWithDefaultColumnSuffix;
import static org.hibernate.cfg.AnnotatedJoinColumns.buildJoinTableJoinColumns;
import static org.hibernate.cfg.AnnotationBinder.fillComponent;
import static org.hibernate.cfg.BinderHelper.PRIMITIVE_NAMES;
import static org.hibernate.cfg.BinderHelper.buildAnyValue;
@ -201,7 +203,6 @@ public abstract class CollectionBinder {
private String mappedBy;
private XClass collectionElementType;
private XClass targetEntity;
protected AnnotatedJoinColumn[] inverseJoinColumns;
private String cascadeStrategy;
private String cacheConcurrencyStrategy;
private String cacheRegionName;
@ -211,8 +212,9 @@ public abstract class CollectionBinder {
protected String mapKeyPropertyName;
private boolean insertable = true;
private boolean updatable = true;
protected AnnotatedJoinColumn[] foreignJoinColumns;
private AnnotatedJoinColumn[] joinColumns;
protected AnnotatedJoinColumns inverseJoinColumns;
protected AnnotatedJoinColumns foreignJoinColumns;
private AnnotatedJoinColumns joinColumns;
private boolean isExplicitAssociationTable;
private AnnotatedColumn[] elementColumns;
protected boolean isEmbedded;
@ -220,7 +222,7 @@ public abstract class CollectionBinder {
protected NotFoundAction notFoundAction;
private TableBinder tableBinder;
protected AnnotatedColumn[] mapKeyColumns;
protected AnnotatedJoinColumn[] mapKeyManyToManyColumns;
protected AnnotatedJoinColumns mapKeyManyToManyColumns;
protected Map<String, IdentifierGeneratorDefinition> localGenerators;
protected Map<XClass, InheritanceState> inheritanceStatePerClass;
private XClass declaringClass;
@ -258,7 +260,7 @@ public abstract class CollectionBinder {
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
AnnotatedJoinColumn[] joinColumns) {
AnnotatedJoinColumns joinColumns) {
final OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
final ManyToMany manyToManyAnn = property.getAnnotation( ManyToMany.class );
@ -302,8 +304,8 @@ public abstract class CollectionBinder {
collectionBinder.setCache( property.getAnnotation( Cache.class ) );
collectionBinder.setPropertyHolder(propertyHolder);
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
final Cascade hibernateCascade = property.getAnnotation( Cascade.class );
final NotFound notFound = property.getAnnotation( NotFound.class );
if ( notFound != null ) {
if ( manyToManyAnn == null ) {
throw new AnnotationException( "Collection '" + getPath( propertyHolder, inferredData )
@ -337,7 +339,7 @@ public abstract class CollectionBinder {
collectionBinder,
comment
);
final AnnotatedJoinColumn[] mapJoinColumns = buildJoinColumnsWithDefaultColumnSuffix(
final AnnotatedJoinColumns mapJoinColumns = buildJoinColumnsWithDefaultColumnSuffix(
joinKeyColumns,
comment,
null,
@ -377,8 +379,8 @@ public abstract class CollectionBinder {
mappedBy
);
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action();
final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
final boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action();
collectionBinder.setCascadeDeleteEnabled( onDeleteCascade );
if ( isIdentifierMapper ) {
collectionBinder.setInsertable( false );
@ -400,7 +402,7 @@ public abstract class CollectionBinder {
PropertyData inferredData,
MetadataBuildingContext context,
XProperty property,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
OneToMany oneToManyAnn,
ManyToMany manyToManyAnn,
ElementCollection elementCollectionAnn,
@ -412,15 +414,15 @@ public abstract class CollectionBinder {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' is annotated both '@OneToMany' and '@ManyToMany'" );
}
String mappedBy = null;
ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
final String mappedBy;
final ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
if ( oneToManyAnn != null ) {
for ( AnnotatedJoinColumn column : joinColumns) {
for ( AnnotatedJoinColumn column : joinColumns.getColumns() ) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns(joinColumns);
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = oneToManyAnn.mappedBy();
//noinspection unchecked
collectionBinder.setTargetEntity( reflectionManager.toXClass( oneToManyAnn.targetEntity() ) );
@ -430,7 +432,7 @@ public abstract class CollectionBinder {
collectionBinder.setOneToMany( true );
}
else if ( elementCollectionAnn != null ) {
for ( AnnotatedJoinColumn column : joinColumns ) {
for ( AnnotatedJoinColumn column : joinColumns.getColumns() ) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
@ -459,6 +461,9 @@ public abstract class CollectionBinder {
);
collectionBinder.setOneToMany( false );
}
else {
mappedBy = null;
}
collectionBinder.setMappedBy( mappedBy );
return mappedBy;
}
@ -588,11 +593,11 @@ public abstract class CollectionBinder {
PropertyHolder propertyHolder,
PropertyData inferredData,
String mappedBy) {
TableBinder associationTableBinder = new TableBinder();
JoinColumn[] annJoins;
JoinColumn[] annInverseJoins;
JoinTable assocTable = propertyHolder.getJoinTable( property );
CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
final TableBinder associationTableBinder = new TableBinder();
final JoinTable assocTable = propertyHolder.getJoinTable( property );
final CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
final JoinColumn[] annJoins;
final JoinColumn[] annInverseJoins;
if ( assocTable != null || collectionTable != null ) {
final String catalog;
@ -603,7 +608,6 @@ public abstract class CollectionBinder {
final JoinColumn[] inverseJoins;
final Index[] jpaIndexes;
//JPA 2 has priority
if ( collectionTable != null ) {
catalog = collectionTable.catalog();
@ -647,7 +651,7 @@ public abstract class CollectionBinder {
annJoins = null;
annInverseJoins = null;
}
final AnnotatedJoinColumn[] joinColumns = buildJoinTableJoinColumns(
final AnnotatedJoinColumns joinColumns = buildJoinTableJoinColumns(
annJoins,
entityBinder.getSecondaryTables(),
propertyHolder,
@ -655,7 +659,7 @@ public abstract class CollectionBinder {
mappedBy,
buildingContext
);
final AnnotatedJoinColumn[] inverseJoinColumns = buildJoinTableJoinColumns(
final AnnotatedJoinColumns inverseJoinColumns = buildJoinTableJoinColumns(
annInverseJoins,
entityBinder.getSecondaryTables(),
propertyHolder,
@ -709,11 +713,11 @@ public abstract class CollectionBinder {
this.accessType = accessType;
}
public void setInverseJoinColumns(AnnotatedJoinColumn[] inverseJoinColumns) {
public void setInverseJoinColumns(AnnotatedJoinColumns inverseJoinColumns) {
this.inverseJoinColumns = inverseJoinColumns;
}
public void setJoinColumns(AnnotatedJoinColumn[] joinColumns) {
public void setJoinColumns(AnnotatedJoinColumns joinColumns) {
this.joinColumns = joinColumns;
}
@ -1491,8 +1495,8 @@ public abstract class CollectionBinder {
&& !reversePropertyInJoin
&& oneToMany
&& !isExplicitAssociationTable
&& ( joinColumns[0].isImplicit() && hasMappedBy() //implicit @JoinColumn
|| !foreignJoinColumns[0].isImplicit() ) //this is an explicit @JoinColumn
&& ( joinColumns.getColumns()[0].isImplicit() && hasMappedBy() //implicit @JoinColumn
|| !foreignJoinColumns.getColumns()[0].isImplicit() ) //this is an explicit @JoinColumn
) {
//this is a foreign key
bindOneToManySecondPass( persistentClasses );
@ -1517,7 +1521,7 @@ public abstract class CollectionBinder {
throw new AssertionFailure( "null was passed for argument property" );
}
logOneToManySeconPass();
logOneToManySecondPass();
final org.hibernate.mapping.OneToMany oneToMany =
new org.hibernate.mapping.OneToMany( buildingContext, getCollection().getOwner() );
@ -1525,27 +1529,28 @@ public abstract class CollectionBinder {
oneToMany.setReferencedEntityName( getElementType().getName() );
oneToMany.setNotFoundAction( notFoundAction );
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
final String assocClass = oneToMany.getReferencedEntityName();
final PersistentClass associatedClass = persistentClasses.get( assocClass );
final String referencedEntityName = oneToMany.getReferencedEntityName();
final PersistentClass associatedClass = persistentClasses.get( referencedEntityName );
handleJpaOrderBy( collection, associatedClass );
final Map<String, Join> joins = collector.getJoins( assocClass );
if ( associatedClass == null ) {
throw new MappingException(
String.format( "Association [%s] for entity [%s] references unmapped class [%s]",
propertyName, propertyHolder.getClassName(), assocClass )
propertyName, propertyHolder.getClassName(), referencedEntityName )
);
}
oneToMany.setAssociatedClass( associatedClass );
for ( AnnotatedJoinColumn column : foreignJoinColumns ) {
column.setPersistentClass( associatedClass, joins, inheritanceStatePerClass );
final Map<String, Join> joins = buildingContext.getMetadataCollector().getJoins( referencedEntityName );
foreignJoinColumns.setPersistentClass( associatedClass, joins, inheritanceStatePerClass );
for ( AnnotatedJoinColumn column : foreignJoinColumns.getColumns() ) {
column.setJoins( joins );
//TODO: this is lame, should get the table from foreignJoinColumns
collection.setCollectionTable( column.getTable() );
}
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Mapping collection: %s -> %s", collection.getRole(), collection.getCollectionTable().getName() );
}
bindFilters( false );
handleWhere( false );
@ -1553,21 +1558,28 @@ public abstract class CollectionBinder {
bindCollectionSecondPass( targetEntity, foreignJoinColumns, cascadeDeleteEnabled, buildingContext );
if ( !collection.isInverse() && !collection.getKey().isNullable() ) {
// for non-inverse one-to-many, with a not-null fk, add a backref!
final String entityName = oneToMany.getReferencedEntityName();
final PersistentClass referenced = collector.getEntityBinding( entityName );
final Backref backref = new Backref();
final AnnotatedJoinColumn column = foreignJoinColumns[0];
backref.setName( '_' + column.getPropertyName() + '_' + column.getLogicalColumnName() + "Backref" );
backref.setUpdateable( false );
backref.setSelectable( false );
backref.setCollectionRole( collection.getRole() );
backref.setEntityName( collection.getOwner().getEntityName() );
backref.setValue( collection.getKey() );
referenced.addProperty( backref );
createOneToManyBackref( oneToMany );
}
}
private void createOneToManyBackref(org.hibernate.mapping.OneToMany oneToMany) {
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
// for non-inverse one-to-many, with a not-null fk, add a backref!
final String entityName = oneToMany.getReferencedEntityName();
final PersistentClass referenced = collector.getEntityBinding( entityName );
final Backref backref = new Backref();
final String backrefName = '_' + foreignJoinColumns.getPropertyName()
+ '_' + foreignJoinColumns.getColumns()[0].getLogicalColumnName()
+ "Backref";
backref.setName( backrefName );
backref.setUpdateable( false);
backref.setSelectable( false );
backref.setCollectionRole( collection.getRole() );
backref.setEntityName( collection.getOwner().getEntityName() );
backref.setValue( collection.getKey() );
referenced.addProperty( backref );
}
private void handleJpaOrderBy(Collection collection, PersistentClass associatedClass) {
if ( jpaOrderBy != null ) {
final String orderByFragment = buildOrderByClauseFromHql( jpaOrderBy.value(), associatedClass );
@ -1578,24 +1590,24 @@ public abstract class CollectionBinder {
}
private void bindFilters(boolean hasAssociationTable) {
Filter simpleFilter = property.getAnnotation( Filter.class );
final Filter simpleFilter = property.getAnnotation( Filter.class );
//set filtering
//test incompatible choices
//if ( StringHelper.isNotEmpty( where ) ) collection.setWhere( where );
if ( simpleFilter != null ) {
addFilter( hasAssociationTable, simpleFilter );
}
Filters filters = getOverridableAnnotation( property, Filters.class, buildingContext );
final Filters filters = getOverridableAnnotation( property, Filters.class, buildingContext );
if ( filters != null ) {
for ( Filter filter : filters.value() ) {
addFilter( hasAssociationTable, filter );
}
}
FilterJoinTable simpleFilterJoinTable = property.getAnnotation( FilterJoinTable.class );
final FilterJoinTable simpleFilterJoinTable = property.getAnnotation( FilterJoinTable.class );
if ( simpleFilterJoinTable != null ) {
addFilter( hasAssociationTable, simpleFilterJoinTable );
}
FilterJoinTables filterJoinTables = property.getAnnotation( FilterJoinTables.class );
final FilterJoinTables filterJoinTables = property.getAnnotation( FilterJoinTables.class );
if ( filterJoinTables != null ) {
for ( FilterJoinTable filter : filterJoinTables.value() ) {
addFilter( hasAssociationTable, filter );
@ -1821,8 +1833,8 @@ public abstract class CollectionBinder {
}
private DependantValue buildCollectionKey(
Collection collValue,
AnnotatedJoinColumn[] joinColumns,
Collection collection,
AnnotatedJoinColumns joinColumns,
boolean cascadeDeleteEnabled,
boolean noConstraintByDefault,
XProperty property,
@ -1831,21 +1843,23 @@ public abstract class CollectionBinder {
// give a chance to override the referenced property name
// has to do that here because the referencedProperty creation happens in a FKSecondPass for ManyToOne yuk!
overrideReferencedPropertyName( collValue, joinColumns, buildingContext );
overrideReferencedPropertyName( collection, joinColumns, buildingContext );
String propRef = collValue.getReferencedPropertyName();
final String referencedPropertyName = collection.getReferencedPropertyName();
//binding key reference using column
KeyValue keyVal = propRef == null
? collValue.getOwner().getIdentifier()
: (KeyValue) collValue.getOwner().getReferencedProperty(propRef).getValue();
final PersistentClass owner = collection.getOwner();
final KeyValue keyValue = referencedPropertyName == null
? owner.getIdentifier()
: (KeyValue) owner.getReferencedProperty( referencedPropertyName ).getValue();
DependantValue key = new DependantValue( buildingContext, collValue.getCollectionTable(), keyVal );
final DependantValue key = new DependantValue( buildingContext, collection.getCollectionTable(), keyValue );
key.setTypeName( null );
checkPropertyConsistency( joinColumns, collValue.getOwnerEntityName() );
key.setNullable( joinColumns.length == 0 || joinColumns[0].isNullable() );
key.setUpdateable( joinColumns.length == 0 || joinColumns[0].isUpdatable() );
final AnnotatedJoinColumn[] columns = joinColumns.getColumns();
checkPropertyConsistency( columns, collection.getOwnerEntityName() );
key.setNullable( columns.length == 0 || columns[0].isNullable() );
key.setUpdateable( columns.length == 0 || columns[0].isUpdatable() );
key.setCascadeDeleteEnabled( cascadeDeleteEnabled );
collValue.setKey( key );
collection.setKey( key );
if ( property != null ) {
final org.hibernate.annotations.ForeignKey fk = property.getAnnotation( org.hibernate.annotations.ForeignKey.class );
@ -1855,17 +1869,18 @@ public abstract class CollectionBinder {
else {
final CollectionTable collectionTableAnn = property.getAnnotation( CollectionTable.class );
if ( collectionTableAnn != null ) {
if ( collectionTableAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|| collectionTableAnn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
final ForeignKey foreignKey = collectionTableAnn.foreignKey();
if ( foreignKey.value() == ConstraintMode.NO_CONSTRAINT
|| foreignKey.value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
key.disableForeignKey();
}
else {
key.setForeignKeyName( nullIfEmpty( collectionTableAnn.foreignKey().name() ) );
key.setForeignKeyDefinition( nullIfEmpty( collectionTableAnn.foreignKey().foreignKeyDefinition() ) );
if ( key.getForeignKeyName() == null &&
key.getForeignKeyDefinition() == null &&
collectionTableAnn.joinColumns().length == 1 ) {
JoinColumn joinColumn = collectionTableAnn.joinColumns()[0];
key.setForeignKeyName( nullIfEmpty( foreignKey.name() ) );
key.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) );
if ( key.getForeignKeyName() == null
&& key.getForeignKeyDefinition() == null
&& collectionTableAnn.joinColumns().length == 1 ) {
final JoinColumn joinColumn = collectionTableAnn.joinColumns()[0];
key.setForeignKeyName( nullIfEmpty( joinColumn.foreignKey().name() ) );
key.setForeignKeyDefinition( nullIfEmpty( joinColumn.foreignKey().foreignKeyDefinition() ) );
}
@ -1874,9 +1889,10 @@ public abstract class CollectionBinder {
else {
final JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
if ( joinTableAnn != null ) {
String foreignKeyName = joinTableAnn.foreignKey().name();
String foreignKeyDefinition = joinTableAnn.foreignKey().foreignKeyDefinition();
ConstraintMode foreignKeyValue = joinTableAnn.foreignKey().value();
final ForeignKey foreignKey = joinTableAnn.foreignKey();
String foreignKeyName = foreignKey.name();
String foreignKeyDefinition = foreignKey.foreignKeyDefinition();
ConstraintMode foreignKeyValue = foreignKey.value();
if ( joinTableAnn.joinColumns().length != 0 ) {
final JoinColumn joinColumnAnn = joinTableAnn.joinColumns()[0];
if ( foreignKeyName != null && foreignKeyName.isEmpty() ) {
@ -1897,22 +1913,16 @@ public abstract class CollectionBinder {
}
}
else {
final String propertyPath = qualify(propertyHolder.getPath(), property.getName());
final String propertyPath = qualify( propertyHolder.getPath(), property.getName() );
final ForeignKey foreignKey = propertyHolder.getOverriddenForeignKey( propertyPath );
if ( foreignKey != null ) {
if ( foreignKey.value() == ConstraintMode.NO_CONSTRAINT
|| foreignKey.value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
key.disableForeignKey();
}
else {
key.setForeignKeyName( nullIfEmpty( foreignKey.name() ) );
key.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) );
}
handleForeignKeyConstraint( noConstraintByDefault, key, foreignKey );
}
else {
final OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
if ( oneToManyAnn != null && !oneToManyAnn.mappedBy().isEmpty()
if ( oneToManyAnn != null
&& !oneToManyAnn.mappedBy().isEmpty()
&& ( onDeleteAnn == null || onDeleteAnn.action() != OnDeleteAction.CASCADE ) ) {
// foreign key should be up to @ManyToOne side
// @OnDelete generate "on delete cascade" foreign key
@ -1921,14 +1931,7 @@ public abstract class CollectionBinder {
else {
final JoinColumn joinColumnAnn = property.getAnnotation( JoinColumn.class );
if ( joinColumnAnn != null ) {
if ( joinColumnAnn.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
|| joinColumnAnn.foreignKey().value() == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault ) {
key.disableForeignKey();
}
else {
key.setForeignKeyName( nullIfEmpty( joinColumnAnn.foreignKey().name() ) );
key.setForeignKeyDefinition( nullIfEmpty( joinColumnAnn.foreignKey().foreignKeyDefinition() ) );
}
handleForeignKeyConstraint( noConstraintByDefault, key, joinColumnAnn.foreignKey() );
}
}
}
@ -1940,22 +1943,31 @@ public abstract class CollectionBinder {
return key;
}
private static void handleForeignKeyConstraint(boolean noConstraintByDefault, DependantValue key, ForeignKey foreignKey) {
final ConstraintMode constraintMode = foreignKey.value();
if ( constraintMode == ConstraintMode.NO_CONSTRAINT
|| constraintMode == ConstraintMode.PROVIDER_DEFAULT && noConstraintByDefault) {
key.disableForeignKey();
}
else {
key.setForeignKeyName( nullIfEmpty( foreignKey.name() ) );
key.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) );
}
}
private void overrideReferencedPropertyName(
Collection collection,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
MetadataBuildingContext context) {
if ( joinColumns.length > 0 ) {
AnnotatedJoinColumn joinColumn = joinColumns[0];
if ( hasMappedBy() ) {
final String entityName = joinColumn.getManyToManyOwnerSideEntityName() != null
? "inverse__" + joinColumn.getManyToManyOwnerSideEntityName()
: joinColumn.getPropertyHolder().getEntityName();
final InFlightMetadataCollector collector = context.getMetadataCollector();
final String referencedProperty = collector.getPropertyReferencedAssociation( entityName, mappedBy );
if ( referencedProperty != null ) {
collection.setReferencedPropertyName( referencedProperty );
collector.addPropertyReference( collection.getOwnerEntityName(), referencedProperty );
}
if ( hasMappedBy() && joinColumns.getColumns().length > 0 ) {
final String entityName = joinColumns.getManyToManyOwnerSideEntityName() != null
? "inverse__" + joinColumns.getManyToManyOwnerSideEntityName()
: joinColumns.getPropertyHolder().getEntityName();
final InFlightMetadataCollector collector = context.getMetadataCollector();
final String referencedProperty = collector.getPropertyReferencedAssociation( entityName, mappedBy );
if ( referencedProperty != null ) {
collection.setReferencedPropertyName( referencedProperty );
collector.addPropertyReference( collection.getOwnerEntityName(), referencedProperty );
}
}
}
@ -2195,15 +2207,15 @@ public abstract class CollectionBinder {
}
private ManyToOne handleCollectionOfEntities(
Collection collValue,
Collection collection,
XClass elementType,
NotFoundAction notFoundAction,
XProperty property,
MetadataBuildingContext buildingContext,
PersistentClass collectionEntity,
String hqlOrderBy) {
ManyToOne element = new ManyToOne( buildingContext, collValue.getCollectionTable() );
collValue.setElement( element );
ManyToOne element = new ManyToOne( buildingContext, collection.getCollectionTable() );
collection.setElement( element );
element.setReferencedEntityName( elementType.getName() );
//element.setFetchMode( fetchMode );
//element.setLazy( fetchMode != FetchMode.JOIN );
@ -2213,7 +2225,7 @@ public abstract class CollectionBinder {
element.setNotFoundAction( notFoundAction );
// as per 11.1.38 of JPA 2.0 spec, default to primary key if no column is specified by @OrderBy.
if ( hqlOrderBy != null ) {
collValue.setManyToManyOrdering( buildOrderByClauseFromHql( hqlOrderBy, collectionEntity ) );
collection.setManyToManyOrdering( buildOrderByClauseFromHql( hqlOrderBy, collectionEntity ) );
}
final org.hibernate.annotations.ForeignKey fk = property.getAnnotation( org.hibernate.annotations.ForeignKey.class );
@ -2247,8 +2259,8 @@ public abstract class CollectionBinder {
}
private void handleManyToAny(
Collection collValue,
AnnotatedJoinColumn[] inverseJoinColumns,
Collection collection,
AnnotatedJoinColumns inverseJoinColumns,
boolean cascadeDeleteEnabled,
XProperty property,
MetadataBuildingContext buildingContext) {
@ -2266,8 +2278,8 @@ public abstract class CollectionBinder {
final Formula discriminatorFormulaAnn = getOverridableAnnotation( prop, Formula.class, buildingContext);
//override the table
for (AnnotatedColumn column : inverseJoinColumns) {
column.setTable( collValue.getCollectionTable() );
for ( AnnotatedColumn column : inverseJoinColumns.getColumns() ) {
column.setTable( collection.getCollectionTable() );
}
ManyToAny anyAnn = property.getAnnotation( ManyToAny.class );
@ -2284,33 +2296,33 @@ public abstract class CollectionBinder {
true,
buildingContext
);
collValue.setElement( any );
collection.setElement( any );
}
private PropertyData getSpecialMembers(XClass elementClass) {
if ( isMap() ) {
//"value" is the JPA 2 prefix for map values (used to be "element")
if ( isHibernateExtensionMapping() ) {
return new PropertyPreloadedData( AccessType.PROPERTY, "element", elementClass);
return new PropertyPreloadedData( AccessType.PROPERTY, "element", elementClass );
}
else {
return new PropertyPreloadedData( AccessType.PROPERTY, "value", elementClass);
return new PropertyPreloadedData( AccessType.PROPERTY, "value", elementClass );
}
}
else {
if ( isHibernateExtensionMapping() ) {
return new PropertyPreloadedData( AccessType.PROPERTY, "element", elementClass);
return new PropertyPreloadedData( AccessType.PROPERTY, "element", elementClass );
}
else {
//"collection&&element" is not a valid property name => placeholder
return new PropertyPreloadedData( AccessType.PROPERTY, "collection&&element", elementClass);
return new PropertyPreloadedData( AccessType.PROPERTY, "collection&&element", elementClass );
}
}
}
private void handleOwnedManyToMany(
Collection collection,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
TableBinder associationTableBinder,
XProperty property,
MetadataBuildingContext context,
@ -2320,15 +2332,11 @@ public abstract class CollectionBinder {
//FIXME NamingStrategy
final InFlightMetadataCollector collector = context.getMetadataCollector();
final PersistentClass owner = collection.getOwner();
for ( AnnotatedJoinColumn column : joinColumns ) {
column.setMappedBy(
owner.getEntityName(),
collector.getLogicalTableName( owner.getTable() ),
collector.getFromMappedBy( owner.getEntityName(), column.getPropertyName() )
);
// String header = ( mappedByProperty == null ) ? mappings.getLogicalTableName( ownerTable ) : mappedByProperty;
// column.setDefaultColumnHeader( header );
}
joinColumns.setMappedBy(
owner.getEntityName(),
collector.getLogicalTableName( owner.getTable() ),
collector.getFromMappedBy( owner.getEntityName(), joinColumns.getPropertyName() )
);
if ( isEmpty( associationTableBinder.getName() ) ) {
//default value
associationTableBinder.setDefaultName(
@ -2340,7 +2348,7 @@ public abstract class CollectionBinder {
collectionEntity != null ? collectionEntity.getEntityName() : null,
collectionEntity != null ? collectionEntity.getJpaEntityName() : null,
collectionEntity != null ? collector.getLogicalTableName( collectionEntity.getTable() ) : null,
joinColumns[0].getPropertyName()
joinColumns.getPropertyName()
);
}
associationTableBinder.setJPA2ElementCollection(
@ -2350,8 +2358,8 @@ public abstract class CollectionBinder {
}
private void handleUnownedManyToMany(
Collection collValue,
AnnotatedJoinColumn[] joinColumns,
Collection collection,
AnnotatedJoinColumns joinColumns,
XClass elementType,
PersistentClass collectionEntity,
boolean isCollectionOfEntities) {
@ -2361,7 +2369,9 @@ public abstract class CollectionBinder {
+ "' targets the type '" + elementType.getName() + "' which is not an '@Entity' type" );
}
Property otherSideProperty;
joinColumns.setManyToManyOwnerSideEntityName( collectionEntity.getEntityName() );
final Property otherSideProperty;
try {
otherSideProperty = collectionEntity.getRecursiveProperty( mappedBy );
}
@ -2370,17 +2380,13 @@ public abstract class CollectionBinder {
"is 'mappedBy' a property named '" + mappedBy
+ "' which does not exist in the target entity '" + elementType.getName() + "'" );
}
Table table = otherSideProperty.getValue() instanceof Collection
? ( (Collection) otherSideProperty.getValue() ).getCollectionTable()
: otherSideProperty.getValue().getTable();
//this is a collection on the other side
//This is a ToOne with a @JoinTable or a regular property
collValue.setCollectionTable( table );
String entityName = collectionEntity.getEntityName();
for (AnnotatedJoinColumn column : joinColumns) {
//column.setDefaultColumnHeader( joinColumns[0].getMappedBy() ); //seems not to be used, make sense
column.setManyToManyOwnerSideEntityName( entityName );
}
final Value otherSidePropertyValue = otherSideProperty.getValue();
final Table table = otherSidePropertyValue instanceof Collection
// this is a collection on the other side
? ( (Collection) otherSidePropertyValue ).getCollectionTable()
// this is a ToOne with a @JoinTable or a regular property
: otherSidePropertyValue.getTable();
collection.setCollectionTable( table );
}
private void detectManyToManyProblems(
@ -2402,7 +2408,7 @@ public abstract class CollectionBinder {
}
}
else {
JoinTable joinTableAnn = parentPropertyHolder.getJoinTable( property );
final JoinTable joinTableAnn = parentPropertyHolder.getJoinTable( property );
if ( joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0 ) {
throw new AnnotationException( "Association '" + safeCollectionRole()
+ " has a '@JoinTable' with 'inverseJoinColumns' and targets the type '"
@ -2479,7 +2485,7 @@ public abstract class CollectionBinder {
private void bindCollectionSecondPass(
PersistentClass targetEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
boolean cascadeDeleteEnabled,
MetadataBuildingContext context) {
@ -2505,9 +2511,8 @@ public abstract class CollectionBinder {
context
);
//TODO: this is really warty
if ( property.isAnnotationPresent( ElementCollection.class ) && joinColumns.length > 0 ) {
joinColumns[0].setJPA2ElementCollection( true );
if ( property.isAnnotationPresent( ElementCollection.class ) ) {
joinColumns.setElementCollection( true );
}
TableBinder.bindForeignKey(
@ -2529,7 +2534,6 @@ public abstract class CollectionBinder {
return propertyHolder != null ? propertyHolder.getEntityName() + "." + propertyName : "";
}
/**
* Bind the inverse foreign key of a {@link ManyToMany}, that is, the columns
* specified by {@code @JoinTable(inverseJoinColumns=...)}, which are the
@ -2539,15 +2543,16 @@ public abstract class CollectionBinder {
*/
public void bindManyToManyInverseForeignKey(
PersistentClass targetEntity,
AnnotatedJoinColumn[] columns,
AnnotatedJoinColumns joinColumns,
SimpleValue value,
boolean unique,
MetadataBuildingContext context) {
if ( hasMappedBy() ) {
final Property property = targetEntity.getRecursiveProperty( mappedBy );
final List<Selectable> mappedByColumns = mappedByColumns( targetEntity, property );
final AnnotatedJoinColumn firstColumn = joinColumns.getColumns()[0];
for ( Selectable selectable: mappedByColumns ) {
columns[0].linkValueUsingAColumnCopy( (Column) selectable, value );
firstColumn.linkValueUsingAColumnCopy( (Column) selectable, value );
}
final String referencedPropertyName = context.getMetadataCollector()
.getPropertyReferencedAssociation( targetEntity.getEntityName(), mappedBy );
@ -2562,7 +2567,7 @@ public abstract class CollectionBinder {
}
else {
createSyntheticPropertyReference(
columns,
joinColumns,
targetEntity,
collection.getOwner(),
value,
@ -2576,7 +2581,7 @@ public abstract class CollectionBinder {
TableBinder.bindForeignKey(
targetEntity,
collection.getOwner(),
columns,
joinColumns,
value,
unique,
context
@ -2604,7 +2609,7 @@ public abstract class CollectionBinder {
}
}
public void setFkJoinColumns(AnnotatedJoinColumn[] annotatedJoinColumns) {
public void setFkJoinColumns(AnnotatedJoinColumns annotatedJoinColumns) {
this.foreignJoinColumns = annotatedJoinColumns;
}
@ -2636,7 +2641,7 @@ public abstract class CollectionBinder {
this.mapKeyColumns = mapKeyColumns;
}
public void setMapKeyManyToManyColumns(AnnotatedJoinColumn[] mapJoinColumns) {
public void setMapKeyManyToManyColumns(AnnotatedJoinColumns mapJoinColumns) {
this.mapKeyManyToManyColumns = mapJoinColumns;
}
@ -2644,7 +2649,7 @@ public abstract class CollectionBinder {
this.localGenerators = localGenerators;
}
private void logOneToManySeconPass() {
private void logOneToManySecondPass() {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding a OneToMany: %s through a foreign key", safeCollectionRole() );
}

View File

@ -90,6 +90,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotatedClassType;
import org.hibernate.cfg.AnnotatedDiscriminatorColumn;
import org.hibernate.cfg.AnnotatedJoinColumns;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.AvailableSettings;
@ -217,7 +218,7 @@ public class EntityBinder {
final PersistentClass persistentClass = makePersistentClass( inheritanceState, superEntity, context);
final EntityBinder entityBinder = new EntityBinder( clazzToProcess, persistentClass, context );
final AnnotatedJoinColumn[] inheritanceJoinedColumns =
final AnnotatedJoinColumns inheritanceJoinedColumns =
makeInheritanceJoinColumns( clazzToProcess, context, inheritanceState, superEntity );
final AnnotatedDiscriminatorColumn discriminatorColumn =
handleDiscriminatorColumn( clazzToProcess, context, inheritanceState, entityBinder );
@ -673,7 +674,7 @@ public class EntityBinder {
InheritanceState inheritanceState,
PersistentClass persistentClass,
EntityBinder entityBinder,
AnnotatedJoinColumn[] inheritanceJoinedColumns,
AnnotatedJoinColumns inheritanceJoinedColumns,
AnnotatedDiscriminatorColumn discriminatorColumn,
PropertyHolder propertyHolder) {
@ -984,7 +985,7 @@ public class EntityBinder {
}
}
private static AnnotatedJoinColumn[] makeInheritanceJoinColumns(
private static AnnotatedJoinColumns makeInheritanceJoinColumns(
XClass clazzToProcess,
MetadataBuildingContext context,
InheritanceState inheritanceState,
@ -995,7 +996,7 @@ public class EntityBinder {
&& InheritanceType.JOINED == inheritanceState.getType();
if ( hasJoinedColumns ) {
//@Inheritance(JOINED) subclass need to link back to the super entity
PrimaryKeyJoinColumns jcsAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
final PrimaryKeyJoinColumns jcsAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
if ( explicitInheritanceJoinedColumns ) {
int nbrOfInhJoinedColumns = jcsAnn.value().length;
@ -1014,7 +1015,7 @@ public class EntityBinder {
}
}
else {
PrimaryKeyJoinColumn jcAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
final PrimaryKeyJoinColumn jcAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
inheritanceJoinedColumns = new AnnotatedJoinColumn[1];
inheritanceJoinedColumns[0] = buildJoinColumn(
jcAnn,
@ -1033,7 +1034,15 @@ public class EntityBinder {
LOG.invalidPrimaryKeyJoinColumnAnnotation( clazzToProcess.getName() );
}
}
return inheritanceJoinedColumns;
if ( inheritanceJoinedColumns == null ) {
return null;
}
else {
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( context );
joinColumns.setColumns( inheritanceJoinedColumns );
return joinColumns;
}
}
private static PersistentClass getSuperEntity(
@ -1746,24 +1755,23 @@ public class EntityBinder {
}
private void createPrimaryColumnsToSecondaryTable(Object column, PropertyHolder propertyHolder, Join join) {
final AnnotatedJoinColumn[] annotatedJoinColumns;
final PrimaryKeyJoinColumn[] pkColumnsAnn = column instanceof PrimaryKeyJoinColumn[]
? (PrimaryKeyJoinColumn[]) column
: null;
final JoinColumn[] joinColumnsAnn = column instanceof JoinColumn[]
? (JoinColumn[]) column
: null;
annotatedJoinColumns = pkColumnsAnn == null && joinColumnsAnn == null
final AnnotatedJoinColumns annotatedJoinColumns = pkColumnsAnn == null && joinColumnsAnn == null
? createDefaultJoinColumn( propertyHolder )
: createJoinColumns( propertyHolder, pkColumnsAnn, joinColumnsAnn );
for (AnnotatedJoinColumn joinColumn : annotatedJoinColumns) {
for ( AnnotatedJoinColumn joinColumn : annotatedJoinColumns.getColumns() ) {
joinColumn.forceNotNull();
}
bindJoinToPersistentClass( join, annotatedJoinColumns, context );
}
private AnnotatedJoinColumn[] createDefaultJoinColumn(PropertyHolder propertyHolder) {
private AnnotatedJoinColumns createDefaultJoinColumn(PropertyHolder propertyHolder) {
final AnnotatedJoinColumn[] annotatedJoinColumns = new AnnotatedJoinColumn[1];
annotatedJoinColumns[0] = buildJoinColumn(
null,
@ -1773,10 +1781,14 @@ public class EntityBinder {
propertyHolder,
context
);
return annotatedJoinColumns;
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( context );
joinColumns.setPropertyHolder( propertyHolder );
joinColumns.setColumns( annotatedJoinColumns );
return joinColumns;
}
private AnnotatedJoinColumn[] createJoinColumns(
private AnnotatedJoinColumns createJoinColumns(
PropertyHolder propertyHolder,
PrimaryKeyJoinColumn[] pkColumnsAnn,
JoinColumn[] joinColumnsAnn) {
@ -1787,8 +1799,8 @@ public class EntityBinder {
else {
final AnnotatedJoinColumn[] annotatedJoinColumns = new AnnotatedJoinColumn[joinColumnCount];
for (int colIndex = 0; colIndex < joinColumnCount; colIndex++) {
PrimaryKeyJoinColumn pkJoinAnn = pkColumnsAnn != null ? pkColumnsAnn[colIndex] : null;
JoinColumn joinAnn = joinColumnsAnn != null ? joinColumnsAnn[colIndex] : null;
final PrimaryKeyJoinColumn pkJoinAnn = pkColumnsAnn != null ? pkColumnsAnn[colIndex] : null;
final JoinColumn joinAnn = joinColumnsAnn != null ? joinColumnsAnn[colIndex] : null;
annotatedJoinColumns[colIndex] = buildJoinColumn(
pkJoinAnn,
joinAnn,
@ -1798,11 +1810,15 @@ public class EntityBinder {
context
);
}
return annotatedJoinColumns;
final AnnotatedJoinColumns joinColumns = new AnnotatedJoinColumns();
joinColumns.setBuildingContext( context );
joinColumns.setPropertyHolder( propertyHolder );
joinColumns.setColumns( annotatedJoinColumns );
return joinColumns;
}
}
private void bindJoinToPersistentClass(Join join, AnnotatedJoinColumn[] joinColumns, MetadataBuildingContext context) {
private void bindJoinToPersistentClass(Join join, AnnotatedJoinColumns joinColumns, MetadataBuildingContext context) {
DependantValue key = new DependantValue( context, join.getTable(), persistentClass.getIdentifier() );
join.setKey( key );
setForeignKeyNameIfDefined( join );
@ -1822,7 +1838,7 @@ public class EntityBinder {
key.setForeignKeyName( matchingTable.foreignKey().name() );
}
else {
SecondaryTable jpaSecondaryTable = findMatchingSecondaryTable( join );
final SecondaryTable jpaSecondaryTable = findMatchingSecondaryTable( join );
if ( jpaSecondaryTable != null ) {
final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault();
if ( jpaSecondaryTable.foreignKey().value() == ConstraintMode.NO_CONSTRAINT
@ -1839,11 +1855,11 @@ public class EntityBinder {
private SecondaryTable findMatchingSecondaryTable(Join join) {
final String nameToMatch = join.getTable().getQuotedName();
SecondaryTable secondaryTable = annotatedClass.getAnnotation( SecondaryTable.class );
final SecondaryTable secondaryTable = annotatedClass.getAnnotation( SecondaryTable.class );
if ( secondaryTable != null && nameToMatch.equals( secondaryTable.name() ) ) {
return secondaryTable;
}
SecondaryTables secondaryTables = annotatedClass.getAnnotation( SecondaryTables.class );
final SecondaryTables secondaryTables = annotatedClass.getAnnotation( SecondaryTables.class );
if ( secondaryTables != null ) {
for ( SecondaryTable secondaryTablesEntry : secondaryTables.value() ) {
if ( secondaryTablesEntry != null && nameToMatch.equals( secondaryTablesEntry.name() ) ) {
@ -1856,7 +1872,7 @@ public class EntityBinder {
private org.hibernate.annotations.Table findMatchingComplementaryTableAnnotation(Join join) {
final String tableName = join.getTable().getQuotedName();
org.hibernate.annotations.Table table = annotatedClass.getAnnotation( org.hibernate.annotations.Table.class );
final org.hibernate.annotations.Table table = annotatedClass.getAnnotation( org.hibernate.annotations.Table.class );
if ( table != null && tableName.equals( table.appliesTo() ) ) {
return table;
}
@ -1875,7 +1891,7 @@ public class EntityBinder {
private SecondaryRow findMatchingComplementarySecondaryRowAnnotation(Join join) {
final String tableName = join.getTable().getQuotedName();
SecondaryRow row = annotatedClass.getAnnotation( SecondaryRow.class );
final SecondaryRow row = annotatedClass.getAnnotation( SecondaryRow.class );
if ( row != null && ( row.table().isEmpty() || tableName.equals( row.table() ) ) ) {
return row;
}
@ -1907,7 +1923,7 @@ public class EntityBinder {
PropertyHolder propertyHolder,
boolean noDelayInPkColumnCreation) {
// A non-null propertyHolder means than we process the Pk creation without delay
Join join = new Join();
final Join join = new Join();
join.setPersistentClass( persistentClass );
final String schema;
@ -1979,8 +1995,8 @@ public class EntityBinder {
//Has to do the work later because it needs persistentClass id!
LOG.debugf( "Adding secondary table to entity %s -> %s",
persistentClass.getEntityName(), join.getTable().getName() );
SecondaryRow matchingRow = findMatchingComplementarySecondaryRowAnnotation( join );
org.hibernate.annotations.Table matchingTable = findMatchingComplementaryTableAnnotation( join );
final SecondaryRow matchingRow = findMatchingComplementarySecondaryRowAnnotation( join );
final org.hibernate.annotations.Table matchingTable = findMatchingComplementaryTableAnnotation( join );
if ( matchingRow != null ) {
join.setInverse( !matchingRow.owned() );
join.setOptional( matchingRow.optional() );
@ -2042,7 +2058,7 @@ public class EntityBinder {
}
public static String getCacheConcurrencyStrategy(CacheConcurrencyStrategy strategy) {
org.hibernate.cache.spi.access.AccessType accessType = strategy.toAccessType();
final org.hibernate.cache.spi.access.AccessType accessType = strategy.toAccessType();
return accessType == null ? null : accessType.getExternalName();
}
@ -2100,9 +2116,10 @@ public class EntityBinder {
}
public void processComplementaryTableDefinitions(Tables tables) {
if ( tables == null ) return;
for (org.hibernate.annotations.Table table : tables.value()) {
processComplementaryTableDefinitions( table );
if ( tables != null ) {
for ( org.hibernate.annotations.Table table : tables.value() ) {
processComplementaryTableDefinitions( table );
}
}
}
@ -2128,7 +2145,7 @@ public class EntityBinder {
public AccessType getExplicitAccessType(XAnnotatedElement element) {
AccessType accessType = null;
Access access = element.getAnnotation( Access.class );
final Access access = element.getAnnotation( Access.class );
if ( access != null ) {
accessType = AccessType.getAccessStrategy( access.value() );
}
@ -2160,14 +2177,14 @@ public class EntityBinder {
}
private static void bindFilters(XAnnotatedElement annotatedElement, EntityBinder entityBinder, MetadataBuildingContext context) {
Filters filtersAnn = getOverridableAnnotation( annotatedElement, Filters.class, context );
final Filters filtersAnn = getOverridableAnnotation( annotatedElement, Filters.class, context );
if ( filtersAnn != null ) {
for ( Filter filter : filtersAnn.value() ) {
entityBinder.addFilter(filter);
}
}
Filter filterAnn = annotatedElement.getAnnotation( Filter.class );
final Filter filterAnn = annotatedElement.getAnnotation( Filter.class );
if ( filterAnn != null ) {
entityBinder.addFilter(filterAnn);
}

View File

@ -22,6 +22,7 @@ import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AccessType;
import org.hibernate.cfg.AnnotatedClassType;
import org.hibernate.cfg.AnnotatedJoinColumns;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.CollectionPropertyHolder;
import org.hibernate.cfg.CollectionSecondPass;
@ -102,7 +103,7 @@ public class MapBinder extends CollectionBinder {
buildingContext,
mapKeyColumns,
mapKeyManyToManyColumns,
inverseJoinColumns != null ? inverseJoinColumns[0].getPropertyName() : null
inverseJoinColumns != null ? inverseJoinColumns.getPropertyName() : null
);
makeOneToManyMapKeyColumnNullableIfNotInProperty( property );
}
@ -159,7 +160,7 @@ public class MapBinder extends CollectionBinder {
boolean isEmbedded,
MetadataBuildingContext buildingContext,
AnnotatedColumn[] mapKeyColumns,
AnnotatedJoinColumn[] mapKeyManyToManyColumns,
AnnotatedJoinColumns mapKeyManyToManyColumns,
String targetPropertyName) {
if ( mapKeyPropertyName != null ) {
//this is an EJB3 @MapKey
@ -340,7 +341,7 @@ public class MapBinder extends CollectionBinder {
//FIXME pass the Index Entity JoinColumns
if ( !collection.isOneToMany() ) {
//index column should not be null
for (AnnotatedJoinColumn col : mapKeyManyToManyColumns) {
for ( AnnotatedJoinColumn col : mapKeyManyToManyColumns.getColumns() ) {
col.forceNotNull();
}
}

View File

@ -24,6 +24,7 @@ import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.AnnotatedJoinColumns;
import org.hibernate.cfg.IndexOrUniqueKeySecondPass;
import org.hibernate.cfg.JPAIndexHolder;
import org.hibernate.cfg.ObjectNameSource;
@ -533,26 +534,25 @@ public class TableBinder {
public static void bindForeignKey(
PersistentClass referencedEntity,
PersistentClass destinationEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value,
boolean unique,
MetadataBuildingContext buildingContext) {
final AnnotatedJoinColumn firstColumn = joinColumns[0];
final PersistentClass associatedClass;
if ( destinationEntity != null ) {
//overridden destination
associatedClass = destinationEntity;
}
else {
final PropertyHolder holder = firstColumn.getPropertyHolder();
final PropertyHolder holder = joinColumns.getPropertyHolder();
associatedClass = holder == null ? null : holder.getPersistentClass();
}
if ( firstColumn.hasMappedBy() ) {
final AnnotatedJoinColumn firstColumn = joinColumns.getColumns()[0];
if ( joinColumns.hasMappedBy() ) {
// use the columns of the property referenced by mappedBy
// copy them and link the copy to the actual value
bindUnownedAssociation( joinColumns, value, associatedClass, firstColumn.getMappedBy() );
bindUnownedAssociation( joinColumns, value, associatedClass, joinColumns.getMappedBy() );
}
else if ( firstColumn.isImplicit() ) {
// if columns are implicit, then create the columns based
@ -570,7 +570,7 @@ public class TableBinder {
private static void bindExplicitColumns(
PersistentClass referencedEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value,
MetadataBuildingContext buildingContext,
PersistentClass associatedClass) {
@ -588,16 +588,16 @@ public class TableBinder {
private static void bindImplicitPrimaryKeyReference(
PersistentClass referencedEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value,
PersistentClass associatedClass) {
//implicit case, we hope PK and FK columns are in the same order
if ( joinColumns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
if ( joinColumns.getColumns().length != referencedEntity.getIdentifier().getColumnSpan() ) {
// TODO: what about secondary tables?? associatedClass is null?
throw new AnnotationException(
"An association that targets entity '" + referencedEntity.getEntityName()
+ "' from entity '" + associatedClass.getEntityName()
+ "' has " + joinColumns.length + " '@JoinColumn's but the primary key has "
+ "' has " + joinColumns.getColumns().length + " '@JoinColumn's but the primary key has "
+ referencedEntity.getIdentifier().getColumnSpan() + " columns"
);
}
@ -614,7 +614,7 @@ public class TableBinder {
private static void bindPrimaryKeyReference(
PersistentClass referencedEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value,
PersistentClass associatedClass,
MetadataBuildingContext buildingContext) {
@ -631,7 +631,7 @@ public class TableBinder {
boolean match = false;
// for each PK column, find the associated FK column.
final String quotedName = column.getQuotedName( dialect );
for ( AnnotatedJoinColumn joinColumn : joinColumns ) {
for ( AnnotatedJoinColumn joinColumn : joinColumns.getColumns() ) {
final String referencedColumn = buildingContext.getMetadataCollector()
.getPhysicalColumnName( referencedEntity.getTable(), joinColumn.getReferencedColumn() );
// in JPA 2 referencedColumnName is case-insensitive
@ -664,14 +664,14 @@ public class TableBinder {
private static void bindNonPrimaryKeyReference(
PersistentClass referencedEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value) {
final String referencedPropertyName;
if ( value instanceof ToOne ) {
referencedPropertyName = ( (ToOne) value).getReferencedPropertyName();
}
else if ( value instanceof DependantValue ) {
final String propertyName = joinColumns[0].getPropertyName();
final String propertyName = joinColumns.getPropertyName();
if ( propertyName != null ) {
Collection collection = (Collection) referencedEntity.getRecursiveProperty( propertyName ).getValue();
referencedPropertyName = collection.getReferencedPropertyName();
@ -698,25 +698,27 @@ public class TableBinder {
private static void bindImplicitColumns(
PersistentClass referencedEntity,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value) {
final List<Column> idColumns = referencedEntity instanceof JoinedSubclass
? referencedEntity.getKey().getColumns()
: referencedEntity.getIdentifier().getColumns();
for ( Column column: idColumns ) {
joinColumns[0].linkValueUsingDefaultColumnNaming( column, referencedEntity, value);
joinColumns[0].overrideFromReferencedColumnIfNecessary( column );
final AnnotatedJoinColumn firstColumn = joinColumns.getColumns()[0];
firstColumn.linkValueUsingDefaultColumnNaming( column, referencedEntity, value);
firstColumn.overrideFromReferencedColumnIfNecessary( column );
}
}
private static void bindUnownedAssociation(
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue value,
PersistentClass associatedClass,
String mappedByProperty) {
final AnnotatedJoinColumn firstColumn = joinColumns.getColumns()[0];
for ( Column column: mappedByColumns( associatedClass, mappedByProperty ) ) {
joinColumns[0].overrideFromReferencedColumnIfNecessary( column );
joinColumns[0].linkValueUsingAColumnCopy( column, value);
firstColumn.overrideFromReferencedColumnIfNecessary( column );
firstColumn.linkValueUsingAColumnCopy( column, value);
}
}
@ -739,19 +741,20 @@ public class TableBinder {
public static void linkJoinColumnWithValueOverridingNameIfImplicit(
PersistentClass referencedEntity,
Value value,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumns joinColumns,
SimpleValue simpleValue) {
final List<Column> valueColumns = value.getColumns();
for ( int i = 0; i < joinColumns.length; i++ ) {
final AnnotatedJoinColumn joinCol = joinColumns[i];
final AnnotatedJoinColumn[] columns = joinColumns.getColumns();
for (int i = 0; i < columns.length; i++ ) {
final AnnotatedJoinColumn joinColumn = columns[i];
final Column synthCol = valueColumns.get(i);
if ( joinCol.isNameDeferred() ) {
if ( joinColumn.isNameDeferred() ) {
//this has to be the default value
joinCol.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, simpleValue );
joinColumn.linkValueUsingDefaultColumnNaming( synthCol, referencedEntity, simpleValue );
}
else {
joinCol.linkWithValue( simpleValue );
joinCol.overrideFromReferencedColumnIfNecessary( synthCol );
joinColumn.linkWithValue( simpleValue );
joinColumn.overrideFromReferencedColumnIfNecessary( synthCol );
}
}
}

View File

@ -415,7 +415,7 @@ public abstract class Collection implements Fetchable, Value, Filterable {
}
private void checkColumnDuplication() throws MappingException {
HashSet<String> cols = new HashSet<>();
final HashSet<String> cols = new HashSet<>();
checkColumnDuplication( cols, getKey() );
if ( isIndexed() ) {
checkColumnDuplication(

View File

@ -76,10 +76,10 @@ public class ManyToOne extends ToOne {
}
public void createPropertyRefConstraints(Map<String, PersistentClass> persistentClasses) {
if (referencedPropertyName!=null) {
if ( referencedPropertyName != null ) {
// Ensure properties are sorted before we create a foreign key
sortProperties();
PersistentClass pc = persistentClasses.get(getReferencedEntityName() );
PersistentClass pc = persistentClasses.get( getReferencedEntityName() );
Property property = pc.getReferencedProperty( getReferencedPropertyName() );

View File

@ -886,7 +886,7 @@ public abstract class SimpleValue implements KeyValue {
this.typeParameters = parameterMap;
}
public void setTypeParameters(Map<String, String> parameters) {
public void setTypeParameters(Map<String, ? extends Object> parameters) {
if ( parameters != null ) {
Properties properties = new Properties();
properties.putAll( parameters );

View File

@ -185,7 +185,7 @@ public class ManyToManyImplicitNamingTest extends BaseNonConfigCoreFunctionalTes
);
// The default owner and inverse join columns can only be computed if they have PK with 1 column.
assertEquals ( 1, ownerCollection.getOwner().getKey().getColumnSpan() );
assertEquals( ownerForeignKeyNameExpected, ownerCollection.getKey().getColumnIterator().next().getText() );
assertEquals( ownerForeignKeyNameExpected, ownerCollection.getKey().getColumns().get(0).getText() );
final EntityType associatedEntityType = (EntityType) ownerCollection.getElement().getType();
final PersistentClass associatedPersistentClass =