Package org.hibernate.mapping

Source Code of org.hibernate.mapping.SimpleValue$ParameterTypeImpl

/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. 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.mapping;

import javax.persistence.AttributeConverter;
import java.lang.annotation.Annotation;
import java.lang.reflect.TypeVariable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.jboss.logging.Logger;

import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.cfg.AttributeConverterDefinition;
import org.hibernate.cfg.Environment;
import org.hibernate.cfg.Mappings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.factory.IdentifierGeneratorFactory;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.type.AbstractSingleColumnStandardBasicType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaTypeDescriptor;
import org.hibernate.type.descriptor.java.JavaTypeDescriptorRegistry;
import org.hibernate.type.descriptor.sql.BasicBinder;
import org.hibernate.type.descriptor.sql.BasicExtractor;
import org.hibernate.type.descriptor.sql.JdbcTypeJavaClassMappings;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorRegistry;
import org.hibernate.usertype.DynamicParameterizedType;

/**
* Any value that maps to columns.
* @author Gavin King
*/
public class SimpleValue implements KeyValue {
  private static final Logger log = Logger.getLogger( SimpleValue.class );

  public static final String DEFAULT_ID_GEN_STRATEGY = "assigned";

  private final Mappings mappings;

  private final List columns = new ArrayList();
  private String typeName;
  private Properties identifierGeneratorProperties;
  private String identifierGeneratorStrategy = DEFAULT_ID_GEN_STRATEGY;
  private String nullValue;
  private Table table;
  private String foreignKeyName;
  private boolean alternateUniqueKey;
  private Properties typeParameters;
  private boolean cascadeDeleteEnabled;

  private AttributeConverterDefinition jpaAttributeConverterDefinition;
  private Type type;

  public SimpleValue(Mappings mappings) {
    this.mappings = mappings;
  }

  public SimpleValue(Mappings mappings, Table table) {
    this( mappings );
    this.table = table;
  }

  public Mappings getMappings() {
    return mappings;
  }

  public boolean isCascadeDeleteEnabled() {
    return cascadeDeleteEnabled;
  }

  public void setCascadeDeleteEnabled(boolean cascadeDeleteEnabled) {
    this.cascadeDeleteEnabled = cascadeDeleteEnabled;
  }
 
  public void addColumn(Column column) {
    if ( !columns.contains(column) ) columns.add(column);
    column.setValue(this);
    column.setTypeIndex( columns.size()-1 );
  }
 
  public void addFormula(Formula formula) {
    columns.add(formula);
  }
 
  public boolean hasFormula() {
    Iterator iter = getColumnIterator();
    while ( iter.hasNext() ) {
      Object o = iter.next();
      if (o instanceof Formula) return true;
    }
    return false;
  }

  public int getColumnSpan() {
    return columns.size();
  }
  public Iterator getColumnIterator() {
    return columns.iterator();
  }
  public List getConstraintColumns() {
    return columns;
  }
  public String getTypeName() {
    return typeName;
  }
  public void setTypeName(String type) {
    this.typeName = type;
  }
  public void setTable(Table table) {
    this.table = table;
  }

  public void createForeignKey() throws MappingException {}

  public void createForeignKeyOfEntity(String entityName) {
    if ( !hasFormula() && !"none".equals(getForeignKeyName())) {
      ForeignKey fk = table.createForeignKey( getForeignKeyName(), getConstraintColumns(), entityName );
      fk.setCascadeDeleteEnabled(cascadeDeleteEnabled);
    }
  }

