anns, null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) {
JoinColumnsOrFormulas ann = property.getAnnotation( JoinColumnsOrFormulas.class );
joinColumns = Ejb3JoinColumn.buildJoinColumnsOrFormulas(
ann, null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
}
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent( Formula.class ) ) {
Column ann = property.getAnnotation( Column.class );
Formula formulaAnn = property.getAnnotation( Formula.class );
columns = Ejb3Column.buildColumnFromAnnotation(
new Column[] { ann }, formulaAnn, nullability, propertyHolder, inferredData,
entityBinder.getSecondaryTables(), mappings
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
Columns anns = property.getAnnotation( Columns.class );
columns = Ejb3Column.buildColumnFromAnnotation(
anns.columns(), null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(),
mappings
);
}
//set default values if needed
if ( joinColumns == null &&
( property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class ) )
) {
JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
if ( joinTableAnn != null ) {
joinColumns = Ejb3JoinColumn.buildJoinColumns(
joinTableAnn.inverseJoinColumns(), null, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
if ( StringHelper.isEmpty( joinTableAnn.name() ) ) {
throw new AnnotationException(
"JoinTable.name() on a @ToOne association has to be explicit: "
+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() )
);
}
}
else {
OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
String mappedBy = oneToOneAnn != null ?
oneToOneAnn.mappedBy() :
null;
joinColumns = Ejb3JoinColumn.buildJoinColumns(
(JoinColumn[]) null,
mappedBy, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
}
else if ( joinColumns == null &&
( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( CollectionOfElements.class ) //legacy Hibernate
|| property.isAnnotationPresent( ElementCollection.class )
) ) {
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
String mappedBy = oneToMany != null ?
oneToMany.mappedBy() :
"";
joinColumns = Ejb3JoinColumn.buildJoinColumns(
(JoinColumn[]) null,
mappedBy, entityBinder.getSecondaryTables(),
propertyHolder, inferredData.getPropertyName(), mappings
);
}
else if ( joinColumns == null && property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
throw new AnnotationException( "@Any requires an explicit @JoinColumn(s): "
+ StringHelper.qualify( propertyHolder.getPath(), property.getName() ) );
}
if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
//useful for collection of embedded elements
columns = Ejb3Column.buildColumnFromAnnotation(
null, null, nullability, propertyHolder, inferredData, entityBinder.getSecondaryTables(), mappings
);
}
if ( nullability == Nullability.FORCED_NOT_NULL ) {
//force columns to not null
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 properties"
);
}
log.debug( "{} is an id", inferredData.getPropertyName() );
//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 );
AccessType propertyAccessor = entityBinder.getPropertyAccessor( returnedClass );
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
bindId(
generatorType,
generator,
inferredData,
columns,
propertyHolder,
localGenerators,
isComponent,
propertyAccessor, entityBinder,
false,
isIdentifierMapper,
mappings,
inheritanceStatePerClass
);
log.debug(
"Bind {} on {}", ( isComponent ? "@EmbeddedId" : "@Id" ), 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()
);
}
if ( ! propertyHolder.isEntity() ) {
throw new AnnotationException(
"Unable to define @Version on an embedded class: "
+ propertyHolder.getEntityName()
);
}
log.debug( "{} is a version property", inferredData.getPropertyName() );
RootClass rootClass = (RootClass) propertyHolder.getPersistentClass();
PropertyBinder propBinder = new PropertyBinder();
propBinder.setName( inferredData.getPropertyName() );
propBinder.setReturnedClassName( inferredData.getTypeName() );
propBinder.setLazy( false );
propBinder.setAccessType( inferredData.getDefaultAccess() );
propBinder.setColumns( columns );
propBinder.setHolder( propertyHolder ); //PropertyHolderBuilder.buildPropertyHolder(rootClass)
propBinder.setProperty( property );
propBinder.setReturnedClass( inferredData.getPropertyClass() );
propBinder.setMappings( mappings );
propBinder.setDeclaringClass( inferredData.getDeclaringClass() );
Property prop = propBinder.bind();
propBinder.getSimpleValueBinder().setVersion(true);
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.setOptimisticLockMode( Versioning.OPTIMISTIC_LOCK_VERSION );
log.debug(
"Version name: {}, unsavedValue: {}", rootClass.getVersion().getName(),
( (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 = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for (Ejb3JoinColumn joinColumn : joinColumns) {
joinColumn.setSecondaryTableName( join.getTable().getName() );
}
}
bindManyToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, false),
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 = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
Join join = propertyHolder.addJoin( assocTable, false );
for (Ejb3JoinColumn joinColumn : joinColumns) {
joinColumn.setSecondaryTableName( join.getTable().getName() );
}
}
bindOneToOne(
getCascadeStrategy( ann.cascade(), hibernateCascade, false),
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( 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: "
+ StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() ) );
}
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.setSecondaryTableName( join.getTable().getName() );
}
}
bindAny( getCascadeStrategy( null, hibernateCascade, false), //@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( CollectionOfElements.class ) //legacy Hibernate
|| 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 );
CollectionOfElements collectionOfElementsAnn = property.getAnnotation( CollectionOfElements.class ); //legacy hibernate
final IndexColumn indexColumn;
if ( property.isAnnotationPresent( OrderColumn.class ) ) {
indexColumn = IndexColumn.buildColumnFromAnnotation(
property.getAnnotation(OrderColumn.class),
propertyHolder,
inferredData,
entityBinder.getSecondaryTables(),
mappings
);
}
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( CollectionOfElements.class )
|| property.isAnnotationPresent( org.hibernate.annotations.MapKey.class )
// || property.isAnnotationPresent( ManyToAny.class )
);
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.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 ) ) };
}
else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKey.class ) ) {
if ( isJPA2 == null) {
isJPA2 = Boolean.FALSE;
}
keyColumns = property.getAnnotation( org.hibernate.annotations.MapKey.class ).columns();
}
//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 ( joinKeyColumns != null ) {
throw new AnnotationException( "@MapKeyJoinColumn and @MapKeyJoinColumns used on the same property: "
+ StringHelper.qualify( propertyHolder.getClassName(), property.getName() ) );
}
}
else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
isJPA2 = Boolean.TRUE;
joinKeyColumns = new JoinColumn[] { new MapKeyJoinColumnDelegator( property.getAnnotation( MapKeyJoinColumn.class ) ) };
}
else if ( property.isAnnotationPresent( org.hibernate.annotations.MapKeyManyToMany.class ) ) {
if ( isJPA2 == null) {
isJPA2 = Boolean.FALSE;
}
joinKeyColumns = property.getAnnotation( org.hibernate.annotations.MapKeyManyToMany.class ).joinColumns();
}
//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;
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, oneToManyAnn.orphanRemoval()) );
collectionBinder.setOneToMany( true );
}
else if ( elementCollectionAnn != null
|| collectionOfElementsAnn != null //Hibernate legacy
) {
for (Ejb3JoinColumn column : joinColumns) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns( joinColumns );
mappedBy = "";
final Class<?> targetElement = elementCollectionAnn != null ?
elementCollectionAnn.targetClass() :
collectionOfElementsAnn.targetElement();
collectionBinder.setTargetEntity(
mappings.getReflectionManager().toXClass( 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, false) );
collectionBinder.setOneToMany( false );
}
else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
mappedBy = "";
collectionBinder.setTargetEntity(
mappings.getReflectionManager().toXClass( void.class )
);
collectionBinder.setCascadeStrategy( getCascadeStrategy( null, hibernateCascade, false) );
collectionBinder.setOneToMany( false );
}
collectionBinder.setMappedBy( mappedBy );
bindJoinedTableAssociation(
property, 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.setInheritanceStatePerClass( inheritanceStatePerClass );
collectionBinder.setDeclaringClass( inferredData.getDeclaringClass() );
collectionBinder.bind();
}
else {
//define whether the type is a component or not
boolean isComponent;
Embeddable embeddableAnn = returnedClass.getAnnotation( Embeddable.class );
Embedded embeddedAnn = property.getAnnotation( Embedded.class );
isComponent = embeddedAnn != null || embeddableAnn != null;
if ( isComponent ) {
AccessType propertyAccessor = entityBinder.getPropertyAccessor( property );
bindComponent(
inferredData, propertyHolder, propertyAccessor, entityBinder,
isIdentifierMapper,
mappings, isComponentEmbedded, inheritanceStatePerClass
);
}
else {
//provide the basic property mapping
boolean optional = true;
boolean lazy = false;
if ( property.isAnnotationPresent( Basic.class ) ) {
Basic ann = property.getAnnotation( Basic.class );
optional = ann.optional();
lazy = ann.fetch() == FetchType.LAZY;
}
//implicit type will check basic types and Serializable classes
if ( !optional && nullability != Nullability.FORCED_NULL ) {
//force columns to not null
for (Ejb3Column col : columns) {