String hqlOrderBy,
Mappings mappings) throws MappingException {
PersistentClass collectionEntity = (PersistentClass) persistentClasses.get( collType.getName() );
boolean isCollectionOfEntities = collectionEntity != null;
ManyToAny anyAnn = property.getAnnotation( ManyToAny.class );
if ( log.isDebugEnabled() ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
if ( isCollectionOfEntities && unique ) {
log.debug( "Binding a OneToMany: {} through an association table", path );
}
else if ( isCollectionOfEntities ) {
log.debug( "Binding as ManyToMany: {}", path );
}
else if ( anyAnn != null ) {
log.debug( "Binding a ManyToAny: {}", path );
}
else {
log.debug( "Binding a collection of element: {}", path );
}
}
//check for user error
if ( !isCollectionOfEntities ) {
if ( property.isAnnotationPresent( ManyToMany.class ) || property.isAnnotationPresent( OneToMany.class ) ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
throw new AnnotationException(
"Use of @OneToMany or @ManyToMany targeting an unmapped class: " + path + "[" + collType + "]"
);
}
else if ( anyAnn != null ) {
if ( parentPropertyHolder.getJoinTable( property ) == null ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
throw new AnnotationException(
"@JoinTable is mandatory when @ManyToAny is used: " + path
);
}
}
else {
JoinTable joinTableAnn = parentPropertyHolder.getJoinTable( property );
if ( joinTableAnn != null && joinTableAnn.inverseJoinColumns().length > 0 ) {
String path = collValue.getOwnerEntityName() + "." + joinColumns[0].getPropertyName();
throw new AnnotationException(
"Use of @JoinTable.inverseJoinColumns targeting an unmapped class: " + path + "[" + collType + "]"
);
}
}
}
boolean mappedBy = !BinderHelper.isEmptyAnnotationValue( joinColumns[0].getMappedBy() );
if ( mappedBy ) {
if ( !isCollectionOfEntities ) {
StringBuilder error = new StringBuilder( 80 )
.append(
"Collection of elements must not have mappedBy or association reference an unmapped entity: "
)
.append( collValue.getOwnerEntityName() )
.append( "." )
.append( joinColumns[0].getPropertyName() );
throw new AnnotationException( error.toString() );
}
Property otherSideProperty;
try {
otherSideProperty = collectionEntity.getRecursiveProperty( joinColumns[0].getMappedBy() );
}
catch (MappingException e) {
StringBuilder error = new StringBuilder( 80 );
error.append( "mappedBy reference an unknown target entity property: " )
.append( collType ).append( "." ).append( joinColumns[0].getMappedBy() )
.append( " in " )
.append( collValue.getOwnerEntityName() )
.append( "." )
.append( joinColumns[0].getPropertyName() );
throw new AnnotationException( error.toString() );
}
Table table;
if ( otherSideProperty.getValue() instanceof Collection ) {
//this is a collection on the other side
table = ( (Collection) otherSideProperty.getValue() ).getCollectionTable();
}
else {
//This is a ToOne with a @JoinTable or a regular property
table = otherSideProperty.getValue().getTable();
}
collValue.setCollectionTable( table );
String entityName = collectionEntity.getEntityName();
for (Ejb3JoinColumn column : joinColumns) {
//column.setDefaultColumnHeader( joinColumns[0].getMappedBy() ); //seems not to be used, make sense
column.setManyToManyOwnerSideEntityName( entityName );
}
}
else {
//TODO: only for implicit columns?
//FIXME NamingStrategy
for (Ejb3JoinColumn column : joinColumns) {
String mappedByProperty = mappings.getFromMappedBy(
collValue.getOwnerEntityName(), column.getPropertyName()
);
Table ownerTable = collValue.getOwner().getTable();
column.setMappedBy(
collValue.getOwner().getEntityName(), mappings.getLogicalTableName( ownerTable ),
mappedByProperty
);
// String header = ( mappedByProperty == null ) ? mappings.getLogicalTableName( ownerTable ) : mappedByProperty;
// column.setDefaultColumnHeader( header );
}
if ( StringHelper.isEmpty( associationTableBinder.getName() ) ) {
//default value
associationTableBinder.setDefaultName(
collValue.getOwner().getEntityName(),
mappings.getLogicalTableName( collValue.getOwner().getTable() ),
collectionEntity != null ? collectionEntity.getEntityName() : null,
collectionEntity != null ? mappings.getLogicalTableName( collectionEntity.getTable() ) : null,
joinColumns[0].getPropertyName()
);
}
associationTableBinder.setJPA2ElementCollection( !isCollectionOfEntities && property.isAnnotationPresent( ElementCollection.class ));
collValue.setCollectionTable( associationTableBinder.bind() );
}
bindFilters( isCollectionOfEntities );
bindCollectionSecondPass( collValue, collectionEntity, joinColumns, cascadeDeleteEnabled, property, mappings );
ManyToOne element = null;
if ( isCollectionOfEntities ) {
element =
new ManyToOne( mappings, collValue.getCollectionTable() );
collValue.setElement( element );
element.setReferencedEntityName( collType.getName() );
//element.setFetchMode( fetchMode );
//element.setLazy( fetchMode != FetchMode.JOIN );
//make the second join non lazy
element.setFetchMode( FetchMode.JOIN );
element.setLazy( false );
element.setIgnoreNotFound( ignoreNotFound );
// as per 11.1.38 of JPA-2 spec, default to primary key if no column is specified by @OrderBy.
if ( hqlOrderBy != null ) {
collValue.setManyToManyOrdering(
buildOrderByClauseFromHql( hqlOrderBy, collectionEntity, collValue.getRole() )
);
}
ForeignKey fk = property != null ? property.getAnnotation( ForeignKey.class ) : null;
String fkName = fk != null ? fk.inverseName() : "";
if ( !BinderHelper.isEmptyAnnotationValue( fkName ) ) element.setForeignKeyName( fkName );
}
else if ( anyAnn != null ) {
//@ManyToAny
//Make sure that collTyp is never used during the @ManyToAny branch: it will be set to void.class
PropertyData inferredData = new PropertyInferredData(null, property, "unsupported", mappings.getReflectionManager() );
//override the table
for (Ejb3Column column : inverseJoinColumns) {
column.setTable( collValue.getCollectionTable() );
}
Any any = BinderHelper.buildAnyValue( anyAnn.metaDef(), inverseJoinColumns, anyAnn.metaColumn(),
inferredData, cascadeDeleteEnabled, Nullability.NO_CONSTRAINT,
propertyHolder, new EntityBinder(), true, mappings );
collValue.setElement( any );
}
else {