  public IdentifierGenerator createIdentifierGenerator(
      IdentifierGeneratorFactory identifierGeneratorFactory,
      Dialect dialect,
      String defaultCatalog,
      String defaultSchema,
      RootClass rootClass) throws MappingException {
   
    Properties params = new Properties();
   
    //if the hibernate-mapping did not specify a schema/catalog, use the defaults
    //specified by properties - but note that if the schema/catalog were specified
    //in hibernate-mapping, or as params, they will already be initialized and
    //will override the values set here (they are in identifierGeneratorProperties)
    if ( defaultSchema!=null ) {
      params.setProperty(PersistentIdentifierGenerator.SCHEMA, defaultSchema);
    }
    if ( defaultCatalog!=null ) {
      params.setProperty(PersistentIdentifierGenerator.CATALOG, defaultCatalog);
    }
   
    //pass the entity-name, if not a collection-id
    if (rootClass!=null) {
      params.setProperty( IdentifierGenerator.ENTITY_NAME, rootClass.getEntityName() );
      params.setProperty( IdentifierGenerator.JPA_ENTITY_NAME, rootClass.getJpaEntityName() );
    }
   
    //init the table here instead of earlier, so that we can get a quoted table name
    //TODO: would it be better to simply pass the qualified table name, instead of
    //      splitting it up into schema/catalog/table names
    String tableName = getTable().getQuotedName(dialect);
    params.setProperty( PersistentIdentifierGenerator.TABLE, tableName );
   
    //pass the column name (a generated id almost always has a single column)
    String columnName = ( (Column) getColumnIterator().next() ).getQuotedName(dialect);
    params.setProperty( PersistentIdentifierGenerator.PK, columnName );
   
    if (rootClass!=null) {
      StringBuilder tables = new StringBuilder();
      Iterator iter = rootClass.getIdentityTables().iterator();
      while ( iter.hasNext() ) {
        Table table= (Table) iter.next();
        tables.append( table.getQuotedName(dialect) );
        if ( iter.hasNext() ) tables.append(", ");
      }
      params.setProperty( PersistentIdentifierGenerator.TABLES, tables.toString() );
    }
    else {
      params.setProperty( PersistentIdentifierGenerator.TABLES, tableName );
    }

    if (identifierGeneratorProperties!=null) {
      params.putAll(identifierGeneratorProperties);
    }

    // TODO : we should pass along all settings once "config lifecycle" is hashed out...
    params.put(
        Environment.PREFER_POOLED_VALUES_LO,
        mappings.getConfigurationProperties().getProperty( Environment.PREFER_POOLED_VALUES_LO, "false" )
    );

    identifierGeneratorFactory.setDialect( dialect );
    return identifierGeneratorFactory.createIdentifierGenerator( identifierGeneratorStrategy, getType(), params );
   
  }

  public boolean isUpdateable() {
    //needed to satisfy KeyValue
    return true;
  }
 
  public FetchMode getFetchMode() {
    return FetchMode.SELECT;
  }

  public Properties getIdentifierGeneratorProperties() {
    return identifierGeneratorProperties;
  }

  public String getNullValue() {
    return nullValue;
  }

  public Table getTable() {
    return table;
  }

  /**
   * Returns the identifierGeneratorStrategy.
   * @return String
   */
  public String getIdentifierGeneratorStrategy() {
    return identifierGeneratorStrategy;
  }
 
  public boolean isIdentityColumn(IdentifierGeneratorFactory identifierGeneratorFactory, Dialect dialect) {
    identifierGeneratorFactory.setDialect( dialect );
    return identifierGeneratorFactory.getIdentifierGeneratorClass( identifierGeneratorStrategy )
        .equals( IdentityGenerator.class );
  }

  /**
   * Sets the identifierGeneratorProperties.
   * @param identifierGeneratorProperties The identifierGeneratorProperties to set
   */
  public void setIdentifierGeneratorProperties(Properties identifierGeneratorProperties) {
    this.identifierGeneratorProperties = identifierGeneratorProperties;
  }

  /**
   * Sets the identifierGeneratorStrategy.
   * @param identifierGeneratorStrategy The identifierGeneratorStrategy to set
   */
  public void setIdentifierGeneratorStrategy(String identifierGeneratorStrategy) {
    this.identifierGeneratorStrategy = identifierGeneratorStrategy;
  }

  /**
   * Sets the nullValue.
   * @param nullValue The nullValue to set
   */
  public void setNullValue(String nullValue) {
    this.nullValue = nullValue;
  }

  public String getForeignKeyName() {
    return foreignKeyName;
  }

  public void setForeignKeyName(String foreignKeyName) {
    this.foreignKeyName = foreignKeyName;
  }

  public boolean isAlternateUniqueKey() {
    return alternateUniqueKey;
  }

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

  public boolean isNullable() {
    if ( hasFormula() ) return true;
    boolean nullable = true;
    Iterator iter = getColumnIterator();
    while ( iter.hasNext() ) {
      if ( !( (Column) iter.next() ).isNullable() ) {
        nullable = false;
        return nullable; //shortcut
      }
    }
    return nullable;
  }

  public boolean isSimpleValue() {
    return true;
  }

  public boolean isValid(Mapping mapping) throws MappingException {
    return getColumnSpan()==getType().getColumnSpan(mapping);
  }

  public Type getType() throws MappingException {
    if ( type != null ) {
      return type;
    }

    if ( typeName == null ) {
      throw new MappingException( "No type name" );
    }
    if ( typeParameters != null
        && Boolean.valueOf( typeParameters.getProperty( DynamicParameterizedType.IS_DYNAMIC ) )
        && typeParameters.get( DynamicParameterizedType.PARAMETER_TYPE ) == null ) {
      createParameterImpl();
    }

    Type result = mappings.getTypeResolver().heuristicType( typeName, typeParameters );
    if ( result == null ) {
      String msg = "Could not determine type for: " + typeName;
      if ( table != null ) {
        msg += ", at table: " + table.getName();
      }
      if ( columns != null && columns.size() > 0 ) {
        msg += ", for columns: " + columns;
      }
      throw new MappingException( msg );
    }

    return result;
  }

