Package org.hibernate.cfg

Source Code of org.hibernate.cfg.Ejb3Column

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors.  All third-party contributions are
* distributed under license by Red Hat, Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA  02110-1301  USA
*/
package org.hibernate.cfg;

import java.util.Map;

import org.jboss.logging.Logger;

import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;

/**
* Wrap state of an EJB3 @Column annotation
* and build the Hibernate column mapping element
*
* @author Emmanuel Bernard
*/
public class Ejb3Column {

    private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, Ejb3Column.class.getName());

  private Column mappingColumn;
  private boolean insertable = true;
  private boolean updatable = true;
  private String secondaryTableName;
  protected Map<String, Join> joins;
  protected PropertyHolder propertyHolder;
  private Mappings mappings;
  private boolean isImplicit;
  public static final int DEFAULT_COLUMN_LENGTH = 255;
  public String sqlType;
  private int length = DEFAULT_COLUMN_LENGTH;
  private int precision;
  private int scale;
  private String logicalColumnName;
  private String propertyName;
  private boolean unique;
  private boolean nullable = true;
  private String formulaString;
  private Formula formula;
  private Table table;
  private String readExpression;
  private String writeExpression;

  public void setTable(Table table) {
    this.table = table;
  }

  public String getLogicalColumnName() {
    return logicalColumnName;
  }

  public String getSqlType() {
    return sqlType;
  }

  public int getLength() {
    return length;
  }

  public int getPrecision() {
    return precision;
  }

  public int getScale() {
    return scale;
  }

  public boolean isUnique() {
    return unique;
  }

  public boolean isFormula() {
    return StringHelper.isNotEmpty( formulaString );
  }

  public String getFormulaString() {
    return formulaString;
  }

  public String getSecondaryTableName() {
    return secondaryTableName;
  }

  public void setFormula(String formula) {
    this.formulaString = formula;
  }

  public boolean isImplicit() {
    return isImplicit;
  }

  public void setInsertable(boolean insertable) {
    this.insertable = insertable;
  }

  public void setUpdatable(boolean updatable) {
    this.updatable = updatable;
  }

  protected Mappings getMappings() {
    return mappings;
  }

  public void setMappings(Mappings mappings) {
    this.mappings = mappings;
  }

  public void setImplicit(boolean implicit) {
    isImplicit = implicit;
  }

  public void setSqlType(String sqlType) {
    this.sqlType = sqlType;
  }

  public void setLength(int length) {
    this.length = length;
  }

  public void setPrecision(int precision) {
    this.precision = precision;
  }

  public void setScale(int scale) {
    this.scale = scale;
  }

  public void setLogicalColumnName(String logicalColumnName) {
    this.logicalColumnName = logicalColumnName;
  }

  public void setPropertyName(String propertyName) {
    this.propertyName = propertyName;
  }

  public String getPropertyName() {
    return propertyName;
  }

  public void setUnique(boolean unique) {
    this.unique = unique;
  }

  public boolean isNullable() {
    return mappingColumn.isNullable();
  }

  public Ejb3Column() {
  }

  public void bind() {
    if ( StringHelper.isNotEmpty( formulaString ) ) {
      LOG.debugf( "Binding formula %s", formulaString );
      formula = new Formula();
      formula.setFormula( formulaString );
    }
    else {
      initMappingColumn(
          logicalColumnName, propertyName, length, precision, scale, nullable, sqlType, unique, true
      );
      if ( LOG.isDebugEnabled() ) {
        LOG.debugf( "Binding column: %s", toString() );
      }
    }
  }

  protected void initMappingColumn(
      String columnName,
      String propertyName,
      int length,
      int precision,
      int scale,
      boolean nullable,
      String sqlType,
      boolean unique,
      boolean applyNamingStrategy) {
    if ( StringHelper.isNotEmpty( formulaString ) ) {
      this.formula = new Formula();
      this.formula.setFormula( formulaString );
    }
    else {
      this.mappingColumn = new Column();
      redefineColumnName( columnName, propertyName, applyNamingStrategy );
      this.mappingColumn.setLength( length );
      if ( precision > 0 ) {  //revelent precision
        this.mappingColumn.setPrecision( precision );
        this.mappingColumn.setScale( scale );
      }
      this.mappingColumn.setNullable( nullable );
      this.mappingColumn.setSqlType( sqlType );
      this.mappingColumn.setUnique( unique );

      if(writeExpression != null && !writeExpression.matches("[^?]*\\?[^?]*")) {
        throw new AnnotationException(
            "@WriteExpression must contain exactly one value placeholder ('?') character: property ["
                + propertyName + "] and column [" + logicalColumnName + "]"
        );
      }
      if ( readExpression != null) {
        this.mappingColumn.setCustomRead( readExpression );
      }
      if ( writeExpression != null) {
        this.mappingColumn.setCustomWrite( writeExpression );
      }
    }
  }

  public boolean isNameDeferred() {
    return mappingColumn == null || StringHelper.isEmpty( mappingColumn.getName() );
  }

  public void redefineColumnName(String columnName, String propertyName, boolean applyNamingStrategy) {
    if ( applyNamingStrategy ) {
      if ( StringHelper.isEmpty( columnName ) ) {
        if ( propertyName != null ) {
          mappingColumn.setName(
              mappings.getObjectNameNormalizer().normalizeIdentifierQuoting(
                  mappings.getNamingStrategy().propertyToColumnName( propertyName )
              )
          );
        }
        //Do nothing otherwise
      }
      else {
        columnName = mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( columnName );
        columnName = mappings.getNamingStrategy().columnName( columnName );
        columnName = mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( columnName );
        mappingColumn.setName( columnName );
      }
    }
    else {
      if ( StringHelper.isNotEmpty( columnName ) ) {
        mappingColumn.setName( mappings.getObjectNameNormalizer().normalizeIdentifierQuoting( columnName ) );
      }
    }
  }

  public String getName() {
    return mappingColumn.getName();
  }

  public Column getMappingColumn() {
    return mappingColumn;
  }

  public boolean isInsertable() {
    return insertable;
  }

  public boolean isUpdatable() {
    return updatable;
  }

  public void setNullable(boolean nullable) {
    if ( mappingColumn != null ) {
      mappingColumn.setNullable( nullable );
    }
    else {
      this.nullable = nullable;
    }
  }

  public void setJoins(Map<String, Join> joins) {
    this.joins = joins;
  }

  public PropertyHolder getPropertyHolder() {
    return propertyHolder;
  }

  public void setPropertyHolder(PropertyHolder propertyHolder) {
    this.propertyHolder = propertyHolder;
  }

  protected void setMappingColumn(Column mappingColumn) {
    this.mappingColumn = mappingColumn;
  }

  public void linkWithValue(SimpleValue value) {
    if ( formula != null ) {
      value.addFormula( formula );
    }
    else {
      getMappingColumn().setValue( value );
      value.addColumn( getMappingColumn() );
      value.getTable().addColumn( getMappingColumn() );
      addColumnBinding( value );
      table = value.getTable();
    }
  }

  protected void addColumnBinding(SimpleValue value) {
    String logicalColumnName = mappings.getNamingStrategy()
        .logicalColumnName( this.logicalColumnName, propertyName );
    mappings.addColumnBinding( logicalColumnName, getMappingColumn(), value.getTable() );
  }

  /**
   * Find appropriate table of the column.
   * It can come from a secondary table or from the main table of the persistent class
   *
   * @return appropriate table
   * @throws AnnotationException missing secondary table
   */
  public Table getTable() {
    if ( table != null ) return table; //association table
    if ( isSecondary() ) {
      return getJoin().getTable();
    }
    else {
      return propertyHolder.getTable();
    }
  }

  public boolean isSecondary() {
    if ( propertyHolder == null ) {
      throw new AssertionFailure( "Should not call getTable() on column wo persistent class defined" );
    }
    if ( StringHelper.isNotEmpty( secondaryTableName ) ) {
      return true;
    }
    // else {
    return false;
  }

  public Join getJoin() {
    Join join = joins.get( secondaryTableName );
    if ( join == null ) {
      throw new AnnotationException(
          "Cannot find the expected secondary table: no "
              + secondaryTableName + " available for " + propertyHolder.getClassName()
      );
    }
    else {
      return join;
    }
  }

  public void forceNotNull() {
    mappingColumn.setNullable( false );
  }

  public void setSecondaryTableName(String secondaryTableName) {
    if ( "``".equals( secondaryTableName ) ) {
      this.secondaryTableName = "";
    }
    else {
      this.secondaryTableName = secondaryTableName;
    }
  }

  public static Ejb3Column[] buildColumnFromAnnotation(
      javax.persistence.Column[] anns,
      org.hibernate.annotations.Formula formulaAnn,
      Nullability nullability,
      PropertyHolder propertyHolder,
      PropertyData inferredData,
      Map<String, Join> secondaryTables,
      Mappings mappings){
    return buildColumnFromAnnotation(
        anns, formulaAnn, nullability, propertyHolder, inferredData, null, secondaryTables, mappings
    );
  }
  public static Ejb3Column[] buildColumnFromAnnotation(
      javax.persistence.Column[] anns,
      org.hibernate.annotations.Formula formulaAnn,
      Nullability nullability,
      PropertyHolder propertyHolder,
      PropertyData inferredData,
      String suffixForDefaultColumnName,
      Map<String, Join> secondaryTables,
      Mappings mappings) {
    Ejb3Column[] columns;
    if ( formulaAnn != null ) {
      Ejb3Column formulaColumn = new Ejb3Column();
      formulaColumn.setFormula( formulaAnn.value() );
      formulaColumn.setImplicit( false );
      formulaColumn.setMappings( mappings );
      formulaColumn.setPropertyHolder( propertyHolder );
      formulaColumn.bind();
      columns = new Ejb3Column[] { formulaColumn };
    }
    else {
      javax.persistence.Column[] actualCols = anns;
      javax.persistence.Column[] overriddenCols = propertyHolder.getOverriddenColumn(
          StringHelper.qualify( propertyHolder.getPath(), inferredData.getPropertyName() )
      );
      if ( overriddenCols != null ) {
        //check for overridden first
        if ( anns != null && overriddenCols.length != anns.length ) {
          throw new AnnotationException( "AttributeOverride.column() should override all columns for now" );
        }
        actualCols = overriddenCols.length == 0 ? null : overriddenCols;
        LOG.debugf( "Column(s) overridden for property %s", inferredData.getPropertyName() );
      }
      if ( actualCols == null ) {
        columns = buildImplicitColumn(
            inferredData,
            suffixForDefaultColumnName,
            secondaryTables,
            propertyHolder,
            nullability,
            mappings
        );
      }
      else {
        final int length = actualCols.length;
        columns = new Ejb3Column[length];
        for (int index = 0; index < length; index++) {
          final ObjectNameNormalizer nameNormalizer = mappings.getObjectNameNormalizer();
          javax.persistence.Column col = actualCols[index];
          final String sqlType = col.columnDefinition().equals( "" )
              ? null
              : nameNormalizer.normalizeIdentifierQuoting( col.columnDefinition() );
          final String tableName = ! StringHelper.isEmpty(col.table())
                                             ? nameNormalizer.normalizeIdentifierQuoting( mappings.getNamingStrategy().tableName( col.table() ) )
                                             : "";
          final String columnName = nameNormalizer.normalizeIdentifierQuoting( col.name() );
          Ejb3Column column = new Ejb3Column();
          column.setImplicit( false );
          column.setSqlType( sqlType );
          column.setLength( col.length() );
          column.setPrecision( col.precision() );
          column.setScale( col.scale() );
          if ( StringHelper.isEmpty( columnName ) && ! StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
            column.setLogicalColumnName( inferredData.getPropertyName() + suffixForDefaultColumnName );
          }
          else {
            column.setLogicalColumnName( columnName );
          }

          column.setPropertyName(
              BinderHelper.getRelativePath( propertyHolder, inferredData.getPropertyName() )
          );
           column.setNullable(
            col.nullable()
          ); //TODO force to not null if available? This is a (bad) user choice.
          column.setUnique( col.unique() );
          column.setInsertable( col.insertable() );
          column.setUpdatable( col.updatable() );
          column.setSecondaryTableName( tableName );
          column.setPropertyHolder( propertyHolder );
          column.setJoins( secondaryTables );
          column.setMappings( mappings );
          column.extractDataFromPropertyData(inferredData);
          column.bind();
          columns[index] = column;
        }
      }
    }
    return columns;
  }

  //must only be called after all setters are defined and before bind
  private void extractDataFromPropertyData(PropertyData inferredData) {
    if ( inferredData != null ) {
      XProperty property = inferredData.getProperty();
      if ( property != null ) {
        processExpression( property.getAnnotation( ColumnTransformer.class ) );
        ColumnTransformers annotations = property.getAnnotation( ColumnTransformers.class );
        if (annotations != null) {
          for ( ColumnTransformer annotation : annotations.value() ) {
            processExpression( annotation );
          }
        }
      }
    }
  }

  private void processExpression(ColumnTransformer annotation) {
    String nonNullLogicalColumnName = logicalColumnName != null ? logicalColumnName : ""; //use the default for annotations
    if ( annotation != null &&
        ( StringHelper.isEmpty( annotation.forColumn() )
            || annotation.forColumn().equals( nonNullLogicalColumnName ) ) ) {
      readExpression = annotation.read();
      if ( StringHelper.isEmpty( readExpression ) ) {
        readExpression = null;
      }
      writeExpression = annotation.write();
      if ( StringHelper.isEmpty( writeExpression ) ) {
        writeExpression = null;
      }
    }
  }

  private static Ejb3Column[] buildImplicitColumn(
      PropertyData inferredData,
      String suffixForDefaultColumnName,
      Map<String, Join> secondaryTables,
      PropertyHolder propertyHolder,
      Nullability nullability,
      Mappings mappings) {
    Ejb3Column column = new Ejb3Column();
    Ejb3Column[] columns = new Ejb3Column[1];
    columns[0] = column;

    //not following the spec but more clean
    if ( nullability != Nullability.FORCED_NULL
        && inferredData.getClassOrElement().isPrimitive()
        && !inferredData.getProperty().isArray() ) {
      column.setNullable( false );
    }
    column.setLength( DEFAULT_COLUMN_LENGTH );
    final String propertyName = inferredData.getPropertyName();
    column.setPropertyName(
        BinderHelper.getRelativePath( propertyHolder, propertyName )
    );
    column.setPropertyHolder( propertyHolder );
    column.setJoins( secondaryTables );
    column.setMappings( mappings );

    // property name + suffix is an "explicit" column name
    if ( !StringHelper.isEmpty( suffixForDefaultColumnName ) ) {
      column.setLogicalColumnName( propertyName + suffixForDefaultColumnName );
      column.setImplicit( false );
    }
    else {
      column.setImplicit( true );
    }
    column.extractDataFromPropertyData( inferredData );
    column.bind();
    return columns;
  }

  public static void checkPropertyConsistency(Ejb3Column[] columns, String propertyName) {
    int nbrOfColumns = columns.length;

    if ( nbrOfColumns > 1 ) {
      for (int currentIndex = 1; currentIndex < nbrOfColumns; currentIndex++) {

        if (columns[currentIndex].isFormula() || columns[currentIndex - 1].isFormula()) {
          continue;
        }

        if ( columns[currentIndex].isInsertable() != columns[currentIndex - 1].isInsertable() ) {
          throw new AnnotationException(
              "Mixing insertable and non insertable columns in a property is not allowed: " + propertyName
          );
        }
        if ( columns[currentIndex].isNullable() != columns[currentIndex - 1].isNullable() ) {
          throw new AnnotationException(
              "Mixing nullable and non nullable columns in a property is not allowed: " + propertyName
          );
        }
        if ( columns[currentIndex].isUpdatable() != columns[currentIndex - 1].isUpdatable() ) {
          throw new AnnotationException(
              "Mixing updatable and non updatable columns in a property is not allowed: " + propertyName
          );
        }
        if ( !columns[currentIndex].getTable().equals( columns[currentIndex - 1].getTable() ) ) {
          throw new AnnotationException(
              "Mixing different tables in a property is not allowed: " + propertyName
          );
        }
      }
    }

  }

  public void addIndex(Index index, boolean inSecondPass) {
    if ( index == null ) return;
    String indexName = index.name();
    addIndex( indexName, inSecondPass );
  }

  void addIndex(String indexName, boolean inSecondPass) {
    IndexOrUniqueKeySecondPass secondPass = new IndexOrUniqueKeySecondPass( indexName, this, mappings, false );
    if ( inSecondPass ) {
      secondPass.doSecondPass( mappings.getClasses() );
    }
    else {
      mappings.addSecondPass(
          secondPass
      );
    }
  }

  void addUniqueKey(String uniqueKeyName, boolean inSecondPass) {
    IndexOrUniqueKeySecondPass secondPass = new IndexOrUniqueKeySecondPass( uniqueKeyName, this, mappings, true );
    if ( inSecondPass ) {
      secondPass.doSecondPass( mappings.getClasses() );
    }
    else {
      mappings.addSecondPass(
          secondPass
      );
    }
  }

  @Override
  public String toString() {
    final StringBuilder sb = new StringBuilder();
    sb.append( "Ejb3Column" );
    sb.append( "{table=" ).append( getTable() );
    sb.append( ", mappingColumn=" ).append( mappingColumn.getName() );
    sb.append( ", insertable=" ).append( insertable );
    sb.append( ", updatable=" ).append( updatable );
    sb.append( ", unique=" ).append( unique );
    sb.append( '}' );
    return sb.toString();
  }
}
TOP

Related Classes of org.hibernate.cfg.Ejb3Column

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.