HHH-4905 - Allow consistent handling of numeric primary key values by any integral data type

git-svn-id: https://svn.jboss.org/repos/hibernate/core/trunk@18789 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
Steve Ebersole 2010-02-12 06:22:52 +00:00
parent 19de9d4ed9
commit 23ee88a55b
36 changed files with 1738 additions and 256 deletions

View File

@ -91,7 +91,9 @@ public class VersionValue {
}
}
public Object getDefaultValue(Object currentValue) {
return IdentifierGeneratorHelper.createNumber( -1l, currentValue.getClass() );
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( currentValue.getClass() )
.initialize( -1L )
.makeValue();
}
public String toString() {
return "VERSION_NEGATIVE";

View File

@ -25,6 +25,9 @@
package org.hibernate.id;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -119,10 +122,17 @@ public final class IdentifierGeneratorHelper {
else if ( clazz == String.class ) {
return rs.getString( 1 );
}
else {
throw new IdentifierGenerationException( "this id generator generates long, integer, short or string" );
else if ( clazz == BigInteger.class ) {
return rs.getBigDecimal( 1 ).setScale( 0, BigDecimal.ROUND_UNNECESSARY ).toBigInteger();
}
else if ( clazz == BigDecimal.class ) {
return rs.getBigDecimal( 1 ).setScale( 0, BigDecimal.ROUND_UNNECESSARY );
}
else {
throw new IdentifierGenerationException(
"unrecognized id type : " + type.getName() + " -> " + clazz.getName()
);
}
}
/**
@ -130,8 +140,12 @@ public final class IdentifierGeneratorHelper {
*
* @param value The primitive value to wrap.
* @param clazz The Java numeric type in which to wrap the value.
*
* @return The wrapped type.
*
* @throws IdentifierGenerationException Indicates an unhandled 'clazz'.
*
* @deprecated Use the {@link #getIntegralDataTypeHolder holders} instead.
*/
public static Number createNumber(long value, Class clazz) throws IdentifierGenerationException {
if ( clazz == Long.class ) {
@ -144,7 +158,510 @@ public final class IdentifierGeneratorHelper {
return new Short( ( short ) value );
}
else {
throw new IdentifierGenerationException( "this id generator generates long, integer, short" );
throw new IdentifierGenerationException( "unrecognized id type : " + clazz.getName() );
}
}
public static IntegralDataTypeHolder getIntegralDataTypeHolder(Class integralType) {
if ( integralType == Long.class
|| integralType == Integer.class
|| integralType == Short.class ) {
return new BasicHolder( integralType );
}
else if ( integralType == BigInteger.class ) {
return new BigIntegerHolder();
}
else if ( integralType == BigDecimal.class ) {
return new BigDecimalHolder();
}
else {
throw new IdentifierGenerationException(
"Unknown integral data type for ids : " + integralType.getName()
);
}
}
public static long extractLong(IntegralDataTypeHolder holder) {
if ( holder.getClass() == BasicHolder.class ) {
( (BasicHolder) holder ).checkInitialized();
return ( (BasicHolder) holder ).value;
}
else if ( holder.getClass() == BigIntegerHolder.class ) {
( (BigIntegerHolder) holder ).checkInitialized();
return ( (BigIntegerHolder) holder ).value.longValue();
}
else if ( holder.getClass() == BigDecimalHolder.class ) {
( (BigDecimalHolder) holder ).checkInitialized();
return ( (BigDecimalHolder) holder ).value.longValue();
}
throw new IdentifierGenerationException( "Unknown IntegralDataTypeHolder impl [" + holder + "]" );
}
public static BigInteger extractBigInteger(IntegralDataTypeHolder holder) {
if ( holder.getClass() == BasicHolder.class ) {
( (BasicHolder) holder ).checkInitialized();
return BigInteger.valueOf( ( (BasicHolder) holder ).value );
}
else if ( holder.getClass() == BigIntegerHolder.class ) {
( (BigIntegerHolder) holder ).checkInitialized();
return ( (BigIntegerHolder) holder ).value;
}
else if ( holder.getClass() == BigDecimalHolder.class ) {
( (BigDecimalHolder) holder ).checkInitialized();
// scale should already be set...
return ( (BigDecimalHolder) holder ).value.toBigInteger();
}
throw new IdentifierGenerationException( "Unknown IntegralDataTypeHolder impl [" + holder + "]" );
}
public static BigDecimal extractBigDecimal(IntegralDataTypeHolder holder) {
if ( holder.getClass() == BasicHolder.class ) {
( (BasicHolder) holder ).checkInitialized();
return BigDecimal.valueOf( ( (BasicHolder) holder ).value );
}
else if ( holder.getClass() == BigIntegerHolder.class ) {
( (BigIntegerHolder) holder ).checkInitialized();
return new BigDecimal( ( (BigIntegerHolder) holder ).value );
}
else if ( holder.getClass() == BigDecimalHolder.class ) {
( (BigDecimalHolder) holder ).checkInitialized();
// scale should already be set...
return ( (BigDecimalHolder) holder ).value;
}
throw new IdentifierGenerationException( "Unknown IntegralDataTypeHolder impl [" + holder + "]" );
}
public static class BasicHolder implements IntegralDataTypeHolder {
private final Class exactType;
private long value = Long.MIN_VALUE;
public BasicHolder(Class exactType) {
this.exactType = exactType;
if ( exactType != Long.class && exactType != Integer.class && exactType != Short.class ) {
throw new IdentifierGenerationException( "Invalid type for basic integral holder : " + exactType );
}
}
public long getActualLongValue() {
return value;
}
public IntegralDataTypeHolder initialize(long value) {
this.value = value;
return this;
}
public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException {
long value = resultSet.getLong( 1 );
if ( resultSet.wasNull() ) {
value = defaultValue;
}
return initialize( value );
}
public void bind(PreparedStatement preparedStatement, int position) throws SQLException {
// TODO : bind it as 'exact type'? Not sure if that gains us anything...
preparedStatement.setLong( position, value );
}
public IntegralDataTypeHolder increment() {
checkInitialized();
value++;
return this;
}
private void checkInitialized() {
if ( value == Long.MIN_VALUE ) {
throw new IdentifierGenerationException( "integral holder was not initialized" );
}
}
public IntegralDataTypeHolder add(long addend) {
checkInitialized();
value += addend;
return this;
}
public IntegralDataTypeHolder decrement() {
checkInitialized();
value--;
return this;
}
public IntegralDataTypeHolder subtract(long subtrahend) {
checkInitialized();
value -= subtrahend;
return this;
}
public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) {
return multiplyBy( extractLong( factor ) );
}
public IntegralDataTypeHolder multiplyBy(long factor) {
checkInitialized();
value *= factor;
return this;
}
public boolean eq(IntegralDataTypeHolder other) {
return eq( extractLong( other ) );
}
public boolean eq(long value) {
checkInitialized();
return this.value == value;
}
public boolean lt(IntegralDataTypeHolder other) {
return lt( extractLong( other ) );
}
public boolean lt(long value) {
checkInitialized();
return this.value < value;
}
public boolean gt(IntegralDataTypeHolder other) {
return gt( extractLong( other ) );
}
public boolean gt(long value) {
checkInitialized();
return this.value > value;
}
public IntegralDataTypeHolder copy() {
BasicHolder copy = new BasicHolder( exactType );
copy.value = value;
return copy;
}
public Number makeValue() {
// TODO : should we check for truncation?
checkInitialized();
if ( exactType == Long.class ) {
return new Long( value );
}
else if ( exactType == Integer.class ) {
return new Integer( ( int ) value );
}
else {
return new Short( ( short ) value );
}
}
public Number makeValueThenIncrement() {
final Number result = makeValue();
value++;
return result;
}
public Number makeValueThenAdd(long addend) {
final Number result = makeValue();
value += addend;
return result;
}
public String toString() {
return "BasicHolder[" + exactType.getName() + "[" + value + "]]";
}
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
BasicHolder that = (BasicHolder) o;
return value == that.value;
}
public int hashCode() {
return (int) ( value ^ ( value >>> 32 ) );
}
}
public static class BigIntegerHolder implements IntegralDataTypeHolder {
private BigInteger value;
public IntegralDataTypeHolder initialize(long value) {
this.value = BigInteger.valueOf( value );
return this;
}
public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException {
final BigDecimal rsValue = resultSet.getBigDecimal( 1 );
if ( resultSet.wasNull() ) {
return initialize( defaultValue );
}
this.value = rsValue.setScale( 0, BigDecimal.ROUND_UNNECESSARY ).toBigInteger();
return this;
}
public void bind(PreparedStatement preparedStatement, int position) throws SQLException {
preparedStatement.setBigDecimal( position, new BigDecimal( value ) );
}
public IntegralDataTypeHolder increment() {
checkInitialized();
value = value.add( BigInteger.ONE );
return this;
}
private void checkInitialized() {
if ( value == null ) {
throw new IdentifierGenerationException( "integral holder was not initialized" );
}
}
public IntegralDataTypeHolder add(long increment) {
checkInitialized();
value = value.add( BigInteger.valueOf( increment ) );
return this;
}
public IntegralDataTypeHolder decrement() {
checkInitialized();
value = value.subtract( BigInteger.ONE );
return this;
}
public IntegralDataTypeHolder subtract(long subtrahend) {
checkInitialized();
value = value.subtract( BigInteger.valueOf( subtrahend ) );
return this;
}
public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) {
checkInitialized();
value = value.multiply( extractBigInteger( factor ) );
return this;
}
public IntegralDataTypeHolder multiplyBy(long factor) {
checkInitialized();
value = value.multiply( BigInteger.valueOf( factor ) );
return this;
}
public boolean eq(IntegralDataTypeHolder other) {
checkInitialized();
return value.compareTo( extractBigInteger( other ) ) == 0;
}
public boolean eq(long value) {
checkInitialized();
return this.value.compareTo( BigInteger.valueOf( value ) ) == 0;
}
public boolean lt(IntegralDataTypeHolder other) {
checkInitialized();
return value.compareTo( extractBigInteger( other ) ) < 0;
}
public boolean lt(long value) {
checkInitialized();
return this.value.compareTo( BigInteger.valueOf( value ) ) < 0;
}
public boolean gt(IntegralDataTypeHolder other) {
checkInitialized();
return value.compareTo( extractBigInteger( other ) ) > 0;
}
public boolean gt(long value) {
checkInitialized();
return this.value.compareTo( BigInteger.valueOf( value ) ) > 0;
}
public IntegralDataTypeHolder copy() {
BigIntegerHolder copy = new BigIntegerHolder();
copy.value = value;
return copy;
}
public Number makeValue() {
checkInitialized();
return value;
}
public Number makeValueThenIncrement() {
final Number result = makeValue();
value = value.add( BigInteger.ONE );
return result;
}
public Number makeValueThenAdd(long addend) {
final Number result = makeValue();
value = value.add( BigInteger.valueOf( addend ) );
return result;
}
public String toString() {
return "BigIntegerHolder[" + value + "]";
}
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
BigIntegerHolder that = (BigIntegerHolder) o;
return this.value == null
? that.value == null
: value.equals( that.value );
}
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
}
public static class BigDecimalHolder implements IntegralDataTypeHolder {
private BigDecimal value;
public IntegralDataTypeHolder initialize(long value) {
this.value = BigDecimal.valueOf( value );
return this;
}
public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException {
final BigDecimal rsValue = resultSet.getBigDecimal( 1 );
if ( resultSet.wasNull() ) {
return initialize( defaultValue );
}
this.value = rsValue.setScale( 0, BigDecimal.ROUND_UNNECESSARY );
return this;
}
public void bind(PreparedStatement preparedStatement, int position) throws SQLException {
preparedStatement.setBigDecimal( position, value );
}
public IntegralDataTypeHolder increment() {
checkInitialized();
value = value.add( BigDecimal.ONE );
return this;
}
private void checkInitialized() {
if ( value == null ) {
throw new IdentifierGenerationException( "integral holder was not initialized" );
}
}
public IntegralDataTypeHolder add(long increment) {
checkInitialized();
value = value.add( BigDecimal.valueOf( increment ) );
return this;
}
public IntegralDataTypeHolder decrement() {
checkInitialized();
value = value.subtract( BigDecimal.ONE );
return this;
}
public IntegralDataTypeHolder subtract(long subtrahend) {
checkInitialized();
value = value.subtract( BigDecimal.valueOf( subtrahend ) );
return this;
}
public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor) {
checkInitialized();
value = value.multiply( extractBigDecimal( factor ) );
return this;
}
public IntegralDataTypeHolder multiplyBy(long factor) {
checkInitialized();
value = value.multiply( BigDecimal.valueOf( factor ) );
return this;
}
public boolean eq(IntegralDataTypeHolder other) {
checkInitialized();
return value.compareTo( extractBigDecimal( other ) ) == 0;
}
public boolean eq(long value) {
checkInitialized();
return this.value.compareTo( BigDecimal.valueOf( value ) ) == 0;
}
public boolean lt(IntegralDataTypeHolder other) {
checkInitialized();
return value.compareTo( extractBigDecimal( other ) ) < 0;
}
public boolean lt(long value) {
checkInitialized();
return this.value.compareTo( BigDecimal.valueOf( value ) ) < 0;
}
public boolean gt(IntegralDataTypeHolder other) {
checkInitialized();
return value.compareTo( extractBigDecimal( other ) ) > 0;
}
public boolean gt(long value) {
checkInitialized();
return this.value.compareTo( BigDecimal.valueOf( value ) ) > 0;
}
public IntegralDataTypeHolder copy() {
BigDecimalHolder copy = new BigDecimalHolder();
copy.value = value;
return copy;
}
public Number makeValue() {
checkInitialized();
return value;
}
public Number makeValueThenIncrement() {
final Number result = makeValue();
value = value.add( BigDecimal.ONE );
return result;
}
public Number makeValueThenAdd(long addend) {
final Number result = makeValue();
value = value.add( BigDecimal.valueOf( addend ) );
return result;
}
public String toString() {
return "BigDecimalHolder[" + value + "]";
}
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
BigDecimalHolder that = (BigDecimalHolder) o;
return this.value == null
? that.value == null
: this.value.equals( that.value );
}
public int hashCode() {
return value != null ? value.hashCode() : 0;
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
@ -53,20 +52,21 @@ import org.hibernate.util.StringHelper;
* (The tables parameter specified a comma-separated list of table names.)
*
* @author Gavin King
* @author Steve Ebersole
*/
public class IncrementGenerator implements IdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger(IncrementGenerator.class);
private long next;
private String sql;
private Class returnClass;
private String sql;
private IntegralDataTypeHolder previousValueHolder;
public synchronized Serializable generate(SessionImplementor session, Object object) throws HibernateException {
if ( sql != null ) {
getNext( session );
initializePreviousValueHolder( session );
}
return IdentifierGeneratorHelper.createNumber( next++, returnClass );
return previousValueHolder.makeValueThenIncrement();
}
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
@ -117,22 +117,23 @@ public class IncrementGenerator implements IdentifierGenerator, Configurable {
sql = "select max(" + column + ") from " + buf.toString();
}
private void getNext( SessionImplementor session ) {
private void initializePreviousValueHolder(SessionImplementor session) {
previousValueHolder = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );
log.debug( "fetching initial value: " + sql );
try {
PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
try {
ResultSet rs = st.executeQuery();
try {
if ( rs.next() ) {
next = rs.getLong(1) + 1;
if ( rs.wasNull() ) next = 1;
previousValueHolder.initialize( rs, 0L ).increment();
}
else {
next = 1;
previousValueHolder.initialize( 1L );
}
sql=null;
log.debug("first free id: " + next);
sql = null;
log.debug( "first free id: " + previousValueHolder.makeValue() );
}
finally {
rs.close();
@ -141,7 +142,6 @@ public class IncrementGenerator implements IdentifierGenerator, Configurable {
finally {
session.getBatcher().closeStatement(st);
}
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(

View File

@ -0,0 +1,206 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Defines a common api for dealing with data of integral data type.
*
* @author Steve Ebersole
*/
public interface IntegralDataTypeHolder extends Serializable {
/**
* Initialize the internal value from the given primitive long.
*
* @param value The primitive integral value.
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder initialize(long value);
/**
* Initialize the internal value from the given result set, using the specified default value
* if we could not get a value from the result set (aka result was null).
*
* @param resultSet The JDBC result set
* @param defaultValue The default value to use if we did not get a result set value.
*
* @return <tt>this</tt>, for method chaining
*
* @throws SQLException Any exception from accessing the result set
*/
public IntegralDataTypeHolder initialize(ResultSet resultSet, long defaultValue) throws SQLException;
/**
* Bind this holders internal value to the given result set.
*
* @param preparedStatement The JDBC prepared statement
* @param position The position at which to bind
*
* @throws SQLException Any exception from accessing the statement
*/
public void bind(PreparedStatement preparedStatement, int position) throws SQLException;
/**
* Equivalent to a ++ operation
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder increment();
/**
* Perform an addition
*
* @param addend The value to add to this integral.
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder add(long addend);
/**
* Equivalent to a -- operation
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder decrement();
/**
* Perform a subtraction
*
* @param subtrahend The value to subtract from this integral.
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder subtract(long subtrahend);
/**
* Perform a multiplication.
*
* @param factor The factor by which to multiple this integral
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder multiplyBy(IntegralDataTypeHolder factor);
/**
* Perform a multiplication.
*
* @param factor The factor by which to multiple this integral
*
* @return <tt>this</tt>, for method chaining
*/
public IntegralDataTypeHolder multiplyBy(long factor);
/**
* Perform an equality comparison check
*
* @param other The other value to check against our internal state
*
* @return True if the two are equal
*/
public boolean eq(IntegralDataTypeHolder other);
/**
* Perform an equality comparison check
*
* @param other The other value to check against our internal state
*
* @return True if the two are equal
*/
public boolean eq(long other);
/**
* Perform a "less than" comparison check. We check to see if our value is less than
* the incoming value...
*
* @param other The other value to check against our internal state
*
* @return True if our value is less than the 'other' value.
*/
public boolean lt(IntegralDataTypeHolder other);
/**
* Perform a "less than" comparison check. We check to see if our value is less than
* the incoming value...
*
* @param other The other value to check against our internal state
*
* @return True if our value is less than the 'other' value.
*/
public boolean lt(long other);
/**
* Perform a "greater than" comparison check. We check to see if our value is greater
* than the incoming value...
*
* @param other The other value to check against our internal state
*
* @return True if our value is greater than the 'other' value.
*/
public boolean gt(IntegralDataTypeHolder other);
/**
* Perform a "greater than" comparison check. We check to see if our value is greater
* than the incoming value...
*
* @param other The other value to check against our internal state
*
* @return True if our value is greater than the 'other' value.
*/
public boolean gt(long other);
/**
* Make a copy of this holder.
*
* @return The copy.
*/
public IntegralDataTypeHolder copy();
/**
* Return the internal value.
*
* @return The current internal value
*/
public Number makeValue();
/**
* Increment the internal state, but return the pre-incremented value.
*
* @return The pre-incremented internal value
*/
public Number makeValueThenIncrement();
/**
* Increment the internal state by the given addend, but return the pre-incremented value.
*
* @param addend The value to be added to our internal state
*
* @return The pre-incremented internal value
*/
public Number makeValueThenAdd(long addend);
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
@ -104,9 +103,10 @@ public class MultipleHiLoPerTableGenerator
//hilo params
public static final String MAX_LO = "max_lo";
private long hi;
private int lo;
private int maxLo;
private int lo;
private IntegralDataTypeHolder value;
private Class returnClass;
private int keySize;
@ -146,7 +146,7 @@ public class MultipleHiLoPerTableGenerator
}
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
int result;
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );
int rows;
do {
// The loop ensures atomicity of the
@ -158,18 +158,16 @@ public class MultipleHiLoPerTableGenerator
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);
value.initialize( 0 );
ips = conn.prepareStatement( insert );
value.bind( ips, 1 );
ips.execute();
}
else {
result = rs.getInt(1);
value.initialize( rs, 0 );
}
rs.close();
}
@ -184,12 +182,10 @@ public class MultipleHiLoPerTableGenerator
qps.close();
}
//sql = update;
PreparedStatement ups = conn.prepareStatement(update);
try {
ups.setInt( 1, result + 1 );
ups.setInt( 2, result );
//ups.setString( 3, key );
value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 );
rows = ups.executeUpdate();
}
catch (SQLException sqle) {
@ -201,24 +197,30 @@ public class MultipleHiLoPerTableGenerator
}
}
while (rows==0);
return new Integer(result);
return value;
}
public synchronized Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
if (maxLo < 1) {
// maxLo < 1 indicates a hilo generator with no hilo :?
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 IdentifierGeneratorHelper.createNumber( val, returnClass );
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 1 ) ) {
value = (IntegralDataTypeHolder) doWorkInNewTransaction( session );
}
return value.makeValue();
}
if (lo>maxLo) {
int hival = ( (Integer) doWorkInNewTransaction(session) ).intValue();
lo = (hival == 0) ? 1 : 0;
hi = hival * (maxLo+1);
log.debug("new hi value: " + hival);
if ( lo > maxLo ) {
IntegralDataTypeHolder hiVal = (IntegralDataTypeHolder) doWorkInNewTransaction( session );
lo = ( hiVal.eq( 0 ) ) ? 1 : 0;
value = hiVal.copy().multiplyBy( maxLo+1 ).add( lo );
if ( log.isDebugEnabled() ) {
log.debug("new hi value: " + hiVal);
}
}
return IdentifierGeneratorHelper.createNumber( hi + lo++, returnClass );
return value.makeValueThenIncrement();
}
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
@ -98,19 +97,19 @@ public class SequenceGenerator implements PersistentIdentifierGenerator, Configu
sql = dialect.getSequenceNextValString( sequenceName );
}
public Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
try {
public Serializable generate(SessionImplementor session, Object obj) {
return generateHolder( session ).makeValue();
}
protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
try {
PreparedStatement st = session.getBatcher().prepareSelectStatement(sql);
try {
ResultSet rs = st.executeQuery();
try {
rs.next();
Serializable result = IdentifierGeneratorHelper.get(
rs, identifierType
);
IntegralDataTypeHolder result = buildHolder();
result.initialize( rs, 1 );
if ( log.isDebugEnabled() ) {
log.debug("Sequence identifier generated: " + result);
}
@ -123,7 +122,7 @@ public class SequenceGenerator implements PersistentIdentifierGenerator, Configu
finally {
session.getBatcher().closeStatement(st);
}
}
catch (SQLException sqle) {
throw JDBCExceptionHelper.convert(
@ -131,9 +130,12 @@ public class SequenceGenerator implements PersistentIdentifierGenerator, Configu
sqle,
"could not get next sequence value",
sql
);
);
}
}
protected IntegralDataTypeHolder buildHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
}
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,7 +20,6 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
@ -29,7 +28,6 @@ import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
@ -59,33 +57,36 @@ public class SequenceHiLoGenerator extends SequenceGenerator {
private int maxLo;
private int lo;
private long hi;
private Class returnClass;
private IntegralDataTypeHolder value;
public void configure(Type type, Properties params, Dialect d) throws MappingException {
super.configure(type, params, d);
maxLo = PropertiesHelper.getInt(MAX_LO, params, 9);
lo = maxLo + 1; // so we "clock over" on the first invocation
returnClass = type.getReturnedClass();
}
public synchronized Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
if (maxLo < 1) {
public synchronized Serializable generate(SessionImplementor session, Object obj) {
// maxLo < 1 indicates a hilo generator with no hilo :?
if ( maxLo < 1 ) {
//keep the behavior consistent even for boundary usages
long val = ( (Number) super.generate(session, obj) ).longValue();
if (val == 0) val = ( (Number) super.generate(session, obj) ).longValue();
return IdentifierGeneratorHelper.createNumber( val, returnClass );
}
if ( lo>maxLo ) {
long hival = ( (Number) super.generate(session, obj) ).longValue();
lo = (hival == 0) ? 1 : 0;
hi = hival * ( maxLo+1 );
if ( log.isDebugEnabled() )
log.debug("new hi value: " + hival);
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 0 ) ) {
value = super.generateHolder( session );
}
return value.makeValue();
}
return IdentifierGeneratorHelper.createNumber( hi + lo++, returnClass );
if ( lo > maxLo ) {
IntegralDataTypeHolder hiVal = generateHolder( session );
lo = ( hiVal.eq( 0 ) ) ? 1 : 0;
value = hiVal.copy().multiplyBy( maxLo+1 ).add( lo );
if ( log.isDebugEnabled() ) {
log.debug("new hi value: " + hiVal);
}
}
return value.makeValueThenIncrement();
}
}

