MultipleHiLoPerTableGeneratorpublic class MultipleHiLoPerTableGenerator extends org.hibernate.engine.TransactionHelper implements PersistentIdentifierGenerator, ConfigurableA 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)
|
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 |
Methods Summary |
---|
public void | configure(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.Serializable | doWorkInCurrentTransaction(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.Serializable | generate(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.Object | generatorKey()
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() };
|
|