clean up collection SecondPass stuff

This commit is contained in:
Gavin King 2022-10-28 17:46:32 +02:00
parent 79642022a6
commit 070f7e5d3a
15 changed files with 347 additions and 589 deletions

View File

@ -481,11 +481,10 @@ public class AnnotatedColumn {
* @throws AnnotationException missing secondary table
*/
public Table getTable() {
if ( table != null ){
if ( table != null ) {
return table;
}
if ( isSecondary() ) {
else if ( isSecondary() ) {
return getJoin().getTable();
}
else {
@ -1013,9 +1012,15 @@ public class AnnotatedColumn {
@Override
public String toString() {
return String.format(
"Column{table=%s, mappingColumn=%s, insertable=%s, updatable=%s, unique=%s}",
getTable(), mappingColumn.getName(), insertable, updatable, unique
);
StringBuilder string = new StringBuilder();
string.append( getClass().getSimpleName() ).append( "(" );
if ( isNotEmpty( logicalColumnName ) ) {
string.append( "column='" ).append( logicalColumnName ).append( "'" );
}
if ( isNotEmpty( formulaString ) ) {
string.append( "formula='" ).append( formulaString ).append( "'" );
}
string.append( "'" );
return string.toString();
}
}

View File

@ -101,13 +101,4 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
}
}
}
@Override
public String toString() {
return String.format(
"DiscriminatorColumn{logicalColumnName'%s', discriminatorTypeName='%s'}",
getLogicalColumnName(),
discriminatorTypeName
);
}
}

View File

@ -66,22 +66,27 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
private String mappedByEntityName;
private String mappedByJpaEntityName;
private boolean JPA2ElementCollection;
private String manyToManyOwnerSideEntityName;
//TODO: this is a bad way to determine the ImplicitJoinColumnNameSource.Nature
@Deprecated
public void setJPA2ElementCollection(boolean JPA2ElementCollection) {
this.JPA2ElementCollection = JPA2ElementCollection;
}
// TODO hacky solution to get the information at property ref resolution
//TODO: this is a bad way to get the information at property ref resolution
@Deprecated
public String getManyToManyOwnerSideEntityName() {
return manyToManyOwnerSideEntityName;
}
@Deprecated
public void setManyToManyOwnerSideEntityName(String manyToManyOwnerSideEntityName) {
this.manyToManyOwnerSideEntityName = manyToManyOwnerSideEntityName;
}
private String manyToManyOwnerSideEntityName;
public void setReferencedColumn(String referencedColumn) {
this.referencedColumn = referencedColumn;
}
@ -874,9 +879,15 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
@Override
public String toString() {
return String.format(
"JoinColumn{logicalColumnName='%s', referencedColumn='%s', mappedBy='%s'}",
getLogicalColumnName(), referencedColumn, mappedBy
);
StringBuilder string = new StringBuilder();
string.append( getClass().getSimpleName() ).append( "(" );
if ( isNotEmpty( getLogicalColumnName() ) ) {
string.append( "column='" ).append( getLogicalColumnName() ).append( "'" );
}
if ( isNotEmpty( referencedColumn ) ) {
string.append( "referencedColumn='" ).append( referencedColumn ).append( "'" );
}
string.append( "'" );
return string.toString();
}
}

View File

