if ( property.isAnnotationPresent( Parent.class ) ) {
if ( propertyHolder.isComponent() ) {
propertyHolder.setParentProperty( property.getName() );
}
else {
throw new AnnotationException(
"@Parent cannot be applied outside an embeddable object: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
return;
}
ColumnsBuilder columnsBuilder = new ColumnsBuilder(
propertyHolder, nullability, property, inferredData, entityBinder, mappings
).extractMetadata();
Ejb3Column[] columns = columnsBuilder.getColumns();
Ejb3JoinColumn[] joinColumns = columnsBuilder.getJoinColumns();
final XClass returnedClass = inferredData.getClassOrElement();
//prepare PropertyBinder
PropertyBinder propertyBinder = new PropertyBinder();
propertyBinder.setName( inferredData.getPropertyName() );
propertyBinder.setReturnedClassName( inferredData.getTypeName() );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setHolder( propertyHolder );
propertyBinder.setProperty( property );
propertyBinder.setReturnedClass( inferredData.getPropertyClass() );
propertyBinder.setMappings( mappings );
if ( isIdentifierMapper ) {
propertyBinder.setInsertable( false );
propertyBinder.setUpdatable( false );
}
propertyBinder.setDeclaringClass( inferredData.getDeclaringClass() );
propertyBinder.setEntityBinder( entityBinder );
propertyBinder.setInheritanceStatePerClass( inheritanceStatePerClass );
boolean isId = !entityBinder.isIgnoreIdAnnotations() &&
( property.isAnnotationPresent( Id.class )
|| property.isAnnotationPresent( EmbeddedId.class ) );
propertyBinder.setId( isId );
if ( property.isAnnotationPresent( Version.class ) ) {
if ( isIdentifierMapper ) {
throw new AnnotationException(
"@IdClass class should not have @Version property"
);
}
if ( !( propertyHolder.getPersistentClass() instanceof RootClass ) ) {
throw new AnnotationException(
"Unable to define/override @Version on a subclass: "
+ propertyHolder.getEntityName()
);
}
if ( !propertyHolder.isEntity() ) {
throw new AnnotationException(
"Unable to define @Version on an embedded class: "
+ propertyHolder.getEntityName()
);
}
if ( traceEnabled ) {
LOG.tracev( "{0} is a version property", inferredData.getPropertyName() );
}
RootClass rootClass = ( RootClass ) propertyHolder.getPersistentClass();
propertyBinder.setColumns( columns );
Property prop = propertyBinder.makePropertyValueAndBind();
setVersionInformation( property, propertyBinder );
rootClass.setVersion( prop );
//If version is on a mapped superclass, update the mapping
final org.hibernate.mapping.MappedSuperclass superclass = BinderHelper.getMappedSuperclassOrNull(
inferredData.getDeclaringClass(),
inheritanceStatePerClass,
mappings
);
if ( superclass != null ) {
superclass.setDeclaredVersion( prop );
}
else {
//we know the property is on the actual entity
rootClass.setDeclaredVersion( prop );
}
SimpleValue simpleValue = ( SimpleValue ) prop.getValue();
simpleValue.setNullValue( "undefined" );
rootClass.setOptimisticLockStyle( OptimisticLockStyle.VERSION );
if ( traceEnabled ) {
LOG.tracev( "Version name: {0}, unsavedValue: {1}", rootClass.getVersion().getName(),
( (SimpleValue) rootClass.getVersion().getValue() ).getNullValue() );
}
}
else {
final boolean forcePersist = property.isAnnotationPresent( MapsId.class )
|| property.isAnnotationPresent( Id.class );
if ( property.isAnnotationPresent( ManyToOne.class ) ) {
ManyToOne ann = property.getAnnotation( ManyToOne.class );
//check validity
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException(
"@Column(s) not allowed on a @ManyToOne property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
final boolean mandatory = !ann.optional() || forcePersist;
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, false, forcePersist ),
joinColumns,
!mandatory,
ignoreNotFound, onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, mappings ),
propertyHolder,
inferredData, false, isIdentifierMapper,
inSecondPass, propertyBinder, mappings
);
}
else if ( property.isAnnotationPresent( OneToOne.class ) ) {
OneToOne ann = property.getAnnotation( OneToOne.class );
//check validity
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException(
"@Column(s) not allowed on a @OneToOne property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
//FIXME support a proper PKJCs
boolean trueOneToOne = property.isAnnotationPresent( PrimaryKeyJoinColumn.class )
|| property.isAnnotationPresent( PrimaryKeyJoinColumns.class );
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
//MapsId means the columns belong to the pk => not null
//@OneToOne with @PKJC can still be optional
final boolean mandatory = !ann.optional() || forcePersist;
bindOneToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, ann.orphanRemoval(), forcePersist ),
joinColumns,
!mandatory,
getFetchMode( ann.fetch() ),
ignoreNotFound, onDeleteCascade,
ToOneBinder.getTargetEntity( inferredData, mappings ),
propertyHolder,
inferredData,
ann.mappedBy(),
trueOneToOne,
isIdentifierMapper,
inSecondPass,
propertyBinder,
mappings
);
}
else if ( property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
//check validity
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
throw new AnnotationException(
"@Column(s) not allowed on a @Any property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setExplicitTableName( join.getTable().getName() );
}
}
bindAny(
getCascadeStrategy( null, hibernateCascade, false, forcePersist ),
//@Any has not cascade attribute
joinColumns,
onDeleteCascade,
nullability,
propertyHolder,
inferredData,
entityBinder,
isIdentifierMapper,
mappings
);
}
else if ( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( ManyToMany.class )
|| property.isAnnotationPresent( ElementCollection.class )
|| property.isAnnotationPresent( ManyToAny.class ) ) {
OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
ManyToMany manyToManyAnn = property.getAnnotation( ManyToMany.class );
ElementCollection elementCollectionAnn = property.getAnnotation( ElementCollection.class );
final IndexColumn indexColumn;
if ( property.isAnnotationPresent( OrderColumn.class ) ) {
indexColumn = IndexColumn.buildColumnFromAnnotation(
property.getAnnotation( OrderColumn.class ),
propertyHolder,
inferredData,
entityBinder.getSecondaryTables(),
mappings
);
if ( property.isAnnotationPresent( ListIndexBase.class ) ) {
indexColumn.setBase( ( property.getAnnotation( ListIndexBase.class ) ).value() );
}
}
else {
//if @IndexColumn is not there, the generated IndexColumn is an implicit column and not used.
//so we can leave the legacy processing as the default
indexColumn = IndexColumn.buildColumnFromAnnotation(
property.getAnnotation( org.hibernate.annotations.IndexColumn.class ),
propertyHolder,
inferredData,
mappings
);
}
CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
propertyHolder.getEntityName(),
property,
!indexColumn.isImplicit(),
property.isAnnotationPresent( MapKeyType.class ),
mappings
);
collectionBinder.setIndexColumn( indexColumn );
collectionBinder.setMapKey( property.getAnnotation( MapKey.class ) );
collectionBinder.setPropertyName( inferredData.getPropertyName() );
collectionBinder.setBatchSize( property.getAnnotation( BatchSize.class ) );
collectionBinder.setJpaOrderBy( property.getAnnotation( javax.persistence.OrderBy.class ) );
collectionBinder.setSqlOrderBy( property.getAnnotation( OrderBy.class ) );
collectionBinder.setSort( property.getAnnotation( Sort.class ) );
collectionBinder.setNaturalSort( property.getAnnotation( SortNatural.class ) );
collectionBinder.setComparatorSort( property.getAnnotation( SortComparator.class ) );
Cache cachAnn = property.getAnnotation( Cache.class );
collectionBinder.setCache( cachAnn );
collectionBinder.setPropertyHolder( propertyHolder );
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
collectionBinder.setIgnoreNotFound( ignoreNotFound );
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
collectionBinder.setMappings( mappings );
collectionBinder.setAccessType( inferredData.getDefaultAccess() );
Ejb3Column[] elementColumns;
//do not use "element" if you are a JPA 2 @ElementCollection only for legacy Hibernate mappings
boolean isJPA2ForValueMapping = property.isAnnotationPresent( ElementCollection.class );
PropertyData virtualProperty = isJPA2ForValueMapping ? inferredData : new WrappedInferredData(
inferredData, "element"
);
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent(
Formula.class
) ) {
Column ann = property.getAnnotation( Column.class );
Formula formulaAnn = property.getAnnotation( Formula.class );
elementColumns = Ejb3Column.buildColumnFromAnnotation(
new Column[] { ann },
formulaAnn,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
mappings
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
Columns anns = property.getAnnotation( Columns.class );
elementColumns = Ejb3Column.buildColumnFromAnnotation(
anns.columns(), null, nullability, propertyHolder, virtualProperty,
entityBinder.getSecondaryTables(), mappings
);
}
else {
elementColumns = Ejb3Column.buildColumnFromAnnotation(
null,
null,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
mappings
);
}
{
Column[] keyColumns = null;
//JPA 2 has priority and has different default column values, differenciate legacy from JPA 2
Boolean isJPA2 = null;
if ( property.isAnnotationPresent( MapKeyColumn.class ) ) {
isJPA2 = Boolean.TRUE;
keyColumns = new Column[] { new MapKeyColumnDelegator( property.getAnnotation( MapKeyColumn.class ) ) };
}
//not explicitly legacy
if ( isJPA2 == null ) {
isJPA2 = Boolean.TRUE;
}
//nullify empty array
keyColumns = keyColumns != null && keyColumns.length > 0 ? keyColumns : null;
//"mapkey" is the legacy column name of the key column pre JPA 2
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
keyColumns,
null,
Nullability.FORCED_NOT_NULL,
propertyHolder,
isJPA2 ? inferredData : mapKeyVirtualProperty,
isJPA2 ? "_KEY" : null,
entityBinder.getSecondaryTables(),
mappings
);
collectionBinder.setMapKeyColumns( mapColumns );
}
{
JoinColumn[] joinKeyColumns = null;
//JPA 2 has priority and has different default column values, differenciate legacy from JPA 2
Boolean isJPA2 = null;
if ( property.isAnnotationPresent( MapKeyJoinColumns.class ) ) {
isJPA2 = Boolean.TRUE;
final MapKeyJoinColumn[] mapKeyJoinColumns = property.getAnnotation( MapKeyJoinColumns.class )
.value();
joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length];
int index = 0;
for ( MapKeyJoinColumn joinColumn : mapKeyJoinColumns ) {
joinKeyColumns[index] = new MapKeyJoinColumnDelegator( joinColumn );
index++;
}
if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
throw new AnnotationException(
"@MapKeyJoinColumn and @MapKeyJoinColumns used on the same property: "
+ BinderHelper.getPath( propertyHolder, inferredData )
);
}
}
else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
isJPA2 = Boolean.TRUE;
joinKeyColumns = new JoinColumn[] {
new MapKeyJoinColumnDelegator(
property.getAnnotation(
MapKeyJoinColumn.class
)
)
};
}
//not explicitly legacy
if ( isJPA2 == null ) {
isJPA2 = Boolean.TRUE;
}
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumnsWithDefaultColumnSuffix(
joinKeyColumns,
null,
entityBinder.getSecondaryTables(),
propertyHolder,
isJPA2 ? inferredData.getPropertyName() : mapKeyVirtualProperty.getPropertyName(),
isJPA2 ? "_KEY" : null,
mappings
);
collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
}
//potential element
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
collectionBinder.setElementColumns( elementColumns );
collectionBinder.setProperty( property );
//TODO enhance exception with @ManyToAny and @CollectionOfElements
if ( oneToManyAnn != null && manyToManyAnn != null ) {
throw new AnnotationException(
"@OneToMany and @ManyToMany on the same property is not allowed: "
+ propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
);
}
String mappedBy = null;