Package org.hibernate.cfg

Source Code of org.hibernate.cfg.Ejb3JoinColumn

//$Id: Ejb3JoinColumn.java 14761 2008-06-11 13:51:06Z hardy.ferentschik $
package org.hibernate.cfg;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.persistence.JoinColumn;
import javax.persistence.PrimaryKeyJoinColumn;

import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.util.StringHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;

/**
* Wrap state of an EJB3 @JoinColumn annotation
* and build the Hibernate column mapping element
*
* @author Emmanuel Bernard
*/
@SuppressWarnings("unchecked")
public class Ejb3JoinColumn extends Ejb3Column {
  /**
   * property name repated to this column
   */
  private String referencedColumn;
  private String mappedBy;
  //property name on the mapped by side if any
  private String mappedByPropertyName;
  //table name on the mapped by side if any
  private String mappedByTableName;
  private String mappedByEntityName;

  //FIXME hacky solution to get the information at property ref resolution
  public String getManyToManyOwnerSideEntityName() {
    return manyToManyOwnerSideEntityName;
  }

  public void setManyToManyOwnerSideEntityName(String manyToManyOwnerSideEntityName) {
    this.manyToManyOwnerSideEntityName = manyToManyOwnerSideEntityName;
  }

  private String manyToManyOwnerSideEntityName;

  public void setReferencedColumn(String referencedColumn) {
    this.referencedColumn = referencedColumn;
  }

  public String getMappedBy() {
    return mappedBy;
  }

  public void setMappedBy(String mappedBy) {
    this.mappedBy = mappedBy;
  }

  //Due to @AnnotationOverride overriding rules, I don't want the constructor to be public
  private Ejb3JoinColumn() {
    setMappedBy( BinderHelper.ANNOTATION_STRING_DEFAULT );
  }

  //Due to @AnnotationOverride overriding rules, I don't want the constructor to be public
  //TODO get rid of it and use setters
  private Ejb3JoinColumn(
      String sqlType,
      String name,
      boolean nullable,
      boolean unique,
      boolean insertable,
      boolean updatable,
      String referencedColumn,
      String secondaryTable,
      Map<String, Join> joins,
      PropertyHolder propertyHolder,
      String propertyName,
      String mappedBy,
      boolean isImplicit,
      ExtendedMappings mappings
  ) {
    super();
    setImplicit( isImplicit );
    setSqlType( sqlType );
    setLogicalColumnName( name );
    setNullable( nullable );
    setUnique( unique );
    setInsertable( insertable );
    setUpdatable( updatable );
    setSecondaryTableName( secondaryTable );
    setPropertyHolder( propertyHolder );
    setJoins( joins );
    setMappings( mappings );
    setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
    bind();
    this.referencedColumn = referencedColumn;
    this.mappedBy = mappedBy;
  }

  public String getReferencedColumn() {
    return referencedColumn;
  }

  public static Ejb3JoinColumn[] buildJoinColumns(
      JoinColumn[] anns,
      String mappedBy, Map<String, Join> joins,
      PropertyHolder propertyHolder,
      String propertyName,
      ExtendedMappings mappings
  ) {
    JoinColumn[] actualColumns = propertyHolder.getOverriddenJoinColumn(
        StringHelper.qualify( propertyHolder.getPath(), propertyName )
    );
    if ( actualColumns == null ) actualColumns = anns;
    if ( actualColumns == null || actualColumns.length == 0 ) {
      return new Ejb3JoinColumn[] {
          buildJoinColumn( (JoinColumn) null, mappedBy, joins, propertyHolder, propertyName, mappings )
      };
    }
    else {
      int size = actualColumns.length;
      Ejb3JoinColumn[] result = new Ejb3JoinColumn[size];
      for (int index = 0; index < size; index++) {
        result[index] = buildJoinColumn(
            actualColumns[index],
            mappedBy,
            joins,
            propertyHolder,
            propertyName,
            mappings
        );
      }
      return result;
    }
  }

  /**
   * build join column for SecondaryTables
   */
  private static Ejb3JoinColumn buildJoinColumn(
      JoinColumn ann,
      String mappedBy, Map<String, Join> joins,
      PropertyHolder propertyHolder,
      String propertyName,
      ExtendedMappings mappings
  ) {
    if ( ann != null ) {
      if ( BinderHelper.isDefault( mappedBy ) ) {
        throw new AnnotationException(
            "Illegal attempt to define a @JoinColumn with a mappedBy association: "
                + BinderHelper.getRelativePath( propertyHolder, propertyName )
        );
      }
      Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
      joinColumn.setJoinAnnotation( ann, null );
      joinColumn.setJoins( joins );
      joinColumn.setPropertyHolder( propertyHolder );
      joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
      joinColumn.setImplicit( false );
      joinColumn.setMappings( mappings );
      joinColumn.bind();
      return joinColumn;
    }
    else {
      Ejb3JoinColumn joinColumn = new Ejb3JoinColumn();
      joinColumn.setMappedBy( mappedBy );
      joinColumn.setJoins( joins );
      joinColumn.setPropertyHolder( propertyHolder );
      joinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
      joinColumn.setImplicit( true );
      joinColumn.setMappings( mappings );
      joinColumn.bind();
      return joinColumn;
    }
  }


