From hibernate-commits at lists.jboss.org Fri Aug 29 02:22:52 2008 Content-Type: multipart/mixed; boundary="===============8699101726955744707==" MIME-Version: 1.0 From: hibernate-commits at lists.jboss.org To: hibernate-commits at lists.jboss.org Subject: [hibernate-commits] Hibernate SVN: r15145 - core/trunk/core/src/main/java/org/hibernate/id/enhanced. Date: Fri, 29 Aug 2008 02:22:52 -0400 Message-ID: --===============8699101726955744707== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Author: steve.ebersole(a)jboss.com Date: 2008-08-29 02:22:52 -0400 (Fri, 29 Aug 2008) New Revision: 15145 Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.j= ava Log: HHH-2686 : enhanced.TableGenerator - generator table PK; HHH-3231 : enhanced.TableGenerator - improper SELECT ... FOR UPDATE OF buil= ding; HHH-3249 : enhanced.TableGenerator - extensibility; HHH-3454 : enhanced.TableGenerator - allow segment value default to be the = entity table name Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGene= rator.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.= java 2008-08-29 06:22:14 UTC (rev 15144) +++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.= java 2008-08-29 06:22:52 UTC (rev 15145) @@ -31,6 +31,8 @@ import java.sql.ResultSet; import java.util.Properties; import java.util.HashMap; +import java.util.Collections; +import java.util.Map; import java.io.Serializable; = import org.slf4j.Logger; @@ -49,17 +51,29 @@ import org.hibernate.mapping.Table; import org.hibernate.util.PropertiesHelper; import org.hibernate.util.StringHelper; -import org.hibernate.util.CollectionHelper; = /** - * An enhanced version of explicit table-based generator. The main basis - * conceptualization is similiar to the legacy + * An enhanced version of table-based id generation. + *

+ * Unlike the simplistic legacy one (which, btw, was only ever intended fo= r subclassing + * support) we "segment" the table into multiple values. Thus a single ta= ble can + * actually serve as the persistent storage for multiple independent gener= ators. One + * approach would be to segment the values by the name of the entity for w= hich we are + * performing generation, which would mean that we would have a row in the= generator + * table for each entity name. Or any configuration really; the setup is = very flexible. + *

+ * In this respect it is very simliar to the legacy * {@link org.hibernate.id.MultipleHiLoPerTableGenerator} in terms of the * underlying storage structure (namely a single table capable of holding * multiple generator values). The differentiator is, as with - * {@link SequenceStyleGenerator} as well, the externalization of the noti= on + * {@link SequenceStyleGenerator} as well, the externalized notion * of an optimizer. *

+ * NOTE that by default we use a single row for all genertators (ba= sed + * on {@link #DEF_SEGMENT_VALUE}). The configuration parameter + * {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} can be used to change that to + * instead default to using a row for each entity name. + *

* Configuration parameters: * * @@ -114,6 +128,8 @@ public class TableGenerator extends TransactionHelper implements Persisten= tIdentifierGenerator, Configurable { private static final Logger log =3D LoggerFactory.getLogger( TableGenerat= or.class ); = + public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY =3D "hibernat= e.id.enhanced.table.prefer_segment_per_entity"; + public static final String TABLE_PARAM =3D "table_name"; public static final String DEF_TABLE =3D "hibernate_sequences"; = @@ -138,101 +154,281 @@ public static final String OPT_PARAM =3D "optimizer"; = = + private Type identifierType; + private String tableName; - private String valueColumnName; + private String segmentColumnName; private String segmentValue; private int segmentValueLength; + + private String valueColumnName; private int initialValue; private int incrementSize; = - private Type identifierType; + private String selectQuery; + private String insertQuery; + private String updateQuery; = - private String query; - private String insert; - private String update; - private Optimizer optimizer; private long accessCount =3D 0; = - public String getTableName() { + /** + * {@inheritDoc} + */ + public Object generatorKey() { return tableName; } = - public String getSegmentColumnName() { + /** + * Type mapping for the identifier. + * + * @return The identifier type mapping. + */ + public final Type getIdentifierType() { + return identifierType; + } + + /** + * The name of the table in which we store this generator's persistent st= ate. + * + * @return The table name. + */ + public final String getTableName() { + return tableName; + } + + /** + * The name of the column in which we store the segment to which each row + * belongs. The value here acts as PK. + * + * @return The segment column name + */ + public final String getSegmentColumnName() { return segmentColumnName; } = - public String getSegmentValue() { + /** + * The value in {@link #getSegmentColumnName segment column} which + * corresponding to this generator instance. In other words this value + * indicates the row in which this generator instance will store values. + * + * @return The segment value for this generator instance. + */ + public final String getSegmentValue() { return segmentValue; } = - public int getSegmentValueLength() { + /** + * The size of the {@link #getSegmentColumnName segment column} in the + * underlying table. + *

+ * NOTE : should really have been called 'segmentColumnLength' or + * even better 'segmentColumnSize' + * + * @return the column size. + */ + public final int getSegmentValueLength() { return segmentValueLength; } = - public String getValueColumnName() { + /** + * The name of the column in which we store our persistent generator valu= e. + * + * @return The name of the value column. + */ + public final String getValueColumnName() { return valueColumnName; } = - public Type getIdentifierType() { - return identifierType; - } - - public int getInitialValue() { + /** + * The initial value to use when we find no previous state in the + * generator table corresponding to our sequence. + * + * @return The initial value to use. + */ + public final int getInitialValue() { return initialValue; } = - public int getIncrementSize() { + /** + * The amount of increment to use. The exact implications of this + * depends on the {@link #getOptimizer() optimizer} being used. + * + * @return The increment amount. + */ + public final int getIncrementSize() { return incrementSize; } = - public Optimizer getOptimizer() { + /** + * The optimizer being used by this generator. + * + * @return Out optimizer. + */ + public final Optimizer getOptimizer() { return optimizer; } = - public long getTableAccessCount() { + /** + * Getter for property 'tableAccessCount'. Only really useful for unit t= est + * assertions. + * + * @return Value for property 'tableAccessCount'. + */ + public final long getTableAccessCount() { return accessCount; } = + /** + * {@inheritDoc} + */ public void configure(Type type, Properties params, Dialect dialect) thro= ws MappingException { - tableName =3D PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE= ); - if ( tableName.indexOf( '.' ) < 0 ) { + identifierType =3D type; + + tableName =3D determneGeneratorTableName( params ); + segmentColumnName =3D determineSegmentColumnName( params ); + valueColumnName =3D determineValueColumnName( params ); + + segmentValue =3D determineSegmentValue( params ); + + segmentValueLength =3D determineSegmentColumnSize( params ); + initialValue =3D determineInitialValue( params ); + incrementSize =3D determineIncrementSize( params ); + + this.selectQuery =3D buildSelectQuery( dialect ); + this.updateQuery =3D buildUpdateQuery(); + this.insertQuery =3D buildInsertQuery(); + + String defOptStrategy =3D incrementSize <=3D 1 ? OptimizerFactory.NONE := OptimizerFactory.POOL; + String optimizationStrategy =3D PropertiesHelper.getString( OPT_PARAM, p= arams, defOptStrategy ); + optimizer =3D OptimizerFactory.buildOptimizer( optimizationStrategy, ide= ntifierType.getReturnedClass(), incrementSize ); + } + + /** + * Determine the table name to use for the generator values. + *

+ * Called during {@link #configure configuration}. + * + * @see #getTableName() + * @param params The params supplied in the generator config (plus some s= tandard useful extras). + * @return The table name to use. + */ + protected String determneGeneratorTableName(Properties params) { + String name =3D PropertiesHelper.getString( TABLE_PARAM, params, DEF_TAB= LE ); + boolean isGivenNameUnqualified =3D name.indexOf( '.' ) < 0; + if ( isGivenNameUnqualified ) { + // if the given name is un-qualified we may neen to qualify it String schemaName =3D params.getProperty( SCHEMA ); String catalogName =3D params.getProperty( CATALOG ); - tableName =3D Table.qualify( catalogName, schemaName, tableName ); + name =3D Table.qualify( catalogName, schemaName, name ); } + return name; + } = - segmentColumnName =3D PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, = params, DEF_SEGMENT_COLUMN ); - segmentValue =3D params.getProperty( SEGMENT_VALUE_PARAM ); + /** + * Determine the name of the column used to indicate the segment for each + * row. This column acts as the primary key. + *

+ * Called during {@link #configure configuration}. + * + * @see #getSegmentColumnName() + * @param params The params supplied in the generator config (plus some s= tandard useful extras). + * @return The name of the segment column + */ + protected String determineSegmentColumnName(Properties params) { + return PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEG= MENT_COLUMN ); + } + + /** + * Determine the name of the column in which we will store the generator = persistent value. + *

+ * Called during {@link #configure configuration}. + * + * @see #getValueColumnName() + * @param params The params supplied in the generator config (plus some s= tandard useful extras). + * @return The name of the value column + */ + protected String determineValueColumnName(Properties params) { + return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE= _COLUMN ); + } + + /** + * Determine the segment value corresponding to this generator instance. + *

+ * Called during {@link #configure configuration}. + * + * @see #getSegmentValue() + * @param params The params supplied in the generator config (plus some s= tandard useful extras). + * @return The name of the value column + */ + protected String determineSegmentValue(Properties params) { + String segmentValue =3D params.getProperty( SEGMENT_VALUE_PARAM ); if ( StringHelper.isEmpty( segmentValue ) ) { - log.debug( "explicit segment value for id generator [" + tableName + '.= ' + segmentColumnName + "] suggested; using default [" + DEF_SEGMENT_VALUE = + "]" ); - segmentValue =3D DEF_SEGMENT_VALUE; + segmentValue =3D determineDefaultSegmentValue( params ); } - segmentValueLength =3D PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, pa= rams, DEF_SEGMENT_LENGTH ); - valueColumnName =3D PropertiesHelper.getString( VALUE_COLUMN_PARAM, para= ms, DEF_VALUE_COLUMN ); - initialValue =3D PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT= _INITIAL_VALUE ); - incrementSize =3D PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFA= ULT_INCREMENT_SIZE ); - identifierType =3D type; + return segmentValue; + } = - String query =3D "select " + valueColumnName + - " from " + tableName + " tbl" + - " where tbl." + segmentColumnName + "=3D?"; + /** + * Used in the cases where {@link #determineSegmentValue} is unable to + * determine the value to use. + * + * @param params The params supplied in the generator config (plus some s= tandard useful extras). + * @return The default segment value to use. + */ + protected String determineDefaultSegmentValue(Properties params) { + boolean preferSegmentPerEntity =3D PropertiesHelper.getBoolean( CONFIG_P= REFER_SEGMENT_PER_ENTITY, params, false ); + String defaultToUse =3D preferSegmentPerEntity ? params.getProperty( TAB= LE ) : DEF_SEGMENT_VALUE; + log.info( "explicit segment value for id generator [" + tableName + '.' = + segmentColumnName + "] suggested; using default [" + defaultToUse + "]" ); + return defaultToUse; + } + + /** + * Determine the size of the {@link #getSegmentColumnName segment column} + *

+ * Called during {@link #configure configuration}. + * + * @see #getSegmentValueLength() + * @param params The params supplied in the generator config (plus some s= tandard useful extras). + * @return The size of the segment column + */ + protected int determineSegmentColumnSize(Properties params) { + return PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMEN= T_LENGTH ); + } + + protected int determineInitialValue(Properties params) { + return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_V= ALUE ); + } + + protected int determineIncrementSize(Properties params) { + return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREME= NT_SIZE ); + } + + protected String buildSelectQuery(Dialect dialect) { + final String alias =3D "tbl"; + String query =3D "select " + StringHelper.qualify( alias, valueColumnNam= e ) + + " from " + tableName + ' ' + alias + + " where " + StringHelper.qualify( alias, segmentColumnName ) + "=3D?"; HashMap lockMap =3D new HashMap(); - lockMap.put( "tbl", LockMode.UPGRADE ); - this.query =3D dialect.applyLocksToSql( query, lockMap, CollectionHelper= .EMPTY_MAP ); + lockMap.put( alias, LockMode.UPGRADE ); + Map updateTargetColumnsMap =3D Collections.singletonMap( alias, new Stri= ng[] { valueColumnName } ); + return dialect.applyLocksToSql( query, lockMap, updateTargetColumnsMap ); + } = - update =3D "update " + tableName + + protected String buildUpdateQuery() { + return "update " + tableName + " set " + valueColumnName + "=3D? " + " where " + valueColumnName + "=3D? and " + segmentColumnName + "=3D?"; + } = - insert =3D "insert into " + tableName + " (" + segmentColumnName + ", " = + valueColumnName + ") " + " values (?,?)"; - - String defOptStrategy =3D incrementSize <=3D 1 ? OptimizerFactory.NONE := OptimizerFactory.POOL; - String optimizationStrategy =3D PropertiesHelper.getString( OPT_PARAM, p= arams, defOptStrategy ); - optimizer =3D OptimizerFactory.buildOptimizer( optimizationStrategy, ide= ntifierType.getReturnedClass(), incrementSize ); + protected String buildInsertQuery() { + return "insert into " + tableName + " (" + segmentColumnName + ", " + va= lueColumnName + ") " + " values (?,?)"; } = + /** + * {@inheritDoc} + */ public synchronized Serializable generate(final SessionImplementor sessio= n, Object obj) { return optimizer.generate( new AccessCallback() { @@ -243,23 +439,24 @@ ); } = + /** + * {@inheritDoc} + */ public Serializable doWorkInCurrentTransaction(Connection conn, String sq= l) throws SQLException { int result; int rows; do { - sql =3D query; - SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC ); - PreparedStatement queryPS =3D conn.prepareStatement( query ); + SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC ); + PreparedStatement selectPS =3D conn.prepareStatement( selectQuery ); try { - queryPS.setString( 1, segmentValue ); - ResultSet queryRS =3D queryPS.executeQuery(); - if ( !queryRS.next() ) { + selectPS.setString( 1, segmentValue ); + ResultSet selectRS =3D selectPS.executeQuery(); + if ( !selectRS.next() ) { PreparedStatement insertPS =3D null; try { result =3D initialValue; - sql =3D insert; - SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC ); - insertPS =3D conn.prepareStatement( insert ); + SQL_STATEMENT_LOGGER.logStatement( insertQuery, FormatStyle.BASIC ); + insertPS =3D conn.prepareStatement( insertQuery ); insertPS.setString( 1, segmentValue ); insertPS.setLong( 2, result ); insertPS.execute(); @@ -271,21 +468,20 @@ } } else { - result =3D queryRS.getInt( 1 ); + result =3D selectRS.getInt( 1 ); } - queryRS.close(); + selectRS.close(); } catch ( SQLException sqle ) { log.error( "could not read or init a hi value", sqle ); throw sqle; } finally { - queryPS.close(); + selectPS.close(); } = - sql =3D update; - SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC ); - PreparedStatement updatePS =3D conn.prepareStatement( update ); + SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC ); + PreparedStatement updatePS =3D conn.prepareStatement( updateQuery ); try { long newValue =3D optimizer.applyIncrementSizeToSourceValues() ? result + incrementSize : result + 1; @@ -295,7 +491,7 @@ rows =3D updatePS.executeUpdate(); } catch ( SQLException sqle ) { - log.error( "could not update hi value in: " + tableName, sqle ); + log.error( "could not updateQuery hi value in: " + tableName, sqle ); throw sqle; } finally { @@ -309,6 +505,9 @@ return new Integer( result ); } = + /** + * {@inheritDoc} + */ public String[] sqlCreateStrings(Dialect dialect) throws HibernateExcepti= on { return new String[] { new StringBuffer() @@ -323,11 +522,16 @@ .append( valueColumnName ) .append( ' ' ) .append( dialect.getTypeName( Types.BIGINT ) ) - .append( " ) " ) + .append( ", primary key ( " ) + .append( segmentColumnName ) + .append( " ) ) " ) .toString() }; } = + /** + * {@inheritDoc} + */ public String[] sqlDropStrings(Dialect dialect) throws HibernateException= { StringBuffer sqlDropString =3D new StringBuffer().append( "drop table " = ); if ( dialect.supportsIfExistsBeforeTableName() ) { @@ -339,8 +543,4 @@ } return new String[] { sqlDropString.toString() }; } - - public Object generatorKey() { - return tableName; - } } --===============8699101726955744707==--