  @SuppressWarnings("unchecked")
  public void setTypeUsingReflection(String className, String propertyName) throws MappingException {
    // NOTE : this is called as the last piece in setting SimpleValue type information, and implementations
    // rely on that fact, using it as a signal that all information it is going to get is defined at this point...

    if ( typeName != null ) {
      // assume either (a) explicit type was specified or (b) determine was already performed
      return;
    }

    if ( type != null ) {
      return;
    }

    if ( jpaAttributeConverterDefinition == null ) {
      // this is here to work like legacy.  This should change when we integrate with metamodel to
      // look for SqlTypeDescriptor and JavaTypeDescriptor individually and create the BasicType (well, really
      // keep a registry of [SqlTypeDescriptor,JavaTypeDescriptor] -> BasicType...)
      if ( className == null ) {
        throw new MappingException( "you must specify types for a dynamic entity: " + propertyName );
      }
      typeName = ReflectHelper.reflectedPropertyClass( className, propertyName ).getName();
      return;
    }

    // we had an AttributeConverter...

    // todo : we should validate the number of columns present
    // todo : ultimately I want to see attributeConverterJavaType and attributeConverterJdbcTypeCode specify-able separately
    //    then we can "play them against each other" in terms of determining proper typing
    // todo : see if we already have previously built a custom on-the-fly BasicType for this AttributeConverter; see note below about caching

    // AttributeConverter works totally in memory, meaning it converts between one Java representation (the entity
    // attribute representation) and another (the value bound into JDBC statements or extracted from results).
    // However, the Hibernate Type system operates at the lower level of actually dealing with those JDBC objects.
    // So even though we have an AttributeConverter, we still need to "fill out" the rest of the BasicType
    // data.  For the JavaTypeDescriptor portion we simply resolve the "entity attribute representation" part of
    // the AttributeConverter to resolve the corresponding descriptor.  For the SqlTypeDescriptor portion we use the
    // "database column representation" part of the AttributeConverter to resolve the "recommended" JDBC type-code
    // and use that type-code to resolve the SqlTypeDescriptor to use.
    final Class entityAttributeJavaType = jpaAttributeConverterDefinition.getEntityAttributeType();
    final Class databaseColumnJavaType = jpaAttributeConverterDefinition.getDatabaseColumnType();
    final int jdbcTypeCode = JdbcTypeJavaClassMappings.INSTANCE.determineJdbcTypeCodeForJavaClass( databaseColumnJavaType );

    final JavaTypeDescriptor javaTypeDescriptor = JavaTypeDescriptorRegistry.INSTANCE.getDescriptor( entityAttributeJavaType );
    final SqlTypeDescriptor sqlTypeDescriptor = SqlTypeDescriptorRegistry.INSTANCE.getDescriptor( jdbcTypeCode );
    // the adapter here injects the AttributeConverter calls into the binding/extraction process...
    final SqlTypeDescriptor sqlTypeDescriptorAdapter = new AttributeConverterSqlTypeDescriptorAdapter(
        jpaAttributeConverterDefinition.getAttributeConverter(),
        sqlTypeDescriptor
    );

    final String name = "BasicType adapter for AttributeConverter<" + entityAttributeJavaType + "," + databaseColumnJavaType + ">";
    type = new AbstractSingleColumnStandardBasicType( sqlTypeDescriptorAdapter, javaTypeDescriptor ) {
      @Override
      public String getName() {
        return name;
      }
    };
    log.debug( "Created : " + name );

    // todo : cache the BasicType we just created in case that AttributeConverter is applied multiple times.
  }

  private Class extractType(TypeVariable typeVariable) {
    java.lang.reflect.Type[] boundTypes = typeVariable.getBounds();
    if ( boundTypes == null || boundTypes.length != 1 ) {
      return null;
    }

    return (Class) boundTypes[0];
  }

  public boolean isTypeSpecified() {
    return typeName!=null;
  }

  public void setTypeParameters(Properties parameterMap) {
    this.typeParameters = parameterMap;
  }
 
  public Properties getTypeParameters() {
    return typeParameters;
  }

  @Override
    public String toString() {
    return getClass().getName() + '(' + columns.toString() + ')';
  }

  public Object accept(ValueVisitor visitor) {
    return visitor.accept(this);
  }
 
  public boolean[] getColumnInsertability() {
    boolean[] result = new boolean[ getColumnSpan() ];
    int i = 0;
    Iterator iter = getColumnIterator();
    while ( iter.hasNext() ) {
      Selectable s = (Selectable) iter.next();
      result[i++] = !s.isFormula();
    }
    return result;
  }
 
  public boolean[] getColumnUpdateability() {
    return getColumnInsertability();
  }