View File

@ -50,17 +50,21 @@ import org.hibernate.util.PropertiesHelper;
* table to store the last generated value. It is not
* intended that applications use this strategy directly.
* However, it may be used to build other (efficient)
* strategies. The returned type is <tt>Integer</tt>.<br>
* <br>
* The hi value MUST be fetched in a seperate transaction
* to the <tt>Session</tt> transaction so the generator must
* be able to obtain a new connection and commit it. Hence
* this implementation may not be used when Hibernate is
* fetching connections when the user is supplying
* connections.<br>
* <br>
* The returned value is of type <tt>integer</tt>.<br>
* <br>
* strategies. The returned type is any supported by
* {@link IntegralDataTypeHolder}
* <p/>
* The value MUST be fetched in a separate transaction
* from that of the main {@link SessionImplementor session}
* transaction so the generator must be able to obtain a new
* connection and commit it. Hence this implementation may only
* be used when Hibernate is fetching connections, not when the
* user is supplying connections.
* <p/>
* Again, the return types supported here are any of the ones
* supported by {@link IntegralDataTypeHolder}. This is new
* as of 3.5. Prior to that this generator only returned {@link Integer}
* values.
* <p/>
* Mapping parameters supported: table, column
*
* @see TableHiLoGenerator
@ -83,12 +87,15 @@ public class TableGenerator extends TransactionHelper
private static final Logger log = LoggerFactory.getLogger(TableGenerator.class);
private Type identifierType;
private String tableName;
private String columnName;
private String query;
private String update;
public void configure(Type type, Properties params, Dialect dialect) {
identifierType = type;
ObjectNameNormalizer normalizer = ( ObjectNameNormalizer ) params.get( IDENTIFIER_NORMALIZER );
tableName = PropertiesHelper.getString( TABLE, params, DEFAULT_TABLE_NAME );
@ -127,12 +134,13 @@ public class TableGenerator extends TransactionHelper
" = ?";
}
public synchronized Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
int result = ( (Integer) doWorkInNewTransaction(session) ).intValue();
return new Integer(result);
public synchronized Serializable generate(SessionImplementor session, Object object) {
return generateHolder( session ).makeValue();
}
protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
return (IntegralDataTypeHolder) doWorkInNewTransaction( session );
}
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
@ -157,8 +165,19 @@ public class TableGenerator extends TransactionHelper
return tableName;
}
/**
* Get the next value.
*
* @param conn The sql connection to use.
* @param sql n/a
*
* @return Prior to 3.5 this method returned an {@link Integer}. Since 3.5 it now
* returns a {@link IntegralDataTypeHolder}
*
* @throws SQLException
*/
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
int result;
IntegralDataTypeHolder value = buildHolder();
int rows;
do {
// The loop ensures atomicity of the
@ -175,7 +194,7 @@ public class TableGenerator extends TransactionHelper
log.error(err);
throw new IdentifierGenerationException(err);
}
result = rs.getInt(1);
value.initialize( rs, 1 );
rs.close();
}
catch (SQLException sqle) {
@ -190,8 +209,8 @@ public class TableGenerator extends TransactionHelper
SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
PreparedStatement ups = conn.prepareStatement(update);
try {
ups.setInt( 1, result + 1 );
ups.setInt( 2, result );
value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 );
rows = ups.executeUpdate();
}
catch (SQLException sqle) {
@ -203,6 +222,10 @@ public class TableGenerator extends TransactionHelper
}
}
while (rows==0);
return new Integer(result);
return value;
}
protected IntegralDataTypeHolder buildHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
}
}

