Open Source Repository

Home /hibernate/hibernate-3.2.7.ga | Repository Home


org/hibernate/id/enhanced/SequenceStyleGenerator.java
package org.hibernate.id.enhanced;

import java.util.Properties;
import java.io.Serializable;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.Configurable;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.mapping.Table;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.type.Type;
import org.hibernate.dialect.Dialect;

/**
 * Generates identifier values based on an sequence-style database structure.
 * Variations range from actually using a sequence to using a table to mimic
 * a sequence.  These variations are encapsulated by the {@link DatabaseStructure}
 * interface internally.
 <p/>
 * General configuration parameters:
 <table>
 *    <tr>
 *     <td><b>NAME</b></td>
 *     <td><b>DEFAULT</b></td>
 *     <td><b>DESCRIPTION</b></td>
 *   </tr>
 *   <tr>
 *     <td>{@link #SEQUENCE_PARAM}</td>
 *     <td>{@link #DEF_SEQUENCE_NAME}</td>
 *     <td>The name of the sequence/table to use to store/retrieve values</td>
 *   </tr>
 *   <tr>
 *     <td>{@link #INITIAL_PARAM}</td>
 *     <td>{@link #DEFAULT_INITIAL_VALUE}</td>
 *     <td>The initial value to be stored for the given segment; the effect in terms of storage varies based on {@link Optimizer} and {@link DatabaseStructure}</td>
 *   </tr>
 *   <tr>
 *     <td>{@link #INCREMENT_PARAM}</td>
 *     <td>{@link #DEFAULT_INCREMENT_SIZE}</td>
 *     <td>The increment size for the underlying segment; the effect in terms of storage varies based on {@link Optimizer} and {@link DatabaseStructure}</td>
 *   </tr>
 *   <tr>
 *     <td>{@link #OPT_PARAM}</td>
 *     <td><i>depends on defined increment size</i></td>
 *     <td>Allows explicit definition of which optimization strategy to use</td>
 *   </tr>
 *     <td>{@link #FORCE_TBL_PARAM}</td>
 *     <td><b><i>false<i/></b></td>
 *     <td>Allows explicit definition of which optimization strategy to use</td>
 *   </tr>
 </table>
 <p/>
 * Configuration parameters used specifically when the underlying structure is a table:
 <table>
 *    <tr>
 *     <td><b>NAME</b></td>
 *     <td><b>DEFAULT</b></td>
 *     <td><b>DESCRIPTION</b></td>
 *   </tr>
 *   <tr>
 *     <td>{@link #VALUE_COLUMN_PARAM}</td>
 *     <td>{@link #DEF_VALUE_COLUMN}</td>
 *     <td>The name of column which holds the sequence value for the given segment</td>
 *   </tr>
 </table>
 *
 @author Steve Ebersole
 */