  //FIXME default name still useful in association table
  public void setJoinAnnotation(JoinColumn annJoin, String defaultName) {
    if ( annJoin == null ) {
      setImplicit( true );
    }
    else {
      setImplicit( false );
      if ( !BinderHelper.isDefault( annJoin.columnDefinition() ) ) setSqlType( annJoin.columnDefinition() );
      if ( !BinderHelper.isDefault( annJoin.name() ) ) setLogicalColumnName( annJoin.name() );
      setNullable( annJoin.nullable() );
      setUnique( annJoin.unique() );
      setInsertable( annJoin.insertable() );
      setUpdatable( annJoin.updatable() );
      setReferencedColumn( annJoin.referencedColumnName() );
      setSecondaryTableName( annJoin.table() );
    }
  }

  /**
   * Build JoinColumn for a JOINED hierarchy
   */
  public static Ejb3JoinColumn buildJoinColumn(
      PrimaryKeyJoinColumn pkJoinAnn,
      JoinColumn joinAnn,
      Value identifier,
      Map<String, Join> joins,
      PropertyHolder propertyHolder, ExtendedMappings mappings
  ) {

    Column col = (Column) identifier.getColumnIterator().next();
    String defaultName = mappings.getLogicalColumnName( col.getQuotedName(), identifier.getTable() );
    if ( pkJoinAnn != null || joinAnn != null ) {
      String colName;
      String columnDefinition;
      String referencedColumnName;
      if ( pkJoinAnn != null ) {
        colName = pkJoinAnn.name();
        columnDefinition = pkJoinAnn.columnDefinition();
        referencedColumnName = pkJoinAnn.referencedColumnName();
      }
      else {
        colName = joinAnn.name();
        columnDefinition = joinAnn.columnDefinition();
        referencedColumnName = joinAnn.referencedColumnName();
      }
      String sqlType = "".equals( columnDefinition ) ? null : columnDefinition;
      String name = "".equals( colName ) ? defaultName : colName;
      return new Ejb3JoinColumn(
          sqlType,
          name, false, false,
          true, true,
          referencedColumnName,
          null, joins,
          propertyHolder, null, null, false, mappings
      );
    }
    else {
      return new Ejb3JoinColumn(
          (String) null, defaultName,
          false, false, true, true, null, (String) null,
          joins, propertyHolder, null, null, true, mappings
      );
    }
  }

  /**
   * Override persistent class on oneToMany Cases for late settings
   * Must only be used on second level pass binding
   */
  public void setPersistentClass(PersistentClass persistentClass, Map<String, Join> joins) {
    //FIXME shouldn't we deduce the classname from the persistentclasS?
    this.propertyHolder = PropertyHolderBuilder.buildPropertyHolder( persistentClass, joins, getMappings() );
  }

  public static void checkIfJoinColumn(Object columns, PropertyHolder holder, PropertyData property) {
    if ( !( columns instanceof Ejb3JoinColumn[] ) ) {
      throw new AnnotationException(
          "@Column cannot be used on an association property: "
              + holder.getEntityName()
              + "."
              + property.getPropertyName()
      );
    }
  }

