Package javax.persistence

Examples of javax.persistence.JoinColumns


      JoinColumn[] anns = null;
      if ( property.isAnnotationPresent( JoinColumn.class ) ) {
        anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
      }
      else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
        JoinColumns ann = property.getAnnotation( JoinColumns.class );
        anns = ann.value();
        int length = anns.length;
        if ( length == 0 ) {
          throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
        }
      }
      if ( anns != null ) {
        joinColumns = Ejb3JoinColumn.buildJoinColumns(
            anns, 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 ) )
        ) {
      if ( property.isAnnotationPresent( JoinTable.class ) ) {
        JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
        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 ) ) ) {
      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 );
      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
           
      bindId(
          generatorType,
          generator,
          inferredData,
          columns,
          propertyHolder,
          localGenerators,
          isComponent,
          propertyAnnotated,
          propertyAccessor, entityBinder,
          false,
          isIdentifierMapper, mappings
      );
           
      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.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: {}, 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 = 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( 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 );
      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() );
        }
      }
      bindAny( getCascadeStrategy( null, hibernateCascade ), //@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 )
        || property.isAnnotationPresent( ManyToAny.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 );

      //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 ) );
        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 );
      }
      else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
        mappedBy = "";
        collectionBinder.setTargetEntity(
            mappings.getReflectionManager().toXClass( void.class )
        );
        collectionBinder.setCascadeStrategy( getCascadeStrategy( null, 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 = returnedClass.getAnnotation( Embeddable.class );
      Embedded embeddedAnn = property.getAnnotation( Embedded.class );
      isComponent = embeddedAnn != null || embeddableAnn != null;

      if ( isComponent ) {
        //process component object
        //boolean propertyAccess = true;
        //if ( embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
        boolean propertyAnnotated = entityBinder.isPropertyAnnotated( property );
        String propertyAccessor = entityBinder.getPropertyAccessor( property );
        bindComponent(
            inferredData, propertyHolder, propertyAnnotated, propertyAccessor, entityBinder,
            isIdentifierMapper,
            mappings, isComponentEmbedded
        );
      }
      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) {
View Full Code Here


              /**
               * The detection of a configured individual JoinColumn differs between Annotation
               * and XML configuration processing.
               */
              boolean isRequiredAnnotationPresent = false;
              JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
              if ( (prop.isAnnotationPresent( JoinColumn.class )
                  && prop.getAnnotation( JoinColumn.class ).name().equals( columnName )) ) {
                isRequiredAnnotationPresent = true;
              }
              else if ( prop.isAnnotationPresent( JoinColumns.class ) ) {
                for ( JoinColumn columnAnnotation : groupAnnotation.value() ) {
                  if ( columnName.equals( columnAnnotation.name() ) ) {
                    isRequiredAnnotationPresent = true;
                    break;
                  }
                }
View Full Code Here

      JoinColumn[] anns = null;
      if ( property.isAnnotationPresent( JoinColumn.class ) ) {
        anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
      }
      else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
        JoinColumns ann = property.getAnnotation( JoinColumns.class );
        anns = ann.value();
        int length = anns.length;
        if ( length == 0 ) {
          throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
        }
      }
      if ( anns != null ) {
        joinColumns = Ejb3JoinColumn.buildJoinColumns(
            anns, 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 ) )
        ) {
      if ( property.isAnnotationPresent( JoinTable.class ) ) {
        JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
        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 ) ) ) {
      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( 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

      bindId(
          generatorType,
          generator,
          inferredData,
          columns,
          propertyHolder,
          localGenerators,
          isComponent,
          propertyAnnotated,
          propertyAccessor, entityBinder,
          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()
        );
      }
      if ( ! propertyHolder.isEntity() ) {
        throw new AnnotationException(
            "Unable to define @Version on an embedded class: "
                + propertyHolder.getEntityName()
        );
      }
      log.debug( inferredData.getPropertyName() + " is a version property" );
      RootClass rootClass = (RootClass) propertyHolder.getPersistentClass();
      PropertyBinder propBinder = new PropertyBinder();
      propBinder.setName( inferredData.getPropertyName() );
      propBinder.setReturnedClassName( inferredData.getTypeName() );
      propBinder.setLazy( false );
      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( 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 );
      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() );
        }
      }
      bindAny( getCascadeStrategy( null, hibernateCascade ), //@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 )
        || property.isAnnotationPresent( ManyToAny.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 );

      //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 ) );
        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 );
      }
      else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
        mappedBy = "";
        collectionBinder.setTargetEntity(
            mappings.getReflectionManager().toXClass( void.class )
        );
        collectionBinder.setCascadeStrategy( getCascadeStrategy( null, 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 = returnedClass.getAnnotation( Embeddable.class );
      Embedded embeddedAnn = property.getAnnotation( Embedded.class );
      isComponent = embeddedAnn != null || embeddableAnn != null;

      if ( isComponent ) {
        //process component object
        //boolean propertyAccess = true;
        //if ( embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
        boolean propertyAnnotated = entityBinder.isPropertyAnnotated( property );
        String propertyAccessor = entityBinder.getPropertyAccessor( property );
        bindComponent(
            inferredData, propertyHolder, propertyAnnotated, propertyAccessor, entityBinder,
            isIdentifierMapper,
            mappings, isComponentEmbedded
        );
      }
      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) {
View Full Code Here

      if ( property.isAnnotationPresent( JoinColumn.class ) ) {
        anns = new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
      }
      else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
        JoinColumns ann = property.getAnnotation( JoinColumns.class );
        anns = ann.value();
        int length = anns.length;
        if ( length == 0 ) {
          throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
        }
      }
      if ( anns != null ) {
        joinColumns = Ejb3JoinColumn.buildJoinColumns(
            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) {
View Full Code Here

      JoinColumn[] anns = null;
      if ( property.isAnnotationPresent( JoinColumn.class ) ) {
        anns = new JoinColumn[]{property.getAnnotation( JoinColumn.class )};
      }
      else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
        JoinColumns ann = property.getAnnotation( JoinColumns.class );
        anns = ann.value();
        int length = anns.length;
        if ( length == 0 ) {
          throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
        }
      }
      if ( anns != null ) {
        joinColumns = Ejb3JoinColumn.buildJoinColumns(
            anns, 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 ) )
        ) {
      if ( property.isAnnotationPresent( JoinTable.class ) ) {
        JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
        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 ) ) ) {
      OneToMany oneToMany = property.getAnnotation( OneToMany.class );
      String mappedBy = oneToMany != null ?
          oneToMany.mappedBy() :
          "";
      joinColumns = Ejb3JoinColumn.buildJoinColumns(
          (JoinColumn[]) null,
          mappedBy, entityBinder.getSecondaryTables(),
          propertyHolder, inferredData.getPropertyName(), mappings
      );
    }
    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 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
        //boolean propertyAccess = true;
        //if ( embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
        boolean propertyAnnotated = entityBinder.isPropertyAnnotated( property );
        String propertyAccessor = entityBinder.getPropertyAccessor( property );
        bindComponent(
            inferredData, propertyHolder, propertyAnnotated, propertyAccessor, entityBinder,
            isIdentifierMapper,
            mappings, isComponentEmbedded
        );
      }
      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 ) {
View Full Code Here

      JoinColumn[] anns = null;
      if ( property.isAnnotationPresent( JoinColumn.class ) ) {
        anns = new JoinColumn[]{property.getAnnotation( JoinColumn.class )};
      }
      else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
        JoinColumns ann = property.getAnnotation( JoinColumns.class );
        anns = ann.value();
        int length = anns.length;
        if ( length == 0 ) {
          throw new AnnotationException( "Cannot bind an empty @JoinColumns" );
        }
      }
      if ( anns != null ) {
        joinColumns = Ejb3JoinColumn.buildJoinColumns(
            anns, 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 ) )
        ) {
      if ( property.isAnnotationPresent( JoinTable.class ) ) {
        JoinTable joinTableAnn = property.getAnnotation( JoinTable.class );
        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 ) ) ) {
      OneToMany oneToMany = property.getAnnotation( OneToMany.class );
      String mappedBy = oneToMany != null ?
          oneToMany.mappedBy() :
          "";
      joinColumns = Ejb3JoinColumn.buildJoinColumns(
          (JoinColumn[]) null,
          mappedBy, entityBinder.getSecondaryTables(),
          propertyHolder, inferredData.getPropertyName(), mappings
      );
    }
    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 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
        //boolean propertyAccess = true;
        //if ( embeddableAnn != null && embeddableAnn.access() == AccessType.FIELD ) propertyAccess = false;
        boolean propertyAnnotated = entityBinder.isPropertyAnnotated( property );
        String propertyAccessor = entityBinder.getPropertyAccessor( property );
        bindComponent(
            inferredData, propertyHolder, propertyAnnotated, propertyAccessor, entityBinder,
            isIdentifierMapper,
            mappings, isComponentEmbedded
        );
      }
      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 ) {
View Full Code Here

              /**
               * The detection of a configured individual JoinColumn differs between Annotation
               * and XML configuration processing.
               */
              boolean isRequiredAnnotationPresent = false;
              JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
              if ( (prop.isAnnotationPresent( JoinColumn.class )
                  && prop.getAnnotation( JoinColumn.class ).name().equals( columnName )) ) {
                isRequiredAnnotationPresent = true;
              }
              else if ( prop.isAnnotationPresent( JoinColumns.class ) ) {
                for ( JoinColumn columnAnnotation : groupAnnotation.value() ) {
                  if ( columnName.equals( columnAnnotation.name() ) ) {
                    isRequiredAnnotationPresent = true;
                    break;
                  }
                }
View Full Code Here

              /**
               * The detection of a configured individual JoinColumn differs between Annotation
               * and XML configuration processing.
               */
              boolean isRequiredAnnotationPresent = false;
              JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
              if ( (prop.isAnnotationPresent( JoinColumn.class )
                  && prop.getAnnotation( JoinColumn.class ).name().equals( columnName )) ) {
                isRequiredAnnotationPresent = true;
              }
              else if ( prop.isAnnotationPresent( JoinColumns.class ) ) {
                for ( JoinColumn columnAnnotation : groupAnnotation.value() ) {
                  if ( columnName.equals( columnAnnotation.name() ) ) {
                    isRequiredAnnotationPresent = true;
                    break;
                  }
                }
View Full Code Here

              /**
               * The detection of a configured individual JoinColumn differs between Annotation
               * and XML configuration processing.
               */
              boolean isRequiredAnnotationPresent = false;
              JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
              if ( (prop.isAnnotationPresent( JoinColumn.class )
                  && prop.getAnnotation( JoinColumn.class ).name().equals( columnName )) ) {
                isRequiredAnnotationPresent = true;
              }
              else if ( prop.isAnnotationPresent( JoinColumns.class ) ) {
                for ( JoinColumn columnAnnotation : groupAnnotation.value() ) {
                  if ( columnName.equals( columnAnnotation.name() ) ) {
                    isRequiredAnnotationPresent = true;
                    break;
                  }
                }
View Full Code Here

              /**
               * The detection of a configured individual JoinColumn differs between Annotation
               * and XML configuration processing.
               */
              boolean isRequiredAnnotationPresent = false;
              JoinColumns groupAnnotation = prop.getAnnotation( JoinColumns.class );
              if ( (prop.isAnnotationPresent( JoinColumn.class )
                  && prop.getAnnotation( JoinColumn.class ).name().equals( columnName )) ) {
                isRequiredAnnotationPresent = true;
              }
              else if ( prop.isAnnotationPresent( JoinColumns.class ) ) {
                for ( JoinColumn columnAnnotation : groupAnnotation.value() ) {
                  if ( columnName.equals( columnAnnotation.name() ) ) {
                    isRequiredAnnotationPresent = true;
                    break;
                  }
                }
View Full Code Here

TOP

Related Classes of javax.persistence.JoinColumns

Copyright © 2018 www.massapicom. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.