diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedDiscriminatorColumn.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedDiscriminatorColumn.java
index 97f0470b6b..a4889f69e6 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedDiscriminatorColumn.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotatedDiscriminatorColumn.java
@@ -46,31 +46,31 @@ public void setDiscriminatorTypeName(String discriminatorTypeName) {
public static AnnotatedDiscriminatorColumn buildDiscriminatorColumn(
DiscriminatorType type,
- DiscriminatorColumn discAnn,
- DiscriminatorFormula discFormulaAnn,
+ DiscriminatorColumn discriminatorColumn,
+ DiscriminatorFormula discriminatorFormula,
MetadataBuildingContext context) {
- final AnnotatedDiscriminatorColumn discriminatorColumn = new AnnotatedDiscriminatorColumn();
- discriminatorColumn.setBuildingContext( context );
- if ( discFormulaAnn != null ) {
- discriminatorColumn.setImplicit( false );
- discriminatorColumn.setFormula( discFormulaAnn.value() );
+ final AnnotatedDiscriminatorColumn column = new AnnotatedDiscriminatorColumn();
+ column.setBuildingContext( context );
+ if ( discriminatorFormula != null ) {
+ column.setImplicit( false );
+ column.setFormula( discriminatorFormula.value() );
}
- else if ( discAnn != null ) {
- discriminatorColumn.setImplicit( false );
- if ( !isEmptyAnnotationValue( discAnn.columnDefinition() ) ) {
- discriminatorColumn.setSqlType( discAnn.columnDefinition() );
+ else if ( discriminatorColumn != null ) {
+ column.setImplicit( false );
+ if ( !isEmptyAnnotationValue( discriminatorColumn.columnDefinition() ) ) {
+ column.setSqlType( discriminatorColumn.columnDefinition() );
}
- if ( !isEmptyAnnotationValue( discAnn.name() ) ) {
- discriminatorColumn.setLogicalColumnName( discAnn.name() );
+ if ( !isEmptyAnnotationValue( discriminatorColumn.name() ) ) {
+ column.setLogicalColumnName( discriminatorColumn.name() );
}
- discriminatorColumn.setNullable( false );
+ column.setNullable( false );
}
else {
- discriminatorColumn.setImplicit( true );
+ column.setImplicit( true );
}
- setDiscriminatorType( type, discAnn, discriminatorColumn );
- discriminatorColumn.bind();
- return discriminatorColumn;
+ setDiscriminatorType( type, discriminatorColumn, column );
+ column.bind();
+ return column;
}
private static void setDiscriminatorType(
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 74bc5a51ec..8971a92728 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java
@@ -505,7 +505,7 @@ else if ( generatorAnn instanceof GenericGenerator ) {
}
/**
- * Bind a class having JSR175 annotations. Subclasses have to be bound after its parent class.
+ * Bind an annotated class. A subclass must be bound after its superclass.
*
* @param clazzToProcess entity to bind as {@code XClass} instance
* @param inheritanceStatePerClass Metadata about the inheritance relationships for all mapped classes
@@ -1048,10 +1048,9 @@ private static boolean hasIdAnnotation(XAnnotatedElement element) {
|| element.isAnnotationPresent(EmbeddedId.class);
}
- /*
- * Process annotation of a particular property
+ /**
+ * Process annotation of a particular property or field.
*/
-
public static void processElementAnnotations(
PropertyHolder propertyHolder,
Nullability nullability,
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 19f6d9f4fa..c1227ca2aa 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java
@@ -164,6 +164,7 @@ public static void createSyntheticPropertyReference(
// the entity which declares the association (used for exception message)
PersistentClass associatedEntity,
Value value,
+ String propertyName,
// true when we do the reverse side of a @ManyToMany
boolean inverse,
MetadataBuildingContext context) {
@@ -174,8 +175,6 @@ public static void createSyntheticPropertyReference(
// Property instance
final AnnotatedJoinColumn firstColumn = columns[0];
if ( !firstColumn.isImplicit()
- // only necessary for owning side of association
- && !firstColumn.hasMappedBy()
// not necessary for a primary key reference
&& checkReferencedColumnsType( columns, targetEntity, context ) == NON_PK_REFERENCE ) {
@@ -184,29 +183,21 @@ && checkReferencedColumnsType( columns, targetEntity, context ) == NON_PK_REFERE
// for a PersistentClass or Join in the hierarchy of
// the target entity which has the first column
final Object columnOwner = findColumnOwner( targetEntity, firstColumn.getReferencedColumn(), context );
- for ( AnnotatedJoinColumn col: columns ) {
- final Object owner = findColumnOwner( targetEntity, col.getReferencedColumn(), context );
- if ( owner == null ) {
- throw new AnnotationException( "A '@JoinColumn' for association "
- + associationMessage( associatedEntity, columns[0] )
- + " references a column named '" + col.getReferencedColumn()
- + "' which is not mapped by the target entity '"
- + targetEntity.getEntityName() + "'" );
- }
- if ( owner != columnOwner ) {
- throw new AnnotationException( "The '@JoinColumn's for association "
- + associationMessage( associatedEntity, columns[0] )
- + " reference columns of different tables mapped by the target entity '"
- + targetEntity.getEntityName() + "' ('" + col.getReferencedColumn() +
- "' belongs to a different table to '" + firstColumn.getReferencedColumn() + "'" );
- }
- }
+ checkColumnInSameTable( columns, targetEntity, associatedEntity, context, columnOwner );
// find all properties mapped to each column
final List properties = findPropertiesByColumns( columnOwner, columns, associatedEntity, context );
// create a Property along with the new synthetic
// Component if necessary (or reuse the existing
// Property that matches exactly)
- final Property property = referencedProperty( targetEntity, inverse, columns, columnOwner, properties, context );
+ final Property property = referencedProperty(
+ targetEntity,
+ associatedEntity,
+ propertyName,
+ inverse,
+ columnOwner,
+ properties,
+ context
+ );
// register the mapping with the InFlightMetadataCollector
registerSyntheticProperty(
targetEntity,
@@ -220,6 +211,41 @@ && checkReferencedColumnsType( columns, targetEntity, context ) == NON_PK_REFERE
}
}
+ /**
+ * All the referenced columns must belong to the same table, that is
+ * {@link #findColumnOwner(PersistentClass, String, MetadataBuildingContext)}
+ * must return the same value for all of them.
+ */
+ private static void checkColumnInSameTable(
+ AnnotatedJoinColumn[] columns,
+ 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");
+ }
+ final Object owner = findColumnOwner( targetEntity, column.getReferencedColumn(), context );
+ if ( owner == null ) {
+ throw new AnnotationException( "A '@JoinColumn' for association "
+ + associationMessage(associatedEntity, firstColumn)
+ + " references a column named '" + column.getReferencedColumn()
+ + "' which is not mapped by the target entity '"
+ + targetEntity.getEntityName() + "'" );
+ }
+ if ( owner != columnOwner) {
+ throw new AnnotationException( "The '@JoinColumn's for association "
+ + associationMessage(associatedEntity, firstColumn)
+ + " reference columns of different tables mapped by the target entity '"
+ + targetEntity.getEntityName() + "' ('" + column.getReferencedColumn() +
+ "' belongs to a different table to '" + firstColumn.getReferencedColumn() + "'" );
+ }
+ }
+ }
+
/**
* If the referenced columns correspond to exactly one property
* of the primary table of the exact target entity subclass,
@@ -232,8 +258,9 @@ && checkReferencedColumnsType( columns, targetEntity, context ) == NON_PK_REFERE
*/
private static Property referencedProperty(
PersistentClass ownerEntity,
+ PersistentClass associatedEntity,
+ String propertyName,
boolean inverse,
- AnnotatedJoinColumn[] columns,
Object columnOwner,
List properties,
MetadataBuildingContext context) {
@@ -253,9 +280,7 @@ private static Property referencedProperty(
// mapped to the referenced columns. We need to shallow
// clone those properties to mark them as non-insertable
// and non-updatable
- final AnnotatedJoinColumn firstColumn = columns[0];
- final PersistentClass associatedClass = firstColumn.getPropertyHolder().getPersistentClass();
- final String syntheticPropertyName = syntheticPropertyName( firstColumn.getPropertyName(), inverse, associatedClass );
+ final String syntheticPropertyName = syntheticPropertyName( propertyName, inverse, associatedEntity );
return makeSyntheticComponentProperty( ownerEntity, columnOwner, context, syntheticPropertyName, properties );
}
}
@@ -826,7 +851,7 @@ private static IdentifierGeneratorDefinition getIdentifierGenerator(
final GeneratedValue generatedValueAnn = idXProperty.getAnnotation( GeneratedValue.class );
if ( generatedValueAnn == null ) {
- // this should really never happen, but its easy to protect against it...
+ // this should really never happen, but it's easy to protect against it...
return new IdentifierGeneratorDefinition( "assigned", "assigned" );
}
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 8db1cade08..d4c704fddb 100644
--- a/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java
+++ b/hibernate-core/src/main/java/org/hibernate/cfg/ToOneFkSecondPass.java
@@ -107,6 +107,7 @@ public void doSecondPass(java.util.Map persistentClasse
targetEntity,
persistentClass,
manyToOne,
+ path,
false,
buildingContext
);
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 43d19eb291..c320c60c70 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
@@ -94,7 +94,6 @@
import org.hibernate.boot.BootLogging;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.TypeDefinition;
-import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.InFlightMetadataCollector.CollectionTypeRegistrationDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
@@ -245,6 +244,9 @@ protected CollectionBinder(
this.buildingContext = buildingContext;
}
+ /**
+ * The first pass at binding a collection.
+ */
public static void bindCollection(
PropertyHolder propertyHolder,
Nullability nullability,
@@ -1506,8 +1508,10 @@ private boolean hasMappedBy() {
return isNotEmpty( mappedBy );
}
+ /**
+ * Bind a {@link OneToMany} association.
+ */
protected void bindOneToManySecondPass(Map persistentClasses) {
-
if ( property == null ) {
throw new AssertionFailure( "null was passed for argument property" );
}
@@ -1544,7 +1548,7 @@ protected void bindOneToManySecondPass(Map persistentCl
bindFilters( false );
handleWhere( false );
- PersistentClass targetEntity = persistentClasses.get( getElementType().getName() );
+ final PersistentClass targetEntity = persistentClasses.get( getElementType().getName() );
bindCollectionSecondPass( targetEntity, foreignJoinColumns, cascadeDeleteEnabled, buildingContext );
if ( !collection.isInverse() && !collection.getKey().isNullable() ) {
@@ -1955,14 +1959,16 @@ private void overrideReferencedPropertyName(
}
}
+ /**
+ * Bind a {@link ManyToMany} association or {@link ElementCollection}.
+ */
private void bindManyToManySecondPass(Map persistentClasses) throws MappingException {
-
if ( property == null ) {
throw new AssertionFailure( "null was passed for argument property" );
}
final XClass elementType = getElementType();
- final PersistentClass targetEntity = persistentClasses.get( elementType.getName() );
+ final PersistentClass targetEntity = persistentClasses.get( elementType.getName() ); //null if this is an @ElementCollection
final String hqlOrderBy = extractHqlOrderBy( jpaOrderBy );
final boolean isCollectionOfEntities = targetEntity != null;
@@ -2482,14 +2488,17 @@ private void bindCollectionSecondPass(
boolean cascadeDeleteEnabled,
MetadataBuildingContext context) {
- createSyntheticPropertyReference(
- joinColumns,
- collection.getOwner(),
- collection.getOwner(),
- collection,
- false,
- context
- );
+ if ( !hasMappedBy() ) {
+ createSyntheticPropertyReference(
+ joinColumns,
+ collection.getOwner(),
+ collection.getOwner(),
+ collection,
+ propertyName,
+ false,
+ context
+ );
+ }
final DependantValue key = buildCollectionKey(
collection,
@@ -2527,9 +2536,11 @@ String safeCollectionRole() {
/**
- * 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, delegate to the usual algorithm.
+ * Bind the inverse foreign key of a {@link ManyToMany}, that is, the columns
+ * specified by {@code @JoinTable(inverseJoinColumns=...)}, which are the
+ * columns that reference the target entity of the many-to-many association.
+ * If we are in a {@code mappedBy} case, read the columns from the associated
+ * collection element in the target entity.
*/
public void bindManyToManyInverseForeignKey(
PersistentClass targetEntity,
@@ -2560,6 +2571,7 @@ public void bindManyToManyInverseForeignKey(
targetEntity,
collection.getOwner(),
value,
+ propertyName,
true,
context
);
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 19aff56469..9afd464c89 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
@@ -197,6 +197,9 @@ public class EntityBinder {
private boolean cacheLazyProperty;
private String naturalIdCacheRegion;
+ /**
+ * Bind an entity class. This can be done in a single pass.
+ */
public static void bindEntityClass(
XClass clazzToProcess,
Map inheritanceStatePerClass,
@@ -1555,7 +1558,6 @@ private static Cache buildCacheMock(String region, MetadataBuildingContext conte
return new LocalCacheAnnotationStub( region, determineCacheConcurrencyStrategy( context ) );
}
- @SuppressWarnings("ClassExplicitlyAnnotation")
private static class LocalCacheAnnotationStub implements Cache {
private final String region;
private final CacheConcurrencyStrategy usage;
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 1e9bca93e1..6f67c39e7f 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
@@ -93,10 +93,8 @@ public void setEntityBinder(EntityBinder entityBinder) {
this.entityBinder = entityBinder;
}
- /*
- * property can be null
- * prefer propertyName to property.getName() since some are overloaded
- */
+ // property can be null
+ // prefer propertyName to property.getName() since some are overloaded
private XProperty property;
private XClass returnedClass;
private boolean isId;
@@ -234,7 +232,7 @@ public void setXToMany(boolean xToMany) {
}
private Property bind(Property prop) {
- if (isId) {
+ if ( isId ) {
final RootClass rootClass = ( RootClass ) holder.getPersistentClass();
//if an xToMany, it has to be wrapped today.
//FIXME this poses a problem as the PK is the class instead of the associated class which is not really compliant with the spec