FileDocCategorySizeDatePackage
MultipleHiLoPerTableGenerator.javaAPI DocHibernate 3.2.57934Tue Mar 20 05:51:10 GMT 2007org.hibernate.id

MultipleHiLoPerTableGenerator

public class MultipleHiLoPerTableGenerator extends org.hibernate.engine.TransactionHelper implements PersistentIdentifierGenerator, Configurable
A hilo IdentifierGenerator that returns a Long, constructed using a hi/lo algorithm. The hi value MUST be fetched in a seperate transaction to the Session transaction so the generator must be able to obtain a new connection and commit it. Hence this implementation may not be used when the user is supplying connections. In this case a SequenceHiLoGenerator would be a better choice (where supported).

A hilo IdentifierGenerator that uses a database table to store the last generated values. A table can contains several hi values. They are distinct from each other through a key

This implementation is not compliant with a user connection

Allowed parameters (all of them are optional):

  • table: table name (default hibernate_sequences)
  • primary_key_column: key column name (default sequence_name)
  • value_column: hi value column name(default sequence_next_hi_value)
  • primary_key_value: key value for the current entity (default to the entity's primary table name)
  • primary_key_length: length of the key column in DB represented as a varchar (default to 255)
  • max_lo: max low value before increasing hi (default to Short.MAX_VALUE)
author
Emmanuel Bernard
author
Klaus Richarz.

Fields Summary
private static final Log
log
public static final String
ID_TABLE
public static final String
PK_COLUMN_NAME
public static final String
PK_VALUE_NAME
public static final String
VALUE_COLUMN_NAME
public static final String
PK_LENGTH_NAME
private static final int
DEFAULT_PK_LENGTH
public static final String
DEFAULT_TABLE
private static final String
DEFAULT_PK_COLUMN
private static final String
DEFAULT_VALUE_COLUMN
private String
tableName
private String
pkColumnName
private String
valueColumnName
private String
query
private String
insert
private String
update
public static final String
MAX_LO
private long
hi
private int
lo
private int
maxLo
private Class
returnClass
private int
keySize
Constructors Summary
Methods Summary
public voidconfigure(org.hibernate.type.Type type, java.util.Properties params, org.hibernate.dialect.Dialect dialect)

		tableName = PropertiesHelper.getString(ID_TABLE, params, DEFAULT_TABLE);
		pkColumnName = PropertiesHelper.getString(PK_COLUMN_NAME, params, DEFAULT_PK_COLUMN);
		valueColumnName = PropertiesHelper.getString(VALUE_COLUMN_NAME, params, DEFAULT_VALUE_COLUMN);
		String schemaName = params.getProperty(SCHEMA);
		String catalogName = params.getProperty(CATALOG);
		keySize = PropertiesHelper.getInt(PK_LENGTH_NAME, params, DEFAULT_PK_LENGTH);
		String keyValue = PropertiesHelper.getString(PK_VALUE_NAME, params, params.getProperty(TABLE) );

		if ( tableName.indexOf( '." )<0 ) {
			tableName = Table.qualify( catalogName, schemaName, tableName );
		}

		query = "select " +
			valueColumnName +
			" from " +
			dialect.appendLockHint(LockMode.UPGRADE, tableName) +
			" where " + pkColumnName + " = '" + keyValue + "'" +
			dialect.getForUpdateString();

		update = "update " +
			tableName +
			" set " +
			valueColumnName +
			" = ? where " +
			valueColumnName +
			" = ? and " +
			pkColumnName +
			" = '" + 
			keyValue 
			+ "'";
		
		insert = "insert into " + tableName +
			"(" + pkColumnName + ", " +	valueColumnName + ") " +
			"values('"+ keyValue +"', ?)";


		//hilo config
		maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
		lo = maxLo + 1; // so we "clock over" on the first invocation
		returnClass = type.getReturnedClass();
	
public java.io.SerializabledoWorkInCurrentTransaction(java.sql.Connection conn, java.lang.String sql)

		int result;
		int rows;
		do {
			// The loop ensures atomicity of the
			// select + update even for no transaction
			// or read committed isolation level

			//sql = query;
			SQL.debug(query);
			PreparedStatement qps = conn.prepareStatement(query);
			PreparedStatement ips = null;
			try {
				//qps.setString(1, key);
				ResultSet rs = qps.executeQuery();
				boolean isInitialized = rs.next();
				if ( !isInitialized ) {
					result = 0;
					ips = conn.prepareStatement(insert);
					//ips.setString(1, key);
					ips.setInt(1, result);
					ips.execute();
				}
				else {
					result = rs.getInt(1);
				}
				rs.close();
			}
			catch (SQLException sqle) {
				log.error("could not read or init a hi value", sqle);
				throw sqle;
			}
			finally {
				if (ips != null) {
					ips.close();
				}
				qps.close();
			}

			//sql = update;
			PreparedStatement ups = conn.prepareStatement(update);
			try {
				ups.setInt( 1, result + 1 );
				ups.setInt( 2, result );
				//ups.setString( 3, key );
				rows = ups.executeUpdate();
			}
			catch (SQLException sqle) {
				log.error("could not update hi value in: " + tableName, sqle);
				throw sqle;
			}
			finally {
				ups.close();
			}
		}
		while (rows==0);
		return new Integer(result);
	
public synchronized java.io.Serializablegenerate(org.hibernate.engine.SessionImplementor session, java.lang.Object obj)

		if (maxLo < 1) {
			//keep the behavior consistent even for boundary usages
			int val = ( (Integer) doWorkInNewTransaction(session) ).intValue();
			if (val == 0) val = ( (Integer) doWorkInNewTransaction(session) ).intValue();
			return IdentifierGeneratorFactory.createNumber( val, returnClass );
		}
		if (lo>maxLo) {
			int hival = ( (Integer) doWorkInNewTransaction(session) ).intValue();
			lo = (hival == 0) ? 1 : 0;
			hi = hival * (maxLo+1);
			log.debug("new hi value: " + hival);
		}
		return IdentifierGeneratorFactory.createNumber( hi + lo++, returnClass );
	
public java.lang.ObjectgeneratorKey()

		return tableName;
	
public java.lang.String[]sqlCreateStrings(org.hibernate.dialect.Dialect dialect)



	      
		return new String[] {
			new StringBuffer( dialect.getCreateTableString() )
					.append( ' " )
					.append( tableName )
					.append( " ( " )
					.append( pkColumnName )
					.append( ' " )
					.append( dialect.getTypeName( Types.VARCHAR, keySize, 0, 0 ) )
					.append( ",  " )
					.append( valueColumnName )
					.append( ' " )
					.append( dialect.getTypeName( Types.INTEGER ) )
					.append( " ) " )
					.toString()
		};
	
public java.lang.String[]sqlDropStrings(org.hibernate.dialect.Dialect dialect)

		StringBuffer sqlDropString = new StringBuffer( "drop table " );
		if ( dialect.supportsIfExistsBeforeTableName() ) {
			sqlDropString.append( "if exists " );
		}
		sqlDropString.append( tableName ).append( dialect.getCascadeConstraintsString() );
		if ( dialect.supportsIfExistsAfterTableName() ) {
			sqlDropString.append( " if exists" );
		}
		return new String[] { sqlDropString.toString() };