diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ObjectNameNormalizer.java b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ObjectNameNormalizer.java index a0a2cae8e3..ba9befc993 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ObjectNameNormalizer.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/naming/ObjectNameNormalizer.java @@ -58,10 +58,7 @@ public abstract class ObjectNameNormalizer { */ public String normalizeIdentifierQuotingAsString(String identifierText) { final Identifier identifier = normalizeIdentifierQuoting( identifierText ); - if ( identifier == null ) { - return null; - } - return identifier.render( database().getDialect() ); + return identifier == null ? null : identifier.render( database().getDialect() ); } public String toDatabaseIdentifierText(String identifierText) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java index a1aaeeed21..0044f62436 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedColumn.java @@ -40,6 +40,7 @@ import org.jboss.logging.Logger; import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation; import static org.hibernate.cfg.BinderHelper.getPath; import static org.hibernate.cfg.BinderHelper.getRelativePath; +import static org.hibernate.internal.util.StringHelper.isEmpty; import static org.hibernate.internal.util.StringHelper.isNotEmpty; /** @@ -310,7 +311,7 @@ public class AnnotatedColumn { } public boolean isNameDeferred() { - return mappingColumn == null || StringHelper.isEmpty( mappingColumn.getName() ); + return mappingColumn == null || isEmpty( mappingColumn.getName() ); } public void redefineColumnName(String columnName, String propertyName, boolean applyNamingStrategy) { @@ -655,7 +656,7 @@ public class AnnotatedColumn { } public static AnnotatedColumn[] buildColumnsOrFormulaFromAnnotation( - jakarta.persistence.Column[] columnAnns, + jakarta.persistence.Column[] columns, org.hibernate.annotations.Formula formulaAnn, Comment comment, Nullability nullability, @@ -675,7 +676,7 @@ public class AnnotatedColumn { return new AnnotatedColumn[] { formulaColumn }; } else { - jakarta.persistence.Column[] actualCols = overrideColumns( columnAnns, propertyHolder, inferredData); + jakarta.persistence.Column[] actualCols = overrideColumns( columns, propertyHolder, inferredData); if ( actualCols == null ) { return buildImplicitColumn( inferredData, @@ -702,7 +703,7 @@ public class AnnotatedColumn { } private static jakarta.persistence.Column[] overrideColumns( - jakarta.persistence.Column[] columnAnns, + jakarta.persistence.Column[] columns, PropertyHolder propertyHolder, PropertyData inferredData ) { final jakarta.persistence.Column[] overriddenCols = propertyHolder.getOverriddenColumn( @@ -710,11 +711,11 @@ public class AnnotatedColumn { ); if ( overriddenCols != null ) { //check for overridden first - if ( columnAnns != null && overriddenCols.length != columnAnns.length ) { + if ( columns != null && overriddenCols.length != columns.length ) { //TODO: unfortunately, we never actually see this nice error message, since // PersistentClass.validate() gets called first and produces a worse message throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData ) - + "' specifies " + columnAnns.length + + "' specifies " + columns.length + " '@AttributeOverride's but the overridden property has " + overriddenCols.length + " columns (every column must have exactly one '@AttributeOverride')" ); } @@ -722,7 +723,7 @@ public class AnnotatedColumn { return overriddenCols.length == 0 ? null : overriddenCols; } else { - return columnAnns; + return columns; } } @@ -741,7 +742,7 @@ public class AnnotatedColumn { final Database database = context.getMetadataCollector().getDatabase(); final String sqlType = column.columnDefinition().isEmpty() ? null : context.getObjectNameNormalizer().applyGlobalQuoting( column.columnDefinition() ); - final String tableName = StringHelper.isEmpty( column.table() ) ? "" + final String tableName = isEmpty( column.table() ) ? "" : database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( column.table() ).render(); // final Identifier logicalName = database.getJdbcEnvironment() // .getIdentifierHelper() @@ -813,7 +814,7 @@ public class AnnotatedColumn { final String columnName = column.name() != null && column.name().isEmpty() ? null : database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( column.name() ).render(); // NOTE : this is the logical column name, not the physical! - return StringHelper.isEmpty( columnName ) && !StringHelper.isEmpty(suffixForDefaultColumnName) + return isEmpty( columnName ) && isNotEmpty( suffixForDefaultColumnName ) ? inferredData.getPropertyName() + suffixForDefaultColumnName : columnName; } @@ -823,16 +824,14 @@ public class AnnotatedColumn { if ( xProperty != null ) { ColumnDefault columnDefault = getOverridableAnnotation( xProperty, ColumnDefault.class, context ); if ( columnDefault != null ) { - if (length!=1) { + if ( length!=1 ) { throw new MappingException("@ColumnDefault may only be applied to single-column mappings"); } setDefaultValue( columnDefault.value() ); } } else { - LOG.trace( - "Could not perform @ColumnDefault lookup as 'PropertyData' did not give access to XProperty" - ); + LOG.trace("Could not perform @ColumnDefault lookup as 'PropertyData' did not give access to XProperty"); } } @@ -848,9 +847,7 @@ public class AnnotatedColumn { } } else { - LOG.trace( - "Could not perform @GeneratedColumn lookup as 'PropertyData' did not give access to XProperty" - ); + LOG.trace("Could not perform @GeneratedColumn lookup as 'PropertyData' did not give access to XProperty"); } } @@ -866,9 +863,7 @@ public class AnnotatedColumn { } } else { - LOG.trace( - "Could not perform @Check lookup as 'PropertyData' did not give access to XProperty" - ); + LOG.trace("Could not perform @Check lookup as 'PropertyData' did not give access to XProperty"); } } @@ -893,7 +888,7 @@ public class AnnotatedColumn { private void processColumnTransformerExpressions(ColumnTransformer annotation) { if ( annotation != null ) { - if ( StringHelper.isEmpty( annotation.forColumn() ) + if ( isEmpty( annotation.forColumn() ) // "" is the default value for annotations || annotation.forColumn().equals( logicalColumnName != null ? logicalColumnName : "" ) ) { readExpression = StringHelper.nullIfEmpty( annotation.read() ); @@ -947,7 +942,7 @@ public class AnnotatedColumn { column.setJoins(secondaryTables); column.setBuildingContext(context); // property name + suffix is an "explicit" column name - boolean implicit = StringHelper.isEmpty( suffixForDefaultColumnName ); + boolean implicit = isEmpty( suffixForDefaultColumnName ); if ( !implicit ) { column.setLogicalColumnName( propertyName + suffixForDefaultColumnName ); } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java index 04195be0e0..832c46139a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedJoinColumn.java @@ -43,6 +43,10 @@ import org.hibernate.mapping.Value; import static org.hibernate.cfg.BinderHelper.findColumnOwner; import static org.hibernate.cfg.BinderHelper.getRelativePath; 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.qualify; /** * A {@link jakarta.persistence.JoinColumn} annotation @@ -90,46 +94,13 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { this.mappedBy = mappedBy; } - //Due to @AnnotationOverride overriding rules, I don't want the constructor to be public - private AnnotatedJoinColumn() { - setMappedBy(""); + public boolean hasMappedBy() { + return isNotEmpty( mappedBy ); } //Due to @AnnotationOverride overriding rules, I don't want the constructor to be public - //TODO get rid of it and use setters - private AnnotatedJoinColumn( - String sqlType, - String name, - String comment, - boolean nullable, - boolean unique, - boolean insertable, - boolean updatable, - String referencedColumn, - String secondaryTable, - Map joins, - PropertyHolder propertyHolder, - String propertyName, - String mappedBy, - boolean isImplicit, - MetadataBuildingContext buildingContext) { - super(); - setImplicit( isImplicit ); - setSqlType( sqlType ); - setLogicalColumnName( name ); - setComment( comment ); - setNullable( nullable ); - setUnique( unique ); - setInsertable( insertable ); - setUpdatable( updatable ); - setExplicitTableName( secondaryTable ); - setPropertyHolder( propertyHolder ); - setJoins( joins ); - setBuildingContext( buildingContext ); - setPropertyName( getRelativePath( propertyHolder, propertyName ) ); - bind(); - this.referencedColumn = referencedColumn; - this.mappedBy = mappedBy; + private AnnotatedJoinColumn() { + setMappedBy(""); } public String getReferencedColumn() { @@ -137,44 +108,55 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { } public static AnnotatedJoinColumn[] buildJoinColumnsOrFormulas( - JoinColumnOrFormula[] anns, + JoinColumnOrFormula[] joinColumnOrFormulas, String mappedBy, Map joins, PropertyHolder propertyHolder, String propertyName, MetadataBuildingContext buildingContext) { - AnnotatedJoinColumn[] joinColumns = new AnnotatedJoinColumn[anns.length]; - for (int i = 0; i < anns.length; i++) { - JoinColumnOrFormula join = anns[i]; - JoinFormula formula = join.formula(); + 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, mappedBy, joins, propertyHolder, propertyName, buildingContext - ); + joinColumns[i] = buildJoinFormula( formula, joins, propertyHolder, propertyName, buildingContext ); } else { - joinColumns[i] = buildJoinColumns( - new JoinColumn[] { join.column() }, null, mappedBy, joins, propertyHolder, propertyName, buildingContext - )[0]; + joinColumns[i] = buildJoinColumn( mappedBy, joins, propertyHolder, propertyName, buildingContext, columnOrFormula ); } } - return joinColumns; } + private static AnnotatedJoinColumn buildJoinColumn( + String mappedBy, + Map joins, + PropertyHolder propertyHolder, + String propertyName, MetadataBuildingContext buildingContext, + JoinColumnOrFormula join) { + return buildJoinColumns( + new JoinColumn[]{ join.column() }, + null, + mappedBy, + joins, + propertyHolder, + propertyName, + buildingContext + )[0]; + } + /** * build join formula */ public static AnnotatedJoinColumn buildJoinFormula( - JoinFormula ann, - String mappedBy, + JoinFormula joinFormula, Map joins, PropertyHolder propertyHolder, String propertyName, MetadataBuildingContext buildingContext) { AnnotatedJoinColumn formulaColumn = new AnnotatedJoinColumn(); - formulaColumn.setFormula( ann.value() ); - formulaColumn.setReferencedColumn(ann.referencedColumnName()); + formulaColumn.setFormula( joinFormula.value() ); + formulaColumn.setReferencedColumn(joinFormula.referencedColumnName()); formulaColumn.setBuildingContext( buildingContext ); formulaColumn.setPropertyHolder( propertyHolder ); formulaColumn.setJoins( joins ); @@ -184,7 +166,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { } public static AnnotatedJoinColumn[] buildJoinColumns( - JoinColumn[] anns, + JoinColumn[] joinColumns, Comment comment, String mappedBy, Map joins, @@ -192,12 +174,19 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { String propertyName, MetadataBuildingContext buildingContext) { return buildJoinColumnsWithDefaultColumnSuffix( - anns, comment, mappedBy, joins, propertyHolder, propertyName, "", buildingContext + joinColumns, + comment, + mappedBy, + joins, + propertyHolder, + propertyName, + "", + buildingContext ); } public static AnnotatedJoinColumn[] buildJoinColumnsWithDefaultColumnSuffix( - JoinColumn[] anns, + JoinColumn[] joinColumns, Comment comment, String mappedBy, Map joins, @@ -205,10 +194,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { String propertyName, String suffixForDefaultColumnName, MetadataBuildingContext buildingContext) { - JoinColumn[] actualColumns = propertyHolder.getOverriddenJoinColumn( - StringHelper.qualify( propertyHolder.getPath(), propertyName ) - ); - if ( actualColumns == null ) actualColumns = anns; + JoinColumn[] overriddes = propertyHolder.getOverriddenJoinColumn( qualify( propertyHolder.getPath(), propertyName ) ); + JoinColumn[] actualColumns = overriddes == null ? joinColumns : overriddes; if ( actualColumns == null || actualColumns.length == 0 ) { return new AnnotatedJoinColumn[] { buildJoinColumn( @@ -224,9 +211,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { }; } else { - int size = actualColumns.length; - AnnotatedJoinColumn[] result = new AnnotatedJoinColumn[size]; - for (int index = 0; index < size; index++) { + final AnnotatedJoinColumn[] result = new AnnotatedJoinColumn[actualColumns.length]; + for (int index = 0; index < actualColumns.length; index++ ) { result[index] = buildJoinColumn( actualColumns[index], comment, @@ -246,88 +232,84 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { * build join column for SecondaryTables */ private static AnnotatedJoinColumn buildJoinColumn( - JoinColumn ann, + JoinColumn joinColumn, Comment comment, String mappedBy, Map joins, PropertyHolder propertyHolder, String propertyName, String suffixForDefaultColumnName, MetadataBuildingContext buildingContext) { - if ( ann != null ) { - if ( !BinderHelper.isEmptyOrNullAnnotationValue( mappedBy ) ) { + if ( joinColumn != null ) { + if ( !isEmptyOrNullAnnotationValue( mappedBy ) ) { throw new AnnotationException( "Association '" + getRelativePath( propertyHolder, propertyName ) + "' is 'mappedBy' a different entity and may not explicitly specify the '@JoinColumn'" ); } - AnnotatedJoinColumn joinColumn = new AnnotatedJoinColumn(); - joinColumn.setComment( comment != null ? comment.value() : null ); - joinColumn.setBuildingContext( buildingContext ); - joinColumn.setJoinAnnotation( ann, null ); - if ( StringHelper.isEmpty( joinColumn.getLogicalColumnName() ) - && ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) { - joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName ); + 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 ); } - joinColumn.setJoins( joins ); - joinColumn.setPropertyHolder( propertyHolder ); - joinColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); - joinColumn.setImplicit( false ); - joinColumn.bind(); - return joinColumn; + column.setJoins( joins ); + column.setPropertyHolder( propertyHolder ); + column.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + column.setImplicit( false ); + column.bind(); + return column; } else { - AnnotatedJoinColumn joinColumn = new AnnotatedJoinColumn(); - joinColumn.setMappedBy( mappedBy ); - joinColumn.setJoins( joins ); - joinColumn.setPropertyHolder( propertyHolder ); - joinColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + 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 ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) { - joinColumn.setLogicalColumnName( propertyName + suffixForDefaultColumnName ); - joinColumn.setImplicit( false ); + if ( isNotEmpty( suffixForDefaultColumnName ) ) { + column.setLogicalColumnName( propertyName + suffixForDefaultColumnName ); + column.setImplicit( false ); } else { - joinColumn.setImplicit( true ); + column.setImplicit( true ); } - joinColumn.setBuildingContext( buildingContext ); - joinColumn.bind(); - return joinColumn; + column.setBuildingContext( buildingContext ); + column.bind(); + return column; } } // TODO default name still useful in association table - public void setJoinAnnotation(JoinColumn annJoin, String defaultName) { - if ( annJoin == null ) { + public void setJoinAnnotation(JoinColumn joinColumn, String defaultName) { + if ( joinColumn == null ) { setImplicit( true ); } else { setImplicit( false ); - if ( !isEmptyAnnotationValue( annJoin.columnDefinition() ) ) { - setSqlType( getBuildingContext().getObjectNameNormalizer().applyGlobalQuoting( annJoin.columnDefinition() ) ); + if ( !isEmptyAnnotationValue( joinColumn.columnDefinition() ) ) { + setSqlType( getBuildingContext().getObjectNameNormalizer().applyGlobalQuoting( joinColumn.columnDefinition() ) ); } - if ( !isEmptyAnnotationValue( annJoin.name() ) ) { - setLogicalColumnName( annJoin.name() ); + if ( !isEmptyAnnotationValue( joinColumn.name() ) ) { + setLogicalColumnName( joinColumn.name() ); } - setNullable( annJoin.nullable() ); - setUnique( annJoin.unique() ); - setInsertable( annJoin.insertable() ); - setUpdatable( annJoin.updatable() ); - setReferencedColumn( annJoin.referencedColumnName() ); + setNullable( joinColumn.nullable() ); + setUnique( joinColumn.unique() ); + setInsertable( joinColumn.insertable() ); + setUpdatable( joinColumn.updatable() ); + setReferencedColumn( joinColumn.referencedColumnName() ); - if ( isEmptyAnnotationValue( annJoin.table() ) ) { + if ( isEmptyAnnotationValue( joinColumn.table() ) ) { setExplicitTableName( "" ); } else { - final Identifier logicalIdentifier = getBuildingContext().getMetadataCollector() - .getDatabase() - .toIdentifier( annJoin.table() ); + final Database database = getBuildingContext().getMetadataCollector().getDatabase(); + final Identifier logicalIdentifier = database.toIdentifier( joinColumn.table() ); final Identifier physicalIdentifier = getBuildingContext().getBuildingOptions() .getPhysicalNamingStrategy() - .toPhysicalTableName( logicalIdentifier, getBuildingContext().getMetadataCollector().getDatabase().getJdbcEnvironment() ); - setExplicitTableName( - physicalIdentifier.render( getBuildingContext().getMetadataCollector().getDatabase().getDialect() ) - ); + .toPhysicalTableName( logicalIdentifier, database.getJdbcEnvironment() ); + setExplicitTableName( physicalIdentifier.render( database.getDialect() ) ); } } } @@ -336,73 +318,60 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { * Build JoinColumn for a JOINED hierarchy */ public static AnnotatedJoinColumn buildJoinColumn( - PrimaryKeyJoinColumn pkJoinAnn, - JoinColumn joinAnn, + PrimaryKeyJoinColumn primaryKeyJoinColumn, + JoinColumn joinColumn, Value identifier, Map joins, PropertyHolder propertyHolder, MetadataBuildingContext context) { - final String defaultName = context.getMetadataCollector().getLogicalColumnName( + final String defaultColumnName = context.getMetadataCollector().getLogicalColumnName( identifier.getTable(), identifier.getColumns().get(0).getQuotedName() ); final ObjectNameNormalizer normalizer = context.getObjectNameNormalizer(); - if ( pkJoinAnn != null || joinAnn != null ) { - String colName; - String columnDefinition; - String referencedColumnName; - if ( pkJoinAnn != null ) { - colName = pkJoinAnn.name(); - columnDefinition = pkJoinAnn.columnDefinition(); - referencedColumnName = pkJoinAnn.referencedColumnName(); + 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 { - colName = joinAnn.name(); - columnDefinition = joinAnn.columnDefinition(); - referencedColumnName = joinAnn.referencedColumnName(); + columnName = joinColumn.name(); + columnDefinition = joinColumn.columnDefinition(); + referencedColumnName = joinColumn.referencedColumnName(); } - return new AnnotatedJoinColumn( - columnDefinition.isEmpty() - ? null - : normalizer.toDatabaseIdentifierText( columnDefinition ), - colName != null && colName.isEmpty() - ? normalizer.normalizeIdentifierQuotingAsString( defaultName ) - : normalizer.normalizeIdentifierQuotingAsString( colName ), - null, - false, - false, - true, - true, - referencedColumnName, - null, - joins, - propertyHolder, - null, - null, - false, - context - ); + 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 { - return new AnnotatedJoinColumn( - null, - normalizer.normalizeIdentifierQuotingAsString( defaultName ), - null, - false, - false, - true, - true, - null, - null, - joins, - propertyHolder, - null, - null, - true, - context - ); + 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; } } @@ -737,31 +706,25 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { @Override protected void addColumnBinding(SimpleValue value) { - if ( StringHelper.isEmpty( mappedBy ) ) { + if ( isEmpty( mappedBy ) ) { // 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 ); - - String logicalCollectionColumnName = StringHelper.isNotEmpty( unquotedLogColName ) + final String collectionColName = isNotEmpty( unquotedLogColName ) ? unquotedLogColName : getPropertyName() + '_' + unquotedRefColumn; - logicalCollectionColumnName = getBuildingContext().getMetadataCollector() - .getDatabase() + final InFlightMetadataCollector collector = getBuildingContext().getMetadataCollector(); + final String logicalCollectionColumnName = collector.getDatabase() .getJdbcEnvironment() .getIdentifierHelper() - .toIdentifier( logicalCollectionColumnName, isLogicalColumnQuoted ) + .toIdentifier( collectionColName, isLogicalColumnQuoted ) .render(); - getBuildingContext().getMetadataCollector().addColumnNameBinding( - value.getTable(), - logicalCollectionColumnName, - getMappingColumn() - ); + collector.addColumnNameBinding( value.getTable(), logicalCollectionColumnName, getMappingColumn() ); } } @@ -802,7 +765,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { boolean explicitColumnReference = false; for ( AnnotatedJoinColumn column : columns ) { final String logicalReferencedColumnName = column.getReferencedColumn(); - if ( StringHelper.isNotEmpty( logicalReferencedColumnName ) ) { + if ( isNotEmpty( logicalReferencedColumnName ) ) { explicitColumnReference = true; if ( !keyColumns.contains( column( context, table, logicalReferencedColumnName ) ) ) { // we have a column which does not belong to the PK @@ -844,7 +807,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { if ( mappingColumn != null ) { // columnDefinition can also be specified using @JoinColumn, hence we have to check // whether it is set or not - if ( StringHelper.isEmpty( sqlType ) ) { + if ( isEmpty( sqlType ) ) { sqlType = column.getSqlType(); mappingColumn.setSqlType( sqlType ); } @@ -862,46 +825,44 @@ public class AnnotatedJoinColumn extends AnnotatedColumn { } public static AnnotatedJoinColumn[] buildJoinTableJoinColumns( - JoinColumn[] annJoins, + JoinColumn[] joinColumns, Map secondaryTables, PropertyHolder propertyHolder, String propertyName, String mappedBy, MetadataBuildingContext buildingContext) { - AnnotatedJoinColumn[] joinColumns; - if ( annJoins == null ) { - AnnotatedJoinColumn currentJoinColumn = new AnnotatedJoinColumn(); - currentJoinColumn.setImplicit( true ); - currentJoinColumn.setNullable( false ); //I break the spec, but it's for good - currentJoinColumn.setPropertyHolder( propertyHolder ); - currentJoinColumn.setJoins( secondaryTables ); - currentJoinColumn.setBuildingContext( buildingContext ); - currentJoinColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); - currentJoinColumn.setMappedBy( mappedBy ); - currentJoinColumn.bind(); - joinColumns = new AnnotatedJoinColumn[] { currentJoinColumn }; + 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.setJoins( secondaryTables ); + column.setBuildingContext( buildingContext ); + column.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + column.setMappedBy( mappedBy ); + column.bind(); + return new AnnotatedJoinColumn[] { column }; } else { - joinColumns = new AnnotatedJoinColumn[annJoins.length]; - JoinColumn annJoin; - int length = annJoins.length; + final AnnotatedJoinColumn[] columns = new AnnotatedJoinColumn[joinColumns.length]; + int length = joinColumns.length; for (int index = 0; index < length; index++) { - annJoin = annJoins[index]; - AnnotatedJoinColumn currentJoinColumn = new AnnotatedJoinColumn(); - currentJoinColumn.setImplicit( true ); - currentJoinColumn.setPropertyHolder( propertyHolder ); - currentJoinColumn.setJoins( secondaryTables ); - currentJoinColumn.setBuildingContext( buildingContext ); - currentJoinColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); - currentJoinColumn.setMappedBy( mappedBy ); - currentJoinColumn.setJoinAnnotation( annJoin, propertyName ); - currentJoinColumn.setNullable( false ); //I break the spec, but it's for good + final JoinColumn joinColumn = joinColumns[index]; + final AnnotatedJoinColumn column = new AnnotatedJoinColumn(); + column.setImplicit( true ); + column.setPropertyHolder( propertyHolder ); + column.setJoins( secondaryTables ); + column.setBuildingContext( buildingContext ); + column.setPropertyName( getRelativePath( propertyHolder, propertyName ) ); + 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 - currentJoinColumn.bind(); - joinColumns[index] = currentJoinColumn; + column.bind(); + columns[index] = column; } + return columns; } - return joinColumns; } public void setMappedBy(String entityName, String jpaEntityName, String logicalTableName, String mappedByProperty) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 2d2f9f8a6a..e9391b0357 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -178,26 +178,12 @@ import static org.hibernate.internal.util.StringHelper.nullIfEmpty; import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY; /** - * JSR 175 annotation binder which reads the annotations from classes, applies the - * principles of the EJB3 spec and produces the Hibernate configuration-time metamodel - * (the classes in the {@link org.hibernate.mapping} package) - *

- * Some design description - * I tried to remove any link to annotation except from the 2 first level of - * method call. - * It'll enable to: - * - facilitate annotation overriding - * - mutualize one day xml and annotation binder (probably a dream though) - * - split this huge class in smaller mapping oriented classes - * - * bindSomething usually create the mapping container and is accessed by one of the 2 first level method - * makeSomething usually create the mapping container and is accessed by bindSomething[else] - * fillSomething take the container into parameter and fill it. + * Reads annotations from Java classes and produces the Hibernate configuration-time metamodel, + * that is, the objects defined in the package {@link org.hibernate.mapping}. * * @author Emmanuel Bernard * @author Hardy Ferentschik */ -@SuppressWarnings("deprecation") public final class AnnotationBinder { private static final CoreMessageLogger LOG = messageLogger( AnnotationBinder.class ); @@ -1262,8 +1248,7 @@ public final class AnnotationBinder { columnsBuilder, columns, returnedClass, - propertyBinder, - isId + propertyBinder ); } } @@ -1349,18 +1334,16 @@ public final class AnnotationBinder { ColumnsBuilder columnsBuilder, AnnotatedColumn[] columns, XClass returnedClass, - PropertyBinder propertyBinder, - boolean isId) { - //define whether the type is a component or not + PropertyBinder propertyBinder) { - boolean isComponent = false; - - //Overrides from @MapsId if needed - boolean isOverridden = false; - if ( isId || propertyHolder.isOrWithinEmbeddedId() || propertyHolder.isInIdClass() ) { - //the associated entity could be using an @IdClass making the overridden property a component - PropertyData overridingProperty = getPropertyOverriddenByMapperOrMapsId( - isId, + // overrides from @MapsId or @IdClass if needed + final boolean isComposite; + final boolean isOverridden; + final AnnotatedColumn[] actualColumns; + if ( propertyBinder.isId() || propertyHolder.isOrWithinEmbeddedId() || propertyHolder.isInIdClass() ) { + // the associated entity could be using an @IdClass making the overridden property a component + final PropertyData overridingProperty = getPropertyOverriddenByMapperOrMapsId( + propertyBinder.isId(), propertyHolder, property.getName(), context @@ -1368,143 +1351,210 @@ public final class AnnotationBinder { if ( overridingProperty != null ) { isOverridden = true; final InheritanceState state = inheritanceStatePerClass.get( overridingProperty.getClassOrElement() ); - if ( state != null ) { - isComponent = state.hasIdClassOrEmbeddedId(); - } + isComposite = state != null ? state.hasIdClassOrEmbeddedId() : isEmbedded( property, returnedClass ); //Get the new column - columns = columnsBuilder.overrideColumnFromMapperOrMapsIdProperty(isId); - } - } - - isComponent = isComponent || isEmbedded( property, returnedClass ); - final Class> compositeUserType = resolveCompositeUserType( - inferredData.getProperty(), - inferredData.getClassOrElement(), - context - ); - - if ( isComponent || compositeUserType != null ) { - final String referencedEntityName; - final String propertyName; - if ( isOverridden ) { - // careful: not always a @MapsId property, sometimes it's from an @IdClass - PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( - isId, propertyHolder, property.getName(), context - ); - referencedEntityName = mapsIdProperty.getClassOrElementName(); - propertyName = mapsIdProperty.getPropertyName(); + actualColumns = columnsBuilder.overrideColumnFromMapperOrMapsIdProperty( propertyBinder.isId() ); } else { - referencedEntityName = null; - propertyName = null; + isOverridden = false; + isComposite = isEmbedded( property, returnedClass ); + actualColumns = columns; } + } + else { + isOverridden = false; + isComposite = isEmbedded( property, returnedClass ); + actualColumns = columns; + } - propertyBinder = bindComponent( - inferredData, + final Class> compositeUserType = resolveCompositeUserType( inferredData, context ); + + if ( isComposite || compositeUserType != null ) { + propertyBinder = createCompositeBinder( propertyHolder, - entityBinder.getPropertyAccessor(property), + inferredData, entityBinder, isIdentifierMapper, - context, isComponentEmbedded, - isId, + context, inheritanceStatePerClass, - referencedEntityName, - propertyName, - determineCustomInstantiator( property, returnedClass, context ), - compositeUserType, - isOverridden ? (AnnotatedJoinColumn[]) columns : null + property, + actualColumns, + returnedClass, + propertyBinder, + isOverridden, + compositeUserType ); } else { - //provide the basic property mapping - boolean optional = true; - boolean lazy = false; - if ( property.isAnnotationPresent( Basic.class ) ) { - Basic ann = property.getAnnotation( Basic.class ); - optional = ann.optional(); - lazy = ann.fetch() == FetchType.LAZY; - } - //implicit type will check basic types and Serializable classes - if ( isId || !optional && nullability != Nullability.FORCED_NULL ) { - //force columns to not null - for ( AnnotatedColumn col : columns) { - if ( isId && col.isFormula() ) { - throw new CannotForceNonNullableException( - String.format( - Locale.ROOT, - "Identifier property [%s] cannot contain formula mapping [%s]", - HCANNHelper.annotatedElementSignature(property), - col.getFormulaString() - ) - ); - } - col.forceNotNull(); - } - } - - propertyBinder.setLazy( lazy ); - propertyBinder.setColumns(columns); - if ( isOverridden ) { - final PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( - isId, propertyHolder, property.getName(), context - ); - propertyBinder.setReferencedEntityName( mapsIdProperty.getClassOrElementName() ); - } - - propertyBinder.makePropertyValueAndBind(); - + createBasicBinder( + propertyHolder, + inferredData, + nullability, + context, + property, + actualColumns, + propertyBinder, + isOverridden + ); } if ( isOverridden ) { - final PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( - isId, propertyHolder, property.getName(), context + handleGeneratorsForOverriddenId( + propertyHolder, + classGenerators, + context, + property, + propertyBinder ); - final IdentifierGeneratorDefinition.Builder foreignGeneratorBuilder = - new IdentifierGeneratorDefinition.Builder(); - foreignGeneratorBuilder.setName( "Hibernate-local--foreign generator" ); - foreignGeneratorBuilder.setStrategy( "foreign" ); - foreignGeneratorBuilder.addParam( "property", mapsIdProperty.getPropertyName() ); - final IdentifierGeneratorDefinition foreignGenerator = foreignGeneratorBuilder.build(); - - if ( isGlobalGeneratorNameGlobal( context ) ) { - SecondPass secondPass = new IdGeneratorResolverSecondPass( - (SimpleValue) propertyBinder.getValue(), - property, - foreignGenerator.getStrategy(), - foreignGenerator.getName(), - context, - foreignGenerator - ); - context.getMetadataCollector().addSecondPass( secondPass ); - } - else { - Map localGenerators = new HashMap<>(classGenerators); - localGenerators.put( foreignGenerator.getName(), foreignGenerator ); - - makeIdGenerator( - (SimpleValue) propertyBinder.getValue(), - property, - foreignGenerator.getStrategy(), - foreignGenerator.getName(), - context, - localGenerators - ); - } } - if (isId) { + else if ( propertyBinder.isId() ) { //components and regular basic types create SimpleValue objects - if ( !isOverridden ) { - processId( - propertyHolder, - inferredData, - (SimpleValue) propertyBinder.getValue(), - classGenerators, - isIdentifierMapper, - context - ); + processId( + propertyHolder, + inferredData, + (SimpleValue) propertyBinder.getValue(), + classGenerators, + isIdentifierMapper, + context + ); + } + return actualColumns; + } + + private static void handleGeneratorsForOverriddenId( + PropertyHolder propertyHolder, + Map classGenerators, + MetadataBuildingContext context, + XProperty property, + PropertyBinder propertyBinder) { + final PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( + propertyBinder.isId(), propertyHolder, property.getName(), context + ); + final IdentifierGeneratorDefinition.Builder foreignGeneratorBuilder = + new IdentifierGeneratorDefinition.Builder(); + foreignGeneratorBuilder.setName( "Hibernate-local--foreign generator" ); + foreignGeneratorBuilder.setStrategy( "foreign" ); + foreignGeneratorBuilder.addParam( "property", mapsIdProperty.getPropertyName() ); + final IdentifierGeneratorDefinition foreignGenerator = foreignGeneratorBuilder.build(); + if ( isGlobalGeneratorNameGlobal(context) ) { + SecondPass secondPass = new IdGeneratorResolverSecondPass( + (SimpleValue) propertyBinder.getValue(), + property, + foreignGenerator.getStrategy(), + foreignGenerator.getName(), + context, + foreignGenerator + ); + context.getMetadataCollector().addSecondPass( secondPass ); + } + else { + final Map localGenerators = new HashMap<>(classGenerators); + localGenerators.put( foreignGenerator.getName(), foreignGenerator ); + makeIdGenerator( + (SimpleValue) propertyBinder.getValue(), + property, + foreignGenerator.getStrategy(), + foreignGenerator.getName(), + context, + localGenerators + ); + } + } + + private static void createBasicBinder( + PropertyHolder propertyHolder, + PropertyData inferrredData, + Nullability nullability, + MetadataBuildingContext context, + XProperty property, + AnnotatedColumn[] columns, + PropertyBinder propertyBinder, + boolean isOverridden) { + //provide the basic property mapping + final boolean optional; + final boolean lazy; + if ( property.isAnnotationPresent( Basic.class ) ) { + Basic ann = property.getAnnotation( Basic.class ); + optional = ann.optional(); + lazy = ann.fetch() == FetchType.LAZY; + } + else { + optional = true; + lazy = false; + } + + //implicit type will check basic types and Serializable classes + if ( propertyBinder.isId() || !optional && nullability != Nullability.FORCED_NULL ) { + //force columns to not null + for ( AnnotatedColumn col : columns ) { + if ( propertyBinder.isId() && col.isFormula() ) { + throw new CannotForceNonNullableException( "Identifier property '" + + getPath( propertyHolder, inferrredData ) + "' cannot map to a '@Formula'" ); + } + col.forceNotNull(); } } - return columns; + + propertyBinder.setLazy( lazy ); + propertyBinder.setColumns( columns ); + if (isOverridden) { + final PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( + propertyBinder.isId(), propertyHolder, property.getName(), context + ); + propertyBinder.setReferencedEntityName( mapsIdProperty.getClassOrElementName() ); + } + + propertyBinder.makePropertyValueAndBind(); + } + + private static PropertyBinder createCompositeBinder( + PropertyHolder propertyHolder, + PropertyData inferredData, + EntityBinder entityBinder, + boolean isIdentifierMapper, + boolean isComponentEmbedded, + MetadataBuildingContext context, + Map inheritanceStatePerClass, + XProperty property, + AnnotatedColumn[] columns, + XClass returnedClass, + PropertyBinder propertyBinder, + boolean isOverridden, + Class> compositeUserType) { + final String referencedEntityName; + final String propertyName; + final AnnotatedJoinColumn[] actualColumns; + if ( isOverridden ) { + // careful: not always a @MapsId property, sometimes it's from an @IdClass + PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId( + propertyBinder.isId(), propertyHolder, property.getName(), context + ); + referencedEntityName = mapsIdProperty.getClassOrElementName(); + propertyName = mapsIdProperty.getPropertyName(); + actualColumns = (AnnotatedJoinColumn[]) columns; + } + else { + referencedEntityName = null; + propertyName = null; + actualColumns = null; + } + + return bindComponent( + inferredData, + propertyHolder, + entityBinder.getPropertyAccessor( property ), + entityBinder, + isIdentifierMapper, + context, + isComponentEmbedded, + propertyBinder.isId(), + inheritanceStatePerClass, + referencedEntityName, + propertyName, + determineCustomInstantiator( property, returnedClass, context ), + compositeUserType, + actualColumns + ); } private static boolean isEmbedded(XProperty property, XClass returnedClass) { @@ -1759,9 +1809,11 @@ public final class AnnotationBinder { } private static Class> resolveCompositeUserType( - XProperty property, - XClass returnedClass, + PropertyData inferredData, MetadataBuildingContext context) { + final XProperty property = inferredData.getProperty(); + final XClass returnedClass = inferredData.getClassOrElement(); + if ( property != null ) { final CompositeType compositeType = property.getAnnotation( CompositeType.class ); if ( compositeType != null ) { @@ -1941,9 +1993,9 @@ public final class AnnotationBinder { Class customInstantiatorImpl, Class> compositeUserTypeClass, AnnotatedJoinColumn[] columns) { - Component comp; + final Component component; if ( referencedEntityName != null ) { - comp = createComponent( + component = createComponent( propertyHolder, inferredData, isComponentEmbedded, @@ -1952,7 +2004,7 @@ public final class AnnotationBinder { buildingContext ); SecondPass sp = new CopyIdentifierComponentSecondPass( - comp, + component, referencedEntityName, propertyName, columns, @@ -1961,7 +2013,7 @@ public final class AnnotationBinder { buildingContext.getMetadataCollector().addSecondPass( sp ); } else { - comp = fillComponent( + component = fillComponent( propertyHolder, inferredData, propertyAccessor, @@ -1977,18 +2029,18 @@ public final class AnnotationBinder { ); } if ( isId ) { - comp.setKey( true ); + component.setKey( true ); if ( propertyHolder.getPersistentClass().getIdentifier() != null ) { throw new AnnotationException( - "Embeddable class '" + comp.getComponentClassName() + "Embeddable class '" + component.getComponentClassName() + "' may not have a property annotated '@Id' since it is used by '" + getPath( propertyHolder, inferredData ) + "' as an '@EmbeddedId'" ); } - if ( referencedEntityName == null && comp.getPropertySpan() == 0 ) { + if ( referencedEntityName == null && component.getPropertySpan() == 0 ) { throw new AnnotationException( - "Embeddable class '" + comp.getComponentClassName() + "Embeddable class '" + component.getComponentClassName() + "' may not be used as an '@EmbeddedId' by '" + getPath( propertyHolder, inferredData ) + "' because it has no properties" @@ -1996,9 +2048,9 @@ public final class AnnotationBinder { } } PropertyBinder binder = new PropertyBinder(); - binder.setDeclaringClass(inferredData.getDeclaringClass()); + binder.setDeclaringClass( inferredData.getDeclaringClass() ); binder.setName( inferredData.getPropertyName() ); - binder.setValue( comp ); + binder.setValue( component ); binder.setProperty( inferredData.getProperty() ); binder.setAccessType( inferredData.getDefaultAccess() ); binder.setEmbedded( isComponentEmbedded ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java index 86b814d236..e709b4cabf 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -163,6 +163,7 @@ public class BinderHelper { //associated entity only used for more precise exception PersistentClass associatedEntity, Value value, + //true when we do the reverse side of a @ManyToMany boolean inverse, MetadataBuildingContext context) { @@ -173,7 +174,7 @@ public class BinderHelper { final AnnotatedJoinColumn firstColumn = columns[0]; if ( !firstColumn.isImplicit() // only necessary for owning side of association - && isEmpty( firstColumn.getMappedBy() ) + && !firstColumn.hasMappedBy() // not necessary for a primary key reference && checkReferencedColumnsType( columns, ownerEntity, context ) == NON_PK_REFERENCE ) { @@ -301,7 +302,7 @@ public class BinderHelper { String syntheticPropertyName = "_" + associatedClass.getEntityName().replace('.', '_') + "_" + propertyName.replace('.', '_'); - if (inverse) { + if ( inverse ) { // Use a different name for inverse synthetic properties to avoid duplicate properties for self-referencing models syntheticPropertyName += "_inverse"; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/CannotForceNonNullableException.java b/hibernate-core/src/main/java/org/hibernate/cfg/CannotForceNonNullableException.java index aaaeca095b..a5726389f4 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/CannotForceNonNullableException.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/CannotForceNonNullableException.java @@ -9,10 +9,10 @@ package org.hibernate.cfg; import org.hibernate.AnnotationException; /** - * Indicates an internal attempt to mark a column as non-nullable (because its part - * of a PK, etc) but we cannot force that column to be non-nullable. - *

- * Typically this indicates that the "column" is actually a formula. + * Indicates an internal attempt to mark a column as non-nullable + * (because it's part of a primary key, for example) when we cannot + * force that column to be non-nullable. This usually indicates that + * the "column" is actually a formula. * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java b/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java index fce30dbdf3..aa7194aecb 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ColumnsBuilder.java @@ -79,7 +79,7 @@ class ColumnsBuilder { public ColumnsBuilder extractMetadata() { columns = null; - joinColumns = buildExplicitJoinColumns(property, inferredData); + joinColumns = buildExplicitJoinColumns( property, inferredData ); Comment comment = property.getAnnotation(Comment.class); @@ -164,7 +164,7 @@ class ColumnsBuilder { return this; } - AnnotatedJoinColumn[] buildDefaultJoinColumnsForXToOne(XProperty property, PropertyData inferredData) { + private AnnotatedJoinColumn[] buildDefaultJoinColumnsForXToOne(XProperty property, PropertyData inferredData) { AnnotatedJoinColumn[] joinColumns; JoinTable joinTableAnn = propertyHolder.getJoinTable( property ); Comment comment = property.getAnnotation(Comment.class); @@ -201,23 +201,10 @@ class ColumnsBuilder { return joinColumns; } - AnnotatedJoinColumn[] buildExplicitJoinColumns(XProperty property, PropertyData inferredData) { - //process @JoinColumn(s) before @Column(s) to handle collection of entities properly - JoinColumn[] joinColumnAnnotations = null; - - if ( property.isAnnotationPresent( JoinColumn.class ) ) { - joinColumnAnnotations = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) }; - } - else if ( property.isAnnotationPresent( JoinColumns.class ) ) { - JoinColumns joinColumnAnnotation = property.getAnnotation( JoinColumns.class ); - joinColumnAnnotations = joinColumnAnnotation.value(); - int length = joinColumnAnnotations.length; - if ( length == 0 ) { - throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData ) - + "' has an empty '@JoinColumns' annotation" ); - } - } + private AnnotatedJoinColumn[] buildExplicitJoinColumns(XProperty property, PropertyData inferredData) { + // process @JoinColumns before @Columns to handle collection of entities properly + final JoinColumn[] joinColumnAnnotations = getJoinColumnAnnotations(property, inferredData); if ( joinColumnAnnotations != null ) { return AnnotatedJoinColumn.buildJoinColumns( joinColumnAnnotations, @@ -230,24 +217,8 @@ class ColumnsBuilder { ); } - JoinColumnOrFormula[] joinColumnOrFormulaAnnotations = null; - - if ( property.isAnnotationPresent( JoinColumnOrFormula.class ) ) { - joinColumnOrFormulaAnnotations = new JoinColumnOrFormula[] { - property.getAnnotation( JoinColumnOrFormula.class ) }; - } - else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) { - JoinColumnsOrFormulas joinColumnsOrFormulasAnnotations = property.getAnnotation( - JoinColumnsOrFormulas.class ); - joinColumnOrFormulaAnnotations = joinColumnsOrFormulasAnnotations.value(); - int length = joinColumnOrFormulaAnnotations.length; - if ( length == 0 ) { - throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData ) - + "' has an empty '@JoinColumnsOrFormulas' annotation" ); - } - } - - if (joinColumnOrFormulaAnnotations != null) { + final JoinColumnOrFormula[] joinColumnOrFormulaAnnotations = joinColumnOrFormulaAnnotations( property, inferredData ); + if ( joinColumnOrFormulaAnnotations != null ) { return AnnotatedJoinColumn.buildJoinColumnsOrFormulas( joinColumnOrFormulaAnnotations, null, @@ -258,12 +229,11 @@ class ColumnsBuilder { ); } - if (property.isAnnotationPresent( JoinFormula.class)) { - JoinFormula ann = getOverridableAnnotation( property, JoinFormula.class, buildingContext ); - AnnotatedJoinColumn[] annotatedJoinColumns = new AnnotatedJoinColumn[1]; + if ( property.isAnnotationPresent( JoinFormula.class) ) { + final JoinFormula joinFormula = getOverridableAnnotation( property, JoinFormula.class, buildingContext ); + final AnnotatedJoinColumn[] annotatedJoinColumns = new AnnotatedJoinColumn[1]; annotatedJoinColumns[0] = AnnotatedJoinColumn.buildJoinFormula( - ann, - null, + joinFormula, entityBinder.getSecondaryTables(), propertyHolder, inferredData.getPropertyName(), @@ -275,29 +245,57 @@ class ColumnsBuilder { return null; } + private JoinColumnOrFormula[] joinColumnOrFormulaAnnotations(XProperty property, PropertyData inferredData) { + if ( property.isAnnotationPresent( JoinColumnOrFormula.class ) ) { + return new JoinColumnOrFormula[] { property.getAnnotation( JoinColumnOrFormula.class ) }; + } + else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) { + JoinColumnsOrFormulas joinColumnsOrFormulasAnnotations = property.getAnnotation( JoinColumnsOrFormulas.class ); + final JoinColumnOrFormula[] joinColumnOrFormulaAnnotations = joinColumnsOrFormulasAnnotations.value(); + if ( joinColumnOrFormulaAnnotations.length == 0 ) { + throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData) + + "' has an empty '@JoinColumnsOrFormulas' annotation" ); + } + return joinColumnOrFormulaAnnotations; + } + else { + return null; + } + } + + private JoinColumn[] getJoinColumnAnnotations(XProperty property, PropertyData inferredData) { + if ( property.isAnnotationPresent( JoinColumn.class ) ) { + return new JoinColumn[] { property.getAnnotation( JoinColumn.class ) }; + } + else if ( property.isAnnotationPresent( JoinColumns.class ) ) { + final JoinColumns joinColumnAnnotation = property.getAnnotation( JoinColumns.class ); + final JoinColumn[] joinColumnAnnotations = joinColumnAnnotation.value(); + if ( joinColumnAnnotations.length == 0 ) { + throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData) + + "' has an empty '@JoinColumns' annotation" ); + } + return joinColumnAnnotations; + } + else { + return null; + } + } + AnnotatedColumn[] overrideColumnFromMapperOrMapsIdProperty(boolean isId) { - AnnotatedColumn[] result = columns; final PropertyData overridingProperty = getPropertyOverriddenByMapperOrMapsId( isId, propertyHolder, property.getName(), buildingContext ); - if ( overridingProperty != null ) { - result = buildExcplicitOrDefaultJoinColumn( overridingProperty ); - } - return result; + return overridingProperty != null ? buildExplicitOrDefaultJoinColumn( overridingProperty ) : columns; } /** * useful to override a column either by @MapsId or by @IdClass */ - AnnotatedColumn[] buildExcplicitOrDefaultJoinColumn(PropertyData overridingProperty) { - AnnotatedColumn[] result; - result = buildExplicitJoinColumns( overridingProperty.getProperty(), overridingProperty ); - if (result == null) { - result = buildDefaultJoinColumnsForXToOne( overridingProperty.getProperty(), overridingProperty); - } - return result; + private AnnotatedColumn[] buildExplicitOrDefaultJoinColumn(PropertyData overridingProperty) { + final AnnotatedColumn[] columns = buildExplicitJoinColumns( overridingProperty.getProperty(), overridingProperty ); + return columns == null ? buildDefaultJoinColumnsForXToOne( overridingProperty.getProperty(), overridingProperty ) : columns; } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/IndexColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/IndexColumn.java index f3f9e75881..a4339c06e7 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/IndexColumn.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/IndexColumn.java @@ -22,38 +22,10 @@ import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue; public class IndexColumn extends AnnotatedColumn { private int base; - // TODO move to a getter setter strategy for readability - public IndexColumn( - boolean isImplicit, - String sqlType, - long length, - int precision, - int scale, - String name, - boolean nullable, - boolean unique, - boolean insertable, - boolean updatable, - String secondaryTableName, - Map joins, - PropertyHolder propertyHolder, - MetadataBuildingContext buildingContext) { - super(); - setImplicit( isImplicit ); - setSqlType( sqlType ); - setLength( length ); - setPrecision( precision ); - setScale( scale ); - setLogicalColumnName( name ); - setNullable( nullable ); - setUnique( unique ); - setInsertable( insertable ); - setUpdatable( updatable ); - setExplicitTableName( secondaryTableName ); - setPropertyHolder( propertyHolder ); - setJoins( joins ); - setBuildingContext( buildingContext ); - bind(); + public IndexColumn() { + setLength( 0L ); + setPrecision( 0 ); + setScale( 0 ); } public static IndexColumn fromAnnotations( @@ -64,9 +36,9 @@ public class IndexColumn extends AnnotatedColumn { PropertyData inferredData, Map secondaryTables, MetadataBuildingContext context) { - final IndexColumn indexColumn; + final IndexColumn column; if ( jpaAnnotation != null ) { - indexColumn = buildColumnFromAnnotation( + column = buildColumnFromAnnotation( jpaAnnotation, propertyHolder, inferredData, @@ -75,39 +47,28 @@ public class IndexColumn extends AnnotatedColumn { ); } else if ( hibAnnotation != null ) { - indexColumn = buildColumnFromAnnotation( + column = buildColumnFromAnnotation( hibAnnotation, propertyHolder, inferredData, context ); - indexColumn.setBase( hibAnnotation.base() ); + column.setBase( hibAnnotation.base() ); } else { - indexColumn = new IndexColumn( - true, - null, - 0, - 0, - 0, - // Use the JPA default name... - inferredData.getPropertyName() + "_ORDER", - true, - false, - true, - true, - null, - null, - propertyHolder, - context - ); + column = new IndexColumn(); + column.setLogicalColumnName( inferredData.getPropertyName() + "_ORDER" ); //JPA default name + column.setImplicit( true ); + column.setBuildingContext( context ); + column.setPropertyHolder( propertyHolder ); + column.bind(); } if ( indexBaseAnnotation != null ) { - indexColumn.setBase( indexBaseAnnotation.value() ); + column.setBase( indexBaseAnnotation.value() ); } - return indexColumn; + return column; } public int getBase() { @@ -134,54 +95,30 @@ public class IndexColumn extends AnnotatedColumn { PropertyData inferredData, Map secondaryTables, MetadataBuildingContext buildingContext) { - final IndexColumn column; if ( ann != null ) { final String sqlType = isEmptyAnnotationValue( ann.columnDefinition() ) ? null : ann.columnDefinition(); final String name = isEmptyAnnotationValue( ann.name() ) ? inferredData.getPropertyName() + "_ORDER" : ann.name(); //TODO move it to a getter based system and remove the constructor -// The JPA OrderColumn annotation defines no table element... -// column = new IndexColumn( -// false, sqlType, 0, 0, 0, name, ann.nullable(), -// false, ann.insertable(), ann.updatable(), ann.table(), -// secondaryTables, propertyHolder, mappings -// ); - column = new IndexColumn( - false, - sqlType, - 0, - 0, - 0, - name, - ann.nullable(), - false, - ann.insertable(), - ann.updatable(), - //ann.table() - null, - secondaryTables, - propertyHolder, - buildingContext - ); + final IndexColumn column = new IndexColumn(); + column.setLogicalColumnName( name ); + column.setSqlType( sqlType ); + column.setNullable( ann.nullable() ); + column.setJoins( secondaryTables ); + column.setInsertable( ann.insertable() ); + column.setUpdatable( ann.updatable() ); + column.setBuildingContext( buildingContext ); + column.setPropertyHolder( propertyHolder ); + column.bind(); + return column; } else { - column = new IndexColumn( - true, - null, - 0, - 0, - 0, - null, - true, - false, - true, - true, - null, - null, - propertyHolder, - buildingContext - ); + final IndexColumn column = new IndexColumn(); + column.setImplicit( true ); + column.setBuildingContext( buildingContext ); + column.setPropertyHolder( propertyHolder ); + column.bind(); + return column; } - return column; } /** @@ -198,47 +135,27 @@ public class IndexColumn extends AnnotatedColumn { PropertyHolder propertyHolder, PropertyData inferredData, MetadataBuildingContext buildingContext) { - final IndexColumn column; if ( ann != null ) { final String sqlType = isEmptyAnnotationValue( ann.columnDefinition() ) ? null : ann.columnDefinition(); final String name = isEmptyAnnotationValue( ann.name() ) ? inferredData.getPropertyName() : ann.name(); //TODO move it to a getter based system and remove the constructor - column = new IndexColumn( - false, - sqlType, - 0, - 0, - 0, - name, - ann.nullable(), - false, - true, - true, - null, - null, - propertyHolder, - buildingContext - ); + final IndexColumn column = new IndexColumn(); + column.setLogicalColumnName( name ); + column.setSqlType( sqlType ); + column.setNullable( ann.nullable() ); column.setBase( ann.base() ); + column.setBuildingContext( buildingContext ); + column.setPropertyHolder( propertyHolder ); + column.bind(); + return column; } else { - column = new IndexColumn( - true, - null, - 0, - 0, - 0, - null, - true, - false, - true, - true, - null, - null, - propertyHolder, - buildingContext - ); + final IndexColumn column = new IndexColumn(); + column.setImplicit( true ); + column.setBuildingContext( buildingContext ); + column.setPropertyHolder( propertyHolder ); + column.bind(); + return column; } - return column; } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java index 6db06517b5..94c2782c34 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java @@ -90,18 +90,16 @@ public class ToOneFkSecondPass extends FkSecondPass { public void doSecondPass(java.util.Map persistentClasses) throws MappingException { if ( value instanceof ManyToOne ) { - ManyToOne manyToOne = (ManyToOne) value; - PersistentClass ref = persistentClasses.get( manyToOne.getReferencedEntityName() ); - if ( ref == null ) { + final ManyToOne manyToOne = (ManyToOne) value; + final PersistentClass referencedEntity = persistentClasses.get( manyToOne.getReferencedEntityName() ); + if ( referencedEntity == null ) { throw new AnnotationException( "Association '" + StringHelper.qualify( entityClassName, path ) + "' targets an unknown entity named '" + manyToOne.getReferencedEntityName() + "'" ); } manyToOne.setPropertyName( path ); - createSyntheticPropertyReference( columns, ref, null, manyToOne, false, buildingContext ); - TableBinder.bindForeignKey( ref, null, columns, manyToOne, unique, buildingContext ); - /* - * HbmMetadataSourceProcessorImpl does this only when property-ref != null, but IMO, it makes sense event if it is null - */ + createSyntheticPropertyReference( columns, referencedEntity, null, manyToOne, false, buildingContext ); + TableBinder.bindForeignKey( referencedEntity, null, 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 ); } else if ( value instanceof OneToOne ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index 5a466f6f8b..383d4c6d38 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -178,7 +178,6 @@ import static org.hibernate.internal.util.StringHelper.qualify; * @author inger * @author Emmanuel Bernard */ -@SuppressWarnings("deprecation") public abstract class CollectionBinder { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, CollectionBinder.class.getName()); @@ -284,7 +283,7 @@ public abstract class CollectionBinder { context ); - CollectionBinder collectionBinder = getCollectionBinder( property, hasMapKeyAnnotation( property ), context ); + final CollectionBinder collectionBinder = getCollectionBinder( property, hasMapKeyAnnotation( property ), context ); collectionBinder.setIndexColumn( indexColumn ); collectionBinder.setMapKey( property.getAnnotation( MapKey.class ) ); collectionBinder.setPropertyName( inferredData.getPropertyName() ); @@ -315,7 +314,7 @@ public abstract class CollectionBinder { final PropertyData virtualProperty = property.isAnnotationPresent( ElementCollection.class ) ? inferredData : new WrappedInferredData( inferredData, "element" ); - final Comment comment = property.getAnnotation(Comment.class); + final Comment comment = property.getAnnotation( Comment.class ); final AnnotatedColumn[] elementColumns = elementColumns( propertyHolder, nullability, @@ -427,12 +426,12 @@ public abstract class CollectionBinder { collectionBinder.setOneToMany( true ); } else if ( elementCollectionAnn != null ) { - for ( AnnotatedJoinColumn column : joinColumns) { + for ( AnnotatedJoinColumn column : joinColumns ) { if ( column.isSecondary() ) { throw new NotYetImplementedException( "Collections having FK in secondary table" ); } } - collectionBinder.setFkJoinColumns(joinColumns); + collectionBinder.setFkJoinColumns( joinColumns ); mappedBy = ""; final Class targetElement = elementCollectionAnn.targetClass(); collectionBinder.setTargetEntity( reflectionManager.toXClass( targetElement ) ); @@ -462,19 +461,21 @@ public abstract class CollectionBinder { private static boolean hasMapKeyAnnotation(XProperty property) { return property.isAnnotationPresent(MapKeyJavaType.class) - || property.isAnnotationPresent(MapKeyJdbcType.class) - || property.isAnnotationPresent(MapKeyJdbcTypeCode.class) - || property.isAnnotationPresent(MapKeyMutability.class) - || property.isAnnotationPresent(MapKey.class) - || property.isAnnotationPresent(MapKeyType.class); + || property.isAnnotationPresent(MapKeyJdbcType.class) + || property.isAnnotationPresent(MapKeyJdbcTypeCode.class) + || property.isAnnotationPresent(MapKeyMutability.class) + || property.isAnnotationPresent(MapKey.class) + || property.isAnnotationPresent(MapKeyType.class); } private static boolean isToManyAssociationWithinEmbeddableCollection(PropertyHolder propertyHolder) { - if(propertyHolder instanceof ComponentPropertyHolder) { + if ( propertyHolder instanceof ComponentPropertyHolder ) { ComponentPropertyHolder componentPropertyHolder = (ComponentPropertyHolder) propertyHolder; return componentPropertyHolder.isWithinElementCollection(); } - return false; + else { + return false; + } } private static AnnotatedColumn[] elementColumns( @@ -539,8 +540,8 @@ public abstract class CollectionBinder { CollectionBinder collectionBinder, Comment comment) { - final jakarta.persistence.Column[] keyColumns = property.isAnnotationPresent(MapKeyColumn.class) - ? new jakarta.persistence.Column[] { new MapKeyColumnDelegator( property.getAnnotation(MapKeyColumn.class) ) } + final jakarta.persistence.Column[] keyColumns = property.isAnnotationPresent( MapKeyColumn.class ) + ? new jakarta.persistence.Column[] { new MapKeyColumnDelegator( property.getAnnotation( MapKeyColumn.class ) ) } : null; final AnnotatedColumn[] mapColumns = buildColumnsFromAnnotations( @@ -557,7 +558,7 @@ public abstract class CollectionBinder { if ( property.isAnnotationPresent( MapKeyJoinColumns.class ) ) { final MapKeyJoinColumn[] mapKeyJoinColumns = property.getAnnotation( MapKeyJoinColumns.class ).value(); - JoinColumn[] joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length]; + final JoinColumn[] joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length]; int index = 0; for ( MapKeyJoinColumn joinColumn : mapKeyJoinColumns ) { joinKeyColumns[index] = new MapKeyJoinColumnDelegator( joinColumn ); @@ -570,13 +571,7 @@ public abstract class CollectionBinder { return joinKeyColumns; } else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) { - return new JoinColumn[] { - new MapKeyJoinColumnDelegator( - property.getAnnotation( - MapKeyJoinColumn.class - ) - ) - }; + return new JoinColumn[] { new MapKeyJoinColumnDelegator( property.getAnnotation( MapKeyJoinColumn.class ) ) }; } return null; } @@ -648,7 +643,7 @@ public abstract class CollectionBinder { annJoins = null; annInverseJoins = null; } - AnnotatedJoinColumn[] joinColumns = buildJoinTableJoinColumns( + final AnnotatedJoinColumn[] joinColumns = buildJoinTableJoinColumns( annJoins, entityBinder.getSecondaryTables(), propertyHolder, @@ -656,7 +651,7 @@ public abstract class CollectionBinder { mappedBy, buildingContext ); - AnnotatedJoinColumn[] inverseJoinColumns = buildJoinTableJoinColumns( + final AnnotatedJoinColumn[] inverseJoinColumns = buildJoinTableJoinColumns( annInverseJoins, entityBinder.getSecondaryTables(), propertyHolder, @@ -1537,7 +1532,7 @@ public abstract class CollectionBinder { MetadataBuildingContext buildingContext) { PersistentClass persistentClass = persistentClasses.get( elementType.getName() ); boolean reversePropertyInJoin = false; - if ( persistentClass != null && isNotEmpty( mappedBy ) ) { + if ( persistentClass != null && hasMappedBy() ) { try { reversePropertyInJoin = 0 != persistentClass.getJoinNumber( persistentClass.getRecursiveProperty( mappedBy ) ); @@ -1551,11 +1546,11 @@ public abstract class CollectionBinder { if ( persistentClass != null && !reversePropertyInJoin && oneToMany - && !this.isExplicitAssociationTable - && ( joinColumns[0].isImplicit() && !isEmptyAnnotationValue( this.mappedBy ) //implicit @JoinColumn - || !fkJoinColumns[0].isImplicit() ) //this is an explicit @JoinColumn - ) { - //this is a Foreign key + && !isExplicitAssociationTable + && ( joinColumns[0].isImplicit() && hasMappedBy() //implicit @JoinColumn + || !fkJoinColumns[0].isImplicit() ) //this is an explicit @JoinColumn + ) { + //this is a foreign key bindOneToManySecondPass( getCollection(), persistentClasses, @@ -1590,6 +1585,10 @@ public abstract class CollectionBinder { } } + private boolean hasMappedBy() { + return isNotEmpty( mappedBy ); + } + protected void bindOneToManySecondPass( Collection collection, Map persistentClasses, @@ -1608,16 +1607,16 @@ public abstract class CollectionBinder { "CollectionSecondPass for oneToMany should not be called with null mappings" ); } - org.hibernate.mapping.OneToMany oneToMany = + final org.hibernate.mapping.OneToMany oneToMany = new org.hibernate.mapping.OneToMany( buildingContext, collection.getOwner() ); collection.setElement( oneToMany ); oneToMany.setReferencedEntityName( collectionType.getName() ); oneToMany.setNotFoundAction( notFoundAction ); - String assocClass = oneToMany.getReferencedEntityName(); - PersistentClass associatedClass = persistentClasses.get( assocClass ); + final String assocClass = oneToMany.getReferencedEntityName(); + final PersistentClass associatedClass = persistentClasses.get( assocClass ); handleJpaOrderBy( collection, associatedClass ); - Map joins = buildingContext.getMetadataCollector().getJoins( assocClass ); + final Map joins = buildingContext.getMetadataCollector().getJoins( assocClass ); if ( associatedClass == null ) { throw new MappingException( String.format("Association [%s] for entity [%s] references unmapped class [%s]", @@ -1625,7 +1624,7 @@ public abstract class CollectionBinder { ); } oneToMany.setAssociatedClass( associatedClass ); - for (AnnotatedJoinColumn column : fkJoinColumns) { + for ( AnnotatedJoinColumn column : fkJoinColumns ) { column.setPersistentClass( associatedClass, joins, inheritanceStatePerClass ); column.setJoins( joins ); collection.setCollectionTable( column.getTable() ); @@ -1646,19 +1645,19 @@ public abstract class CollectionBinder { buildingContext ); - if ( !collection.isInverse() - && !collection.getKey().isNullable() ) { + if ( !collection.isInverse() && !collection.getKey().isNullable() ) { // for non-inverse one-to-many, with a not-null fk, add a backref! - String entityName = oneToMany.getReferencedEntityName(); - PersistentClass referenced = buildingContext.getMetadataCollector().getEntityBinding( entityName ); - Backref prop = new Backref(); - prop.setName( '_' + fkJoinColumns[0].getPropertyName() + '_' + fkJoinColumns[0].getLogicalColumnName() + "Backref" ); - prop.setUpdateable( false ); - prop.setSelectable( false ); - prop.setCollectionRole( collection.getRole() ); - prop.setEntityName( collection.getOwner().getEntityName() ); - prop.setValue( collection.getKey() ); - referenced.addProperty( prop ); + final String entityName = oneToMany.getReferencedEntityName(); + final PersistentClass referenced = buildingContext.getMetadataCollector().getEntityBinding( entityName ); + final Backref backref = new Backref(); + AnnotatedJoinColumn fkColumn = fkJoinColumns[0]; + backref.setName( '_' + fkColumn.getPropertyName() + '_' + fkColumn.getLogicalColumnName() + "Backref" ); + backref.setUpdateable( false ); + backref.setSelectable( false ); + backref.setCollectionRole( collection.getRole() ); + backref.setEntityName( collection.getOwner().getEntityName() ); + backref.setValue( collection.getKey() ); + referenced.addProperty( backref ); } } @@ -1698,7 +1697,7 @@ public abstract class CollectionBinder { } private void addFilter(boolean hasAssociationTable, Filter filter) { - if (hasAssociationTable) { + if ( hasAssociationTable ) { collection.addManyToManyFilter( filter.name(), getCondition(filter), @@ -1914,7 +1913,7 @@ public abstract class CollectionBinder { return orderByFragment; } - private static DependantValue buildCollectionKey( + private DependantValue buildCollectionKey( Collection collValue, AnnotatedJoinColumn[] joinColumns, boolean cascadeDeleteEnabled, @@ -2034,18 +2033,18 @@ public abstract class CollectionBinder { return key; } - private static void overrideReferencedPropertyName( + private void overrideReferencedPropertyName( Collection collValue, AnnotatedJoinColumn[] joinColumns, MetadataBuildingContext buildingContext) { if ( joinColumns.length > 0 ) { AnnotatedJoinColumn joinColumn = joinColumns[0]; - if (isNotEmpty( joinColumn.getMappedBy() )) { + if ( hasMappedBy() ) { String entityName = joinColumn.getManyToManyOwnerSideEntityName() != null ? "inverse__" + joinColumn.getManyToManyOwnerSideEntityName() : joinColumn.getPropertyHolder().getEntityName(); InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector(); - String propRef = metadataCollector.getPropertyReferencedAssociation( entityName, joinColumn.getMappedBy() ); + String propRef = metadataCollector.getPropertyReferencedAssociation( entityName, mappedBy ); if ( propRef != null ) { collValue.setReferencedPropertyName( propRef ); metadataCollector.addPropertyReference( collValue.getOwnerEntityName(), propRef ); @@ -2090,7 +2089,7 @@ public abstract class CollectionBinder { isManyToAny ); - if ( !isEmptyAnnotationValue( joinColumns[0].getMappedBy() ) ) { + if ( hasMappedBy() ) { handleUnownedManyToMany( collValue, joinColumns, @@ -2161,7 +2160,7 @@ public abstract class CollectionBinder { //FIXME: do optional = false if ( isCollectionOfEntities ) { - bindManytoManyInverseFk( collectionEntity, inverseJoinColumns, element, unique, buildingContext ); + bindManyToManyInverseForeignKey( collectionEntity, inverseJoinColumns, element, unique, buildingContext ); } } @@ -2495,7 +2494,7 @@ public abstract class CollectionBinder { Property otherSideProperty; try { - otherSideProperty = collectionEntity.getRecursiveProperty( joinColumns[0].getMappedBy() ); + otherSideProperty = collectionEntity.getRecursiveProperty( mappedBy ); } catch (MappingException e) { throw new AnnotationException( "Association '" + safeCollectionRole() + @@ -2633,7 +2632,7 @@ public abstract class CollectionBinder { } } - private static void bindCollectionSecondPass( + private void bindCollectionSecondPass( Collection collValue, PersistentClass collectionEntity, AnnotatedJoinColumn[] joinColumns, @@ -2674,19 +2673,17 @@ public abstract class CollectionBinder { /** - * bind the inverse FK of a {@link ManyToMany}. + * Bind the inverse foreign key of a {@link ManyToMany}. * If we are in a mappedBy case, read the columns from the associated - * collection element - * Otherwise delegates to the usual algorithm + * collection element. Otherwise, delegate to the usual algorithm. */ - public void bindManytoManyInverseFk( + public void bindManyToManyInverseForeignKey( PersistentClass referencedEntity, AnnotatedJoinColumn[] columns, SimpleValue value, boolean unique, MetadataBuildingContext buildingContext) { - final String mappedBy = columns[0].getMappedBy(); - if ( isNotEmpty( mappedBy ) ) { + if ( hasMappedBy() ) { final Property property = referencedEntity.getRecursiveProperty( mappedBy ); final List mappedByColumns = mappedByColumns( referencedEntity, property ); for ( Selectable selectable: mappedByColumns ) { diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java index ecfd1c13af..6d5cec2934 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java @@ -806,23 +806,18 @@ public class EntityBinder { MetadataBuildingContext context, InheritanceState inheritanceState, EntityBinder entityBinder) { - final boolean isRoot = !inheritanceState.hasParents(); DiscriminatorColumn discAnn = clazzToProcess.getAnnotation( DiscriminatorColumn.class ); DiscriminatorType discriminatorType = discAnn != null ? discAnn.discriminatorType() : DiscriminatorType.STRING; - + DiscriminatorFormula discFormulaAnn = getOverridableAnnotation( clazzToProcess, DiscriminatorFormula.class, context ); - AnnotatedDiscriminatorColumn discriminatorColumn = null; - if ( isRoot ) { - discriminatorColumn = buildDiscriminatorColumn( - discriminatorType, - discAnn, - discFormulaAnn, - context - ); - } + final boolean isRoot = !inheritanceState.hasParents(); + final AnnotatedDiscriminatorColumn discriminatorColumn = isRoot + ? buildDiscriminatorColumn( discriminatorType, discAnn, discFormulaAnn, context ) + : null; if ( discAnn != null && !isRoot ) { + //TODO: shouldn't this be an error?! LOG.invalidDiscriminatorAnnotation( clazzToProcess.getName() ); } @@ -1808,7 +1803,7 @@ public class EntityBinder { private void bindJoinToPersistentClass(Join join, AnnotatedJoinColumn[] annotatedJoinColumns, MetadataBuildingContext buildingContext) { DependantValue key = new DependantValue( buildingContext, join.getTable(), persistentClass.getIdentifier() ); join.setKey( key ); - setFKNameIfDefined( join ); + setForeignKeyNameIfDefined( join ); key.setCascadeDeleteEnabled( false ); TableBinder.bindForeignKey( persistentClass, null, annotatedJoinColumns, key, false, buildingContext ); key.sortProperties(); @@ -1817,7 +1812,7 @@ public class EntityBinder { persistentClass.addJoin( join ); } - private void setFKNameIfDefined(Join join) { + private void setForeignKeyNameIfDefined(Join join) { // just awful.. org.hibernate.annotations.Table matchingTable = findMatchingComplementaryTableAnnotation( join ); final SimpleValue key = (SimpleValue) join.getKey(); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java index 79e5d2b542..89cea48734 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java @@ -372,7 +372,7 @@ public class MapBinder extends CollectionBinder { } if ( isIndexOfEntities ) { - bindManytoManyInverseFk( + bindManyToManyInverseForeignKey( collectionEntity, mapKeyManyToManyColumns, element, diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java index cee5813c89..1e9bca93e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java @@ -535,6 +535,10 @@ public class PropertyBinder { this.isId = id; } + public boolean isId() { + return isId; + } + public void setInheritanceStatePerClass(Map inheritanceStatePerClass) { this.inheritanceStatePerClass = inheritanceStatePerClass; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java index 0c0388cf23..8054d67844 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java @@ -547,11 +547,10 @@ public class TableBinder { associatedClass = holder == null ? null : holder.getPersistentClass(); } - final String mappedByProperty = columns[0].getMappedBy(); - if ( isNotEmpty( mappedByProperty ) ) { + if ( columns[0].hasMappedBy() ) { // use the columns of the property referenced by mappedBy // copy them and link the copy to the actual value - bindUnownedAssociation( columns, value, associatedClass, mappedByProperty ); + bindUnownedAssociation( columns, value, associatedClass, columns[0].getMappedBy() ); } else if ( columns[0].isImplicit() ) { // if columns are implicit, then create the columns based @@ -575,20 +574,20 @@ public class TableBinder { PersistentClass associatedClass) { switch ( checkReferencedColumnsType( columns, referencedEntity, buildingContext ) ) { case NON_PK_REFERENCE: { - bindNonPkReference( referencedEntity, columns, value ); + bindNonPrimaryKeyReference( referencedEntity, columns, value ); break; } case NO_REFERENCE: { - bindImplicitPkReference( referencedEntity, columns, value, associatedClass ); + bindImplicitPrimaryKeyReference( referencedEntity, columns, value, associatedClass ); break; } default: { - bindPkReference( referencedEntity, columns, value, associatedClass, buildingContext ); + bindPrimaryKeyReference( referencedEntity, columns, value, associatedClass, buildingContext ); } } } - private static void bindImplicitPkReference( + private static void bindImplicitPrimaryKeyReference( PersistentClass referencedEntity, AnnotatedJoinColumn[] columns, SimpleValue value, @@ -613,7 +612,7 @@ public class TableBinder { } } - private static void bindPkReference( + private static void bindPrimaryKeyReference( PersistentClass referencedEntity, AnnotatedJoinColumn[] columns, SimpleValue value, @@ -663,7 +662,7 @@ public class TableBinder { } } - private static void bindNonPkReference( + private static void bindNonPrimaryKeyReference( PersistentClass referencedEntity, AnnotatedJoinColumn[] columns, SimpleValue value) { @@ -680,7 +679,6 @@ public class TableBinder { else { throw new AnnotationException( "The '@JoinColumn' for a secondary table must reference the primary key" ); } - } else { throw new AssertionFailure( "Property ref to an unexpected Value type: " + value.getClass().getName() ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 40fd5e9692..acff649474 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -546,7 +546,7 @@ public final class StringHelper { } public static boolean isNotEmpty(String string) { - return string != null && string.length() > 0; + return string != null && !string.isEmpty(); } public static boolean isEmpty(String string) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/id/AndFormulaTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/id/AndFormulaTest.java index a33af190b3..d5ddc101e0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/id/AndFormulaTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/id/AndFormulaTest.java @@ -57,7 +57,7 @@ public class AndFormulaTest { fail( "Expecting failure from invalid mapping" ); } catch (CannotForceNonNullableException e) { - assertThat( e.getMessage(), startsWith( "Identifier property [" ) ); + assertThat( e.getMessage(), startsWith( "Identifier property " ) ); } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java index 1ae5a5892d..a7126423d6 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java @@ -22,8 +22,6 @@ import org.hibernate.testing.OnExpectedFailure; import org.hibernate.testing.OnFailure; import org.hibernate.testing.orm.UnclosedFixtureResourcesLogging; -import org.jboss.logging.Logger; - /** * Metadata about various types of callback methods on a given test class. *