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