001: package org.hibernate.id.enhanced;
002:
003: import java.util.Properties;
004: import java.io.Serializable;
005:
006: import org.apache.commons.logging.Log;
007: import org.apache.commons.logging.LogFactory;
008:
009: import org.hibernate.id.PersistentIdentifierGenerator;
010: import org.hibernate.id.Configurable;
011: import org.hibernate.HibernateException;
012: import org.hibernate.MappingException;
013: import org.hibernate.engine.SessionImplementor;
014: import org.hibernate.mapping.Table;
015: import org.hibernate.util.PropertiesHelper;
016: import org.hibernate.type.Type;
017: import org.hibernate.dialect.Dialect;
018:
019: /**
020: * Generates identifier values based on an sequence-style database structure.
021: * Variations range from actually using a sequence to using a table to mimic
022: * a sequence. These variations are encapsulated by the {@link DatabaseStructure}
023: * interface internally.
024: * <p/>
025: * General configuration parameters:
026: * <table>
027: * <tr>
028: * <td><b>NAME</b></td>
029: * <td><b>DEFAULT</b></td>
030: * <td><b>DESCRIPTION</b></td>
031: * </tr>
032: * <tr>
033: * <td>{@link #SEQUENCE_PARAM}</td>
034: * <td>{@link #DEF_SEQUENCE_NAME}</td>
035: * <td>The name of the sequence/table to use to store/retrieve values</td>
036: * </tr>
037: * <tr>
038: * <td>{@link #INITIAL_PARAM}</td>
039: * <td>{@link #DEFAULT_INITIAL_VALUE}</td>
040: * <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>
041: * </tr>
042: * <tr>
043: * <td>{@link #INCREMENT_PARAM}</td>
044: * <td>{@link #DEFAULT_INCREMENT_SIZE}</td>
045: * <td>The increment size for the underlying segment; the effect in terms of storage varies based on {@link Optimizer} and {@link DatabaseStructure}</td>
046: * </tr>
047: * <tr>
048: * <td>{@link #OPT_PARAM}</td>
049: * <td><i>depends on defined increment size</i></td>
050: * <td>Allows explicit definition of which optimization strategy to use</td>
051: * </tr>
052: * <td>{@link #FORCE_TBL_PARAM}</td>
053: * <td><b><i>false<i/></b></td>
054: * <td>Allows explicit definition of which optimization strategy to use</td>
055: * </tr>
056: * </table>
057: * <p/>
058: * Configuration parameters used specifically when the underlying structure is a table:
059: * <table>
060: * <tr>
061: * <td><b>NAME</b></td>
062: * <td><b>DEFAULT</b></td>
063: * <td><b>DESCRIPTION</b></td>
064: * </tr>
065: * <tr>
066: * <td>{@link #VALUE_COLUMN_PARAM}</td>
067: * <td>{@link #DEF_VALUE_COLUMN}</td>
068: * <td>The name of column which holds the sequence value for the given segment</td>
069: * </tr>
070: * </table>
071: *
072: * @author Steve Ebersole
073: */
074: public class SequenceStyleGenerator implements
075: PersistentIdentifierGenerator, Configurable {
076: private static final Log log = LogFactory
077: .getLog(SequenceStyleGenerator.class);
078:
079: // general purpose parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
080: public static final String SEQUENCE_PARAM = "sequence_name";
081: public static final String DEF_SEQUENCE_NAME = "hibernate_sequence";
082:
083: public static final String INITIAL_PARAM = "initial_value";
084: public static final int DEFAULT_INITIAL_VALUE = 1;
085:
086: public static final String INCREMENT_PARAM = "increment_size";
087: public static final int DEFAULT_INCREMENT_SIZE = 1;
088:
089: public static final String OPT_PARAM = "optimizer";
090:
091: public static final String FORCE_TBL_PARAM = "force_table_use";
092:
093: // table-specific parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
094: public static final String VALUE_COLUMN_PARAM = "value_column";
095: public static final String DEF_VALUE_COLUMN = "next_val";
096:
097: // state ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
098: private DatabaseStructure databaseStructure;
099: private Optimizer optimizer;
100: private Type identifierType;
101:
102: public DatabaseStructure getDatabaseStructure() {
103: return databaseStructure;
104: }
105:
106: public Optimizer getOptimizer() {
107: return optimizer;
108: }
109:
110: public Type getIdentifierType() {
111: return identifierType;
112: }
113:
114: // Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
115:
116: public void configure(Type type, Properties params, Dialect dialect)
117: throws MappingException {
118: identifierType = type;
119: boolean forceTableUse = PropertiesHelper.getBoolean(
120: FORCE_TBL_PARAM, params, false);
121:
122: String sequenceName = PropertiesHelper.getString(
123: SEQUENCE_PARAM, params, DEF_SEQUENCE_NAME);
124: if (sequenceName.indexOf('.') < 0) {
125: String schemaName = params.getProperty(SCHEMA);
126: String catalogName = params.getProperty(CATALOG);
127: sequenceName = Table.qualify(catalogName, schemaName,
128: sequenceName);
129: }
130: int initialValue = PropertiesHelper.getInt(INITIAL_PARAM,
131: params, DEFAULT_INITIAL_VALUE);
132: int incrementSize = PropertiesHelper.getInt(INCREMENT_PARAM,
133: params, DEFAULT_INCREMENT_SIZE);
134:
135: String valueColumnName = PropertiesHelper.getString(
136: VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN);
137:
138: String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE
139: : OptimizerFactory.POOL;
140: String optimizationStrategy = PropertiesHelper.getString(
141: OPT_PARAM, params, defOptStrategy);
142: if (OptimizerFactory.NONE.equals(optimizationStrategy)
143: && incrementSize > 1) {
144: log.warn("config specified explicit optimizer of ["
145: + OptimizerFactory.NONE + "], but ["
146: + INCREMENT_PARAM + "=" + incrementSize
147: + "; honoring optimizer setting");
148: incrementSize = 1;
149: }
150: if (dialect.supportsSequences() && !forceTableUse) {
151: if (OptimizerFactory.POOL.equals(optimizationStrategy)
152: && !dialect.supportsPooledSequences()) {
153: // TODO : may even be better to fall back to a pooled table strategy here so that the db stored values remain consistent...
154: optimizationStrategy = OptimizerFactory.HILO;
155: }
156: databaseStructure = new SequenceStructure(dialect,
157: sequenceName, initialValue, incrementSize);
158: } else {
159: databaseStructure = new TableStructure(dialect,
160: sequenceName, valueColumnName, initialValue,
161: incrementSize);
162: }
163:
164: optimizer = OptimizerFactory.buildOptimizer(
165: optimizationStrategy,
166: identifierType.getReturnedClass(), incrementSize);
167: databaseStructure.prepare(optimizer);
168: }
169:
170: // IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
171:
172: public Serializable generate(SessionImplementor session,
173: Object object) throws HibernateException {
174: return optimizer.generate(databaseStructure
175: .buildCallback(session));
176: }
177:
178: // PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
179:
180: public Object generatorKey() {
181: return databaseStructure.getName();
182: }
183:
184: public String[] sqlCreateStrings(Dialect dialect)
185: throws HibernateException {
186: return databaseStructure.sqlCreateStrings(dialect);
187: }
188:
189: public String[] sqlDropStrings(Dialect dialect)
190: throws HibernateException {
191: return databaseStructure.sqlDropStrings(dialect);
192: }
193:
194: }
|