  public void setJpaAttributeConverterDefinition(AttributeConverterDefinition jpaAttributeConverterDefinition) {
    this.jpaAttributeConverterDefinition = jpaAttributeConverterDefinition;
  }

  public static class AttributeConverterSqlTypeDescriptorAdapter implements SqlTypeDescriptor {
    private final AttributeConverter converter;
    private final SqlTypeDescriptor delegate;

    public AttributeConverterSqlTypeDescriptorAdapter(AttributeConverter converter, SqlTypeDescriptor delegate) {
      this.converter = converter;
      this.delegate = delegate;
    }

    @Override
    public int getSqlType() {
      return delegate.getSqlType();
    }

    @Override
    public boolean canBeRemapped() {
      return delegate.canBeRemapped();
    }

    @Override
    public <X> ValueBinder<X> getBinder(JavaTypeDescriptor<X> javaTypeDescriptor) {
      final ValueBinder realBinder = delegate.getBinder( javaTypeDescriptor );
      return new BasicBinder<X>( javaTypeDescriptor, this ) {
        @Override
        @SuppressWarnings("unchecked")
        protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
            throws SQLException {
          realBinder.bind( st, converter.convertToDatabaseColumn( value ), index, options );
        }
      };
    }

    @Override
    public <X> ValueExtractor<X> getExtractor(JavaTypeDescriptor<X> javaTypeDescriptor) {
      final ValueExtractor realExtractor = delegate.getExtractor( javaTypeDescriptor );
      return new BasicExtractor<X>( javaTypeDescriptor, this ) {
        @Override
        @SuppressWarnings("unchecked")
        protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException {
          return (X) converter.convertToEntityAttribute( realExtractor.extract( rs, name, options ) );
        }

        @Override
        @SuppressWarnings("unchecked")
        protected X doExtract(CallableStatement statement, int index, WrapperOptions options)
            throws SQLException {
          return (X) converter.convertToEntityAttribute( realExtractor.extract( statement, index, options ) );
        }

        @Override
        @SuppressWarnings("unchecked")
        protected X doExtract(CallableStatement statement, String name, WrapperOptions options) throws SQLException {
          return (X) converter.convertToEntityAttribute( realExtractor.extract( statement, new String[] {name}, options ) );
        }
      };
    }
  }

  private void createParameterImpl() {
    try {
      String[] columnsNames = new String[columns.size()];
      for ( int i = 0; i < columns.size(); i++ ) {
        columnsNames[i] = ( (Column) columns.get( i ) ).getName();
      }

      final XProperty xProperty = (XProperty) typeParameters.get( DynamicParameterizedType.XPROPERTY );
      // todo : not sure this works for handling @MapKeyEnumerated
      final Annotation[] annotations = xProperty == null
          ? null
          : xProperty.getAnnotations();

      typeParameters.put(
          DynamicParameterizedType.PARAMETER_TYPE,
          new ParameterTypeImpl(
              ReflectHelper.classForName(
                  typeParameters.getProperty( DynamicParameterizedType.RETURNED_CLASS )
              ),
              annotations,
              table.getCatalog(),
              table.getSchema(),
              table.getName(),
              Boolean.valueOf( typeParameters.getProperty( DynamicParameterizedType.IS_PRIMARY_KEY ) ),
              columnsNames
          )
      );
    }
    catch ( ClassNotFoundException cnfe ) {
      throw new MappingException( "Could not create DynamicParameterizedType for type: " + typeName, cnfe );
    }
  }

  private final class ParameterTypeImpl implements DynamicParameterizedType.ParameterType {

    private final Class returnedClass;
    private final Annotation[] annotationsMethod;
    private final String catalog;
    private final String schema;
    private final String table;
    private final boolean primaryKey;
    private final String[] columns;

    private ParameterTypeImpl(Class returnedClass, Annotation[] annotationsMethod, String catalog, String schema,
        String table, boolean primaryKey, String[] columns) {
      this.returnedClass = returnedClass;
      this.annotationsMethod = annotationsMethod;
      this.catalog = catalog;
      this.schema = schema;
      this.table = table;
      this.primaryKey = primaryKey;
      this.columns = columns;
    }

    @Override
    public Class getReturnedClass() {
      return returnedClass;
    }

    @Override
    public Annotation[] getAnnotationsMethod() {
      return annotationsMethod;
    }

    @Override
    public String getCatalog() {
      return catalog;
    }

    @Override
    public String getSchema() {
      return schema;
    }

    @Override
    public String getTable() {
      return table;
    }

    @Override
    public boolean isPrimaryKey() {
      return primaryKey;
    }

    @Override
    public String[] getColumns() {
      return columns;
    }
  }
}
TOP

Related Classes of org.hibernate.mapping.SimpleValue$ParameterTypeImpl

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.