for ( Ejb3Column col : columns ) {
col.forceNotNull();
}
}
final XClass returnedClass = inferredData.getClassOrElement();
if ( !entityBinder.isIgnoreIdAnnotations() &&
( property.isAnnotationPresent( Id.class )
|| property.isAnnotationPresent( EmbeddedId.class ) ) ) {
if ( isIdentifierMapper ) {
throw new AnnotationException(
"@IdClass class should not have @Id nor @EmbeddedId proeperties"
);
}
log.debug( inferredData.getPropertyName() + " is an id" );
//clone classGenerator and override with local values
HashMap<String, IdGenerator> localGenerators = (HashMap<String, IdGenerator>) classGenerators.clone();
localGenerators.putAll( buildLocalGenerators( property, mappings ) );
//manage composite related metadata
//guess if its a component and find id data access (property, field etc)
final boolean isComponent = returnedClass.isAnnotationPresent( Embeddable.class )
|| property.isAnnotationPresent( EmbeddedId.class );
boolean propertyAnnotated = entityBinder.isPropertyAnnotated( returnedClass );
String propertyAccessor = entityBinder.getPropertyAccessor( returnedClass );
//if ( isComponent && embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
String generatorType = generatedValue != null ? generatorType( generatedValue.strategy() ) : "assigned";
String generator = generatedValue != null ? generatedValue.generator() : BinderHelper.ANNOTATION_STRING_DEFAULT;
if ( isComponent ) generatorType = "assigned"; //a component must not have any generator
Type typeAnn = property.getAnnotation( Type.class );
bindId(
generatorType,
generator,
inferredData,
columns,
propertyHolder,
localGenerators,
isComponent,
propertyAnnotated,
propertyAccessor, entityBinder,
typeAnn,
false,
isIdentifierMapper, mappings
);
if ( log.isDebugEnabled() ) {
log.debug(
"Bind " + ( isComponent ? "@EmbeddedId" : "@Id" ) + " on " + inferredData.getPropertyName()
);
}
}
else 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()
);
}
log.debug( inferredData.getPropertyName() + " is a version property" );
RootClass rootClass = (RootClass) propertyHolder.getPersistentClass();
boolean lazy = false;
PropertyBinder propBinder = new PropertyBinder();
propBinder.setName( inferredData.getPropertyName() );
propBinder.setReturnedClassName( inferredData.getTypeName() );
propBinder.setLazy( lazy );
propBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
propBinder.setColumns( columns );
propBinder.setHolder( propertyHolder ); //PropertyHolderBuilder.buildPropertyHolder(rootClass)
propBinder.setProperty( property );
propBinder.setReturnedClass( inferredData.getPropertyClass() );
propBinder.setMappings( mappings );
Property prop = propBinder.bind();
rootClass.setVersion( prop );
SimpleValue simpleValue = (SimpleValue) prop.getValue();
if ( !simpleValue.isTypeSpecified() ) simpleValue.setTypeName( "integer" );
simpleValue.setNullValue( "undefined" );
rootClass.setOptimisticLockMode( Versioning.OPTIMISTIC_LOCK_VERSION );
log.debug(
"Version name: " + rootClass.getVersion().getName() + ", unsavedValue: " + ( (SimpleValue) rootClass
.getVersion()
.getValue() ).getNullValue()
);
}
else 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: "
+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() ) );
}
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 = property.getAnnotation( JoinTable.class );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setSecondaryTableName( join.getTable().getName() );
}
}
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade ),
joinColumns,
ann.optional(),
ignoreNotFound, onDeleteCascade,
mappings.getReflectionManager().toXClass( ann.targetEntity() ),
propertyHolder,
inferredData, false, isIdentifierMapper, inSecondPass, 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: "
+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() ) );
}
//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 = property.getAnnotation( JoinTable.class );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for ( Ejb3JoinColumn joinColumn : joinColumns ) {
joinColumn.setSecondaryTableName( join.getTable().getName() );
}
}
bindOneToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade ),
joinColumns,
ann.optional(),
getFetchMode( ann.fetch() ),
ignoreNotFound, onDeleteCascade,
mappings.getReflectionManager().toXClass( ann.targetEntity() ),
propertyHolder,
inferredData, ann.mappedBy(), trueOneToOne, isIdentifierMapper, inSecondPass, mappings
);
}
else if ( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( ManyToMany.class )
|| property.isAnnotationPresent( CollectionOfElements.class ) ) {
OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
ManyToMany manyToManyAnn = property.getAnnotation( ManyToMany.class );
CollectionOfElements collectionOfElementsAnn = property.getAnnotation( CollectionOfElements.class );
org.hibernate.annotations.IndexColumn indexAnn = property.getAnnotation(
org.hibernate.annotations.IndexColumn.class
);
JoinTable assocTable = property.getAnnotation( JoinTable.class );
IndexColumn indexColumn = IndexColumn.buildColumnFromAnnotation(
indexAnn, propertyHolder, inferredData, mappings
);
CollectionBinder collectionBinder = CollectionBinder.getCollectionBinder(
propertyHolder.getEntityName(),
property,
! indexColumn.isImplicit()
);
collectionBinder.setIndexColumn( indexColumn );
MapKey mapKeyAnn = property.getAnnotation( MapKey.class );
collectionBinder.setMapKey( mapKeyAnn );
collectionBinder.setPropertyName( inferredData.getPropertyName() );
BatchSize batchAnn = property.getAnnotation( BatchSize.class );
collectionBinder.setBatchSize( batchAnn );
javax.persistence.OrderBy ejb3OrderByAnn = property.getAnnotation( javax.persistence.OrderBy.class );
OrderBy orderByAnn = property.getAnnotation( OrderBy.class );
collectionBinder.setEjb3OrderBy( ejb3OrderByAnn );
collectionBinder.setSqlOrderBy( orderByAnn );
Sort sortAnn = property.getAnnotation( Sort.class );
collectionBinder.setSort( sortAnn );
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.setPropertyAccessorName( inferredData.getDefaultAccess() );
Ejb3Column[] elementColumns = null;
PropertyData virtualProperty = 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
);
}
org.hibernate.annotations.MapKey hibMapKeyAnn = property.getAnnotation(
org.hibernate.annotations.MapKey.class
);
PropertyData mapKeyVirtualProperty = new WrappedInferredData( inferredData, "mapkey" );
Ejb3Column[] mapColumns = Ejb3Column.buildColumnFromAnnotation(
hibMapKeyAnn != null && hibMapKeyAnn.columns().length > 0 ? hibMapKeyAnn.columns() : null,
null,
Nullability.FORCED_NOT_NULL,
propertyHolder,
mapKeyVirtualProperty,
entityBinder.getSecondaryTables(),
mappings
);
collectionBinder.setMapKeyColumns( mapColumns );
MapKeyManyToMany mapKeyManyToMany = property.getAnnotation( MapKeyManyToMany.class );
Ejb3JoinColumn[] mapJoinColumns = Ejb3JoinColumn.buildJoinColumns(
mapKeyManyToMany != null ? mapKeyManyToMany.joinColumns() : null,
null, entityBinder.getSecondaryTables(),
propertyHolder, mapKeyVirtualProperty.getPropertyName(), mappings
);
collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
//potential element
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
collectionBinder.setElementColumns( elementColumns );
collectionBinder.setProperty( property );
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;
if ( oneToManyAnn != null ) {
for ( Ejb3JoinColumn column : joinColumns ) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = oneToManyAnn.mappedBy();
collectionBinder.setTargetEntity(
mappings.getReflectionManager().toXClass( oneToManyAnn.targetEntity() )
);
collectionBinder.setCascadeStrategy( getCascadeStrategy( oneToManyAnn.cascade(), hibernateCascade ) );
collectionBinder.setOneToMany( true );
}
else if ( collectionOfElementsAnn != null ) {
for ( Ejb3JoinColumn column : joinColumns ) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = "";
collectionBinder.setTargetEntity(
mappings.getReflectionManager().toXClass( collectionOfElementsAnn.targetElement() )
);
//collectionBinder.setCascadeStrategy( getCascadeStrategy( embeddedCollectionAnn.cascade(), hibernateCascade ) );
collectionBinder.setOneToMany( true );
}
else if ( manyToManyAnn != null ) {
mappedBy = manyToManyAnn.mappedBy();
collectionBinder.setTargetEntity(
mappings.getReflectionManager().toXClass( manyToManyAnn.targetEntity() )
);
collectionBinder.setCascadeStrategy( getCascadeStrategy( manyToManyAnn.cascade(), hibernateCascade ) );
collectionBinder.setOneToMany( false );
}
collectionBinder.setMappedBy( mappedBy );
bindJoinedTableAssociation(
assocTable, mappings, entityBinder, collectionBinder, propertyHolder, inferredData, mappedBy
);
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
collectionBinder.setCascadeDeleteEnabled( onDeleteCascade );
if ( isIdentifierMapper ) {
collectionBinder.setInsertable( false );
collectionBinder.setUpdatable( false );
}
if ( property.isAnnotationPresent( CollectionId.class ) ) { //do not compute the generators unless necessary
HashMap<String, IdGenerator> localGenerators = (HashMap<String, IdGenerator>) classGenerators.clone();
localGenerators.putAll( buildLocalGenerators( property, mappings ) );
collectionBinder.setLocalGenerators(localGenerators);
}
collectionBinder.bind();
}
else {
//define whether the type is a component or not
boolean isComponent = false;
Embeddable embeddableAnn = (Embeddable) returnedClass.getAnnotation( Embeddable.class );
Embedded embeddedAnn = (Embedded) property.getAnnotation( Embedded.class );
isComponent = embeddedAnn != null || embeddableAnn != null;
if ( isComponent ) {
//process component object