@ -175,6 +175,7 @@ import static org.hibernate.cfg.InheritanceState.getSuperclassInheritanceState;
import static org.hibernate.cfg.PropertyHolderBuilder.buildPropertyHolder;
import static org.hibernate.internal.CoreLogging.messageLogger;
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.mapping.SimpleValue.DEFAULT_ID_GEN_STRATEGY;
/**
@ -2405,20 +2406,23 @@ public final class AnnotationBinder {
final String propertyName = inferredData.getPropertyName();
value.setTypeUsingReflection( propertyHolder.getClassName(), propertyName );
final String fullPath = qualify( propertyHolder.getPath(), propertyName );
bindForeignKeyNameAndDefinition(
value,
property,
propertyHolder.getOverriddenForeignKey( StringHelper.qualify( propertyHolder.getPath(), propertyName ) ),
propertyHolder.getOverriddenForeignKey( fullPath ),
joinColumn,
joinColumns,
context
);
final FkSecondPass secondPass = new ToOneFkSecondPass(
value, columns,
value,
columns,
!optional && unique, //cannot have nullable and unique on certain DBs like Derby
propertyHolder.getEntityOwnerClassName(),
propertyHolder.getPath() + "." + propertyName,
propertyHolder.getPersistentClass(),
fullPath,
context
);
if ( inSecondPass ) {
@ -2427,7 +2431,7 @@ public final class AnnotationBinder {
else {
context.getMetadataCollector().addSecondPass( secondPass );
}
AnnotatedColumn.checkPropertyConsistency( columns, propertyHolder.getEntityName() + "." + propertyName );
AnnotatedColumn.checkPropertyConsistency( columns, qualify( propertyHolder.getEntityName(), propertyName ) );
//PropertyBinder binder = new PropertyBinder();
propertyBinder.setName( propertyName );
propertyBinder.setValue( value );

View File

@ -159,11 +159,12 @@ public class BinderHelper {
*/
public static void createSyntheticPropertyReference(
AnnotatedJoinColumn[] columns,
PersistentClass ownerEntity,
//associated entity only used for more precise exception
// the target entity of the association, to which the columns belong
PersistentClass targetEntity,
// the entity which declares the association (used for exception message)
PersistentClass associatedEntity,
Value value,
//true when we do the reverse side of a @ManyToMany
// true when we do the reverse side of a @ManyToMany
boolean inverse,
MetadataBuildingContext context) {
@ -176,27 +177,28 @@ public class BinderHelper {
// only necessary for owning side of association
&& !firstColumn.hasMappedBy()
// not necessary for a primary key reference
&& checkReferencedColumnsType( columns, ownerEntity, context ) == NON_PK_REFERENCE ) {
&& checkReferencedColumnsType( columns, targetEntity, context ) == NON_PK_REFERENCE ) {
// all the columns have to belong to the same table;
// figure out which table has the columns by looking
// for a PersistentClass or Join in the hierarchy of
// the target entity which has the first column
final Object columnOwner = findColumnOwner( ownerEntity, firstColumn.getReferencedColumn(), context );
final Object columnOwner = findColumnOwner( targetEntity, firstColumn.getReferencedColumn(), context );
for ( AnnotatedJoinColumn col: columns ) {
Object owner = findColumnOwner( ownerEntity, col.getReferencedColumn(), context );
final Object owner = findColumnOwner( targetEntity, col.getReferencedColumn(), context );
if ( owner == null ) {
throw new AnnotationException("A '@JoinColumn' for association "
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" );
+ "' 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 ('"
+ col.getReferencedColumn() + "' belongs to a different table to '"
+ firstColumn.getReferencedColumn() + "'" );
+ " 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
@ -204,10 +206,10 @@ public class BinderHelper {
// create a Property along with the new synthetic
// Component if necessary (or reuse the existing
// Property that matches exactly)
final Property property = referencedProperty( ownerEntity, inverse, columns, columnOwner, properties, context );
final Property property = referencedProperty( targetEntity, inverse, columns, columnOwner, properties, context );
// register the mapping with the InFlightMetadataCollector
registerSyntheticProperty(
ownerEntity,
targetEntity,
value,
inverse,
firstColumn.getPropertyHolder().getPersistentClass(),

View File

@ -122,28 +122,27 @@ public class OneToOneSecondPass implements SecondPass {
binder.setLazyGroup( lazyGroupAnnotation.value() );
}
Property prop = binder.makeProperty();
prop.setOptional( optional );
Property property = binder.makeProperty();
property.setOptional( optional );
if ( isEmptyAnnotationValue( mappedBy ) ) {
// we need to check if the columns are in the right order
// if not, then we need to create a many to one and formula
// but actually, since entities linked by a one to one need
// to share the same composite id class, this cannot happen in hibernate
// to share the same composite id class, this cannot happen
boolean rightOrder = true;
if ( rightOrder ) {
String path = qualify( propertyHolder.getPath(), propertyName );
final ToOneFkSecondPass secondPass = new ToOneFkSecondPass(
value,
joinColumns,
!optional, //cannot have nullabe and unique on certain DBs
propertyHolder.getEntityOwnerClassName(),
path,
!optional, //cannot have nullable and unique on certain DBs
propertyHolder.getPersistentClass(),
qualify( propertyHolder.getPath(), propertyName ),
buildingContext
);
secondPass.doSecondPass( persistentClasses );
//no column associated since its a one to one
propertyHolder.addProperty( prop, inferredData.getDeclaringClass() );
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
}
// else {
//this is a many to one with Formula
@ -169,7 +168,7 @@ public class OneToOneSecondPass implements SecondPass {
+ "' which does not exist in the target entity type '" + value.getReferencedEntityName() + "'" );
}
if ( otherSideProperty.getValue() instanceof OneToOne ) {
propertyHolder.addProperty( prop, inferredData.getDeclaringClass() );
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
}
else if ( otherSideProperty.getValue() instanceof ManyToOne ) {
Join otherSideJoin = null;
@ -193,7 +192,7 @@ public class OneToOneSecondPass implements SecondPass {
manyToOne.setReferencedEntityName( value.getReferencedEntityName() );
manyToOne.setUnwrapProxy( value.isUnwrapProxy() );
manyToOne.markAsLogicalOneToOne();
prop.setValue( manyToOne );
property.setValue( manyToOne );
for ( Column column: otherSideJoin.getKey().getColumns() ) {
Column copy = new Column();
copy.setLength( column.getLength() );
@ -210,10 +209,10 @@ public class OneToOneSecondPass implements SecondPass {
copy.setGeneratedAs(column.getGeneratedAs() );
manyToOne.addColumn( copy );
}
mappedByJoin.addProperty( prop );
mappedByJoin.addProperty( property );
}
else {
propertyHolder.addProperty( prop, inferredData.getDeclaringClass() );
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
}
value.setReferencedPropertyName( mappedBy );

View File

@ -11,7 +11,6 @@ import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.TableBinder;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.ManyToOne;
@ -21,6 +20,7 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.ToOne;
import static org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference;
import static org.hibernate.internal.util.StringHelper.qualify;
/**
* Enable a proper set of the FK columns in respect with the id column order
@ -30,6 +30,7 @@ import static org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference;
* @author Emmanuel Bernard
*/
public class ToOneFkSecondPass extends FkSecondPass {
private final PersistentClass persistentClass;
private final MetadataBuildingContext buildingContext;
private final boolean unique;
private final String path;
@ -39,13 +40,14 @@ public class ToOneFkSecondPass extends FkSecondPass {
ToOne value,
AnnotatedJoinColumn[] columns,
boolean unique,
String entityClassName,
PersistentClass persistentClass,
String path,
MetadataBuildingContext buildingContext) {
super( value, columns );
this.persistentClass = persistentClass;
this.buildingContext = buildingContext;
this.unique = unique;
this.entityClassName = entityClassName;
this.entityClassName = persistentClass.getClassName();
this.path = entityClassName != null ? path.substring( entityClassName.length() + 1 ) : path;
}
@ -56,8 +58,11 @@ public class ToOneFkSecondPass extends FkSecondPass {
@Override
public boolean isInPrimaryKey() {
if ( entityClassName == null ) return false;
final PersistentClass persistentClass = buildingContext.getMetadataCollector().getEntityBinding( entityClassName );
if ( entityClassName == null ) {
return false;
}
final PersistentClass persistentClass = buildingContext.getMetadataCollector()
.getEntityBinding( entityClassName );
Property property = persistentClass.getIdentifierProperty();
if ( path == null ) {
return false;
@ -91,14 +96,21 @@ public class ToOneFkSecondPass extends FkSecondPass {
public void doSecondPass(java.util.Map<String, PersistentClass> persistentClasses) throws MappingException {
if ( value instanceof ManyToOne ) {
final ManyToOne manyToOne = (ManyToOne) value;
final PersistentClass referencedEntity = persistentClasses.get( manyToOne.getReferencedEntityName() );
if ( referencedEntity == null ) {
throw new AnnotationException( "Association '" + StringHelper.qualify( entityClassName, path )
final PersistentClass targetEntity = persistentClasses.get( manyToOne.getReferencedEntityName() );
if ( targetEntity == null ) {
throw new AnnotationException( "Association '" + qualify( entityClassName, path )
+ "' targets an unknown entity named '" + manyToOne.getReferencedEntityName() + "'" );
}
manyToOne.setPropertyName( path );
createSyntheticPropertyReference( columns, referencedEntity, null, manyToOne, false, buildingContext );
TableBinder.bindForeignKey( referencedEntity, null, columns, manyToOne, unique, buildingContext );
createSyntheticPropertyReference(
columns,
targetEntity,
persistentClass,
manyToOne,
false,
buildingContext
);
TableBinder.bindForeignKey( targetEntity, persistentClass, columns, manyToOne, unique, buildingContext );
// HbmMetadataSourceProcessorImpl does this only when property-ref != null, but IMO, it makes sense event if it is null
if ( !manyToOne.isIgnoreNotFound() ) manyToOne.createPropertyRefConstraints( persistentClasses );
}

View File

@ -190,18 +190,18 @@ public abstract class CollectionBinder {
java.util.Collection.class
);
private final MetadataBuildingContext buildingContext;
final MetadataBuildingContext buildingContext;
private final Supplier<ManagedBean<? extends UserCollectionType>> customTypeBeanResolver;
private final boolean isSortedCollection;
protected Collection collection;
protected String propertyName;
PropertyHolder propertyHolder;
protected PropertyHolder propertyHolder;
private int batchSize;
private String mappedBy;
private XClass collectionElementType;
private XClass targetEntity;
private AnnotatedJoinColumn[] inverseJoinColumns;
protected AnnotatedJoinColumn[] inverseJoinColumns;
private String cascadeStrategy;
private String cacheConcurrencyStrategy;
private String cacheRegionName;
@ -211,15 +211,16 @@ public abstract class CollectionBinder {
protected String mapKeyPropertyName;
private boolean insertable = true;
private boolean updatable = true;
private AnnotatedJoinColumn[] fkJoinColumns;
protected AnnotatedJoinColumn[] foreignJoinColumns;
private AnnotatedJoinColumn[] joinColumns;
private boolean isExplicitAssociationTable;
private AnnotatedColumn[] elementColumns;
private boolean isEmbedded;
private XProperty property;
private NotFoundAction notFoundAction;
protected boolean isEmbedded;
protected XProperty property;
protected NotFoundAction notFoundAction;
private TableBinder tableBinder;
private AnnotatedColumn[] mapKeyColumns;
private AnnotatedJoinColumn[] mapKeyManyToManyColumns;
protected AnnotatedColumn[] mapKeyColumns;
protected AnnotatedJoinColumn[] mapKeyManyToManyColumns;
protected Map<String, IdentifierGeneratorDefinition> localGenerators;
protected Map<XClass, InheritanceState> inheritanceStatePerClass;
private XClass declaringClass;
@ -713,8 +714,6 @@ public abstract class CollectionBinder {
this.joinColumns = joinColumns;
}
private AnnotatedJoinColumn[] joinColumns;
public void setPropertyHolder(PropertyHolder propertyHolder) {
this.propertyHolder = propertyHolder;
}
@ -764,13 +763,13 @@ public abstract class CollectionBinder {
return binder;
}
private static CollectionBinder createBinderAutomatically(XProperty property, MetadataBuildingContext buildingContext) {
final CollectionClassification classification = determineCollectionClassification( property, buildingContext );
final CollectionTypeRegistrationDescriptor typeRegistration = buildingContext.getMetadataCollector()
private static CollectionBinder createBinderAutomatically(XProperty property, MetadataBuildingContext context) {
final CollectionClassification classification = determineCollectionClassification( property, context );
final CollectionTypeRegistrationDescriptor typeRegistration = context.getMetadataCollector()
.findCollectionTypeRegistration( classification );
return typeRegistration != null
? createBinderFromTypeRegistration( property, classification, typeRegistration, buildingContext )
: createBinderFromProperty( property, buildingContext );
? createBinderFromTypeRegistration( property, classification, typeRegistration, context )
: createBinderFromProperty( property, context );
}
private static CollectionBinder createBinderFromTypeRegistration(
@ -796,16 +795,17 @@ public abstract class CollectionBinder {
Class<? extends UserCollectionType> implementation,
Map<String,String> parameters,
MetadataBuildingContext buildingContext) {
final StandardServiceRegistry serviceRegistry = buildingContext.getBuildingOptions().getServiceRegistry();
final ManagedBeanRegistry beanRegistry = serviceRegistry.getService( ManagedBeanRegistry.class );
final ManagedBeanRegistry beanRegistry = buildingContext.getBuildingOptions()
.getServiceRegistry()
.getService( ManagedBeanRegistry.class );
if ( CollectionHelper.isNotEmpty( parameters ) ) {
return beanRegistry.getBean( implementation );
}
else {
// defined parameters...
if ( ParameterizedType.class.isAssignableFrom( implementation ) ) {
// because there are config parameters and the type is configurable, we need
// a separate bean instance which means uniquely naming it
// because there are config parameters and the type is configurable,
// we need a separate bean instance which means uniquely naming it
final ManagedBean<? extends UserCollectionType> typeBean = beanRegistry.getBean( role, implementation );
final UserCollectionType type = typeBean.getBeanInstance();
final Properties properties = new Properties();
@ -828,11 +828,9 @@ public abstract class CollectionBinder {
}
}
private static CollectionBinder createBinderFromProperty(
XProperty property,
MetadataBuildingContext buildingContext) {
final CollectionClassification classification = determineCollectionClassification( property, buildingContext );
return createBinder( property, null, classification, buildingContext );
private static CollectionBinder createBinderFromProperty(XProperty property, MetadataBuildingContext context) {
final CollectionClassification classification = determineCollectionClassification( property, context );
return createBinder( property, null, classification, context );
}
private static CollectionBinder createBinderFromCustomTypeAnnotation(
@ -853,20 +851,18 @@ public abstract class CollectionBinder {
public static ManagedBean<? extends UserCollectionType> resolveCustomType(
XProperty property,
CollectionType typeAnnotation,
MetadataBuildingContext buildingContext) {
final ManagedBeanRegistry beanRegistry = buildingContext.getBootstrapContext()
MetadataBuildingContext context) {
final ManagedBeanRegistry beanRegistry = context.getBootstrapContext()
.getServiceRegistry()
.getService( ManagedBeanRegistry.class );
final Class<? extends UserCollectionType> typeImpl = typeAnnotation.type();
if ( typeAnnotation.parameters().length == 0 ) {
// no parameters - we can re-use a no-config bean instance
// no parameters - we can reuse a no-config bean instance
return beanRegistry.getBean( typeImpl );
}
else {
// defined parameters...
final String attributeKey = property.getDeclaringClass().getName() + "#" + property.getName();
if ( ParameterizedType.class.isAssignableFrom( typeImpl ) ) {
// because there are config parameters and the type is configurable, we need
// a separate bean instance which means uniquely naming it
@ -883,7 +879,6 @@ public abstract class CollectionBinder {
attributeKey,
ParameterizedType.class.getName()
);
// but still return the bean - we can again use the no-config bean instance
return beanRegistry.getBean( typeImpl );
}
@ -1095,8 +1090,8 @@ public abstract class CollectionBinder {
}
public void bind() {
this.collection = createCollection( propertyHolder.getPersistentClass() );
String role = qualify( propertyHolder.getPath(), propertyName );
collection = createCollection( propertyHolder.getPersistentClass() );
final String role = qualify( propertyHolder.getPath(), propertyName );
LOG.debugf( "Collection role: %s", role );
collection.setRole( role );
collection.setMappedByProperty( mappedBy );
@ -1151,24 +1146,7 @@ public abstract class CollectionBinder {
if ( inheritanceStatePerClass == null) {
throw new AssertionFailure( "inheritanceStatePerClass not set" );
}
metadataCollector.addSecondPass(
getSecondPass(
fkJoinColumns,
joinColumns,
inverseJoinColumns,
elementColumns,
mapKeyColumns,
mapKeyManyToManyColumns,
isEmbedded,
property,
getElementType(),
notFoundAction,
oneToMany,
tableBinder,
buildingContext
),
!isMappedBy
);
metadataCollector.addSecondPass( getSecondPass(), !isMappedBy );
}
@SuppressWarnings({"rawtypes", "unchecked"})
@ -1464,7 +1442,7 @@ public abstract class CollectionBinder {
}
}
private XClass getElementType() {
XClass getElementType() {
if ( AnnotationBinder.isDefault( targetEntity, buildingContext ) ) {
if ( collectionElementType != null ) {
return collectionElementType;
@ -1479,37 +1457,11 @@ public abstract class CollectionBinder {
}
}
public SecondPass getSecondPass(
final AnnotatedJoinColumn[] fkJoinColumns,
final AnnotatedJoinColumn[] keyColumns,
final AnnotatedJoinColumn[] inverseColumns,
final AnnotatedColumn[] elementColumns,
final AnnotatedColumn[] mapKeyColumns,
final AnnotatedJoinColumn[] mapKeyManyToManyColumns,
final boolean isEmbedded,
final XProperty property,
final XClass elementType,
final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
SecondPass getSecondPass() {
return new CollectionSecondPass( collection ) {
@Override
public void secondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
bindStarToManySecondPass(
persistentClasses,
elementType,
fkJoinColumns,
keyColumns,
inverseColumns,
elementColumns,
isEmbedded,
property,
unique,
assocTableBinder,
notFoundAction,
buildingContext
);
bindStarToManySecondPass( persistentClasses );
}
};
}
@ -1517,20 +1469,9 @@ public abstract class CollectionBinder {
/**
* return true if it's a Fk, false if it's an association table
*/
protected boolean bindStarToManySecondPass(
Map<String, PersistentClass> persistentClasses,
XClass elementType,
AnnotatedJoinColumn[] fkJoinColumns,
AnnotatedJoinColumn[] keyColumns,
AnnotatedJoinColumn[] inverseColumns,
AnnotatedColumn[] elementColumns,
boolean isEmbedded,
XProperty property,
boolean unique,
TableBinder associationTableBinder,
NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) {
PersistentClass persistentClass = persistentClasses.get( elementType.getName() );
protected boolean bindStarToManySecondPass(Map<String, PersistentClass> persistentClasses) {
final XClass elementType = getElementType();
final PersistentClass persistentClass = persistentClasses.get( elementType.getName() );
boolean reversePropertyInJoin = false;
if ( persistentClass != null && hasMappedBy() ) {
try {
@ -1548,39 +1489,15 @@ public abstract class CollectionBinder {
&& oneToMany
&& !isExplicitAssociationTable
&& ( joinColumns[0].isImplicit() && hasMappedBy() //implicit @JoinColumn
|| !fkJoinColumns[0].isImplicit() ) //this is an explicit @JoinColumn
|| !foreignJoinColumns[0].isImplicit() ) //this is an explicit @JoinColumn
) {
//this is a foreign key
bindOneToManySecondPass(
getCollection(),
persistentClasses,
fkJoinColumns,
elementType,
cascadeDeleteEnabled,
notFoundAction,
buildingContext,
inheritanceStatePerClass
);
bindOneToManySecondPass( persistentClasses );
return true;
}
else {
//this is an association table
bindManyToManySecondPass(
this.collection,
persistentClasses,
keyColumns,
inverseColumns,
elementColumns,
isEmbedded,
elementType,
notFoundAction,
unique,
cascadeDeleteEnabled,
associationTableBinder,
property,
propertyHolder,
buildingContext
);
bindManyToManySecondPass( persistentClasses );
return false;
}
}
@ -1589,42 +1506,34 @@ public abstract class CollectionBinder {
return isNotEmpty( mappedBy );
}
protected void bindOneToManySecondPass(
Collection collection,
Map<String, PersistentClass> persistentClasses,
AnnotatedJoinColumn[] fkJoinColumns,
XClass collectionType,
boolean cascadeDeleteEnabled,
NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
protected void bindOneToManySecondPass(Map<String, PersistentClass> persistentClasses) {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding a OneToMany: %s.%s through a foreign key", propertyHolder.getEntityName(), propertyName );
}
if ( buildingContext == null ) {
throw new AssertionFailure(
"CollectionSecondPass for oneToMany should not be called with null mappings"
);
if ( property == null ) {
throw new AssertionFailure( "null was passed for argument property" );
}
logOneToManySeconPass();
final org.hibernate.mapping.OneToMany oneToMany =
new org.hibernate.mapping.OneToMany( buildingContext, collection.getOwner() );
new org.hibernate.mapping.OneToMany( buildingContext, getCollection().getOwner() );
collection.setElement( oneToMany );
oneToMany.setReferencedEntityName( collectionType.getName() );
oneToMany.setReferencedEntityName( getElementType().getName() );
oneToMany.setNotFoundAction( notFoundAction );
final InFlightMetadataCollector collector = buildingContext.getMetadataCollector();
final String assocClass = oneToMany.getReferencedEntityName();
final PersistentClass associatedClass = persistentClasses.get( assocClass );
handleJpaOrderBy( collection, associatedClass );
final Map<String, Join> joins = buildingContext.getMetadataCollector().getJoins( assocClass );
final Map<String, Join> joins = collector.getJoins( assocClass );
if ( associatedClass == null ) {
throw new MappingException(
String.format("Association [%s] for entity [%s] references unmapped class [%s]",
propertyName, propertyHolder.getClassName(), assocClass)
String.format( "Association [%s] for entity [%s] references unmapped class [%s]",
propertyName, propertyHolder.getClassName(), assocClass )
);
}
oneToMany.setAssociatedClass( associatedClass );
for ( AnnotatedJoinColumn column : fkJoinColumns ) {
for ( AnnotatedJoinColumn column : foreignJoinColumns ) {
column.setPersistentClass( associatedClass, joins, inheritanceStatePerClass );
column.setJoins( joins );
collection.setCollectionTable( column.getTable() );
@ -1635,23 +1544,16 @@ public abstract class CollectionBinder {
bindFilters( false );
handleWhere( false );
bindCollectionSecondPass(
collection,
null,
fkJoinColumns,
cascadeDeleteEnabled,
property,
propertyHolder,
buildingContext
);
PersistentClass targetEntity = persistentClasses.get( getElementType().getName() );
bindCollectionSecondPass( targetEntity, foreignJoinColumns, cascadeDeleteEnabled, buildingContext );
if ( !collection.isInverse() && !collection.getKey().isNullable() ) {
// for non-inverse one-to-many, with a not-null fk, add a backref!
final String entityName = oneToMany.getReferencedEntityName();
final PersistentClass referenced = buildingContext.getMetadataCollector().getEntityBinding( entityName );
final PersistentClass referenced = collector.getEntityBinding( entityName );
final Backref backref = new Backref();
AnnotatedJoinColumn fkColumn = fkJoinColumns[0];
backref.setName( '_' + fkColumn.getPropertyName() + '_' + fkColumn.getLogicalColumnName() + "Backref" );
final AnnotatedJoinColumn column = foreignJoinColumns[0];
backref.setName( '_' + column.getPropertyName() + '_' + column.getLogicalColumnName() + "Backref" );
backref.setUpdateable( false );
backref.setSelectable( false );
backref.setCollectionRole( collection.getRole() );
@ -2053,90 +1955,70 @@ public abstract class CollectionBinder {
}
}
private void bindManyToManySecondPass(
Collection collValue,
Map<String, PersistentClass> persistentClasses,
AnnotatedJoinColumn[] joinColumns,
AnnotatedJoinColumn[] inverseJoinColumns,
AnnotatedColumn[] elementColumns,
boolean isEmbedded,
XClass elementType,
NotFoundAction notFoundAction,
boolean unique,
boolean cascadeDeleteEnabled,
TableBinder associationTableBinder,
XProperty property,
PropertyHolder parentPropertyHolder,
MetadataBuildingContext buildingContext) throws MappingException {
private void bindManyToManySecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
if ( property == null ) {
throw new IllegalArgumentException( "null was passed for argument property" );
throw new AssertionFailure( "null was passed for argument property" );
}
final PersistentClass collectionEntity = persistentClasses.get( elementType.getName() );
final XClass elementType = getElementType();
final PersistentClass targetEntity = persistentClasses.get( elementType.getName() );
final String hqlOrderBy = extractHqlOrderBy( jpaOrderBy );
boolean isCollectionOfEntities = collectionEntity != null;
boolean isManyToAny = property.isAnnotationPresent( ManyToAny.class );
final boolean isCollectionOfEntities = targetEntity != null;
final boolean isManyToAny = property.isAnnotationPresent( ManyToAny.class );
logManyToManySecondPass( oneToMany, isCollectionOfEntities, isManyToAny );
logManyToManySecondPass( collValue, joinColumns, unique, isCollectionOfEntities, isManyToAny );
//check for user error
detectManyToManyProblems(
elementType,
property,
parentPropertyHolder,
propertyHolder,
isCollectionOfEntities,
isManyToAny
);
if ( hasMappedBy() ) {
handleUnownedManyToMany(
collValue,
collection,
joinColumns,
elementType,
collectionEntity,
targetEntity,
isCollectionOfEntities
);
}
else {
handleOwnedManyToMany(
collValue,
collection,
joinColumns,
associationTableBinder,
tableBinder,
property,
buildingContext,
collectionEntity,
targetEntity,
isCollectionOfEntities
);
}
bindFilters( isCollectionOfEntities );
handleWhere( isCollectionOfEntities );
bindCollectionSecondPass(
collValue,
collectionEntity,
joinColumns,
cascadeDeleteEnabled,
property,
propertyHolder,
buildingContext
);
bindCollectionSecondPass( targetEntity, joinColumns, cascadeDeleteEnabled, buildingContext );
ManyToOne element = null;
if ( isCollectionOfEntities ) {
element = handleCollectionOfEntities(
collValue,
final ManyToOne element = handleCollectionOfEntities(
collection,
elementType,
notFoundAction,
property,
buildingContext,
collectionEntity,
targetEntity,
hqlOrderBy
);
bindManyToManyInverseForeignKey( targetEntity, inverseJoinColumns, element, oneToMany, buildingContext );
}
else if ( isManyToAny ) {
handleManyToAny(
collValue,
collection,
inverseJoinColumns,
cascadeDeleteEnabled,
property,
@ -2145,28 +2027,22 @@ public abstract class CollectionBinder {
}
else {
handleElementCollection(
collValue,
collection,
elementColumns,
isEmbedded,
elementType,
property,
parentPropertyHolder,
propertyHolder,
buildingContext,
hqlOrderBy
);
}
checkFilterConditions( collValue );
//FIXME: do optional = false
if ( isCollectionOfEntities ) {
bindManyToManyInverseForeignKey( collectionEntity, inverseJoinColumns, element, unique, buildingContext );
}
checkFilterConditions( collection );
}
private void handleElementCollection(
Collection collValue,
Collection collection,
AnnotatedColumn[] elementColumns,
boolean isEmbedded,
XClass elementType,
@ -2174,16 +2050,15 @@ public abstract class CollectionBinder {
PropertyHolder parentPropertyHolder,
MetadataBuildingContext buildingContext,
String hqlOrderBy) {
XClass elementClass;
AnnotatedClassType classType;
CollectionPropertyHolder holder;
final XClass elementClass;
final AnnotatedClassType classType;
final CollectionPropertyHolder holder;
if ( PRIMITIVE_NAMES.contains( elementType.getName() ) ) {
classType = AnnotatedClassType.NONE;
elementClass = null;
holder = PropertyHolderBuilder.buildPropertyHolder(
collValue,
collValue.getRole(),
collection,
collection.getRole(),
null,
property,
parentPropertyHolder,
@ -2192,41 +2067,34 @@ public abstract class CollectionBinder {
}
else {
elementClass = elementType;
classType = buildingContext.getMetadataCollector().getClassType( elementClass );
holder = PropertyHolderBuilder.buildPropertyHolder(
collValue,
collValue.getRole(),
collection,
collection.getRole(),
elementClass,
property,
parentPropertyHolder,
buildingContext
);
// 'parentPropertyHolder' is the PropertyHolder for the owner of the collection
// 'holder' is the CollectionPropertyHolder.
// 'property' is the collection XProperty
parentPropertyHolder.startingProperty(property);
parentPropertyHolder.startingProperty( property );
//force in case of attribute override
boolean attributeOverride = property.isAnnotationPresent( AttributeOverride.class )
|| property.isAnnotationPresent( AttributeOverrides.class );
// todo : force in the case of Convert annotation(s) with embedded paths (beyond key/value prefixes)?
if ( isEmbedded || attributeOverride ) {
classType = AnnotatedClassType.EMBEDDABLE;
}
classType = isEmbedded || attributeOverride
? AnnotatedClassType.EMBEDDABLE
: buildingContext.getMetadataCollector().getClassType(elementClass);
}
final Class<? extends CompositeUserType<?>> compositeUserType = resolveCompositeUserType(
property,
elementClass,
buildingContext
);
final Class<? extends CompositeUserType<?>> compositeUserType =
resolveCompositeUserType( property, elementClass, buildingContext );
if ( AnnotatedClassType.EMBEDDABLE == classType || compositeUserType != null ) {
holder.prepare(property);
EntityBinder entityBinder = new EntityBinder();
PersistentClass owner = collValue.getOwner();
final EntityBinder entityBinder = new EntityBinder();
final PersistentClass owner = collection.getOwner();
final AccessType baseAccessType;
final Access accessAnn = property.getAnnotation( Access.class );
@ -2270,17 +2138,17 @@ public abstract class CollectionBinder {
inheritanceStatePerClass
);
collValue.setElement( component );
collection.setElement( component );
if ( isNotEmpty(hqlOrderBy) ) {
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
if ( isNotEmpty( hqlOrderBy ) ) {
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
if ( orderBy != null ) {
collValue.setOrderBy( orderBy );
collection.setOrderBy( orderBy );
}
}
}
else {
holder.prepare(property);
holder.prepare( property );
final BasicValueBinder elementBinder =
new BasicValueBinder( BasicValueBinder.Kind.COLLECTION_ELEMENT, buildingContext);
@ -2300,21 +2168,21 @@ public abstract class CollectionBinder {
}
//override the table
for (AnnotatedColumn column : elementColumns) {
column.setTable( collValue.getCollectionTable() );
column.setTable( collection.getCollectionTable() );
}
elementBinder.setColumns(elementColumns);
elementBinder.setType(
property,
elementClass,
collValue.getOwnerEntityName(),
collection.getOwnerEntityName(),
holder.resolveElementAttributeConverterDescriptor( property, elementClass )
);
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
elementBinder.setAccessType( accessType );
collValue.setElement( elementBinder.make() );
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment(hqlOrderBy);
collection.setElement( elementBinder.make() );
String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy );
if ( orderBy != null ) {
collValue.setOrderBy( orderBy );
collection.setOrderBy( orderBy );
}
}
}
@ -2543,30 +2411,6 @@ public abstract class CollectionBinder {
}
}
private void logManyToManySecondPass(
Collection collValue,
AnnotatedJoinColumn[] joinColumns,
boolean unique,
boolean isCollectionOfEntities,
boolean isManyToAny) {
if ( LOG.isDebugEnabled() ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
if ( isCollectionOfEntities && unique) {
LOG.debugf("Binding a OneToMany: %s through an association table", path);
}
else if (isCollectionOfEntities) {
LOG.debugf("Binding a ManyToMany: %s", path);
}
else if (isManyToAny) {
LOG.debugf("Binding a ManyToAny: %s", path);
}
else {
LOG.debugf("Binding a collection of element: %s", path);
}
}
}
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(
XProperty property,
XClass propertyClass,
@ -2619,47 +2463,57 @@ public abstract class CollectionBinder {
return null; // @OrderBy not found.
}
private static void checkFilterConditions(Collection collValue) {
private static void checkFilterConditions(Collection collection) {
//for now it can't happen, but sometime soon...
if ( ( collValue.getFilters().size() != 0 || isNotEmpty( collValue.getWhere() ) ) &&
collValue.getFetchMode() == FetchMode.JOIN &&
!( collValue.getElement() instanceof SimpleValue ) && //SimpleValue (CollectionOfElements) are always SELECT but it does not matter
collValue.getElement().getFetchMode() != FetchMode.JOIN ) {
if ( ( collection.getFilters().size() != 0 || isNotEmpty( collection.getWhere() ) )
&& collection.getFetchMode() == FetchMode.JOIN
&& !( collection.getElement() instanceof SimpleValue ) //SimpleValue (CollectionOfElements) are always SELECT but it does not matter
&& collection.getElement().getFetchMode() != FetchMode.JOIN ) {
throw new MappingException(
"@ManyToMany or @ElementCollection defining filter or where without join fetching "
+ "not valid within collection using join fetching[" + collValue.getRole() + "]"
+ "not valid within collection using join fetching[" + collection.getRole() + "]"
);
}
}
private void bindCollectionSecondPass(
Collection collValue,
PersistentClass collectionEntity,
PersistentClass targetEntity,
AnnotatedJoinColumn[] joinColumns,
boolean cascadeDeleteEnabled,
XProperty property,
PropertyHolder propertyHolder,
MetadataBuildingContext buildingContext) {
try {
createSyntheticPropertyReference(
joinColumns,
collValue.getOwner(),
collectionEntity,
collValue,
false,
buildingContext
);
}
catch (AnnotationException ex) {
throw new AnnotationException( "Unable to map collection "
+ collValue.getOwner().getClassName() + "." + property.getName(), ex );
}
DependantValue key = buildCollectionKey( collValue, joinColumns, cascadeDeleteEnabled,
buildingContext.getBuildingOptions().isNoConstraintByDefault(), property, propertyHolder, buildingContext );
MetadataBuildingContext context) {
createSyntheticPropertyReference(
joinColumns,
collection.getOwner(),
collection.getOwner(),
collection,
false,
context
);
final DependantValue key = buildCollectionKey(
collection,
joinColumns,
cascadeDeleteEnabled,
context.getBuildingOptions().isNoConstraintByDefault(),
property,
propertyHolder,
context
);
//TODO: this is really warty
if ( property.isAnnotationPresent( ElementCollection.class ) && joinColumns.length > 0 ) {
joinColumns[0].setJPA2ElementCollection( true );
}
TableBinder.bindForeignKey( collValue.getOwner(), collectionEntity, joinColumns, key, false, buildingContext );
TableBinder.bindForeignKey(
collection.getOwner(),
targetEntity,
joinColumns,
key,
false,
context
);
key.sortProperties();
}
@ -2678,36 +2532,48 @@ public abstract class CollectionBinder {
* collection element. Otherwise, delegate to the usual algorithm.
*/
public void bindManyToManyInverseForeignKey(
PersistentClass referencedEntity,
PersistentClass targetEntity,
AnnotatedJoinColumn[] columns,
SimpleValue value,
boolean unique,
MetadataBuildingContext buildingContext) {
MetadataBuildingContext context) {
if ( hasMappedBy() ) {
final Property property = referencedEntity.getRecursiveProperty( mappedBy );
final List<Selectable> mappedByColumns = mappedByColumns( referencedEntity, property );
final Property property = targetEntity.getRecursiveProperty( mappedBy );
final List<Selectable> mappedByColumns = mappedByColumns( targetEntity, property );
for ( Selectable selectable: mappedByColumns ) {
columns[0].linkValueUsingAColumnCopy( (Column) selectable, value );
}
final String referencedPropertyName = buildingContext.getMetadataCollector()
.getPropertyReferencedAssociation( referencedEntity.getEntityName(), mappedBy );
final String referencedPropertyName = context.getMetadataCollector()
.getPropertyReferencedAssociation( targetEntity.getEntityName(), mappedBy );
if ( referencedPropertyName != null ) {
//TODO always a many to one?
( (ManyToOne) value ).setReferencedPropertyName( referencedPropertyName );
buildingContext.getMetadataCollector().addUniquePropertyReference(
referencedEntity.getEntityName(),
referencedPropertyName
);
context.getMetadataCollector()
.addUniquePropertyReference( targetEntity.getEntityName(), referencedPropertyName );
}
( (ManyToOne) value ).setReferenceToPrimaryKey( referencedPropertyName == null );
value.createForeignKey();
}
else {
createSyntheticPropertyReference( columns, referencedEntity, null, value, true, buildingContext );
createSyntheticPropertyReference(
columns,
targetEntity,
collection.getOwner(),
value,
true,
context
);
if ( notFoundAction == NotFoundAction.IGNORE ) {
value.disableForeignKey();
}
TableBinder.bindForeignKey( referencedEntity, null, columns, value, unique, buildingContext );
TableBinder.bindForeignKey(
targetEntity,
collection.getOwner(),
columns,
value,
unique,
context
);
}
}
@ -2732,7 +2598,7 @@ public abstract class CollectionBinder {
}
public void setFkJoinColumns(AnnotatedJoinColumn[] annotatedJoinColumns) {
this.fkJoinColumns = annotatedJoinColumns;
this.foreignJoinColumns = annotatedJoinColumns;
}
public void setExplicitAssociationTable(boolean explicitAssocTable) {
@ -2770,4 +2636,31 @@ public abstract class CollectionBinder {
public void setLocalGenerators(Map<String, IdentifierGeneratorDefinition> localGenerators) {
this.localGenerators = localGenerators;
}
private void logOneToManySeconPass() {
if ( LOG.isDebugEnabled() ) {
LOG.debugf( "Binding a OneToMany: %s through a foreign key", safeCollectionRole() );
}
}
private void logManyToManySecondPass(
boolean isOneToMany,
boolean isCollectionOfEntities,
boolean isManyToAny) {
if ( LOG.isDebugEnabled() ) {
if ( isCollectionOfEntities && isOneToMany ) {
LOG.debugf( "Binding a OneToMany: %s through an association table", safeCollectionRole() );
}
else if ( isCollectionOfEntities ) {
LOG.debugf( "Binding a ManyToMany: %s", safeCollectionRole() );
}
else if ( isManyToAny ) {
LOG.debugf( "Binding a ManyToAny: %s", safeCollectionRole() );
}
else {
LOG.debugf( "Binding a collection of element: %s", safeCollectionRole() );
}
}
}
}

View File

@ -1800,12 +1800,12 @@ public class EntityBinder {
}
}
private void bindJoinToPersistentClass(Join join, AnnotatedJoinColumn[] annotatedJoinColumns, MetadataBuildingContext buildingContext) {
DependantValue key = new DependantValue( buildingContext, join.getTable(), persistentClass.getIdentifier() );
private void bindJoinToPersistentClass(Join join, AnnotatedJoinColumn[] joinColumns, MetadataBuildingContext context) {
DependantValue key = new DependantValue( context, join.getTable(), persistentClass.getIdentifier() );
join.setKey( key );
setForeignKeyNameIfDefined( join );
key.setCascadeDeleteEnabled( false );
TableBinder.bindForeignKey( persistentClass, null, annotatedJoinColumns, key, false, buildingContext );
TableBinder.bindForeignKey( persistentClass, null, joinColumns, key, false, context );
key.sortProperties();
join.createPrimaryKey();
join.createForeignKey();

View File

@ -52,33 +52,8 @@ public class IdBagBinder extends BagBinder {
}
@Override
protected boolean bindStarToManySecondPass(
Map<String, PersistentClass> persistentClasses,
XClass collType,
AnnotatedJoinColumn[] fkJoinColumns,
AnnotatedJoinColumn[] keyColumns,
AnnotatedJoinColumn[] inverseColumns,
AnnotatedColumn[] elementColumns,
boolean isEmbedded,
XProperty property,
boolean unique,
TableBinder associationTableBinder,
NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext) {
boolean result = super.bindStarToManySecondPass(
persistentClasses,
collType,
fkJoinColumns,
keyColumns,
inverseColumns,
elementColumns,
isEmbedded,
property,
unique,
associationTableBinder,
notFoundAction,
getBuildingContext()
);
protected boolean bindStarToManySecondPass(Map<String, PersistentClass> persistentClasses) {
boolean result = super.bindStarToManySecondPass( persistentClasses );
final CollectionId collectionIdAnn = property.getAnnotation( CollectionId.class );
if ( collectionIdAnn == null ) {
@ -120,7 +95,7 @@ public class IdBagBinder extends BagBinder {
valueBinder.setType(
property,
collType,
getElementType(),
null,
null
);

View File

@ -63,39 +63,13 @@ public class ListBinder extends CollectionBinder {
}
@Override
public SecondPass getSecondPass(
final AnnotatedJoinColumn[] fkJoinColumns,
final AnnotatedJoinColumn[] keyColumns,
final AnnotatedJoinColumn[] inverseColumns,
final AnnotatedColumn[] elementColumns,
AnnotatedColumn[] mapKeyColumns,
final AnnotatedJoinColumn[] mapKeyManyToManyColumns,
final boolean isEmbedded,
final XProperty property,
final XClass elementType,
final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
public SecondPass getSecondPass() {
return new CollectionSecondPass( ListBinder.this.collection ) {
@Override
public void secondPass(Map<String, PersistentClass> persistentClasses)
throws MappingException {
bindStarToManySecondPass(
persistentClasses,
elementType,
fkJoinColumns,
keyColumns,
inverseColumns,
elementColumns,
isEmbedded,
property,
unique,
assocTableBinder,
notFoundAction,
buildingContext
);
bindIndex( property, elementType, buildingContext );
bindStarToManySecondPass( persistentClasses );
bindIndex( property, getElementType(), buildingContext );
}
};
}

View File

@ -16,7 +16,6 @@ import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.MapKeyCompositeType;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.BootstrapContext;
@ -89,31 +88,21 @@ public class MapBinder extends CollectionBinder {
}
@Override
public SecondPass getSecondPass(
final AnnotatedJoinColumn[] fkJoinColumns,
final AnnotatedJoinColumn[] keyColumns,
final AnnotatedJoinColumn[] inverseColumns,
final AnnotatedColumn[] elementColumns,
final AnnotatedColumn[] mapKeyColumns,
final AnnotatedJoinColumn[] mapKeyManyToManyColumns,
final boolean isEmbedded,
final XProperty property,
final XClass collType,
final NotFoundAction notFoundAction,
final boolean unique,
final TableBinder assocTableBinder,
final MetadataBuildingContext buildingContext) {
SecondPass getSecondPass() {
return new CollectionSecondPass( MapBinder.this.collection ) {
public void secondPass(Map<String, PersistentClass> persistentClasses)
throws MappingException {
bindStarToManySecondPass(
persistentClasses, collType, fkJoinColumns, keyColumns, inverseColumns, elementColumns,
isEmbedded, property, unique, assocTableBinder, notFoundAction, buildingContext
);
bindStarToManySecondPass( persistentClasses );
bindKeyFromAssociationTable(
collType, persistentClasses, mapKeyPropertyName, property, isEmbedded, buildingContext,
mapKeyColumns, mapKeyManyToManyColumns,
inverseColumns != null ? inverseColumns[0].getPropertyName() : null
getElementType(),
persistentClasses,
mapKeyPropertyName,
property,
isEmbedded,
buildingContext,
mapKeyColumns,
mapKeyManyToManyColumns,
inverseJoinColumns != null ? inverseJoinColumns[0].getPropertyName() : null
);
makeOneToManyMapKeyColumnNullableIfNotInProperty( property );
}

View File

@ -543,7 +543,7 @@ public class TableBinder {
associatedClass = destinationEntity;
}
else {
PropertyHolder holder = columns[0].getPropertyHolder();
final PropertyHolder holder = columns[0].getPropertyHolder();
associatedClass = holder == null ? null : holder.getPersistentClass();
}
@ -594,6 +594,7 @@ public class TableBinder {
PersistentClass associatedClass) {
//implicit case, we hope PK and FK columns are in the same order
if ( columns.length != referencedEntity.getIdentifier().getColumnSpan() ) {
// TODO: what about secondary tables?? associatedClass is null?
throw new AnnotationException(
"An association that targets entity '" + referencedEntity.getEntityName()
+ "' from entity '" + associatedClass.getEntityName()

View File

@ -120,53 +120,57 @@ public enum CollectionClassification {
if ( value == null ) {
return null;
}
if ( value instanceof CollectionClassification ) {
return ( (CollectionClassification) value );
else if ( value instanceof CollectionClassification ) {
return (CollectionClassification) value;
}
if ( value instanceof String ) {
else if ( value instanceof String ) {
final String string = (String) value;
for ( CollectionClassification collectionClassification : values() ) {
if ( collectionClassification.name().equalsIgnoreCase( string ) ) {
return collectionClassification;
}
}
return null;
}
else if ( value instanceof Class ) {
return interpretClass( (Class<?>) value );
}
else {
return null;
}
}
private static CollectionClassification interpretClass(Class<?> configuredClass) {
if ( java.util.List.class.isAssignableFrom(configuredClass) ) {
return LIST;
}
if ( SortedSet.class.isAssignableFrom(configuredClass) ) {
return SORTED_SET;
}
if ( java.util.Set.class.isAssignableFrom(configuredClass) ) {
return SET;
}
if ( SortedMap.class.isAssignableFrom(configuredClass) ) {
return SORTED_MAP;
}
if ( java.util.Map.class.isAssignableFrom(configuredClass) ) {
return MAP;
}
if ( java.util.Collection.class.isAssignableFrom(configuredClass) ) {
return BAG;
}
if ( value instanceof Class ) {
final Class<?> configuredClass = (Class<?>) value;
if ( java.util.List.class.isAssignableFrom( configuredClass ) ) {
return LIST;
}
if ( SortedSet.class.isAssignableFrom( configuredClass ) ) {
return SORTED_SET;
}
if ( java.util.Set.class.isAssignableFrom( configuredClass ) ) {
return SET;
}
if ( SortedMap.class.isAssignableFrom( configuredClass ) ) {
return SORTED_MAP;
}
if ( java.util.Map.class.isAssignableFrom( configuredClass ) ) {
return MAP;
}
if ( java.util.Collection.class.isAssignableFrom( configuredClass ) ) {
return BAG;
}
BootLogging.BOOT_LOGGER.debugf(
"Unexpected Class specified for CollectionClassification resolution (`%s`) - " +
"should be one of `%s`, `%s`, `%s`, `%s`, `%s` or `%s` (or subclass of)",
configuredClass.getName(),
java.util.List.class.getName(),
SortedSet.class.getName(),
java.util.Set.class.getName(),
SortedMap.class.isAssignableFrom( configuredClass ),
java.util.Map.class.isAssignableFrom( configuredClass ),
java.util.Collection.class.getName()
);
}
BootLogging.BOOT_LOGGER.debugf(
"Unexpected Class specified for CollectionClassification resolution (`%s`) - " +
"should be one of `%s`, `%s`, `%s`, `%s`, `%s` or `%s` (or subclass of)",
configuredClass.getName(),
java.util.List.class.getName(),
SortedSet.class.getName(),
java.util.Set.class.getName(),
SortedMap.class.isAssignableFrom(configuredClass),
java.util.Map.class.isAssignableFrom(configuredClass),
java.util.Collection.class.getName()
);
return null;
}

View File

@ -1,102 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.test.cfg.annotations;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AnnotatedJoinColumn;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.annotations.CollectionBinder;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Test for HHH-10106
*
* @author Vyacheslav Rarata
*/
public class CollectionBinderTest extends BaseUnitTestCase {
@Test
@TestForIssue(jiraKey = "HHH-10106")
public void testAssociatedClassException() throws SQLException {
final Collection collection = mock(Collection.class);
final XClass collectionType = mock(XClass.class);
final MetadataBuildingContext buildingContext = mock(MetadataBuildingContext.class);
final InFlightMetadataCollector inFly = mock(InFlightMetadataCollector.class);
final PersistentClass persistentClass = mock(PersistentClass.class);
final Table table = mock(Table.class);
when(buildingContext.getMetadataCollector()).thenReturn(inFly);
when(collection.getOwner()).thenReturn(persistentClass);
when(collectionType.getName()).thenReturn("List");
when(persistentClass.getTable()).thenReturn(table);
when(table.getName()).thenReturn("Hibernate");
String expectMessage = "Association [abc] for entity [CollectionBinderTest] references unmapped class [List]";
try {
new CollectionBinder( null, false, buildingContext ) {
{
final PropertyHolder propertyHolder = Mockito.mock(PropertyHolder.class);
when(propertyHolder.getClassName()).thenReturn( CollectionBinderTest.class.getSimpleName() );
this.propertyName = "abc";
setPropertyHolder( propertyHolder );
}
@Override
protected Collection createCollection(PersistentClass persistentClass) {
return null;
}
@Override
public void bindOneToManySecondPass(
Collection collection,
Map<String, PersistentClass> persistentClasses,
AnnotatedJoinColumn[] fkJoinColumns,
XClass collectionType,
boolean cascadeDeleteEnabled,
NotFoundAction notFoundAction,
MetadataBuildingContext buildingContext,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
super.bindOneToManySecondPass(
collection,
persistentClasses,
fkJoinColumns,
collectionType,
cascadeDeleteEnabled,
notFoundAction,
buildingContext,
inheritanceStatePerClass
);
}
}.bindOneToManySecondPass( collection, new HashMap(), null, collectionType, false, null, buildingContext, null);
} catch (MappingException e) {
assertEquals(expectMessage, e.getMessage());
}
}
}