View File

@ -29,7 +29,6 @@ import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;
@ -58,10 +57,10 @@ public class TableHiLoGenerator extends TableGenerator {
*/
public static final String MAX_LO = "max_lo";
private long hi;
private int lo;
private int maxLo;
private Class returnClass;
private int lo;
private IntegralDataTypeHolder value;
private static final Logger log = LoggerFactory.getLogger(TableHiLoGenerator.class);
@ -69,26 +68,29 @@ public class TableHiLoGenerator extends TableGenerator {
super.configure(type, params, d);
maxLo = PropertiesHelper.getInt(MAX_LO, params, Short.MAX_VALUE);
lo = maxLo + 1; // so we "clock over" on the first invocation
returnClass = type.getReturnedClass();
}
public synchronized Serializable generate(SessionImplementor session, Object obj)
throws HibernateException {
if (maxLo < 1) {
public synchronized Serializable generate(SessionImplementor session, Object obj) {
// maxLo < 1 indicates a hilo generator with no hilo :?
if ( maxLo < 1 ) {
//keep the behavior consistent even for boundary usages
long val = ( (Number) super.generate(session, obj) ).longValue();
if (val == 0) val = ( (Number) super.generate(session, obj) ).longValue();
return IdentifierGeneratorHelper.createNumber( val, returnClass );
}
if (lo>maxLo) {
long hival = ( (Number) super.generate(session, obj) ).longValue();
lo = (hival == 0) ? 1 : 0;
hi = hival * (maxLo+1);
log.debug("new hi value: " + hival);
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 0 ) ) {
value = generateHolder( session );
}
return value.makeValue();
}
return IdentifierGeneratorHelper.createNumber( hi + lo++, returnClass );
if ( lo > maxLo ) {
IntegralDataTypeHolder hiVal = generateHolder( session );
lo = ( hiVal.eq( 0 ) ) ? 1 : 0;
value = hiVal.copy().multiplyBy( maxLo+1 ).add( lo );
if ( log.isDebugEnabled() ) {
log.debug("new hi value: " + hiVal);
}
}
return value.makeValueThenIncrement();
}
}