public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Configurable {
  private static final Log log = LogFactory.getLogSequenceStyleGenerator.class );

  // general purpose parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  public static final String SEQUENCE_PARAM = "sequence_name";
  public static final String DEF_SEQUENCE_NAME = "hibernate_sequence";

  public static final String INITIAL_PARAM = "initial_value";
  public static final int DEFAULT_INITIAL_VALUE = 1;

  public static final String INCREMENT_PARAM = "increment_size";
  public static final int DEFAULT_INCREMENT_SIZE = 1;

  public static final String OPT_PARAM = "optimizer";

  public static final String FORCE_TBL_PARAM = "force_table_use";


  // table-specific parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  public static final String VALUE_COLUMN_PARAM = "value_column";
  public static final String DEF_VALUE_COLUMN = "next_val";


  // state ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  private DatabaseStructure databaseStructure;
  private Optimizer optimizer;
  private Type identifierType;

  /**
   * Getter for property 'databaseStructure'.
   *
   @return Value for property 'databaseStructure'.
   */
  public DatabaseStructure getDatabaseStructure() {
    return databaseStructure;
  }

  /**
   * Getter for property 'optimizer'.
   *
   @return Value for property 'optimizer'.
   */
  public Optimizer getOptimizer() {
    return optimizer;
  }

  /**
   * Getter for property 'identifierType'.
   *
   @return Value for property 'identifierType'.
   */
  public Type getIdentifierType() {
    return identifierType;
  }


  // Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  /**
   * {@inheritDoc}
   */
  public void configure(Type type, Properties params, Dialect dialectthrows MappingException {
    this.identifierType = type;
    boolean forceTableUse = PropertiesHelper.getBooleanFORCE_TBL_PARAM, params, false );

    final String sequenceName = determineSequenceNameparams );

    final int initialValue = determineInitialValueparams );
    int incrementSize = determineIncrementSizeparams );

    final String optimizationStrategy = determineOptimizationStrategyparams, incrementSize );
    incrementSize = determineAdjustedIncrementSizeoptimizationStrategy, incrementSize );

    if dialect.supportsSequences() && !forceTableUse ) {
      if OptimizerFactory.POOL.equalsoptimizationStrategy && !dialect.supportsPooledSequences() ) {
        forceTableUse = true;
        log.info(
            "Forcing table use for sequence-style generator due to pooled optimizer selection where db does not support pooled sequences"
        );
      }
    }

    this.databaseStructure = buildDatabaseStructureparams, dialect, forceTableUse, sequenceName, initialValue, incrementSize );

    this.optimizer = OptimizerFactory.buildOptimizeroptimizationStrategy, identifierType.getReturnedClass(), incrementSize );
    this.databaseStructure.prepareoptimizer );
  }

  /**
   * Determine the name of the sequence (or table if this resolves to a physical table)
   * to use.
   <p/>
   * Called during {@link #configure configuration}.
   *
   @param params The params supplied in the generator config (plus some standard useful extras).
   @return The sequence name
   */
  protected String determineSequenceName(Properties params) {
    String sequenceName = PropertiesHelper.getStringSEQUENCE_PARAM, params, DEF_SEQUENCE_NAME );
    if sequenceName.indexOf'.' ) {
      String schemaName = params.getPropertySCHEMA );
      String catalogName = params.getPropertyCATALOG );
      sequenceName = Table.qualifycatalogName, schemaName, sequenceName );
    }
    return sequenceName;
  }

  /**
   * Determine the name of the column used to store the generator value in
   * the db.
   <p/>
   * Called during {@link #configure configuration} <b>when resolving to a
   * physical table</b>.
   *
   @param params The params supplied in the generator config (plus some standard useful extras).
   @return The value column name
   */
  protected String determineValueColumnName(Properties params) {
    return PropertiesHelper.getStringVALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
  }

  /**
   * Determine the initial sequence value to use.  This value is used when
   * initializing the {@link #getDatabaseStructure() database structure}
   * (i.e. sequence/table).
   <p/>
   * Called during {@link #configure configuration}.
   *
   @param params The params supplied in the generator config (plus some standard useful extras).
   @return The initial value
   */
  protected int determineInitialValue(Properties params) {
    return PropertiesHelper.getIntINITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
  }

  /**
   * Determine the increment size to be applied.  The exact implications of
   * this value depends on the {@link #getOptimizer() optimizer} being used.
   <p/>
   * Called during {@link #configure configuration}.
   *
   @param params The params supplied in the generator config (plus some standard useful extras).
   @return The increment size
   */
  protected int determineIncrementSize(Properties params) {
    return PropertiesHelper.getIntINCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
  }

  /**
   * Determine the optimizer to use.
   <p/>
   * Called during {@link #configure configuration}.
   *
   @param params The params supplied in the generator config (plus some standard useful extras).
   @param incrementSize The {@link #determineIncrementSize determined increment size}
   @return The optimizer strategy (name)
   */
  protected String determineOptimizationStrategy(Properties params, int incrementSize) {
    String defOptStrategy = incrementSize <= ? OptimizerFactory.NONE : OptimizerFactory.POOL;
    return PropertiesHelper.getStringOPT_PARAM, params, defOptStrategy );
  }

  /**
   * In certain cases we need to adjust the increment size based on the
   * selected optimizer.  This is the hook to achieve that.
   *
   @param optimizationStrategy The optimizer strategy (name)
   @param incrementSize The {@link #determineIncrementSize determined increment size}
   @return The adjusted increment size.
   */
  protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
    if OptimizerFactory.NONE.equalsoptimizationStrategy && incrementSize > ) {
      log.warn"config specified explicit optimizer of [" + OptimizerFactory.NONE + "], but [" + INCREMENT_PARAM + "=" + incrementSize + "; honoring optimizer setting" );
      incrementSize = 1;
    }
    return incrementSize;
  }

  /**
   * Build the database structure.
   *
   @param params The params supplied in the generator config (plus some standard useful extras).
   @param dialect The dialect being used.
   @param forceTableUse Should a table be used even if the dialect supports sequences?
   @param sequenceName The name to use for the sequence or table.
   @param initialValue The initial value.
   @param incrementSize the increment size to use (after any adjustments).
   @return The db structure representation
   */
  protected DatabaseStructure buildDatabaseStructure(
      Properties params,
      Dialect dialect,
      boolean forceTableUse,
      String sequenceName,
      int initialValue,
      int incrementSize) {
    boolean useSequence = dialect.supportsSequences() && !forceTableUse;
    if useSequence ) {
      return new SequenceStructuredialect, sequenceName, initialValue, incrementSize );
    }
    else {
      String valueColumnName = determineValueColumnNameparams );
      return new TableStructuredialect, sequenceName, valueColumnName, initialValue, incrementSize );
    }
  }


  // IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  /**
   * {@inheritDoc}
   */
  public Serializable generate(SessionImplementor session, Object objectthrows HibernateException {
    return optimizer.generatedatabaseStructure.buildCallbacksession ) );
  }


  // PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~

  /**
   * {@inheritDoc}
   */
  public Object generatorKey() {
    return databaseStructure.getName();
  }

  /**
   * {@inheritDoc}
   */
  public String[] sqlCreateStrings(Dialect dialectthrows HibernateException {
    return databaseStructure.sqlCreateStringsdialect );
  }

  /**
   * {@inheritDoc}
   */
  public String[] sqlDropStrings(Dialect dialectthrows HibernateException {
    return databaseStructure.sqlDropStringsdialect );
  }

}