  public void linkValueUsingDefaultColumnNaming(
      Column referencedColumn, PersistentClass referencedEntity, SimpleValue value
  ) {
    String columnName;
    String logicalReferencedColumn = getMappings().getLogicalColumnName(
        referencedColumn.getQuotedName(), referencedEntity.getTable()
    );
    boolean mappedBySide = mappedByTableName != null || mappedByPropertyName != null;
    boolean ownerSide = getPropertyName() != null;

    Boolean isRefColumnQuoted = StringHelper.isQuoted( logicalReferencedColumn );
    String unquotedLogicalReferenceColumn = isRefColumnQuoted ?
        StringHelper.unquote( logicalReferencedColumn ) :
        logicalReferencedColumn;

    if ( mappedBySide ) {
      String unquotedMappedbyTable = StringHelper.unquote( mappedByTableName );
      columnName = getMappings().getNamingStrategy().foreignKeyColumnName(
          mappedByPropertyName,
          mappedByEntityName,
          unquotedMappedbyTable,
          unquotedLogicalReferenceColumn
      );
      //one element was quoted so we quote
      if ( isRefColumnQuoted || StringHelper.isQuoted( mappedByTableName ) ) {
        columnName = StringHelper.quote( columnName );
      }
    }
    else if ( ownerSide ) {
      String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() );
      String unquotedLogicalTableName = StringHelper.unquote( logicalTableName );
      columnName = getMappings().getNamingStrategy().foreignKeyColumnName(
          getPropertyName(),
          referencedEntity.getEntityName(),
          unquotedLogicalTableName,
          unquotedLogicalReferenceColumn
      );
      //one element was quoted so we quote
      if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
        columnName = StringHelper.quote( columnName );
      }
    }
    else {
      //is an intra-entity hierarchy table join so copy the name by default
      String logicalTableName = getMappings().getLogicalTableName( referencedEntity.getTable() );
      String unquotedLogicalTableName = StringHelper.unquote( logicalTableName );
      columnName = getMappings().getNamingStrategy().joinKeyColumnName(
          unquotedLogicalReferenceColumn,
          unquotedLogicalTableName
      );
      //one element was quoted so we quote
      if ( isRefColumnQuoted || StringHelper.isQuoted( logicalTableName ) ) {
        columnName = StringHelper.quote( columnName );
      }
    }
    //yuk side effect on an implicit column
    setLogicalColumnName( columnName );
    setReferencedColumn( logicalReferencedColumn );
    initMappingColumn(
        columnName,
        null, referencedColumn.getLength(),
        referencedColumn.getPrecision(),
        referencedColumn.getScale(),
        getMappingColumn().isNullable(),
        referencedColumn.getSqlType(),
        getMappingColumn().isUnique(), false
    );
    linkWithValue( value );
  }

  /**
   * used for mappedBy cases
   */
  public void linkValueUsingAColumnCopy(Column column, SimpleValue value) {
    initMappingColumn(
        //column.getName(),
        column.getQuotedName(),
        null, column.getLength(),
        column.getPrecision(),
        column.getScale(),
        getMappingColumn().isNullable(),
        column.getSqlType(),
        getMappingColumn().isUnique(),
        false //We do copy no strategy here
    );
    linkWithValue( value );
  }

  protected void addColumnBinding(SimpleValue value) {
    if ( StringHelper.isEmpty( mappedBy ) ) {
      String unquotedLogColName = StringHelper.unquote( getLogicalColumnName() );
      String unquotedRefColumn = StringHelper.unquote( getReferencedColumn() );
      String logicalColumnName = getMappings().getNamingStrategy()
          .logicalCollectionColumnName( unquotedLogColName, getPropertyName(), unquotedRefColumn );
      if ( StringHelper.isQuoted( getLogicalColumnName() ) || StringHelper.isQuoted( getLogicalColumnName() ) ) {
        logicalColumnName = StringHelper.quote( logicalColumnName );
      }
      getMappings().addColumnBinding( logicalColumnName, getMappingColumn(), value.getTable() );
    }
  }

  //keep it JDK 1.4 compliant
  //implicit way
  public static final int NO_REFERENCE = 0;
  //reference to the pk in an explicit order
  public static final int PK_REFERENCE = 1;
  //reference to non pk columns
  public static final int NON_PK_REFERENCE = 2;

  public static int checkReferencedColumnsType(
      Ejb3JoinColumn[] columns, PersistentClass referencedEntity,
      ExtendedMappings mappings
  ) {
    //convenient container to find whether a column is an id one or not
    Set<Column> idColumns = new HashSet<Column>();
    Iterator idColumnsIt = referencedEntity.getKey().getColumnIterator();
    while ( idColumnsIt.hasNext() ) {
      idColumns.add( (Column) idColumnsIt.next() );
    }

    boolean isFkReferencedColumnName = false;
    boolean noReferencedColumn = true;
    //build the list of potential tables
    if ( columns.length == 0 ) return NO_REFERENCE; //shortcut
    Object columnOwner = BinderHelper.findColumnOwner(
        referencedEntity, columns[0].getReferencedColumn(), mappings
    );
    if ( columnOwner == null ) {
      try {
        throw new MappingException(
            "Unable to find column with logical name: "
                + columns[0].getReferencedColumn() + " in " + referencedEntity.getTable() + " and its related "
                + "supertables and secondary tables"
        );
      }
      catch (MappingException e) {
        throw new RecoverableException(e);
      }
    }
    Table matchingTable = columnOwner instanceof PersistentClass ?
        ( (PersistentClass) columnOwner ).getTable() :
        ( (Join) columnOwner ).getTable();
    //check each referenced column
    for (Ejb3JoinColumn ejb3Column : columns) {
      String logicalReferencedColumnName = ejb3Column.getReferencedColumn();
      if ( StringHelper.isNotEmpty( logicalReferencedColumnName ) ) {
        String referencedColumnName = null;
        try {
          referencedColumnName = mappings.getPhysicalColumnName( logicalReferencedColumnName, matchingTable );
        }
        catch (MappingException me) {
          //rewrite the exception
          throw new MappingException(
              "Unable to find column with logical name: "
                  + logicalReferencedColumnName + " in " + matchingTable.getName()
          );
        }
        noReferencedColumn = false;
        Column refCol = new Column( referencedColumnName );
        boolean contains = idColumns.contains( refCol );
        if ( !contains ) {
          isFkReferencedColumnName = true;
          break; //we know the state
        }
      }
    }
    if ( isFkReferencedColumnName ) {
      return NON_PK_REFERENCE;
    }
    else if ( noReferencedColumn ) {
      return NO_REFERENCE;
    }
    else if ( idColumns.size() != columns.length ) {
      //reference use PK but is a subset or a superset
      return NON_PK_REFERENCE;
    }
    else {
      return PK_REFERENCE;
    }
  }

  /**
   * Called to apply column definitions from the referenced FK column to this column.
   *
   * @param column the referenced column.
   */
  public void overrideFromReferencedColumnIfNecessary(org.hibernate.mapping.Column column) {
   
    // columnDefinition can also be specified using @JoinColumn, hence we have to check
    // whether it is set or not
    if ( StringHelper.isEmpty( sqlType ) ) {
      sqlType = column.getSqlType();
      if ( getMappingColumn() != null ) getMappingColumn().setSqlType( sqlType );
    }
   
    // these properties can only be applied on the referenced column - we can just take them over
    getMappingColumn().setLength(column.getLength());
    getMappingColumn().setPrecision(column.getPrecision());
    getMappingColumn().setScale(column.getScale());   
  }

  @Override
  public void redefineColumnName(String columnName, String propertyName, boolean applyNamingStrategy) {
    if ( StringHelper.isNotEmpty( columnName ) ) {
      getMappingColumn().setName(
          applyNamingStrategy ?
              getMappings().getNamingStrategy().columnName( columnName ) :
              columnName
      );
    }
  }

  public static Ejb3JoinColumn[] buildJoinTableJoinColumns(
      JoinColumn[] annJoins, Map<String, Join> secondaryTables,
      PropertyHolder propertyHolder, String propertyName, String mappedBy, ExtendedMappings mappings
  ) {
    Ejb3JoinColumn[] joinColumns;
    if ( annJoins == null ) {
      Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
      currentJoinColumn.setImplicit( true );
      currentJoinColumn.setNullable( false ); //I break the spec, but it's for good
      currentJoinColumn.setPropertyHolder( propertyHolder );
      currentJoinColumn.setJoins( secondaryTables );
      currentJoinColumn.setMappings( mappings );
      currentJoinColumn.setPropertyName(
          BinderHelper.getRelativePath( propertyHolder, propertyName )
      );
      currentJoinColumn.setMappedBy( mappedBy );
      currentJoinColumn.bind();

      joinColumns = new Ejb3JoinColumn[] {
          currentJoinColumn

      };
    }
    else {
      joinColumns = new Ejb3JoinColumn[annJoins.length];
      JoinColumn annJoin;
      int length = annJoins.length;
      for (int index = 0; index < length; index++) {
        annJoin = annJoins[index];
        Ejb3JoinColumn currentJoinColumn = new Ejb3JoinColumn();
        currentJoinColumn.setImplicit( true );
        currentJoinColumn.setPropertyHolder( propertyHolder );
        currentJoinColumn.setJoins( secondaryTables );
        currentJoinColumn.setMappings( mappings );
        currentJoinColumn.setPropertyName( BinderHelper.getRelativePath( propertyHolder, propertyName ) );
        currentJoinColumn.setMappedBy( mappedBy );
        currentJoinColumn.setJoinAnnotation( annJoin, propertyName );
        currentJoinColumn.setNullable( false ); //I break the spec, but it's for good
        //done after the annotation to override it
        currentJoinColumn.bind();
        joinColumns[index] = currentJoinColumn;
      }
    }
    return joinColumns;
  }

  public void setMappedBy(String entityName, String logicalTableName, String mappedByProperty) {
    this.mappedByEntityName = entityName;
    this.mappedByTableName = logicalTableName;
    this.mappedByPropertyName = mappedByProperty;
  }
}
TOP

Related Classes of org.hibernate.cfg.Ejb3JoinColumn

TOP
Copyright © 2018 www.massapi.com. 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.