View File

@ -24,6 +24,8 @@
*/
package org.hibernate.id.enhanced;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Contract for providing callback access to a {@link DatabaseStructure},
* typically from the {@link Optimizer}.
@ -36,5 +38,5 @@ public interface AccessCallback {
*
* @return The next value.
*/
public long getNextValue();
public IntegralDataTypeHolder getNextValue();
}

View File

@ -26,6 +26,8 @@ package org.hibernate.id.enhanced;
import java.io.Serializable;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Performs optimization on an optimizable identifier generator. Typically
* this optimization takes the form of trying to ensure we do not have to
@ -51,12 +53,12 @@ public interface Optimizer {
/**
* A common means to access the last value obtained from the underlying
* source. This is intended for testing purposes, since accessing the
* unerlying database source directly is much more difficult.
* underlying database source directly is much more difficult.
*
* @return The last value we obtained from the underlying source;
* -1 indicates we have not yet consulted with the source.
* null indicates we have not yet consulted with the source.
*/
public long getLastSourceValue();
public IntegralDataTypeHolder getLastSourceValue();
/**
* Retrieves the defined increment size.

View File

@ -31,8 +31,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.util.ReflectHelper;
import org.hibernate.id.IdentifierGeneratorHelper;
/**
* Factory for {@link Optimizer} instances.
@ -97,17 +97,6 @@ public class OptimizerFactory {
this.incrementSize = incrementSize;
}
/**
* Take the primitive long value and "make" (or wrap) it into the
* {@link #getReturnClass id type}.
*
* @param value The primitive value to make/wrap.
* @return The wrapped value.
*/
protected final Serializable make(long value) {
return IdentifierGeneratorHelper.createNumber( value, returnClass );
}
/**
* Getter for property 'returnClass'. This is the Java
* class which is used to represent the id (e.g. {@link java.lang.Long}).
@ -131,7 +120,7 @@ public class OptimizerFactory {
* every request.
*/
public static class NoopOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
private IntegralDataTypeHolder lastSourceValue;
public NoopOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
@ -141,21 +130,21 @@ public class OptimizerFactory {
* {@inheritDoc}
*/
public Serializable generate(AccessCallback callback) {
if ( lastSourceValue == -1 ) {
while( lastSourceValue <= 0 ) {
if ( lastSourceValue == null ) {
do {
lastSourceValue = callback.getNextValue();
}
} while ( lastSourceValue.lt( 1 ) );
}
else {
lastSourceValue = callback.getNextValue();
}
return make( lastSourceValue );
return lastSourceValue.makeValue();
}
/**
* {@inheritDoc}
*/
public long getLastSourceValue() {
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@ -170,11 +159,40 @@ public class OptimizerFactory {
/**
* Optimizer which applies a 'hilo' algorithm in memory to achieve
* optimization.
* <p/>
* A 'hilo' algorithm is simply a means for a single value stored in the
* database to represent a "bucket" of possible, contiguous values. The
* database value identifies which particular bucket we are on.
* <p/>
* This database value must be paired with another value that defines the
* size of the bucket; the number of possible values available.
* The {@link #getIncrementSize() incrementSize} serves this purpose. The
* naming here is meant more for consistency in that this value serves the
* same purpose as the increment supplied to the {@link PooledOptimizer}.
* <p/>
* The general algorithms used to determine the bucket are:<ol>
* <li>{@code upperLimit = (databaseValue * incrementSize) + 1}</li>
* <li>{@code lowerLimit = upperLimit - 1}</li>
* </ol>
* As an example, consider a case with incrementSize of 10. Initially the
* database holds 1:<ol>
* <li>{@code upperLimit = (1 * 20) + 1 = 21}</li>
* <li>{@code lowerLimit = 21 - 20 = 1}</li>
* </ol>
* From there we increment the value from lowerLimit until we reach the
* upperLimit, at which point we would define a new bucket. The database
* now contains 2, though incrementSize remains unchanged:<ol>
* <li>{@code upperLimit = (2 * 20) + 1 = 41}</li>
* <li>{@code lowerLimit = 41 - 20 = 21}</li>
* </ol>
* And so on...
* <p/>
* Note, 'value' always (after init) holds the next value to return
*/
public static class HiLoOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
private long value;
private long hiValue;
private IntegralDataTypeHolder lastSourceValue;
private IntegralDataTypeHolder upperLimit;
private IntegralDataTypeHolder value;
public HiLoOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
@ -190,26 +208,30 @@ public class OptimizerFactory {
* {@inheritDoc}
*/
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue < 0 ) {
if ( lastSourceValue == null ) {
// first call, so initialize ourselves. we need to read the database
// value and set up the 'bucket' boundaries
lastSourceValue = callback.getNextValue();
while ( lastSourceValue <= 0 ) {
while ( lastSourceValue.lt( 1 ) ) {
lastSourceValue = callback.getNextValue();
}
hiValue = ( lastSourceValue * incrementSize ) + 1;
value = hiValue - incrementSize;
// upperLimit defines the upper end of the bucket values
upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment();
// initialize value to the low end of the bucket
value = upperLimit.copy().subtract( incrementSize );
}
else if ( value >= hiValue ) {
else if ( ! upperLimit.gt( value ) ) {
lastSourceValue = callback.getNextValue();
hiValue = ( lastSourceValue * incrementSize ) + 1;
upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment();
}
return make( value++ );
return value.makeValueThenIncrement();
}
/**
* {@inheritDoc}
*/
public long getLastSourceValue() {
public IntegralDataTypeHolder getLastSourceValue() {
return lastSourceValue;
}
@ -222,30 +244,38 @@ public class OptimizerFactory {
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
public long getLastValue() {
return value - 1;
public IntegralDataTypeHolder getLastValue() {
return value.copy().decrement();
}
/**
* Getter for property 'hiValue'.
* Getter for property 'upperLimit'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'hiValue'.
* @return Value for property 'upperLimit'.
*/
public long getHiValue() {
return hiValue;
public IntegralDataTypeHolder getHiValue() {
return upperLimit;
}
}
/**
* Optimizer which uses a pool of values, storing the next low value of the
* range in the database.
* <p/>
* Note that this optimizer works essentially the same as the
* {@link HiLoOptimizer} except that here the bucket ranges are actually
* encoded into the database structures.
*/
public static class PooledOptimizer extends OptimizerSupport {
private long value;
private long hiValue = -1;
private IntegralDataTypeHolder hiValue;
private IntegralDataTypeHolder value;
public PooledOptimizer(Class returnClass, int incrementSize) {
super( returnClass, incrementSize );
@ -261,9 +291,9 @@ public class OptimizerFactory {
* {@inheritDoc}
*/
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue < 0 ) {
if ( hiValue == null ) {
value = callback.getNextValue();
if ( value < 1 ) {
if ( value.lt( 1 ) ) {
// unfortunately not really safe to normalize this
// to 1 as an initial value like we do the others
// because we would not be able to control this if
@ -272,17 +302,17 @@ public class OptimizerFactory {
}
hiValue = callback.getNextValue();
}
else if ( value >= hiValue ) {
else if ( ! hiValue.gt( value ) ) {
hiValue = callback.getNextValue();
value = hiValue - incrementSize;
value = hiValue.copy().subtract( incrementSize );
}
return make( value++ );
return value.makeValueThenIncrement();
}
/**
* {@inheritDoc}
*/
public long getLastSourceValue() {
public IntegralDataTypeHolder getLastSourceValue() {
return hiValue;
}
@ -295,11 +325,13 @@ public class OptimizerFactory {
/**
* Getter for property 'lastValue'.
* <p/>
* Exposure intended for testing purposes.
*
* @return Value for property 'lastValue'.
*/
public long getLastValue() {
return value - 1;
public IntegralDataTypeHolder getLastValue() {
return value.copy().decrement();
}
}
}

View File

@ -35,6 +35,8 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.HibernateException;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
/**
* Describes a sequence.
@ -47,14 +49,21 @@ public class SequenceStructure implements DatabaseStructure {
private final String sequenceName;
private final int initialValue;
private final int incrementSize;
private final Class numberType;
private final String sql;
private boolean applyIncrementSizeToSourceValues;
private int accessCounter;
public SequenceStructure(Dialect dialect, String sequenceName, int initialValue, int incrementSize) {
public SequenceStructure(
Dialect dialect,
String sequenceName,
int initialValue,
int incrementSize,
Class numberType) {
this.sequenceName = sequenceName;
this.initialValue = initialValue;
this.incrementSize = incrementSize;
this.numberType = numberType;
sql = dialect.getSequenceNextValString( sequenceName );
}
@ -91,7 +100,7 @@ public class SequenceStructure implements DatabaseStructure {
*/
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
public IntegralDataTypeHolder getNextValue() {
accessCounter++;
try {
PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
@ -99,11 +108,12 @@ public class SequenceStructure implements DatabaseStructure {
ResultSet rs = st.executeQuery();
try {
rs.next();
long result = rs.getLong( 1 );
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
value.initialize( rs, 1 );
if ( log.isDebugEnabled() ) {
log.debug("Sequence identifier generated: " + result);
log.debug( "Sequence value obtained: " + value.makeValue() );
}
return result;
return value;
}
finally {
try {

View File

@ -178,7 +178,15 @@ public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Co
}
}
this.databaseStructure = buildDatabaseStructure( params, dialect, forceTableUse, sequenceName, initialValue, incrementSize );
this.databaseStructure = buildDatabaseStructure(
type,
params,
dialect,
forceTableUse,
sequenceName,
initialValue,
incrementSize
);
this.optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
this.databaseStructure.prepare( optimizer );
@ -291,15 +299,16 @@ public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Co
/**
* Build the database structure.
*
* @param type The Hibernate type of the identifier property
* @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
* @param incrementSize the increment size to use (after any adjustments). @return The db structure representation
*/
protected DatabaseStructure buildDatabaseStructure(
Type type,
Properties params,
Dialect dialect,
boolean forceTableUse,
@ -308,11 +317,11 @@ public class SequenceStyleGenerator implements PersistentIdentifierGenerator, Co
int incrementSize) {
boolean useSequence = dialect.supportsSequences() && !forceTableUse;
if ( useSequence ) {
return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize, type.getReturnedClass() );
}
else {
String valueColumnName = determineValueColumnName( params, dialect );
return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize, type.getReturnedClass() );
}
}

View File

@ -39,6 +39,8 @@ import org.slf4j.LoggerFactory;
import org.hibernate.engine.TransactionHelper;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.Configurable;
import org.hibernate.type.Type;
@ -287,7 +289,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
identifierType = type;
tableName = determneGeneratorTableName( params, dialect );
tableName = determineGeneratorTableName( params, dialect );
segmentColumnName = determineSegmentColumnName( params, dialect );
valueColumnName = determineValueColumnName( params, dialect );
@ -316,7 +318,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
* @param dialect The dialect in effect
* @return The table name to use.
*/
protected String determneGeneratorTableName(Properties params, Dialect dialect) {
protected String determineGeneratorTableName(Properties params, Dialect dialect) {
String name = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0;
if ( isGivenNameUnqualified ) {
@ -450,8 +452,8 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
return optimizer.generate(
new AccessCallback() {
public long getNextValue() {
return ( ( Number ) doWorkInNewTransaction( session ) ).longValue();
public IntegralDataTypeHolder getNextValue() {
return ( IntegralDataTypeHolder ) doWorkInNewTransaction( session );
}
}
);
@ -461,7 +463,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
* {@inheritDoc}
*/
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
int result;
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
int rows;
do {
SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
@ -470,13 +472,13 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
selectPS.setString( 1, segmentValue );
ResultSet selectRS = selectPS.executeQuery();
if ( !selectRS.next() ) {
value.initialize( initialValue );
PreparedStatement insertPS = null;
try {
result = initialValue;
SQL_STATEMENT_LOGGER.logStatement( insertQuery, FormatStyle.BASIC );
insertPS = conn.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
insertPS.setLong( 2, result );
value.bind( insertPS, 2 );
insertPS.execute();
}
finally {
@ -486,7 +488,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
}
}
else {
result = selectRS.getInt( 1 );
value.initialize( selectRS, 1 );
}
selectRS.close();
}
@ -501,10 +503,15 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
long newValue = optimizer.applyIncrementSizeToSourceValues()
? result + incrementSize : result + 1;
updatePS.setLong( 1, newValue );
updatePS.setLong( 2, result );
final IntegralDataTypeHolder updateValue = value.copy();
if ( optimizer.applyIncrementSizeToSourceValues() ) {
updateValue.add( incrementSize );
}
else {
updateValue.increment();
}
updateValue.bind( updatePS, 1 );
value.bind( updatePS, 2 );
updatePS.setString( 3, segmentValue );
rows = updatePS.executeUpdate();
}
@ -520,7 +527,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
accessCount++;
return new Integer( result );
return value;
}
/**

View File

@ -40,6 +40,8 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.TransactionHelper;
import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.jdbc.util.SQLStatementLogger;
@ -56,17 +58,25 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
private final String valueColumnName;
private final int initialValue;
private final int incrementSize;
private final Class numberType;
private final String selectQuery;
private final String updateQuery;
private boolean applyIncrementSizeToSourceValues;
private int accessCounter;
public TableStructure(Dialect dialect, String tableName, String valueColumnName, int initialValue, int incrementSize) {
public TableStructure(
Dialect dialect,
String tableName,
String valueColumnName,
int initialValue,
int incrementSize,
Class numberType) {
this.tableName = tableName;
this.initialValue = initialValue;
this.incrementSize = incrementSize;
this.valueColumnName = valueColumnName;
this.numberType = numberType;
selectQuery = "select " + valueColumnName + " as id_val" +
" from " + dialect.appendLockHint( LockMode.PESSIMISTIC_WRITE, tableName ) +
@ -117,8 +127,8 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
*/
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
return ( ( Number ) doWorkInNewTransaction( session ) ).longValue();
public IntegralDataTypeHolder getNextValue() {
return ( IntegralDataTypeHolder ) doWorkInNewTransaction( session );
}
};
}
@ -152,7 +162,7 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
* {@inheritDoc}
*/
protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
long result;
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
int rows;
do {
SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
@ -164,7 +174,7 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
log.error( err );
throw new IdentifierGenerationException( err );
}
result = selectRS.getLong( 1 );
value.initialize( selectRS, 1 );
selectRS.close();
}
catch ( SQLException sqle ) {
@ -178,9 +188,10 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
updatePS.setLong( 1, result + increment );
updatePS.setLong( 2, result );
final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
final IntegralDataTypeHolder updateValue = value.copy().add( increment );
updateValue.bind( updatePS, 1 );
value.bind( updatePS, 2 );
rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
@ -194,7 +205,7 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
accessCounter++;
return new Long( result );
return value;
}
}

View File

@ -0,0 +1,167 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id;
import junit.framework.TestCase;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public abstract class AbstractHolderTest extends TestCase {
protected abstract IntegralDataTypeHolder makeHolder();
public void testInitializationChecking() {
IntegralDataTypeHolder holder = makeHolder();
try {
holder.increment();
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.add( 1 );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.decrement();
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.subtract( 1 );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.multiplyBy( holder );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.multiplyBy( 1 );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.eq( holder );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.eq( 1 );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.lt( holder );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.lt( 1 );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.gt( holder );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.gt( 1 );
fail();
}
catch ( IdentifierGenerationException expected ) {
}
try {
holder.makeValue();
fail();
}
catch ( IdentifierGenerationException expected ) {
}
}
public void testIncrement() {
IntegralDataTypeHolder holder = makeHolder();
holder.initialize( 0 );
int i = 0;
for ( ; i < 5008; i++ ) {
holder.increment();
}
assertEquals( holder.copy().initialize( i ), holder );
}
public void testBasicHiloAlgorithm() {
// mimic an initialValue of 1 and increment of 20
final long initialValue = 1;
final long incrementSize = 2;
// initialization
IntegralDataTypeHolder lastSourceValue = makeHolder().initialize( 1 );
IntegralDataTypeHolder upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment();
IntegralDataTypeHolder value = upperLimit.copy().subtract( incrementSize );
assertEquals( 1, lastSourceValue.makeValue().longValue() );
assertEquals( 3, upperLimit.makeValue().longValue() );
assertEquals( 1, value.makeValue().longValue() );
value.increment();
value.increment();
assertFalse( upperLimit.gt( value ) );
// at which point we would "clock over"
lastSourceValue.increment();
upperLimit = lastSourceValue.copy().multiplyBy( incrementSize ).increment();
assertEquals( 2, lastSourceValue.makeValue().longValue() );
assertEquals( 5, upperLimit.makeValue().longValue() );
assertEquals( 3, value.makeValue().longValue() );
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id;
import java.math.BigDecimal;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class BigDecimalHolderTest extends AbstractHolderTest {
protected IntegralDataTypeHolder makeHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( BigDecimal.class );
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id;
import java.math.BigInteger;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class BigIntegerHolderTest extends AbstractHolderTest {
protected IntegralDataTypeHolder makeHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( BigInteger.class );
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.id;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class LongHolderTest extends AbstractHolderTest {
protected IntegralDataTypeHolder makeHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( Long.class );
}
}

View File

@ -0,0 +1,73 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.idgen.biginteger.increment;
import java.math.BigInteger;
import junit.framework.Test;
import org.hibernate.Session;
import org.hibernate.junit.functional.FunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
/**
* {@inheritDoc}
*
* @author Steve Ebersole
*/
public class BigIntegerIncrementGeneratorTest extends FunctionalTestCase {
public BigIntegerIncrementGeneratorTest(String string) {
super( string );
}
public String[] getMappings() {
return new String[] { "idgen/biginteger/increment/Mapping.hbm.xml" };
}
public static Test suite() {
return new FunctionalTestClassTestSuite( BigIntegerIncrementGeneratorTest.class );
}
public void testBasics() {
Session s = openSession();
s.beginTransaction();
Entity entity = new Entity( "BigInteger + increment #1" );
s.save( entity );
Entity entity2 = new Entity( "BigInteger + increment #2" );
s.save( entity2 );
s.getTransaction().commit();
s.close();
assertEquals( BigInteger.valueOf( 1 ), entity.getId() );
assertEquals( BigInteger.valueOf( 2 ), entity2.getId() );
s = openSession();
s.beginTransaction();
s.delete( entity );
s.delete( entity2 );
s.getTransaction().commit();
s.close();
}
}

View File

@ -0,0 +1,59 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.idgen.biginteger.increment;
import java.math.BigInteger;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class Entity {
private BigInteger id;
private String name;
public Entity() {
}
public Entity(String name) {
this.name = name;
}
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2010, Red Hat Inc. or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Inc.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Demonstrates use of simple increment generator on a BigInteger property.
-->
<hibernate-mapping package="org.hibernate.test.idgen.biginteger.increment">
<class name="Entity">
<id name="id" column="ID" type="big_integer">
<generator class="increment" />
</id>
<property name="name" type="string"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,79 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.idgen.biginteger.sequence;
import junit.framework.Test;
import org.hibernate.Session;
import org.hibernate.dialect.Dialect;
import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
/**
* {@inheritDoc}
*
* @author Steve Ebersole
*/
public class BigIntegerSequenceGeneratorTest extends DatabaseSpecificFunctionalTestCase {
public BigIntegerSequenceGeneratorTest(String string) {
super( string );
}
public String[] getMappings() {
return new String[] { "idgen/biginteger/sequence/Mapping.hbm.xml" };
}
public static Test suite() {
return new FunctionalTestClassTestSuite( BigIntegerSequenceGeneratorTest.class );
}
@Override
public boolean appliesTo(Dialect dialect) {
return dialect.supportsSequences();
}
public void testBasics() {
Session s = openSession();
s.beginTransaction();
Entity entity = new Entity( "BigInteger + sequence #1" );
s.save( entity );
Entity entity2 = new Entity( "BigInteger + sequence #2" );
s.save( entity2 );
s.getTransaction().commit();
s.close();
// hsqldb defines different behavior for the initial select from a sequence
// then say oracle
// assertEquals( BigInteger.valueOf( 1 ), entity.getId() );
// assertEquals( BigInteger.valueOf( 2 ), entity2.getId() );
s = openSession();
s.beginTransaction();
s.delete( entity );
s.delete( entity2 );
s.getTransaction().commit();
s.close();
}
}

View File

@ -0,0 +1,59 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.test.idgen.biginteger.sequence;
import java.math.BigInteger;
/**
* TODO : javadoc
*
* @author Steve Ebersole
*/
public class Entity {
private BigInteger id;
private String name;
public Entity() {
}
public Entity(String name) {
this.name = name;
}
public BigInteger getId() {
return id;
}
public void setId(BigInteger id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,43 @@
<?xml version="1.0"?>
<!--
~ Hibernate, Relational Persistence for Idiomatic Java
~
~ Copyright (c) 2010, Red Hat Inc. or third-party contributors as
~ indicated by the @author tags or express copyright attribution
~ statements applied by the authors. All third-party contributions are
~ distributed under license by Red Hat Inc.
~
~ This copyrighted material is made available to anyone wishing to use, modify,
~ copy, or redistribute it subject to the terms and conditions of the GNU
~ Lesser General Public License, as published by the Free Software Foundation.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
~ for more details.
~
~ You should have received a copy of the GNU Lesser General Public License
~ along with this distribution; if not, write to:
~ Free Software Foundation, Inc.
~ 51 Franklin Street, Fifth Floor
~ Boston, MA 02110-1301 USA
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!--
Demonstrates use of a sequence generator on a BigInteger property.
-->
<hibernate-mapping package="org.hibernate.test.idgen.biginteger.sequence">
<class name="Entity">
<id name="id" column="ID" type="big_integer">
<generator class="sequence">
</generator>
</id>
<property name="name" type="string"/>
</class>
</hibernate-mapping>

View File

@ -3,6 +3,8 @@ package org.hibernate.test.idgen.enhanced;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.junit.UnitTestCase;
import org.hibernate.id.enhanced.Optimizer;
import org.hibernate.id.enhanced.OptimizerFactory;
@ -88,7 +90,7 @@ public class OptimizerUnitTest extends UnitTestCase {
next = ( Long ) optimizer.generate( sequence );
assertEquals( i, next.intValue() );
}
assertEquals( 2, sequence.getTimesCalled() ); // twice to initialze state
assertEquals( 2, sequence.getTimesCalled() ); // twice to initialize state
assertEquals( 11, sequence.getCurrentValue() );
// force a "clock over"
next = ( Long ) optimizer.generate( sequence );
@ -98,7 +100,7 @@ public class OptimizerUnitTest extends UnitTestCase {
}
private static class SourceMock implements AccessCallback {
private long value;
private IdentifierGeneratorHelper.BasicHolder value = new IdentifierGeneratorHelper.BasicHolder( Long.class );
private int increment;
private int timesCalled = 0;
@ -108,12 +110,12 @@ public class OptimizerUnitTest extends UnitTestCase {
public SourceMock(long initialValue, int increment) {
this.increment = increment;
this.value = initialValue - increment;
this.value.initialize( initialValue - increment );
}
public long getNextValue() {
public IntegralDataTypeHolder getNextValue() {
timesCalled++;
return ( value += increment );
return value.add( increment ).copy();
}
public int getTimesCalled() {
@ -121,7 +123,7 @@ public class OptimizerUnitTest extends UnitTestCase {
}
public long getCurrentValue() {
return value;
return value.getActualLongValue();
}
}

View File

@ -2,13 +2,15 @@ package org.hibernate.test.idgen.enhanced.forcedtable;
import junit.framework.Test;
import org.hibernate.Session;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.Session;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
@ -54,7 +56,7 @@ public class BasicForcedTableSequenceTest extends DatabaseSpecificFunctionalTest
long expectedId = i + 1;
assertEquals( expectedId, entities[i].getId().longValue() );
assertEquals( expectedId, generator.getDatabaseStructure().getTimesAccessed() );
assertEquals( expectedId, generator.getOptimizer().getLastSourceValue() );
assertEquals( expectedId, ( (BasicHolder) generator.getOptimizer().getLastSourceValue() ).getActualLongValue() );
}
s.getTransaction().commit();

View File

@ -2,13 +2,15 @@ package org.hibernate.test.idgen.enhanced.forcedtable;
import junit.framework.Test;
import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.Session;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.Session;
import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
@ -54,18 +56,18 @@ public class HiLoForcedTableSequenceTest extends DatabaseSpecificFunctionalTestC
s.save( entities[i] );
long expectedId = i + 1;
assertEquals( expectedId, entities[i].getId().longValue() );
assertEquals( 1, generator.getOptimizer().getLastSourceValue() );
assertEquals( i + 1, optimizer.getLastValue() );
assertEquals( increment + 1, optimizer.getHiValue() );
assertEquals( 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() );
assertEquals( i + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getHiValue() ).getActualLongValue() );
}
// now force a "clock over"
entities[ increment ] = new Entity( "" + increment );
s.save( entities[ increment ] );
long expectedId = optimizer.getIncrementSize() + 1;
assertEquals( expectedId, entities[ optimizer.getIncrementSize() ].getId().longValue() );
assertEquals( 2, optimizer.getLastSourceValue() ); // initialization + clokc-over
assertEquals( increment + 1, optimizer.getLastValue() );
assertEquals( ( increment * 2 ) + 1, optimizer.getHiValue() );
assertEquals( 2, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization + clock-over
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( ( increment * 2 ) + 1, ( (BasicHolder) optimizer.getHiValue() ).getActualLongValue() );
s.getTransaction().commit();

View File

@ -2,13 +2,15 @@ package org.hibernate.test.idgen.enhanced.forcedtable;
import junit.framework.Test;
import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.Session;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.id.enhanced.TableStructure;
import org.hibernate.Session;
import org.hibernate.junit.functional.DatabaseSpecificFunctionalTestCase;
import org.hibernate.junit.functional.FunctionalTestClassTestSuite;
import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
@ -54,19 +56,21 @@ public class PooledForcedTableSequenceTest extends DatabaseSpecificFunctionalTes
s.save( entities[i] );
long expectedId = i + 1;
assertEquals( expectedId, entities[i].getId().longValue() );
assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() ); // initialization calls table twice
assertEquals( increment + 1, optimizer.getLastSourceValue() ); // initialization calls table twice
assertEquals( i + 1, optimizer.getLastValue() );
assertEquals( increment + 1, optimizer.getLastSourceValue() );
// NOTE : initialization calls table twice
assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() );
assertEquals( i + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() );
}
// now force a "clock over"
entities[ increment ] = new Entity( "" + increment );
s.save( entities[ increment ] );
long expectedId = optimizer.getIncrementSize() + 1;
assertEquals( expectedId, entities[ optimizer.getIncrementSize() ].getId().longValue() );
assertEquals( 3, generator.getDatabaseStructure().getTimesAccessed() ); // initialization (2) + clock over
assertEquals( ( increment * 2 ) + 1, optimizer.getLastSourceValue() ); // initialization (2) + clock over
assertEquals( increment + 1, optimizer.getLastValue() );
// initialization (2) + clock over
assertEquals( 3, generator.getDatabaseStructure().getTimesAccessed() );
assertEquals( ( increment * 2 ) + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
s.getTransaction().commit();
s.beginTransaction();

View File

@ -8,6 +8,8 @@ import org.hibernate.Session;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
*
@ -41,7 +43,7 @@ public class BasicSequenceTest extends FunctionalTestCase {
long expectedId = i + 1;
assertEquals( expectedId, entities[i].getId().longValue() );
assertEquals( expectedId, generator.getDatabaseStructure().getTimesAccessed() );
assertEquals( expectedId, generator.getOptimizer().getLastSourceValue() );
assertEquals( expectedId, ( (BasicHolder) generator.getOptimizer().getLastSourceValue() ).getActualLongValue() );
}
s.getTransaction().commit();

View File

@ -9,6 +9,9 @@ import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.persister.entity.EntityPersister;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
*
@ -42,17 +45,17 @@ public class HiLoSequenceTest extends FunctionalTestCase {
entities[i] = new Entity( "" + ( i + 1 ) );
s.save( entities[i] );
assertEquals( 1, generator.getDatabaseStructure().getTimesAccessed() ); // initialization
assertEquals( 1, optimizer.getLastSourceValue() ); // initialization
assertEquals( i + 1, optimizer.getLastValue() );
assertEquals( increment + 1, optimizer.getHiValue() );
assertEquals( 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization
assertEquals( i + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getHiValue() ).getActualLongValue() );
}
// now force a "clock over"
entities[ increment ] = new Entity( "" + increment );
s.save( entities[ increment ] );
assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() ); // initialization
assertEquals( 2, optimizer.getLastSourceValue() ); // initialization
assertEquals( increment + 1, optimizer.getLastValue() );
assertEquals( ( increment * 2 ) + 1, optimizer.getHiValue() );
assertEquals( 2, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( ( increment * 2 ) + 1, ( (BasicHolder) optimizer.getHiValue() ).getActualLongValue() );
s.getTransaction().commit();

View File

@ -9,6 +9,8 @@ import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.Session;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
*
@ -42,16 +44,16 @@ public class PooledSequenceTest extends FunctionalTestCase {
entities[i] = new Entity( "" + ( i + 1 ) );
s.save( entities[i] );
assertEquals( 2, generator.getDatabaseStructure().getTimesAccessed() ); // initialization calls seq twice
assertEquals( increment + 1, optimizer.getLastSourceValue() ); // initialization calls seq twice
assertEquals( i + 1, optimizer.getLastValue() );
assertEquals( increment + 1, optimizer.getLastSourceValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization calls seq twice
assertEquals( i + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() );
}
// now force a "clock over"
entities[ increment ] = new Entity( "" + increment );
s.save( entities[ increment ] );
assertEquals( 3, generator.getDatabaseStructure().getTimesAccessed() ); // initialization (2) + clock over
assertEquals( ( increment * 2 ) + 1, optimizer.getLastSourceValue() ); // initialization (2) + clock over
assertEquals( increment + 1, optimizer.getLastValue() );
assertEquals( ( increment * 2 ) + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization (2) + clock over
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
s.getTransaction().commit();
s.beginTransaction();

View File

@ -8,6 +8,8 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.Session;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
*
@ -41,7 +43,7 @@ public class BasicTableTest extends FunctionalTestCase {
long expectedId = i + 1;
assertEquals( expectedId, entities[i].getId().longValue() );
assertEquals( expectedId, generator.getTableAccessCount() );
assertEquals( expectedId, generator.getOptimizer().getLastSourceValue() );
assertEquals( expectedId, ( (BasicHolder) generator.getOptimizer().getLastSourceValue() ).getActualLongValue() );
}
s.getTransaction().commit();

View File

@ -9,6 +9,8 @@ import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.Session;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
*
@ -42,17 +44,17 @@ public class HiLoTableTest extends FunctionalTestCase {
entities[i] = new Entity( "" + ( i + 1 ) );
s.save( entities[i] );
assertEquals( 1, generator.getTableAccessCount() ); // initialization
assertEquals( 1, optimizer.getLastSourceValue() ); // initialization
assertEquals( i + 1, optimizer.getLastValue() );
assertEquals( increment + 1, optimizer.getHiValue() );
assertEquals( 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization
assertEquals( i + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getHiValue() ).getActualLongValue() );
}
// now force a "clock over"
entities[ increment ] = new Entity( "" + increment );
s.save( entities[ increment ] );
assertEquals( 2, generator.getTableAccessCount() ); // initialization
assertEquals( 2, optimizer.getLastSourceValue() ); // initialization
assertEquals( increment + 1, optimizer.getLastValue() );
assertEquals( ( increment * 2 ) + 1, optimizer.getHiValue() );
assertEquals( 2, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( ( increment * 2 ) + 1, ( (BasicHolder) optimizer.getHiValue() ).getActualLongValue() );
s.getTransaction().commit();

View File

@ -9,6 +9,8 @@ import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.id.enhanced.TableGenerator;
import org.hibernate.Session;
import static org.hibernate.id.IdentifierGeneratorHelper.BasicHolder;
/**
* {@inheritDoc}
*
@ -42,16 +44,16 @@ public class PooledTableTest extends FunctionalTestCase {
entities[i] = new Entity( "" + ( i + 1 ) );
s.save( entities[i] );
assertEquals( 2, generator.getTableAccessCount() ); // initialization calls seq twice
assertEquals( increment + 1, optimizer.getLastSourceValue() ); // initialization calls seq twice
assertEquals( i + 1, optimizer.getLastValue() );
assertEquals( increment + 1, optimizer.getLastSourceValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization calls seq twice
assertEquals( i + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() );
}
// now force a "clock over"
entities[ increment ] = new Entity( "" + increment );
s.save( entities[ increment ] );
assertEquals( 3, generator.getTableAccessCount() ); // initialization (2) + clock over
assertEquals( ( increment * 2 ) + 1, optimizer.getLastSourceValue() ); // initialization (2) + clock over
assertEquals( increment + 1, optimizer.getLastValue() );
assertEquals( ( increment * 2 ) + 1, ( (BasicHolder) optimizer.getLastSourceValue() ).getActualLongValue() ); // initialization (2) + clock over
assertEquals( increment + 1, ( (BasicHolder) optimizer.getLastValue() ).getActualLongValue() );
s.getTransaction().commit();
s.beginTransaction();