Hibernate SVN: r15151 - core/trunk/core/src/main/java/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 14:42:57 -0400 (Fri, 29 Aug 2008)
New Revision: 15151
Modified:
core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java
core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java
core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java
Log:
HHH-3456 : enhanced.SequenceStyleGenerator extensibility
Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java 2008-08-29 18:42:30 UTC (rev 15150)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java 2008-08-29 18:42:57 UTC (rev 15151)
@@ -76,10 +76,19 @@
return new NoopOptimizer( returnClass, incrementSize );
}
+ /**
+ * Common support for optimizer implementations.
+ */
public static abstract class OptimizerSupport implements Optimizer {
protected final Class returnClass;
protected final int incrementSize;
+ /**
+ * Construct an optimizer
+ *
+ * @param returnClass The expected id class.
+ * @param incrementSize The increment size
+ */
protected OptimizerSupport(Class returnClass, int incrementSize) {
if ( returnClass == null ) {
throw new HibernateException( "return class is required" );
@@ -88,19 +97,39 @@
this.incrementSize = incrementSize;
}
- protected Serializable make(long value) {
+ /**
+ * 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 IdentifierGeneratorFactory.createNumber( value, returnClass );
}
- public Class getReturnClass() {
+ /**
+ * Getter for property 'returnClass'. This is the Java
+ * class which is used to represent the id (e.g. {@link java.lang.Long}).
+ *
+ * @return Value for property 'returnClass'.
+ */
+ public final Class getReturnClass() {
return returnClass;
}
- public int getIncrementSize() {
+ /**
+ * {@inheritDoc}
+ */
+ public final int getIncrementSize() {
return incrementSize;
}
}
+ /**
+ * An optimizer that performs no optimization. The database is hit for
+ * every request.
+ */
public static class NoopOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
@@ -108,6 +137,9 @@
super( returnClass, incrementSize );
}
+ /**
+ * {@inheritDoc}
+ */
public Serializable generate(AccessCallback callback) {
if ( lastSourceValue == -1 ) {
while( lastSourceValue <= 0 ) {
@@ -120,15 +152,25 @@
return make( lastSourceValue );
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return lastSourceValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return false;
}
}
+ /**
+ * Optimizer which applies a 'hilo' algorithm in memory to achieve
+ * optimization.
+ */
public static class HiLoOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
private long value;
@@ -144,6 +186,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue < 0 ) {
lastSourceValue = callback.getNextValue();
@@ -161,23 +206,43 @@
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return lastSourceValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return false;
}
+ /**
+ * Getter for property 'lastValue'.
+ *
+ * @return Value for property 'lastValue'.
+ */
public long getLastValue() {
return value - 1;
}
+ /**
+ * Getter for property 'hiValue'.
+ *
+ * @return Value for property 'hiValue'.
+ */
public long getHiValue() {
return hiValue;
}
}
+ /**
+ * Optimizer which uses a pool of values, storing the next low value of the
+ * range in the database.
+ */
public static class PooledOptimizer extends OptimizerSupport {
private long value;
private long hiValue = -1;
@@ -192,6 +257,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue < 0 ) {
value = callback.getNextValue();
@@ -211,14 +279,25 @@
return make( value++ );
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return hiValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return true;
}
+ /**
+ * Getter for property 'lastValue'.
+ *
+ * @return Value for property 'lastValue'.
+ */
public long getLastValue() {
return value - 1;
}
Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java 2008-08-29 18:42:30 UTC (rev 15150)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java 2008-08-29 18:42:57 UTC (rev 15151)
@@ -58,18 +58,30 @@
sql = dialect.getSequenceNextValString( sequenceName );
}
+ /**
+ * {@inheritDoc}
+ */
public String getName() {
return sequenceName;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
+ /**
+ * {@inheritDoc}
+ */
public int getTimesAccessed() {
return accessCounter;
}
+ /**
+ * {@inheritDoc}
+ */
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
@@ -112,15 +124,24 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return dialect.getDropSequenceStrings( sequenceName );
}
Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2008-08-29 18:42:30 UTC (rev 15150)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2008-08-29 18:42:57 UTC (rev 15151)
@@ -123,14 +123,29 @@
private Optimizer optimizer;
private Type identifierType;
+ /**
+ * Getter for property 'databaseStructure'.
+ *
+ * @return Value for property 'databaseStructure'.
+ */
public DatabaseStructure getDatabaseStructure() {
return databaseStructure;
}
+ /**
+ * Getter for property 'optimizer'.
+ *
+ * @return Value for property 'optimizer'.
+ */
public Optimizer getOptimizer() {
return optimizer;
}
+ /**
+ * Getter for property 'identifierType'.
+ *
+ * @return Value for property 'identifierType'.
+ */
public Type getIdentifierType() {
return identifierType;
}
@@ -138,45 +153,160 @@
// Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
- identifierType = type;
+ this.identifierType = type;
boolean forceTableUse = PropertiesHelper.getBoolean( FORCE_TBL_PARAM, params, false );
+ final String sequenceName = determineSequenceName( params );
+
+ final int initialValue = determineInitialValue( params );
+ int incrementSize = determineIncrementSize( params );
+
+ final String optimizationStrategy = determineOptimizationStrategy( params, incrementSize );
+ incrementSize = determineAdjustedIncrementSize( optimizationStrategy, incrementSize );
+
+ if ( dialect.supportsSequences() && !forceTableUse ) {
+ if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
+ forceTableUse = true;
+ log.info(
+ "Forcing table use for sequence-style generator due to pooled optimizer selection where db does not support pooled sequences"
+ );
+ }
+ }
+
+ this.databaseStructure = buildDatabaseStructure( params, dialect, forceTableUse, sequenceName, initialValue, incrementSize );
+
+ this.optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ this.databaseStructure.prepare( optimizer );
+ }
+
+ /**
+ * Determine the name of the sequence (or table if this resolves to a physical table)
+ * to use.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The sequence name
+ */
+ protected String determineSequenceName(Properties params) {
String sequenceName = PropertiesHelper.getString( SEQUENCE_PARAM, params, DEF_SEQUENCE_NAME );
if ( sequenceName.indexOf( '.' ) < 0 ) {
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
sequenceName = Table.qualify( catalogName, schemaName, sequenceName );
}
- int initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
- int incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ return sequenceName;
+ }
- String valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ /**
+ * Determine the name of the column used to store the generator value in
+ * the db.
+ * <p/>
+ * Called during {@link #configure configuration} <b>when resolving to a
+ * physical table</b>.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The value column name
+ */
+ protected String determineValueColumnName(Properties params) {
+ return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ }
+ /**
+ * Determine the initial sequence value to use. This value is used when
+ * initializing the {@link #getDatabaseStructure() database structure}
+ * (i.e. sequence/table).
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The initial value
+ */
+ protected int determineInitialValue(Properties params) {
+ return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ }
+
+ /**
+ * Determine the increment size to be applied. The exact implications of
+ * this value depends on the {@link #getOptimizer() optimizer} being used.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The increment size
+ */
+ protected int determineIncrementSize(Properties params) {
+ return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ }
+
+ /**
+ * Determine the optimizer to use.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @param incrementSize The {@link #determineIncrementSize determined increment size}
+ * @return The optimizer strategy (name)
+ */
+ protected String determineOptimizationStrategy(Properties params, int incrementSize) {
String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
- String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ return PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ }
+
+ /**
+ * In certain cases we need to adjust the increment size based on the
+ * selected optimizer. This is the hook to achieve that.
+ *
+ * @param optimizationStrategy The optimizer strategy (name)
+ * @param incrementSize The {@link #determineIncrementSize determined increment size}
+ * @return The adjusted increment size.
+ */
+ protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
if ( OptimizerFactory.NONE.equals( optimizationStrategy ) && incrementSize > 1 ) {
log.warn( "config specified explicit optimizer of [" + OptimizerFactory.NONE + "], but [" + INCREMENT_PARAM + "=" + incrementSize + "; honoring optimizer setting" );
incrementSize = 1;
}
- if ( dialect.supportsSequences() && !forceTableUse ) {
- if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
- // TODO : may even be better to fall back to a pooled table strategy here so that the db stored values remain consistent...
- optimizationStrategy = OptimizerFactory.HILO;
- }
- databaseStructure = new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
+ return incrementSize;
+ }
+
+ /**
+ * Build the database structure.
+ *
+ * @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
+ */
+ protected DatabaseStructure buildDatabaseStructure(
+ Properties params,
+ Dialect dialect,
+ boolean forceTableUse,
+ String sequenceName,
+ int initialValue,
+ int incrementSize) {
+ boolean useSequence = dialect.supportsSequences() && !forceTableUse;
+ if ( useSequence ) {
+ return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
}
else {
- databaseStructure = new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
+ String valueColumnName = determineValueColumnName( params );
+ return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
}
-
- optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
- databaseStructure.prepare( optimizer );
}
// IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return optimizer.generate( databaseStructure.buildCallback( session ) );
}
@@ -184,16 +314,24 @@
// PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public Object generatorKey() {
return databaseStructure.getName();
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlCreateStrings( dialect );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlDropStrings( dialect );
}
-
}
Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java 2008-08-29 18:42:30 UTC (rev 15150)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java 2008-08-29 18:42:57 UTC (rev 15151)
@@ -56,8 +56,9 @@
private final String valueColumnName;
private final int initialValue;
private final int incrementSize;
- private final String select;
- private final String update;
+ private final String selectQuery;
+ private final String updateQuery;
+
private boolean applyIncrementSizeToSourceValues;
private int accessCounter;
@@ -67,31 +68,46 @@
this.incrementSize = incrementSize;
this.valueColumnName = valueColumnName;
- select = "select " + valueColumnName + " id_val" +
+ selectQuery = "select " + valueColumnName + " id_val" +
" from " + dialect.appendLockHint( LockMode.UPGRADE, tableName ) +
dialect.getForUpdateString();
- update = "update " + tableName +
+ updateQuery = "update " + tableName +
" set " + valueColumnName + "= ?" +
" where " + valueColumnName + "=?";
}
+ /**
+ * {@inheritDoc}
+ */
public String getName() {
return tableName;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
+ /**
+ * {@inheritDoc}
+ */
public int getTimesAccessed() {
return accessCounter;
}
+ /**
+ * {@inheritDoc}
+ */
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
+ /**
+ * {@inheritDoc}
+ */
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
@@ -100,6 +116,9 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
dialect.getCreateTableString() + " " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
@@ -107,6 +126,9 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@@ -119,46 +141,47 @@
return new String[] { sqlDropString.toString() };
}
+ /**
+ * {@inheritDoc}
+ */
protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
long result;
int rows;
do {
- sql = select;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement qps = conn.prepareStatement( select );
+ SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
+ PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
- ResultSet rs = qps.executeQuery();
- if ( !rs.next() ) {
+ ResultSet selectRS = selectPS.executeQuery();
+ if ( !selectRS.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName;
log.error( err );
throw new IdentifierGenerationException( err );
}
- result = rs.getLong( 1 );
- rs.close();
+ result = selectRS.getLong( 1 );
+ selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read a hi value", sqle );
throw sqle;
}
finally {
- qps.close();
+ selectPS.close();
}
- sql = update;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement ups = conn.prepareStatement( update );
+ SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
+ PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
- ups.setLong( 1, result + increment );
- ups.setLong( 2, result );
- rows = ups.executeUpdate();
+ updatePS.setLong( 1, result + increment );
+ updatePS.setLong( 2, result );
+ rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
- log.error( "could not update hi value in: " + tableName, sqle );
+ log.error( "could not updateQuery hi value in: " + tableName, sqle );
throw sqle;
}
finally {
- ups.close();
+ updatePS.close();
}
} while ( rows == 0 );
16 years, 4 months
Hibernate SVN: r15150 - core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 14:42:30 -0400 (Fri, 29 Aug 2008)
New Revision: 15150
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java
core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java
Log:
HHH-3456 : enhanced.SequenceStyleGenerator extensibility
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java 2008-08-29 18:39:39 UTC (rev 15149)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java 2008-08-29 18:42:30 UTC (rev 15150)
@@ -76,10 +76,19 @@
return new NoopOptimizer( returnClass, incrementSize );
}
+ /**
+ * Common support for optimizer implementations.
+ */
public static abstract class OptimizerSupport implements Optimizer {
protected final Class returnClass;
protected final int incrementSize;
+ /**
+ * Construct an optimizer
+ *
+ * @param returnClass The expected id class.
+ * @param incrementSize The increment size
+ */
protected OptimizerSupport(Class returnClass, int incrementSize) {
if ( returnClass == null ) {
throw new HibernateException( "return class is required" );
@@ -88,19 +97,39 @@
this.incrementSize = incrementSize;
}
- protected Serializable make(long value) {
+ /**
+ * 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 IdentifierGeneratorFactory.createNumber( value, returnClass );
}
- public Class getReturnClass() {
+ /**
+ * Getter for property 'returnClass'. This is the Java
+ * class which is used to represent the id (e.g. {@link java.lang.Long}).
+ *
+ * @return Value for property 'returnClass'.
+ */
+ public final Class getReturnClass() {
return returnClass;
}
- public int getIncrementSize() {
+ /**
+ * {@inheritDoc}
+ */
+ public final int getIncrementSize() {
return incrementSize;
}
}
+ /**
+ * An optimizer that performs no optimization. The database is hit for
+ * every request.
+ */
public static class NoopOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
@@ -108,6 +137,9 @@
super( returnClass, incrementSize );
}
+ /**
+ * {@inheritDoc}
+ */
public Serializable generate(AccessCallback callback) {
if ( lastSourceValue == -1 ) {
while( lastSourceValue <= 0 ) {
@@ -120,15 +152,25 @@
return make( lastSourceValue );
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return lastSourceValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return false;
}
}
+ /**
+ * Optimizer which applies a 'hilo' algorithm in memory to achieve
+ * optimization.
+ */
public static class HiLoOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
private long value;
@@ -144,6 +186,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue < 0 ) {
lastSourceValue = callback.getNextValue();
@@ -161,23 +206,43 @@
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return lastSourceValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return false;
}
+ /**
+ * Getter for property 'lastValue'.
+ *
+ * @return Value for property 'lastValue'.
+ */
public long getLastValue() {
return value - 1;
}
+ /**
+ * Getter for property 'hiValue'.
+ *
+ * @return Value for property 'hiValue'.
+ */
public long getHiValue() {
return hiValue;
}
}
+ /**
+ * Optimizer which uses a pool of values, storing the next low value of the
+ * range in the database.
+ */
public static class PooledOptimizer extends OptimizerSupport {
private long value;
private long hiValue = -1;
@@ -192,6 +257,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue < 0 ) {
value = callback.getNextValue();
@@ -211,14 +279,25 @@
return make( value++ );
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return hiValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return true;
}
+ /**
+ * Getter for property 'lastValue'.
+ *
+ * @return Value for property 'lastValue'.
+ */
public long getLastValue() {
return value - 1;
}
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java 2008-08-29 18:39:39 UTC (rev 15149)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStructure.java 2008-08-29 18:42:30 UTC (rev 15150)
@@ -58,18 +58,30 @@
sql = dialect.getSequenceNextValString( sequenceName );
}
+ /**
+ * {@inheritDoc}
+ */
public String getName() {
return sequenceName;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
+ /**
+ * {@inheritDoc}
+ */
public int getTimesAccessed() {
return accessCounter;
}
+ /**
+ * {@inheritDoc}
+ */
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
@@ -112,15 +124,24 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return dialect.getDropSequenceStrings( sequenceName );
}
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2008-08-29 18:39:39 UTC (rev 15149)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2008-08-29 18:42:30 UTC (rev 15150)
@@ -123,14 +123,29 @@
private Optimizer optimizer;
private Type identifierType;
+ /**
+ * Getter for property 'databaseStructure'.
+ *
+ * @return Value for property 'databaseStructure'.
+ */
public DatabaseStructure getDatabaseStructure() {
return databaseStructure;
}
+ /**
+ * Getter for property 'optimizer'.
+ *
+ * @return Value for property 'optimizer'.
+ */
public Optimizer getOptimizer() {
return optimizer;
}
+ /**
+ * Getter for property 'identifierType'.
+ *
+ * @return Value for property 'identifierType'.
+ */
public Type getIdentifierType() {
return identifierType;
}
@@ -138,45 +153,160 @@
// Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
- identifierType = type;
+ this.identifierType = type;
boolean forceTableUse = PropertiesHelper.getBoolean( FORCE_TBL_PARAM, params, false );
+ final String sequenceName = determineSequenceName( params );
+
+ final int initialValue = determineInitialValue( params );
+ int incrementSize = determineIncrementSize( params );
+
+ final String optimizationStrategy = determineOptimizationStrategy( params, incrementSize );
+ incrementSize = determineAdjustedIncrementSize( optimizationStrategy, incrementSize );
+
+ if ( dialect.supportsSequences() && !forceTableUse ) {
+ if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
+ forceTableUse = true;
+ log.info(
+ "Forcing table use for sequence-style generator due to pooled optimizer selection where db does not support pooled sequences"
+ );
+ }
+ }
+
+ this.databaseStructure = buildDatabaseStructure( params, dialect, forceTableUse, sequenceName, initialValue, incrementSize );
+
+ this.optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ this.databaseStructure.prepare( optimizer );
+ }
+
+ /**
+ * Determine the name of the sequence (or table if this resolves to a physical table)
+ * to use.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The sequence name
+ */
+ protected String determineSequenceName(Properties params) {
String sequenceName = PropertiesHelper.getString( SEQUENCE_PARAM, params, DEF_SEQUENCE_NAME );
if ( sequenceName.indexOf( '.' ) < 0 ) {
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
sequenceName = Table.qualify( catalogName, schemaName, sequenceName );
}
- int initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
- int incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ return sequenceName;
+ }
- String valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ /**
+ * Determine the name of the column used to store the generator value in
+ * the db.
+ * <p/>
+ * Called during {@link #configure configuration} <b>when resolving to a
+ * physical table</b>.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The value column name
+ */
+ protected String determineValueColumnName(Properties params) {
+ return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ }
+ /**
+ * Determine the initial sequence value to use. This value is used when
+ * initializing the {@link #getDatabaseStructure() database structure}
+ * (i.e. sequence/table).
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The initial value
+ */
+ protected int determineInitialValue(Properties params) {
+ return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ }
+
+ /**
+ * Determine the increment size to be applied. The exact implications of
+ * this value depends on the {@link #getOptimizer() optimizer} being used.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The increment size
+ */
+ protected int determineIncrementSize(Properties params) {
+ return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ }
+
+ /**
+ * Determine the optimizer to use.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @param incrementSize The {@link #determineIncrementSize determined increment size}
+ * @return The optimizer strategy (name)
+ */
+ protected String determineOptimizationStrategy(Properties params, int incrementSize) {
String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
- String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ return PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ }
+
+ /**
+ * In certain cases we need to adjust the increment size based on the
+ * selected optimizer. This is the hook to achieve that.
+ *
+ * @param optimizationStrategy The optimizer strategy (name)
+ * @param incrementSize The {@link #determineIncrementSize determined increment size}
+ * @return The adjusted increment size.
+ */
+ protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
if ( OptimizerFactory.NONE.equals( optimizationStrategy ) && incrementSize > 1 ) {
log.warn( "config specified explicit optimizer of [" + OptimizerFactory.NONE + "], but [" + INCREMENT_PARAM + "=" + incrementSize + "; honoring optimizer setting" );
incrementSize = 1;
}
- if ( dialect.supportsSequences() && !forceTableUse ) {
- if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
- // TODO : may even be better to fall back to a pooled table strategy here so that the db stored values remain consistent...
- optimizationStrategy = OptimizerFactory.HILO;
- }
- databaseStructure = new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
+ return incrementSize;
+ }
+
+ /**
+ * Build the database structure.
+ *
+ * @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
+ */
+ protected DatabaseStructure buildDatabaseStructure(
+ Properties params,
+ Dialect dialect,
+ boolean forceTableUse,
+ String sequenceName,
+ int initialValue,
+ int incrementSize) {
+ boolean useSequence = dialect.supportsSequences() && !forceTableUse;
+ if ( useSequence ) {
+ return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
}
else {
- databaseStructure = new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
+ String valueColumnName = determineValueColumnName( params );
+ return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
}
-
- optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
- databaseStructure.prepare( optimizer );
}
// IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return optimizer.generate( databaseStructure.buildCallback( session ) );
}
@@ -184,16 +314,24 @@
// PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public Object generatorKey() {
return databaseStructure.getName();
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlCreateStrings( dialect );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlDropStrings( dialect );
}
-
}
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java 2008-08-29 18:39:39 UTC (rev 15149)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableStructure.java 2008-08-29 18:42:30 UTC (rev 15150)
@@ -56,8 +56,9 @@
private final String valueColumnName;
private final int initialValue;
private final int incrementSize;
- private final String select;
- private final String update;
+ private final String selectQuery;
+ private final String updateQuery;
+
private boolean applyIncrementSizeToSourceValues;
private int accessCounter;
@@ -67,31 +68,46 @@
this.incrementSize = incrementSize;
this.valueColumnName = valueColumnName;
- select = "select " + valueColumnName + " id_val" +
+ selectQuery = "select " + valueColumnName + " id_val" +
" from " + dialect.appendLockHint( LockMode.UPGRADE, tableName ) +
dialect.getForUpdateString();
- update = "update " + tableName +
+ updateQuery = "update " + tableName +
" set " + valueColumnName + "= ?" +
" where " + valueColumnName + "=?";
}
+ /**
+ * {@inheritDoc}
+ */
public String getName() {
return tableName;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
+ /**
+ * {@inheritDoc}
+ */
public int getTimesAccessed() {
return accessCounter;
}
+ /**
+ * {@inheritDoc}
+ */
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
+ /**
+ * {@inheritDoc}
+ */
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
@@ -100,6 +116,9 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
dialect.getCreateTableString() + " " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
@@ -107,6 +126,9 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@@ -119,46 +141,47 @@
return new String[] { sqlDropString.toString() };
}
+ /**
+ * {@inheritDoc}
+ */
protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
long result;
int rows;
do {
- sql = select;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement qps = conn.prepareStatement( select );
+ SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
+ PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
- ResultSet rs = qps.executeQuery();
- if ( !rs.next() ) {
+ ResultSet selectRS = selectPS.executeQuery();
+ if ( !selectRS.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName;
log.error( err );
throw new IdentifierGenerationException( err );
}
- result = rs.getLong( 1 );
- rs.close();
+ result = selectRS.getLong( 1 );
+ selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read a hi value", sqle );
throw sqle;
}
finally {
- qps.close();
+ selectPS.close();
}
- sql = update;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement ups = conn.prepareStatement( update );
+ SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
+ PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
- ups.setLong( 1, result + increment );
- ups.setLong( 2, result );
- rows = ups.executeUpdate();
+ updatePS.setLong( 1, result + increment );
+ updatePS.setLong( 2, result );
+ rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
- log.error( "could not update hi value in: " + tableName, sqle );
+ log.error( "could not updateQuery hi value in: " + tableName, sqle );
throw sqle;
}
finally {
- ups.close();
+ updatePS.close();
}
} while ( rows == 0 );
16 years, 4 months
Hibernate SVN: r15149 - core/branches/Branch_3_2/src/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 14:39:39 -0400 (Fri, 29 Aug 2008)
New Revision: 15149
Modified:
core/branches/Branch_3_2/src/org/hibernate/id/enhanced/OptimizerFactory.java
core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStructure.java
core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java
core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableStructure.java
Log:
HHH-3456 : enhanced.SequenceStyleGenerator extensibility
Modified: core/branches/Branch_3_2/src/org/hibernate/id/enhanced/OptimizerFactory.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/id/enhanced/OptimizerFactory.java 2008-08-29 14:49:34 UTC (rev 15148)
+++ core/branches/Branch_3_2/src/org/hibernate/id/enhanced/OptimizerFactory.java 2008-08-29 18:39:39 UTC (rev 15149)
@@ -52,10 +52,19 @@
return new NoopOptimizer( returnClass, incrementSize );
}
+ /**
+ * Common support for optimizer implementations.
+ */
public static abstract class OptimizerSupport implements Optimizer {
protected final Class returnClass;
protected final int incrementSize;
+ /**
+ * Construct an optimizer
+ *
+ * @param returnClass The expected id class.
+ * @param incrementSize The increment size
+ */
protected OptimizerSupport(Class returnClass, int incrementSize) {
if ( returnClass == null ) {
throw new HibernateException( "return class is required" );
@@ -64,19 +73,39 @@
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 Serializable make(long value) {
return IdentifierGeneratorFactory.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}).
+ *
+ * @return Value for property 'returnClass'.
+ */
public Class getReturnClass() {
return returnClass;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
}
+ /**
+ * An optimizer that performs no optimization. The database is hit for
+ * every request.
+ */
public static class NoopOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
@@ -84,6 +113,9 @@
super( returnClass, incrementSize );
}
+ /**
+ * {@inheritDoc}
+ */
public Serializable generate(AccessCallback callback) {
if ( lastSourceValue == -1 ) {
while( lastSourceValue <= 0 ) {
@@ -96,15 +128,25 @@
return make( lastSourceValue );
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return lastSourceValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return false;
}
}
+ /**
+ * Optimizer which applies a 'hilo' algorithm in memory to achieve
+ * optimization.
+ */
public static class HiLoOptimizer extends OptimizerSupport {
private long lastSourceValue = -1;
private long value;
@@ -120,6 +162,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(AccessCallback callback) {
if ( lastSourceValue < 0 ) {
lastSourceValue = callback.getNextValue();
@@ -137,23 +182,43 @@
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return lastSourceValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return false;
}
+ /**
+ * Getter for property 'lastValue'.
+ *
+ * @return Value for property 'lastValue'.
+ */
public long getLastValue() {
return value - 1;
}
+ /**
+ * Getter for property 'hiValue'.
+ *
+ * @return Value for property 'hiValue'.
+ */
public long getHiValue() {
return hiValue;
}
}
+ /**
+ * Optimizer which uses a pool of values, storing the next low value of the
+ * range in the database.
+ */
public static class PooledOptimizer extends OptimizerSupport {
private long value;
private long hiValue = -1;
@@ -168,6 +233,9 @@
}
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(AccessCallback callback) {
if ( hiValue < 0 ) {
value = callback.getNextValue();
@@ -187,14 +255,25 @@
return make( value++ );
}
+ /**
+ * {@inheritDoc}
+ */
public long getLastSourceValue() {
return hiValue;
}
+ /**
+ * {@inheritDoc}
+ */
public boolean applyIncrementSizeToSourceValues() {
return true;
}
+ /**
+ * Getter for property 'lastValue'.
+ *
+ * @return Value for property 'lastValue'.
+ */
public long getLastValue() {
return value - 1;
}
Modified: core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStructure.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStructure.java 2008-08-29 14:49:34 UTC (rev 15148)
+++ core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStructure.java 2008-08-29 18:39:39 UTC (rev 15149)
@@ -34,18 +34,30 @@
sql = dialect.getSequenceNextValString( sequenceName );
}
+ /**
+ * {@inheritDoc}
+ */
public String getName() {
return sequenceName;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
+ /**
+ * {@inheritDoc}
+ */
public int getTimesAccessed() {
return accessCounter;
}
+ /**
+ * {@inheritDoc}
+ */
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
@@ -88,15 +100,24 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
int sourceIncrementSize = applyIncrementSizeToSourceValues ? incrementSize : 1;
return dialect.getCreateSequenceStrings( sequenceName, initialValue, sourceIncrementSize );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return dialect.getDropSequenceStrings( sequenceName );
}
Modified: core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2008-08-29 14:49:34 UTC (rev 15148)
+++ core/branches/Branch_3_2/src/org/hibernate/id/enhanced/SequenceStyleGenerator.java 2008-08-29 18:39:39 UTC (rev 15149)
@@ -99,14 +99,29 @@
private Optimizer optimizer;
private Type identifierType;
+ /**
+ * Getter for property 'databaseStructure'.
+ *
+ * @return Value for property 'databaseStructure'.
+ */
public DatabaseStructure getDatabaseStructure() {
return databaseStructure;
}
+ /**
+ * Getter for property 'optimizer'.
+ *
+ * @return Value for property 'optimizer'.
+ */
public Optimizer getOptimizer() {
return optimizer;
}
+ /**
+ * Getter for property 'identifierType'.
+ *
+ * @return Value for property 'identifierType'.
+ */
public Type getIdentifierType() {
return identifierType;
}
@@ -114,45 +129,160 @@
// Configurable implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
- identifierType = type;
+ this.identifierType = type;
boolean forceTableUse = PropertiesHelper.getBoolean( FORCE_TBL_PARAM, params, false );
+ final String sequenceName = determineSequenceName( params );
+
+ final int initialValue = determineInitialValue( params );
+ int incrementSize = determineIncrementSize( params );
+
+ final String optimizationStrategy = determineOptimizationStrategy( params, incrementSize );
+ incrementSize = determineAdjustedIncrementSize( optimizationStrategy, incrementSize );
+
+ if ( dialect.supportsSequences() && !forceTableUse ) {
+ if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
+ forceTableUse = true;
+ log.info(
+ "Forcing table use for sequence-style generator due to pooled optimizer selection where db does not support pooled sequences"
+ );
+ }
+ }
+
+ this.databaseStructure = buildDatabaseStructure( params, dialect, forceTableUse, sequenceName, initialValue, incrementSize );
+
+ this.optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ this.databaseStructure.prepare( optimizer );
+ }
+
+ /**
+ * Determine the name of the sequence (or table if this resolves to a physical table)
+ * to use.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The sequence name
+ */
+ protected String determineSequenceName(Properties params) {
String sequenceName = PropertiesHelper.getString( SEQUENCE_PARAM, params, DEF_SEQUENCE_NAME );
if ( sequenceName.indexOf( '.' ) < 0 ) {
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
sequenceName = Table.qualify( catalogName, schemaName, sequenceName );
}
- int initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
- int incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ return sequenceName;
+ }
- String valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ /**
+ * Determine the name of the column used to store the generator value in
+ * the db.
+ * <p/>
+ * Called during {@link #configure configuration} <b>when resolving to a
+ * physical table</b>.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The value column name
+ */
+ protected String determineValueColumnName(Properties params) {
+ return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ }
+ /**
+ * Determine the initial sequence value to use. This value is used when
+ * initializing the {@link #getDatabaseStructure() database structure}
+ * (i.e. sequence/table).
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The initial value
+ */
+ protected int determineInitialValue(Properties params) {
+ return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ }
+
+ /**
+ * Determine the increment size to be applied. The exact implications of
+ * this value depends on the {@link #getOptimizer() optimizer} being used.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The increment size
+ */
+ protected int determineIncrementSize(Properties params) {
+ return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ }
+
+ /**
+ * Determine the optimizer to use.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @param incrementSize The {@link #determineIncrementSize determined increment size}
+ * @return The optimizer strategy (name)
+ */
+ protected String determineOptimizationStrategy(Properties params, int incrementSize) {
String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
- String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ return PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ }
+
+ /**
+ * In certain cases we need to adjust the increment size based on the
+ * selected optimizer. This is the hook to achieve that.
+ *
+ * @param optimizationStrategy The optimizer strategy (name)
+ * @param incrementSize The {@link #determineIncrementSize determined increment size}
+ * @return The adjusted increment size.
+ */
+ protected int determineAdjustedIncrementSize(String optimizationStrategy, int incrementSize) {
if ( OptimizerFactory.NONE.equals( optimizationStrategy ) && incrementSize > 1 ) {
log.warn( "config specified explicit optimizer of [" + OptimizerFactory.NONE + "], but [" + INCREMENT_PARAM + "=" + incrementSize + "; honoring optimizer setting" );
incrementSize = 1;
}
- if ( dialect.supportsSequences() && !forceTableUse ) {
- if ( OptimizerFactory.POOL.equals( optimizationStrategy ) && !dialect.supportsPooledSequences() ) {
- // TODO : may even be better to fall back to a pooled table strategy here so that the db stored values remain consistent...
- optimizationStrategy = OptimizerFactory.HILO;
- }
- databaseStructure = new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
+ return incrementSize;
+ }
+
+ /**
+ * Build the database structure.
+ *
+ * @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
+ */
+ protected DatabaseStructure buildDatabaseStructure(
+ Properties params,
+ Dialect dialect,
+ boolean forceTableUse,
+ String sequenceName,
+ int initialValue,
+ int incrementSize) {
+ boolean useSequence = dialect.supportsSequences() && !forceTableUse;
+ if ( useSequence ) {
+ return new SequenceStructure( dialect, sequenceName, initialValue, incrementSize );
}
else {
- databaseStructure = new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
+ String valueColumnName = determineValueColumnName( params );
+ return new TableStructure( dialect, sequenceName, valueColumnName, initialValue, incrementSize );
}
-
- optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
- databaseStructure.prepare( optimizer );
}
// IdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
return optimizer.generate( databaseStructure.buildCallback( session ) );
}
@@ -160,14 +290,23 @@
// PersistentIdentifierGenerator implementation ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ /**
+ * {@inheritDoc}
+ */
public Object generatorKey() {
return databaseStructure.getName();
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlCreateStrings( dialect );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
return databaseStructure.sqlDropStrings( dialect );
}
Modified: core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableStructure.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableStructure.java 2008-08-29 14:49:34 UTC (rev 15148)
+++ core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableStructure.java 2008-08-29 18:39:39 UTC (rev 15149)
@@ -30,8 +30,9 @@
private final String valueColumnName;
private final int initialValue;
private final int incrementSize;
- private final String select;
- private final String update;
+ private final String selectQuery;
+ private final String updateQuery;
+
private boolean applyIncrementSizeToSourceValues;
private int accessCounter;
@@ -41,31 +42,46 @@
this.incrementSize = incrementSize;
this.valueColumnName = valueColumnName;
- select = "select " + valueColumnName + " id_val" +
+ selectQuery = "select " + valueColumnName + " id_val" +
" from " + dialect.appendLockHint( LockMode.UPGRADE, tableName ) +
dialect.getForUpdateString();
- update = "update " + tableName +
+ updateQuery = "update " + tableName +
" set " + valueColumnName + "= ?" +
" where " + valueColumnName + "=?";
}
+ /**
+ * {@inheritDoc}
+ */
public String getName() {
return tableName;
}
+ /**
+ * {@inheritDoc}
+ */
public int getIncrementSize() {
return incrementSize;
}
+ /**
+ * {@inheritDoc}
+ */
public int getTimesAccessed() {
return accessCounter;
}
+ /**
+ * {@inheritDoc}
+ */
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
+ /**
+ * {@inheritDoc}
+ */
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
public long getNextValue() {
@@ -74,13 +90,19 @@
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
- "create table " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
+ dialect.getCreateTableString() + " " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
"insert into " + tableName + " values ( " + initialValue + " )"
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@@ -97,42 +119,40 @@
long result;
int rows;
do {
- sql = select;
- SQL_LOG.debug( sql );
- PreparedStatement qps = conn.prepareStatement( select );
+ SQL_LOG.debug( selectQuery );
+ PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
- ResultSet rs = qps.executeQuery();
- if ( !rs.next() ) {
+ ResultSet selectRS = selectPS.executeQuery();
+ if ( !selectRS.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName;
log.error( err );
throw new IdentifierGenerationException( err );
}
- result = rs.getLong( 1 );
- rs.close();
+ result = selectRS.getLong( 1 );
+ selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read a hi value", sqle );
throw sqle;
}
finally {
- qps.close();
+ selectPS.close();
}
- sql = update;
- SQL_LOG.debug( sql );
- PreparedStatement ups = conn.prepareStatement( update );
+ SQL_LOG.debug( updateQuery );
+ PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
- ups.setLong( 1, result + increment );
- ups.setLong( 2, result );
- rows = ups.executeUpdate();
+ updatePS.setLong( 1, result + increment );
+ updatePS.setLong( 2, result );
+ rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
log.error( "could not update hi value in: " + tableName, sqle );
throw sqle;
}
finally {
- ups.close();
+ updatePS.close();
}
} while ( rows == 0 );
16 years, 4 months
Hibernate SVN: r15148 - core/trunk/core/src/main/java/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 10:49:34 -0400 (Fri, 29 Aug 2008)
New Revision: 15148
Modified:
core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
Log:
HHH-3454 : allow segment value default to be the entity table name
Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 14:49:07 UTC (rev 15147)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 14:49:34 UTC (rev 15148)
@@ -128,7 +128,7 @@
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger( TableGenerator.class );
- public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "hibernate.id.enhanced.table.prefer_segment_per_entity";
+ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "prefer_entity_table_as_segment_value";
public static final String TABLE_PARAM = "table_name";
public static final String DEF_TABLE = "hibernate_sequences";
16 years, 4 months
Hibernate SVN: r15147 - core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 10:49:07 -0400 (Fri, 29 Aug 2008)
New Revision: 15147
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
Log:
HHH-3454 : allow segment value default to be the entity table name
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 14:48:38 UTC (rev 15146)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 14:49:07 UTC (rev 15147)
@@ -128,7 +128,7 @@
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger( TableGenerator.class );
- public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "hibernate.id.enhanced.table.prefer_segment_per_entity";
+ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "prefer_entity_table_as_segment_value";
public static final String TABLE_PARAM = "table_name";
public static final String DEF_TABLE = "hibernate_sequences";
16 years, 4 months
Hibernate SVN: r15146 - core/branches/Branch_3_2/src/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 10:48:38 -0400 (Fri, 29 Aug 2008)
New Revision: 15146
Modified:
core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java
Log:
HHH-3454 : allow segment value default to be the entity table name
Modified: core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 06:22:52 UTC (rev 15145)
+++ core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 14:48:38 UTC (rev 15146)
@@ -103,7 +103,7 @@
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
private static final Log log = LogFactory.getLog( TableGenerator.class );
- public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "hibernate.id.enhanced.table.prefer_segment_per_entity";
+ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "prefer_entity_table_as_segment_value";
public static final String TABLE_PARAM = "table_name";
public static final String DEF_TABLE = "hibernate_sequences";
16 years, 4 months
Hibernate SVN: r15145 - core/trunk/core/src/main/java/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 02:22:52 -0400 (Fri, 29 Aug 2008)
New Revision: 15145
Modified:
core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
Log:
HHH-2686 : enhanced.TableGenerator - generator table PK;
HHH-3231 : enhanced.TableGenerator - improper SELECT ... FOR UPDATE OF building;
HHH-3249 : enhanced.TableGenerator - extensibility;
HHH-3454 : enhanced.TableGenerator - allow segment value default to be the entity table name
Modified: core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 06:22:14 UTC (rev 15144)
+++ core/trunk/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 06:22:52 UTC (rev 15145)
@@ -31,6 +31,8 @@
import java.sql.ResultSet;
import java.util.Properties;
import java.util.HashMap;
+import java.util.Collections;
+import java.util.Map;
import java.io.Serializable;
import org.slf4j.Logger;
@@ -49,17 +51,29 @@
import org.hibernate.mapping.Table;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.util.StringHelper;
-import org.hibernate.util.CollectionHelper;
/**
- * An enhanced version of explicit table-based generator. The main basis
- * conceptualization is similiar to the legacy
+ * An enhanced version of table-based id generation.
+ * <p/>
+ * Unlike the simplistic legacy one (which, btw, was only ever intended for subclassing
+ * support) we "segment" the table into multiple values. Thus a single table can
+ * actually serve as the persistent storage for multiple independent generators. One
+ * approach would be to segment the values by the name of the entity for which we are
+ * performing generation, which would mean that we would have a row in the generator
+ * table for each entity name. Or any configuration really; the setup is very flexible.
+ * <p/>
+ * In this respect it is very simliar to the legacy
* {@link org.hibernate.id.MultipleHiLoPerTableGenerator} in terms of the
* underlying storage structure (namely a single table capable of holding
* multiple generator values). The differentiator is, as with
- * {@link SequenceStyleGenerator} as well, the externalization of the notion
+ * {@link SequenceStyleGenerator} as well, the externalized notion
* of an optimizer.
* <p/>
+ * <b>NOTE</b> that by default we use a single row for all genertators (based
+ * on {@link #DEF_SEGMENT_VALUE}). The configuration parameter
+ * {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} can be used to change that to
+ * instead default to using a row for each entity name.
+ * <p/>
* Configuration parameters:
* <table>
* <tr>
@@ -114,6 +128,8 @@
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger( TableGenerator.class );
+ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "hibernate.id.enhanced.table.prefer_segment_per_entity";
+
public static final String TABLE_PARAM = "table_name";
public static final String DEF_TABLE = "hibernate_sequences";
@@ -138,101 +154,281 @@
public static final String OPT_PARAM = "optimizer";
+ private Type identifierType;
+
private String tableName;
- private String valueColumnName;
+
private String segmentColumnName;
private String segmentValue;
private int segmentValueLength;
+
+ private String valueColumnName;
private int initialValue;
private int incrementSize;
- private Type identifierType;
+ private String selectQuery;
+ private String insertQuery;
+ private String updateQuery;
- private String query;
- private String insert;
- private String update;
-
private Optimizer optimizer;
private long accessCount = 0;
- public String getTableName() {
+ /**
+ * {@inheritDoc}
+ */
+ public Object generatorKey() {
return tableName;
}
- public String getSegmentColumnName() {
+ /**
+ * Type mapping for the identifier.
+ *
+ * @return The identifier type mapping.
+ */
+ public final Type getIdentifierType() {
+ return identifierType;
+ }
+
+ /**
+ * The name of the table in which we store this generator's persistent state.
+ *
+ * @return The table name.
+ */
+ public final String getTableName() {
+ return tableName;
+ }
+
+ /**
+ * The name of the column in which we store the segment to which each row
+ * belongs. The value here acts as PK.
+ *
+ * @return The segment column name
+ */
+ public final String getSegmentColumnName() {
return segmentColumnName;
}
- public String getSegmentValue() {
+ /**
+ * The value in {@link #getSegmentColumnName segment column} which
+ * corresponding to this generator instance. In other words this value
+ * indicates the row in which this generator instance will store values.
+ *
+ * @return The segment value for this generator instance.
+ */
+ public final String getSegmentValue() {
return segmentValue;
}
- public int getSegmentValueLength() {
+ /**
+ * The size of the {@link #getSegmentColumnName segment column} in the
+ * underlying table.
+ * <p/>
+ * <b>NOTE</b> : should really have been called 'segmentColumnLength' or
+ * even better 'segmentColumnSize'
+ *
+ * @return the column size.
+ */
+ public final int getSegmentValueLength() {
return segmentValueLength;
}
- public String getValueColumnName() {
+ /**
+ * The name of the column in which we store our persistent generator value.
+ *
+ * @return The name of the value column.
+ */
+ public final String getValueColumnName() {
return valueColumnName;
}
- public Type getIdentifierType() {
- return identifierType;
- }
-
- public int getInitialValue() {
+ /**
+ * The initial value to use when we find no previous state in the
+ * generator table corresponding to our sequence.
+ *
+ * @return The initial value to use.
+ */
+ public final int getInitialValue() {
return initialValue;
}
- public int getIncrementSize() {
+ /**
+ * The amount of increment to use. The exact implications of this
+ * depends on the {@link #getOptimizer() optimizer} being used.
+ *
+ * @return The increment amount.
+ */
+ public final int getIncrementSize() {
return incrementSize;
}
- public Optimizer getOptimizer() {
+ /**
+ * The optimizer being used by this generator.
+ *
+ * @return Out optimizer.
+ */
+ public final Optimizer getOptimizer() {
return optimizer;
}
- public long getTableAccessCount() {
+ /**
+ * Getter for property 'tableAccessCount'. Only really useful for unit test
+ * assertions.
+ *
+ * @return Value for property 'tableAccessCount'.
+ */
+ public final long getTableAccessCount() {
return accessCount;
}
+ /**
+ * {@inheritDoc}
+ */
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
- tableName = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
- if ( tableName.indexOf( '.' ) < 0 ) {
+ identifierType = type;
+
+ tableName = determneGeneratorTableName( params );
+ segmentColumnName = determineSegmentColumnName( params );
+ valueColumnName = determineValueColumnName( params );
+
+ segmentValue = determineSegmentValue( params );
+
+ segmentValueLength = determineSegmentColumnSize( params );
+ initialValue = determineInitialValue( params );
+ incrementSize = determineIncrementSize( params );
+
+ this.selectQuery = buildSelectQuery( dialect );
+ this.updateQuery = buildUpdateQuery();
+ this.insertQuery = buildInsertQuery();
+
+ String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
+ String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ }
+
+ /**
+ * Determine the table name to use for the generator values.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getTableName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The table name to use.
+ */
+ protected String determneGeneratorTableName(Properties params) {
+ String name = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
+ boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0;
+ if ( isGivenNameUnqualified ) {
+ // if the given name is un-qualified we may neen to qualify it
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
- tableName = Table.qualify( catalogName, schemaName, tableName );
+ name = Table.qualify( catalogName, schemaName, name );
}
+ return name;
+ }
- segmentColumnName = PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
- segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
+ /**
+ * Determine the name of the column used to indicate the segment for each
+ * row. This column acts as the primary key.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentColumnName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the segment column
+ */
+ protected String determineSegmentColumnName(Properties params) {
+ return PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
+ }
+
+ /**
+ * Determine the name of the column in which we will store the generator persistent value.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getValueColumnName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the value column
+ */
+ protected String determineValueColumnName(Properties params) {
+ return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ }
+
+ /**
+ * Determine the segment value corresponding to this generator instance.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentValue()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the value column
+ */
+ protected String determineSegmentValue(Properties params) {
+ String segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
if ( StringHelper.isEmpty( segmentValue ) ) {
- log.debug( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + DEF_SEGMENT_VALUE + "]" );
- segmentValue = DEF_SEGMENT_VALUE;
+ segmentValue = determineDefaultSegmentValue( params );
}
- segmentValueLength = PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
- valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
- initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
- incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
- identifierType = type;
+ return segmentValue;
+ }
- String query = "select " + valueColumnName +
- " from " + tableName + " tbl" +
- " where tbl." + segmentColumnName + "=?";
+ /**
+ * Used in the cases where {@link #determineSegmentValue} is unable to
+ * determine the value to use.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The default segment value to use.
+ */
+ protected String determineDefaultSegmentValue(Properties params) {
+ boolean preferSegmentPerEntity = PropertiesHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false );
+ String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE;
+ log.info( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + defaultToUse + "]" );
+ return defaultToUse;
+ }
+
+ /**
+ * Determine the size of the {@link #getSegmentColumnName segment column}
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentValueLength()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The size of the segment column
+ */
+ protected int determineSegmentColumnSize(Properties params) {
+ return PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
+ }
+
+ protected int determineInitialValue(Properties params) {
+ return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ }
+
+ protected int determineIncrementSize(Properties params) {
+ return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ }
+
+ protected String buildSelectQuery(Dialect dialect) {
+ final String alias = "tbl";
+ String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
+ " from " + tableName + ' ' + alias +
+ " where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
HashMap lockMap = new HashMap();
- lockMap.put( "tbl", LockMode.UPGRADE );
- this.query = dialect.applyLocksToSql( query, lockMap, CollectionHelper.EMPTY_MAP );
+ lockMap.put( alias, LockMode.UPGRADE );
+ Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
+ return dialect.applyLocksToSql( query, lockMap, updateTargetColumnsMap );
+ }
- update = "update " + tableName +
+ protected String buildUpdateQuery() {
+ return "update " + tableName +
" set " + valueColumnName + "=? " +
" where " + valueColumnName + "=? and " + segmentColumnName + "=?";
+ }
- insert = "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
-
- String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
- String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
- optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ protected String buildInsertQuery() {
+ return "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
return optimizer.generate(
new AccessCallback() {
@@ -243,23 +439,24 @@
);
}
+ /**
+ * {@inheritDoc}
+ */
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
int result;
int rows;
do {
- sql = query;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement queryPS = conn.prepareStatement( query );
+ SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
+ PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
- queryPS.setString( 1, segmentValue );
- ResultSet queryRS = queryPS.executeQuery();
- if ( !queryRS.next() ) {
+ selectPS.setString( 1, segmentValue );
+ ResultSet selectRS = selectPS.executeQuery();
+ if ( !selectRS.next() ) {
PreparedStatement insertPS = null;
try {
result = initialValue;
- sql = insert;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- insertPS = conn.prepareStatement( insert );
+ SQL_STATEMENT_LOGGER.logStatement( insertQuery, FormatStyle.BASIC );
+ insertPS = conn.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
insertPS.setLong( 2, result );
insertPS.execute();
@@ -271,21 +468,20 @@
}
}
else {
- result = queryRS.getInt( 1 );
+ result = selectRS.getInt( 1 );
}
- queryRS.close();
+ selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read or init a hi value", sqle );
throw sqle;
}
finally {
- queryPS.close();
+ selectPS.close();
}
- sql = update;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement updatePS = conn.prepareStatement( update );
+ SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
+ PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
long newValue = optimizer.applyIncrementSizeToSourceValues()
? result + incrementSize : result + 1;
@@ -295,7 +491,7 @@
rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
- log.error( "could not update hi value in: " + tableName, sqle );
+ log.error( "could not updateQuery hi value in: " + tableName, sqle );
throw sqle;
}
finally {
@@ -309,6 +505,9 @@
return new Integer( result );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
new StringBuffer()
@@ -323,11 +522,16 @@
.append( valueColumnName )
.append( ' ' )
.append( dialect.getTypeName( Types.BIGINT ) )
- .append( " ) " )
+ .append( ", primary key ( " )
+ .append( segmentColumnName )
+ .append( " ) ) " )
.toString()
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@@ -339,8 +543,4 @@
}
return new String[] { sqlDropString.toString() };
}
-
- public Object generatorKey() {
- return tableName;
- }
}
16 years, 4 months
Hibernate SVN: r15144 - core/branches/Branch_3_2/src/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 02:22:14 -0400 (Fri, 29 Aug 2008)
New Revision: 15144
Modified:
core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java
Log:
HHH-2686 : enhanced.TableGenerator - generator table PK;
HHH-3231 : enhanced.TableGenerator - improper SELECT ... FOR UPDATE OF building;
HHH-3249 : enhanced.TableGenerator - extensibility;
HHH-3454 : enhanced.TableGenerator - allow segment value default to be the entity table name
Modified: core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 06:13:12 UTC (rev 15143)
+++ core/branches/Branch_3_2/src/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 06:22:14 UTC (rev 15144)
@@ -7,6 +7,8 @@
import java.sql.ResultSet;
import java.util.Properties;
import java.util.HashMap;
+import java.util.Map;
+import java.util.Collections;
import java.io.Serializable;
import org.apache.commons.logging.Log;
@@ -24,13 +26,29 @@
import org.hibernate.mapping.Table;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.util.StringHelper;
-import org.hibernate.util.CollectionHelper;
/**
- * A "segmented" version of the enhanced table generator. The term "segmented"
- * refers to the fact that this table can hold multiple value generators,
- * segmented by a key.
+ * An enhanced version of table-based id generation.
* <p/>
+ * Unlike the simplistic legacy one (which, btw, was only ever intended for subclassing
+ * support) we "segment" the table into multiple values. Thus a single table can
+ * actually serve as the persistent storage for multiple independent generators. One
+ * approach would be to segment the values by the name of the entity for which we are
+ * performing generation, which would mean that we would have a row in the generator
+ * table for each entity name. Or any configuration really; the setup is very flexible.
+ * <p/>
+ * In this respect it is very simliar to the legacy
+ * {@link org.hibernate.id.MultipleHiLoPerTableGenerator} in terms of the
+ * underlying storage structure (namely a single table capable of holding
+ * multiple generator values). The differentiator is, as with
+ * {@link SequenceStyleGenerator} as well, the externalized notion
+ * of an optimizer.
+ * <p/>
+ * <b>NOTE</b> that by default we use a single row for all genertators (based
+ * on {@link #DEF_SEGMENT_VALUE}). The configuration parameter
+ * {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} can be used to change that to
+ * instead default to using a row for each entity name.
+ * <p/>
* Configuration parameters:
* <table>
* <tr>
@@ -85,6 +103,8 @@
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
private static final Log log = LogFactory.getLog( TableGenerator.class );
+ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "hibernate.id.enhanced.table.prefer_segment_per_entity";
+
public static final String TABLE_PARAM = "table_name";
public static final String DEF_TABLE = "hibernate_sequences";
@@ -109,101 +129,281 @@
public static final String OPT_PARAM = "optimizer";
+ private Type identifierType;
+
private String tableName;
- private String valueColumnName;
+
private String segmentColumnName;
private String segmentValue;
private int segmentValueLength;
+
+ private String valueColumnName;
private int initialValue;
private int incrementSize;
- private Type identifierType;
+ private String selectQuery;
+ private String insertQuery;
+ private String updateQuery;
- private String query;
- private String insert;
- private String update;
-
private Optimizer optimizer;
private long accessCount = 0;
- public String getTableName() {
+ /**
+ * {@inheritDoc}
+ */
+ public Object generatorKey() {
return tableName;
}
- public String getSegmentColumnName() {
+ /**
+ * Type mapping for the identifier.
+ *
+ * @return The identifier type mapping.
+ */
+ public final Type getIdentifierType() {
+ return identifierType;
+ }
+
+ /**
+ * The name of the table in which we store this generator's persistent state.
+ *
+ * @return The table name.
+ */
+ public final String getTableName() {
+ return tableName;
+ }
+
+ /**
+ * The name of the column in which we store the segment to which each row
+ * belongs. The value here acts as PK.
+ *
+ * @return The segment column name
+ */
+ public final String getSegmentColumnName() {
return segmentColumnName;
}
- public String getSegmentValue() {
+ /**
+ * The value in {@link #getSegmentColumnName segment column} which
+ * corresponding to this generator instance. In other words this value
+ * indicates the row in which this generator instance will store values.
+ *
+ * @return The segment value for this generator instance.
+ */
+ public final String getSegmentValue() {
return segmentValue;
}
- public int getSegmentValueLength() {
+ /**
+ * The size of the {@link #getSegmentColumnName segment column} in the
+ * underlying table.
+ * <p/>
+ * <b>NOTE</b> : should really have been called 'segmentColumnLength' or
+ * even better 'segmentColumnSize'
+ *
+ * @return the column size.
+ */
+ public final int getSegmentValueLength() {
return segmentValueLength;
}
- public String getValueColumnName() {
+ /**
+ * The name of the column in which we store our persistent generator value.
+ *
+ * @return The name of the value column.
+ */
+ public final String getValueColumnName() {
return valueColumnName;
}
- public Type getIdentifierType() {
- return identifierType;
- }
-
- public int getInitialValue() {
+ /**
+ * The initial value to use when we find no previous state in the
+ * generator table corresponding to our sequence.
+ *
+ * @return The initial value to use.
+ */
+ public final int getInitialValue() {
return initialValue;
}
- public int getIncrementSize() {
+ /**
+ * The amount of increment to use. The exact implications of this
+ * depends on the {@link #getOptimizer() optimizer} being used.
+ *
+ * @return The increment amount.
+ */
+ public final int getIncrementSize() {
return incrementSize;
}
- public Optimizer getOptimizer() {
+ /**
+ * The optimizer being used by this generator.
+ *
+ * @return Out optimizer.
+ */
+ public final Optimizer getOptimizer() {
return optimizer;
}
- public long getTableAccessCount() {
+ /**
+ * Getter for property 'tableAccessCount'. Only really useful for unit test
+ * assertions.
+ *
+ * @return Value for property 'tableAccessCount'.
+ */
+ public final long getTableAccessCount() {
return accessCount;
}
+ /**
+ * {@inheritDoc}
+ */
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
- tableName = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
- if ( tableName.indexOf( '.' ) < 0 ) {
+ identifierType = type;
+
+ tableName = determneGeneratorTableName( params );
+ segmentColumnName = determineSegmentColumnName( params );
+ valueColumnName = determineValueColumnName( params );
+
+ segmentValue = determineSegmentValue( params );
+
+ segmentValueLength = determineSegmentColumnSize( params );
+ initialValue = determineInitialValue( params );
+ incrementSize = determineIncrementSize( params );
+
+ this.selectQuery = buildSelectQuery( dialect );
+ this.updateQuery = buildUpdateQuery();
+ this.insertQuery = buildInsertQuery();
+
+ String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
+ String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ }
+
+ /**
+ * Determine the table name to use for the generator values.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getTableName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The table name to use.
+ */
+ protected String determneGeneratorTableName(Properties params) {
+ String name = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
+ boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0;
+ if ( isGivenNameUnqualified ) {
+ // if the given name is un-qualified we may neen to qualify it
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
- tableName = Table.qualify( catalogName, schemaName, tableName );
+ name = Table.qualify( catalogName, schemaName, name );
}
+ return name;
+ }
- segmentColumnName = PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
- segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
+ /**
+ * Determine the name of the column used to indicate the segment for each
+ * row. This column acts as the primary key.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentColumnName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the segment column
+ */
+ protected String determineSegmentColumnName(Properties params) {
+ return PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
+ }
+
+ /**
+ * Determine the name of the column in which we will store the generator persistent value.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getValueColumnName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the value column
+ */
+ protected String determineValueColumnName(Properties params) {
+ return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ }
+
+ /**
+ * Determine the segment value corresponding to this generator instance.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentValue()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the value column
+ */
+ protected String determineSegmentValue(Properties params) {
+ String segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
if ( StringHelper.isEmpty( segmentValue ) ) {
- log.debug( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + DEF_SEGMENT_VALUE + "]" );
- segmentValue = DEF_SEGMENT_VALUE;
+ segmentValue = determineDefaultSegmentValue( params );
}
- segmentValueLength = PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
- valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
- initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
- incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
- identifierType = type;
+ return segmentValue;
+ }
- String query = "select " + valueColumnName +
- " from " + tableName + " tbl" +
- " where tbl." + segmentColumnName + "=?";
+ /**
+ * Used in the cases where {@link #determineSegmentValue} is unable to
+ * determine the value to use.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The default segment value to use.
+ */
+ protected String determineDefaultSegmentValue(Properties params) {
+ boolean preferSegmentPerEntity = PropertiesHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false );
+ String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE;
+ log.info( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + defaultToUse + "]" );
+ return defaultToUse;
+ }
+
+ /**
+ * Determine the size of the {@link #getSegmentColumnName segment column}
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentValueLength()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The size of the segment column
+ */
+ protected int determineSegmentColumnSize(Properties params) {
+ return PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
+ }
+
+ protected int determineInitialValue(Properties params) {
+ return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ }
+
+ protected int determineIncrementSize(Properties params) {
+ return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ }
+
+ protected String buildSelectQuery(Dialect dialect) {
+ final String alias = "tbl";
+ String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
+ " from " + tableName + ' ' + alias +
+ " where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
HashMap lockMap = new HashMap();
- lockMap.put( "tbl", LockMode.UPGRADE );
- this.query = dialect.applyLocksToSql( query, lockMap, CollectionHelper.EMPTY_MAP );
+ lockMap.put( alias, LockMode.UPGRADE );
+ Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
+ return dialect.applyLocksToSql( query, lockMap, updateTargetColumnsMap );
+ }
- update = "update " + tableName +
+ protected String buildUpdateQuery() {
+ return "update " + tableName +
" set " + valueColumnName + "=? " +
" where " + valueColumnName + "=? and " + segmentColumnName + "=?";
+ }
- insert = "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
-
- String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
- String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
- optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ protected String buildInsertQuery() {
+ return "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
return optimizer.generate(
new AccessCallback() {
@@ -214,23 +414,24 @@
);
}
+ /**
+ * {@inheritDoc}
+ */
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
int result;
int rows;
do {
- sql = query;
- SQL.debug( sql );
- PreparedStatement queryPS = conn.prepareStatement( query );
+ SQL.debug( selectQuery );
+ PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
- queryPS.setString( 1, segmentValue );
- ResultSet queryRS = queryPS.executeQuery();
- if ( !queryRS.next() ) {
+ selectPS.setString( 1, segmentValue );
+ ResultSet selectRS = selectPS.executeQuery();
+ if ( !selectRS.next() ) {
PreparedStatement insertPS = null;
try {
result = initialValue;
- sql = insert;
- SQL.debug( sql );
- insertPS = conn.prepareStatement( insert );
+ SQL.debug( insertQuery );
+ insertPS = conn.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
insertPS.setLong( 2, result );
insertPS.execute();
@@ -242,21 +443,20 @@
}
}
else {
- result = queryRS.getInt( 1 );
+ result = selectRS.getInt( 1 );
}
- queryRS.close();
+ selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read or init a hi value", sqle );
throw sqle;
}
finally {
- queryPS.close();
+ selectPS.close();
}
- sql = update;
- SQL.debug( sql );
- PreparedStatement updatePS = conn.prepareStatement( update );
+ SQL.debug( updateQuery );
+ PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
long newValue = optimizer.applyIncrementSizeToSourceValues()
? result + incrementSize : result + 1;
@@ -280,6 +480,9 @@
return new Integer( result );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
new StringBuffer()
@@ -294,11 +497,16 @@
.append( valueColumnName )
.append( ' ' )
.append( dialect.getTypeName( Types.BIGINT ) )
- .append( " ) " )
+ .append( ", primary key ( " )
+ .append( segmentColumnName )
+ .append( " ) ) " )
.toString()
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@@ -310,8 +518,4 @@
}
return new String[] { sqlDropString.toString() };
}
-
- public Object generatorKey() {
- return tableName;
- }
}
16 years, 4 months
Hibernate SVN: r15143 - core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced.
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2008-08-29 02:13:12 -0400 (Fri, 29 Aug 2008)
New Revision: 15143
Modified:
core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
Log:
HHH-2686 : enhanced.TableGenerator - generator table PK;
HHH-3231 : enhanced.TableGenerator - improper SELECT ... FOR UPDATE OF building;
HHH-3249 : enhanced.TableGenerator - extensibility;
HHH-3454 : enhanced.TableGenerator - allow segment value default to be the entity table name
Modified: core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java
===================================================================
--- core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-28 11:36:34 UTC (rev 15142)
+++ core/branches/Branch_3_3/core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java 2008-08-29 06:13:12 UTC (rev 15143)
@@ -31,6 +31,8 @@
import java.sql.ResultSet;
import java.util.Properties;
import java.util.HashMap;
+import java.util.Collections;
+import java.util.Map;
import java.io.Serializable;
import org.slf4j.Logger;
@@ -49,17 +51,29 @@
import org.hibernate.mapping.Table;
import org.hibernate.util.PropertiesHelper;
import org.hibernate.util.StringHelper;
-import org.hibernate.util.CollectionHelper;
/**
- * An enhanced version of explicit table-based generator. The main basis
- * conceptualization is similiar to the legacy
+ * An enhanced version of table-based id generation.
+ * <p/>
+ * Unlike the simplistic legacy one (which, btw, was only ever intended for subclassing
+ * support) we "segment" the table into multiple values. Thus a single table can
+ * actually serve as the persistent storage for multiple independent generators. One
+ * approach would be to segment the values by the name of the entity for which we are
+ * performing generation, which would mean that we would have a row in the generator
+ * table for each entity name. Or any configuration really; the setup is very flexible.
+ * <p/>
+ * In this respect it is very simliar to the legacy
* {@link org.hibernate.id.MultipleHiLoPerTableGenerator} in terms of the
* underlying storage structure (namely a single table capable of holding
* multiple generator values). The differentiator is, as with
- * {@link SequenceStyleGenerator} as well, the externalization of the notion
+ * {@link SequenceStyleGenerator} as well, the externalized notion
* of an optimizer.
* <p/>
+ * <b>NOTE</b> that by default we use a single row for all genertators (based
+ * on {@link #DEF_SEGMENT_VALUE}). The configuration parameter
+ * {@link #CONFIG_PREFER_SEGMENT_PER_ENTITY} can be used to change that to
+ * instead default to using a row for each entity name.
+ * <p/>
* Configuration parameters:
* <table>
* <tr>
@@ -114,6 +128,8 @@
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger( TableGenerator.class );
+ public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "hibernate.id.enhanced.table.prefer_segment_per_entity";
+
public static final String TABLE_PARAM = "table_name";
public static final String DEF_TABLE = "hibernate_sequences";
@@ -138,101 +154,274 @@
public static final String OPT_PARAM = "optimizer";
+ private Type identifierType;
+
private String tableName;
- private String valueColumnName;
+
private String segmentColumnName;
private String segmentValue;
private int segmentValueLength;
+
+ private String valueColumnName;
private int initialValue;
private int incrementSize;
- private Type identifierType;
+ private String selectQuery;
+ private String insertQuery;
+ private String updateQuery;
- private String query;
- private String insert;
- private String update;
-
private Optimizer optimizer;
private long accessCount = 0;
- public String getTableName() {
+ /**
+ * Type mapping for the identifier.
+ *
+ * @return The identifier type mapping.
+ */
+ public final Type getIdentifierType() {
+ return identifierType;
+ }
+
+ /**
+ * The name of the table in which we store this generator's persistent state.
+ *
+ * @return The table name.
+ */
+ public final String getTableName() {
return tableName;
}
- public String getSegmentColumnName() {
+ /**
+ * The name of the column in which we store the segment to which each row
+ * belongs. The value here acts as PK.
+ *
+ * @return The segment column name
+ */
+ public final String getSegmentColumnName() {
return segmentColumnName;
}
- public String getSegmentValue() {
+ /**
+ * The value in {@link #getSegmentColumnName segment column} which
+ * corresponding to this generator instance. In other words this value
+ * indicates the row in which this generator instance will store values.
+ *
+ * @return The segment value for this generator instance.
+ */
+ public final String getSegmentValue() {
return segmentValue;
}
- public int getSegmentValueLength() {
+ /**
+ * The size of the {@link #getSegmentColumnName segment column} in the
+ * underlying table.
+ * <p/>
+ * <b>NOTE</b> : should really have been called 'segmentColumnLength' or
+ * even better 'segmentColumnSize'
+ *
+ * @return the column size.
+ */
+ public final int getSegmentValueLength() {
return segmentValueLength;
}
- public String getValueColumnName() {
+ /**
+ * The name of the column in which we store our persistent generator value.
+ *
+ * @return The name of the value column.
+ */
+ public final String getValueColumnName() {
return valueColumnName;
}
- public Type getIdentifierType() {
- return identifierType;
- }
-
- public int getInitialValue() {
+ /**
+ * The initial value to use when we find no previous state in the
+ * generator table corresponding to our sequence.
+ *
+ * @return The initial value to use.
+ */
+ public final int getInitialValue() {
return initialValue;
}
- public int getIncrementSize() {
+ /**
+ * The amount of increment to use. The exact implications of this
+ * depends on the {@link #getOptimizer() optimizer} being used.
+ *
+ * @return The increment amount.
+ */
+ public final int getIncrementSize() {
return incrementSize;
}
- public Optimizer getOptimizer() {
+ /**
+ * The optimizer being used by this generator.
+ *
+ * @return Out optimizer.
+ */
+ public final Optimizer getOptimizer() {
return optimizer;
}
- public long getTableAccessCount() {
+ /**
+ * Getter for property 'tableAccessCount'. Only really useful for unit test
+ * assertions.
+ *
+ * @return Value for property 'tableAccessCount'.
+ */
+ public final long getTableAccessCount() {
return accessCount;
}
+ /**
+ * {@inheritDoc}
+ */
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
- tableName = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
- if ( tableName.indexOf( '.' ) < 0 ) {
+ identifierType = type;
+
+ tableName = determneGeneratorTableName( params );
+ segmentColumnName = determineSegmentColumnName( params );
+ valueColumnName = determineValueColumnName( params );
+
+ segmentValue = determineSegmentValue( params );
+
+ segmentValueLength = determineSegmentColumnSize( params );
+ initialValue = determineInitialValue( params );
+ incrementSize = determineIncrementSize( params );
+
+ this.selectQuery = buildSelectQuery( dialect );
+ this.updateQuery = buildUpdateQuery();
+ this.insertQuery = buildInsertQuery();
+
+ String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
+ String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
+ optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ }
+
+ /**
+ * Determine the table name to use for the generator values.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getTableName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The table name to use.
+ */
+ protected String determneGeneratorTableName(Properties params) {
+ String name = PropertiesHelper.getString( TABLE_PARAM, params, DEF_TABLE );
+ boolean isGivenNameUnqualified = name.indexOf( '.' ) < 0;
+ if ( isGivenNameUnqualified ) {
+ // if the given name is un-qualified we may neen to qualify it
String schemaName = params.getProperty( SCHEMA );
String catalogName = params.getProperty( CATALOG );
- tableName = Table.qualify( catalogName, schemaName, tableName );
+ name = Table.qualify( catalogName, schemaName, name );
}
+ return name;
+ }
- segmentColumnName = PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
- segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
+ /**
+ * Determine the name of the column used to indicate the segment for each
+ * row. This column acts as the primary key.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentColumnName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the segment column
+ */
+ protected String determineSegmentColumnName(Properties params) {
+ return PropertiesHelper.getString( SEGMENT_COLUMN_PARAM, params, DEF_SEGMENT_COLUMN );
+ }
+
+ /**
+ * Determine the name of the column in which we will store the generator persistent value.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getValueColumnName()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the value column
+ */
+ protected String determineValueColumnName(Properties params) {
+ return PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
+ }
+
+ /**
+ * Determine the segment value corresponding to this generator instance.
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentValue()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The name of the value column
+ */
+ protected String determineSegmentValue(Properties params) {
+ String segmentValue = params.getProperty( SEGMENT_VALUE_PARAM );
if ( StringHelper.isEmpty( segmentValue ) ) {
- log.debug( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + DEF_SEGMENT_VALUE + "]" );
- segmentValue = DEF_SEGMENT_VALUE;
+ segmentValue = determineDefaultSegmentValue( params );
}
- segmentValueLength = PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
- valueColumnName = PropertiesHelper.getString( VALUE_COLUMN_PARAM, params, DEF_VALUE_COLUMN );
- initialValue = PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
- incrementSize = PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
- identifierType = type;
+ return segmentValue;
+ }
- String query = "select " + valueColumnName +
- " from " + tableName + " tbl" +
- " where tbl." + segmentColumnName + "=?";
+ /**
+ * Used in the cases where {@link #determineSegmentValue} is unable to
+ * determine the value to use.
+ *
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The default segment value to use.
+ */
+ protected String determineDefaultSegmentValue(Properties params) {
+ boolean preferSegmentPerEntity = PropertiesHelper.getBoolean( CONFIG_PREFER_SEGMENT_PER_ENTITY, params, false );
+ String defaultToUse = preferSegmentPerEntity ? params.getProperty( TABLE ) : DEF_SEGMENT_VALUE;
+ log.info( "explicit segment value for id generator [" + tableName + '.' + segmentColumnName + "] suggested; using default [" + defaultToUse + "]" );
+ return defaultToUse;
+ }
+
+ /**
+ * Determine the size of the {@link #getSegmentColumnName segment column}
+ * <p/>
+ * Called during {@link #configure configuration}.
+ *
+ * @see #getSegmentValueLength()
+ * @param params The params supplied in the generator config (plus some standard useful extras).
+ * @return The size of the segment column
+ */
+ protected int determineSegmentColumnSize(Properties params) {
+ return PropertiesHelper.getInt( SEGMENT_LENGTH_PARAM, params, DEF_SEGMENT_LENGTH );
+ }
+
+ protected int determineInitialValue(Properties params) {
+ return PropertiesHelper.getInt( INITIAL_PARAM, params, DEFAULT_INITIAL_VALUE );
+ }
+
+ protected int determineIncrementSize(Properties params) {
+ return PropertiesHelper.getInt( INCREMENT_PARAM, params, DEFAULT_INCREMENT_SIZE );
+ }
+
+ protected String buildSelectQuery(Dialect dialect) {
+ final String alias = "tbl";
+ String query = "select " + StringHelper.qualify( alias, valueColumnName ) +
+ " from " + tableName + ' ' + alias +
+ " where " + StringHelper.qualify( alias, segmentColumnName ) + "=?";
HashMap lockMap = new HashMap();
- lockMap.put( "tbl", LockMode.UPGRADE );
- this.query = dialect.applyLocksToSql( query, lockMap, CollectionHelper.EMPTY_MAP );
+ lockMap.put( alias, LockMode.UPGRADE );
+ Map updateTargetColumnsMap = Collections.singletonMap( alias, new String[] { valueColumnName } );
+ return dialect.applyLocksToSql( query, lockMap, updateTargetColumnsMap );
+ }
- update = "update " + tableName +
+ protected String buildUpdateQuery() {
+ return "update " + tableName +
" set " + valueColumnName + "=? " +
" where " + valueColumnName + "=? and " + segmentColumnName + "=?";
+ }
- insert = "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
-
- String defOptStrategy = incrementSize <= 1 ? OptimizerFactory.NONE : OptimizerFactory.POOL;
- String optimizationStrategy = PropertiesHelper.getString( OPT_PARAM, params, defOptStrategy );
- optimizer = OptimizerFactory.buildOptimizer( optimizationStrategy, identifierType.getReturnedClass(), incrementSize );
+ protected String buildInsertQuery() {
+ return "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
}
+ /**
+ * {@inheritDoc}
+ */
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
return optimizer.generate(
new AccessCallback() {
@@ -243,23 +432,24 @@
);
}
+ /**
+ * {@inheritDoc}
+ */
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
int result;
int rows;
do {
- sql = query;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement queryPS = conn.prepareStatement( query );
+ SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
+ PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
- queryPS.setString( 1, segmentValue );
- ResultSet queryRS = queryPS.executeQuery();
- if ( !queryRS.next() ) {
+ selectPS.setString( 1, segmentValue );
+ ResultSet selectRS = selectPS.executeQuery();
+ if ( !selectRS.next() ) {
PreparedStatement insertPS = null;
try {
result = initialValue;
- sql = insert;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- insertPS = conn.prepareStatement( insert );
+ SQL_STATEMENT_LOGGER.logStatement( insertQuery, FormatStyle.BASIC );
+ insertPS = conn.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
insertPS.setLong( 2, result );
insertPS.execute();
@@ -271,21 +461,20 @@
}
}
else {
- result = queryRS.getInt( 1 );
+ result = selectRS.getInt( 1 );
}
- queryRS.close();
+ selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read or init a hi value", sqle );
throw sqle;
}
finally {
- queryPS.close();
+ selectPS.close();
}
- sql = update;
- SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
- PreparedStatement updatePS = conn.prepareStatement( update );
+ SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
+ PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
long newValue = optimizer.applyIncrementSizeToSourceValues()
? result + incrementSize : result + 1;
@@ -295,7 +484,7 @@
rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
- log.error( "could not update hi value in: " + tableName, sqle );
+ log.error( "could not updateQuery hi value in: " + tableName, sqle );
throw sqle;
}
finally {
@@ -309,6 +498,9 @@
return new Integer( result );
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
new StringBuffer()
@@ -323,11 +515,16 @@
.append( valueColumnName )
.append( ' ' )
.append( dialect.getTypeName( Types.BIGINT ) )
- .append( " ) " )
+ .append( ", primary key ( " )
+ .append( segmentColumnName )
+ .append( " ) ) " )
.toString()
};
}
+ /**
+ * {@inheritDoc}
+ */
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@@ -340,6 +537,9 @@
return new String[] { sqlDropString.toString() };
}
+ /**
+ * {@inheritDoc}
+ */
public Object generatorKey() {
return tableName;
}
16 years, 4 months
Hibernate SVN: r15142 - in search/trunk/src: test/org/hibernate/search/test/configuration and 1 other directory.
by hibernate-commits@lists.jboss.org
Author: sannegrinovero
Date: 2008-08-28 07:36:34 -0400 (Thu, 28 Aug 2008)
New Revision: 15142
Added:
search/trunk/src/test/org/hibernate/search/test/configuration/EventListenerRegisterTest.java
Modified:
search/trunk/src/java/org/hibernate/search/event/EventListenerRegister.java
search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java
Log:
HSEARCH-253 : Inconsistent detection of EventListeners during autoregistration
Modified: search/trunk/src/java/org/hibernate/search/event/EventListenerRegister.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/event/EventListenerRegister.java 2008-08-28 07:05:45 UTC (rev 15141)
+++ search/trunk/src/java/org/hibernate/search/event/EventListenerRegister.java 2008-08-28 11:36:34 UTC (rev 15142)
@@ -3,6 +3,7 @@
import java.util.Properties;
+import org.hibernate.search.Environment;
import org.hibernate.event.EventListeners;
import org.hibernate.event.PostCollectionRecreateEventListener;
import org.hibernate.event.PostCollectionRemoveEventListener;
@@ -10,7 +11,7 @@
import org.hibernate.event.PostDeleteEventListener;
import org.hibernate.event.PostInsertEventListener;
import org.hibernate.event.PostUpdateEventListener;
-import org.slf4j.Logger;
+
import org.slf4j.LoggerFactory;
/**
@@ -18,163 +19,126 @@
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
+ * @author Sanne Grinovero
*/
public class EventListenerRegister {
- private static final Logger log = LoggerFactory.getLogger(EventListenerRegister.class);
-
- @SuppressWarnings("unchecked")
- public static void enableHibernateSearch(EventListeners eventListeners, Properties properties) {
- // check whether search is explicitly enabled - if so there is nothing to do
- String enableSearchListeners = properties.getProperty( "hibernate.search.autoregister_listeners" );
- if("false".equalsIgnoreCase(enableSearchListeners )) {
- log.info("Property hibernate.search.autoregister_listeners is set to false." +
- " No attempt will be made to register Hibernate Search event listeners.");
+ /**
+ * Add the FullTextIndexEventListener to all listeners, if enabled in configuration
+ * and if not already registered.
+ * @param listeners
+ * @param properties the Search configuration
+ */
+ public static void enableHibernateSearch(EventListeners listeners, Properties properties) {
+ // check whether search is explicitly disabled - if so there is nothing to do
+ String enableSearchListeners = properties.getProperty( Environment.AUTOREGISTER_LISTENERS );
+ if ( "false".equalsIgnoreCase( enableSearchListeners ) ) {
+ LoggerFactory.getLogger( EventListenerRegister.class ).info(
+ "Property hibernate.search.autoregister_listeners is set to false." +
+ " No attempt will be made to register Hibernate Search event listeners." );
return;
}
-
- FullTextIndexEventListener searchEventListener = new FullTextIndexEventListener();
+ final FullTextIndexEventListener searchListener = new FullTextIndexEventListener();
+ // PostInsertEventListener
+ listeners.setPostInsertEventListeners(
+ addIfNeeded(
+ listeners.getPostInsertEventListeners(),
+ searchListener,
+ new PostInsertEventListener[] { searchListener } )
+ );
+ // PostUpdateEventListener
+ listeners.setPostUpdateEventListeners(
+ addIfNeeded(
+ listeners.getPostUpdateEventListeners(),
+ searchListener,
+ new PostUpdateEventListener[] { searchListener } )
+ );
+ // PostDeleteEventListener
+ listeners.setPostDeleteEventListeners(
+ addIfNeeded(
+ listeners.getPostDeleteEventListeners(),
+ searchListener,
+ new PostDeleteEventListener[] { searchListener } )
+ );
- //TODO Generalize this. Pretty much the same code all the time. Reflection?
- final Class<? extends FullTextIndexEventListener> searchEventListenerClass = searchEventListener.getClass();
- {
- boolean present = false;
- PostInsertEventListener[] listeners = eventListeners
- .getPostInsertEventListeners();
- if (listeners != null) {
- for (Object eventListener : listeners) {
- // not isAssignableFrom since the user could subclass
- present = present
- || searchEventListenerClass == eventListener.getClass()
- || searchEventListenerClass == eventListener.getClass().getSuperclass(); //for FullTextIndexCollectionEventListener
- }
- if (!present) {
- int length = listeners.length + 1;
- PostInsertEventListener[] newListeners = new PostInsertEventListener[length];
- System.arraycopy(listeners, 0, newListeners, 0, length - 1);
- newListeners[length - 1] = searchEventListener;
- eventListeners.setPostInsertEventListeners(newListeners);
- }
- } else {
- eventListeners
- .setPostInsertEventListeners(new PostInsertEventListener[] { searchEventListener });
- }
+ // PostCollectionRecreateEventListener
+ listeners.setPostCollectionRecreateEventListeners(
+ addIfNeeded(
+ listeners.getPostCollectionRecreateEventListeners(),
+ searchListener,
+ new PostCollectionRecreateEventListener[] { searchListener } )
+ );
+ // PostCollectionRemoveEventListener
+ listeners.setPostCollectionRemoveEventListeners(
+ addIfNeeded(
+ listeners.getPostCollectionRemoveEventListeners(),
+ searchListener,
+ new PostCollectionRemoveEventListener[] { searchListener } )
+ );
+ // PostCollectionUpdateEventListener
+ listeners.setPostCollectionUpdateEventListeners(
+ addIfNeeded(
+ listeners.getPostCollectionUpdateEventListeners(),
+ searchListener,
+ new PostCollectionUpdateEventListener[] { searchListener } )
+ );
+
+ }
+
+ /**
+ * Verifies if a Search listener is already present; if not it will return
+ * a grown address adding the listener to it.
+ * @param <T> the type of listeners
+ * @param listeners
+ * @param searchEventListener
+ * @param toUseOnNull this is returned if listeners==null
+ * @return
+ */
+ private static <T> T[] addIfNeeded(T[] listeners, T searchEventListener, T[] toUseOnNull) {
+ if ( listeners == null ) {
+ return toUseOnNull;
}
- {
- boolean present = false;
- PostUpdateEventListener[] listeners = eventListeners
- .getPostUpdateEventListeners();
- if (listeners != null) {
- for (Object eventListener : listeners) {
- // not isAssignableFrom since the user could subclass
- present = present
- || searchEventListenerClass == eventListener.getClass()
- || searchEventListenerClass == eventListener.getClass().getSuperclass(); //for FullTextIndexCollectionEventListener
- }
- if (!present) {
- int length = listeners.length + 1;
- PostUpdateEventListener[] newListeners = new PostUpdateEventListener[length];
- System.arraycopy(listeners, 0, newListeners, 0, length - 1);
- newListeners[length - 1] = searchEventListener;
- eventListeners.setPostUpdateEventListeners(newListeners);
- }
- } else {
- eventListeners
- .setPostUpdateEventListeners(new PostUpdateEventListener[] { searchEventListener });
- }
+ else if ( ! isPresentInListeners( listeners ) ) {
+ return appendToArray( listeners, searchEventListener );
}
- {
- boolean present = false;
- PostDeleteEventListener[] listeners = eventListeners
- .getPostDeleteEventListeners();
- if (listeners != null) {
- for (Object eventListener : listeners) {
- // not isAssignableFrom since the user could subclass
- present = present
- || searchEventListenerClass == eventListener.getClass()
- || searchEventListenerClass == eventListener.getClass().getSuperclass(); //for FullTextIndexCollectionEventListener
- }
- if (!present) {
- int length = listeners.length + 1;
- PostDeleteEventListener[] newListeners = new PostDeleteEventListener[length];
- System.arraycopy(listeners, 0, newListeners, 0, length - 1);
- newListeners[length - 1] = searchEventListener;
- eventListeners.setPostDeleteEventListeners(newListeners);
- }
- } else {
- eventListeners
- .setPostDeleteEventListeners(new PostDeleteEventListener[] { searchEventListener });
- }
- }
- {
- boolean present = false;
- PostCollectionRecreateEventListener[] listeners = eventListeners.getPostCollectionRecreateEventListeners();
- if ( listeners != null ) {
- for (Object eventListener : listeners) {
- //not isAssignableFrom since the user could subclass
- present = present
- || searchEventListenerClass == eventListener.getClass()
- || searchEventListenerClass == eventListener.getClass().getSuperclass(); //for FullTextIndexCollectionEventListener
- }
- if ( !present ) {
- int length = listeners.length + 1;
- PostCollectionRecreateEventListener[] newListeners = new PostCollectionRecreateEventListener[length];
- System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
- newListeners[length - 1] = searchEventListener;
- eventListeners.setPostCollectionRecreateEventListeners( newListeners );
- }
- }
- else {
- eventListeners.setPostCollectionRecreateEventListeners(
- new PostCollectionRecreateEventListener[] { searchEventListener }
- );
- }
+ else {
+ return listeners;
}
- {
- boolean present = false;
- PostCollectionRemoveEventListener[] listeners = eventListeners.getPostCollectionRemoveEventListeners();
- if ( listeners != null ) {
- for (Object eventListener : listeners) {
- //not isAssignableFrom since the user could subclass
- present = present
- || searchEventListenerClass == eventListener.getClass()
- || searchEventListenerClass == eventListener.getClass().getSuperclass(); //for FullTextIndexCollectionEventListener
- }
- if ( !present ) {
- int length = listeners.length + 1;
- PostCollectionRemoveEventListener[] newListeners = new PostCollectionRemoveEventListener[length];
- System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
- newListeners[length - 1] = searchEventListener;
- eventListeners.setPostCollectionRemoveEventListeners( newListeners );
- }
+ }
+
+ /**
+ * Will add one element to the end of an array.
+ * @param <T> The array type
+ * @param listeners The original array
+ * @param newElement The element to be added
+ * @return A new array containing all listeners and newElement.
+ */
+ @SuppressWarnings("unchecked")
+ private static <T> T[] appendToArray(T[] listeners, T newElement) {
+ int length = listeners.length;
+ T[] ret = (T[])java.lang.reflect.Array.newInstance(
+ listeners.getClass().getComponentType(), length + 1 );
+ System.arraycopy( listeners, 0, ret, 0, length );
+ ret[length] = newElement;
+ return ret;
+ }
+
+ /**
+ * Verifies if a FullTextIndexEventListener is contained in the array.
+ * @param listeners
+ * @return true if it is contained in.
+ */
+ @SuppressWarnings("deprecation")
+ private static boolean isPresentInListeners(Object[] listeners) {
+ for (Object eventListener : listeners) {
+ if ( FullTextIndexEventListener.class == eventListener.getClass() ) {
+ return true;
}
- else {
- eventListeners.setPostCollectionRemoveEventListeners(
- new PostCollectionRemoveEventListener[] { searchEventListener }
- );
+ if ( FullTextIndexCollectionEventListener.class == eventListener.getClass() ) {
+ return true;
}
}
- {
- boolean present = false;
- PostCollectionUpdateEventListener[] listeners = eventListeners.getPostCollectionUpdateEventListeners();
- if ( listeners != null ) {
- for (Object eventListener : listeners) {
- //not isAssignableFrom since the user could subclass
- present = present || searchEventListenerClass == eventListener.getClass();
- }
- if ( !present ) {
- int length = listeners.length + 1;
- PostCollectionUpdateEventListener[] newListeners = new PostCollectionUpdateEventListener[length];
- System.arraycopy( listeners, 0, newListeners, 0, length - 1 );
- newListeners[length - 1] = searchEventListener;
- eventListeners.setPostCollectionUpdateEventListeners( newListeners );
- }
- }
- else {
- eventListeners.setPostCollectionUpdateEventListeners(
- new PostCollectionUpdateEventListener[] { searchEventListener }
- );
- }
- }
+ return false;
}
+
}
Modified: search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java 2008-08-28 07:05:45 UTC (rev 15141)
+++ search/trunk/src/java/org/hibernate/search/event/FullTextIndexEventListener.java 2008-08-28 11:36:34 UTC (rev 15142)
@@ -39,6 +39,7 @@
*/
//TODO work on sharing the same indexWriters and readers across a single post operation...
//TODO implement and use a LockableDirectoryProvider that wraps a DP to handle the lock inside the LDP
+//TODO make this class final as soon as FullTextIndexCollectionEventListener is removed.
@SuppressWarnings( "serial" )
public class FullTextIndexEventListener implements PostDeleteEventListener,
PostInsertEventListener, PostUpdateEventListener,
Added: search/trunk/src/test/org/hibernate/search/test/configuration/EventListenerRegisterTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/configuration/EventListenerRegisterTest.java (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/configuration/EventListenerRegisterTest.java 2008-08-28 11:36:34 UTC (rev 15142)
@@ -0,0 +1,176 @@
+// $Id$
+package org.hibernate.search.test.configuration;
+
+import java.util.Properties;
+
+import org.hibernate.event.EventListeners;
+import org.hibernate.event.PostCollectionRecreateEvent;
+import org.hibernate.event.PostCollectionRecreateEventListener;
+import org.hibernate.event.PostCollectionRemoveEvent;
+import org.hibernate.event.PostCollectionRemoveEventListener;
+import org.hibernate.event.PostCollectionUpdateEvent;
+import org.hibernate.event.PostCollectionUpdateEventListener;
+import org.hibernate.event.PostDeleteEvent;
+import org.hibernate.event.PostDeleteEventListener;
+import org.hibernate.event.PostInsertEvent;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.event.PostUpdateEvent;
+import org.hibernate.event.PostUpdateEventListener;
+import org.hibernate.search.Environment;
+import org.hibernate.search.event.EventListenerRegister;
+import org.hibernate.search.event.FullTextIndexEventListener;
+import org.hibernate.search.event.FullTextIndexCollectionEventListener;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Sanne Grinovero
+ */
+@SuppressWarnings("deprecation")
+public class EventListenerRegisterTest extends TestCase {
+
+ public void testRegisterOnEmptyListeners_CfgDisabled() {
+ EventListeners evListeners = new EventListeners();
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( false ) );
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( false ) );
+ assertPresence( false, evListeners );
+ }
+
+ public void testRegisterOnEmptyListeners_CfgEnabled() {
+ EventListeners evListeners = new EventListeners();
+ //tests registering multiple times is idempotent:
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( true ) );
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( true ) );
+ assertPresence( true, evListeners );
+ }
+
+ public void testRegisterOnEmptyListeners_CfgAuto() {
+ EventListeners evListeners = new EventListeners();
+ EventListenerRegister.enableHibernateSearch( evListeners, new Properties() );
+ EventListenerRegister.enableHibernateSearch( evListeners, new Properties() );
+ assertPresence( true, evListeners );
+ }
+
+ public void testOnAlreadyRegistered() {
+ helperOnAlreadyRegistered( new FullTextIndexEventListener() );
+ }
+
+ public void testOnAlreadyRegisteredDeprecated() {
+ helperOnAlreadyRegistered( new FullTextIndexCollectionEventListener() );
+ }
+
+ public void testOnPopulatedEventListeners() {
+ EventListeners evListeners = makeSomeEventListeners();
+ EventListenerRegister.enableHibernateSearch( evListeners, new Properties() );
+ EventListenerRegister.enableHibernateSearch( evListeners, new Properties() );
+ assertPresence( true, evListeners );
+ }
+
+ private void helperOnAlreadyRegistered(FullTextIndexEventListener listenerFullText) {
+
+ AnotherListener listenerA = new AnotherListener();
+ AnotherListener listenerB = new AnotherListener();
+
+ EventListeners evListeners = new EventListeners();
+ evListeners.setPostInsertEventListeners(
+ new PostInsertEventListener[]{ listenerA, listenerB, listenerFullText } );
+ evListeners.setPostUpdateEventListeners(
+ new PostUpdateEventListener[]{ listenerA, listenerB, listenerFullText } );
+ evListeners.setPostDeleteEventListeners(
+ new PostDeleteEventListener[]{ listenerA, listenerB, listenerFullText } );
+ evListeners.setPostCollectionRecreateEventListeners(
+ new PostCollectionRecreateEventListener[]{ listenerA, listenerB, listenerFullText } );
+ evListeners.setPostCollectionRemoveEventListeners(
+ new PostCollectionRemoveEventListener[]{ listenerA, listenerB, listenerFullText } );
+ evListeners.setPostCollectionUpdateEventListeners(
+ new PostCollectionUpdateEventListener[]{ listenerA, listenerB, listenerFullText } );
+
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( false ) );
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( false ) );
+ EventListenerRegister.enableHibernateSearch( evListeners, makeConfiguration( false ) );
+ assertPresence( true, evListeners );
+ }
+
+ private EventListeners makeSomeEventListeners() {
+
+ AnotherListener listenerA = new AnotherListener();
+ AnotherListener listenerB = new AnotherListener();
+ AnotherListener listenerC = new AnotherListener();
+
+ EventListeners evListeners = new EventListeners();
+ evListeners.setPostInsertEventListeners(
+ new PostInsertEventListener[]{ listenerA, listenerB, listenerC } );
+ evListeners.setPostUpdateEventListeners(
+ new PostUpdateEventListener[]{ listenerA, listenerB, listenerC } );
+ evListeners.setPostDeleteEventListeners(
+ new PostDeleteEventListener[]{ listenerA, listenerB, listenerC } );
+ evListeners.setPostCollectionRecreateEventListeners(
+ new PostCollectionRecreateEventListener[]{ listenerA, listenerB, listenerC } );
+ evListeners.setPostCollectionRemoveEventListeners(
+ new PostCollectionRemoveEventListener[]{ listenerA, listenerB, listenerC } );
+ evListeners.setPostCollectionUpdateEventListeners(
+ new PostCollectionUpdateEventListener[]{ listenerA, listenerB, listenerC } );
+
+ return evListeners;
+ }
+
+ private void assertPresence(boolean expected, EventListeners evListeners) {
+ assertEquals( expected, isPresent( evListeners.getPostInsertEventListeners() ) );
+ assertEquals( expected, isPresent( evListeners.getPostUpdateEventListeners() ) );
+ assertEquals( expected, isPresent( evListeners.getPostDeleteEventListeners() ) );
+ assertEquals( expected, isPresent( evListeners.getPostCollectionRecreateEventListeners() ) );
+ assertEquals( expected, isPresent( evListeners.getPostCollectionRemoveEventListeners() ) );
+ assertEquals( expected, isPresent( evListeners.getPostCollectionUpdateEventListeners() ) );
+ }
+
+ private static Properties makeConfiguration(boolean enableSearch) {
+ Properties p = new Properties();
+ p.setProperty( Environment.AUTOREGISTER_LISTENERS, String.valueOf( enableSearch ) );
+ return p;
+ }
+
+ private static boolean isPresent(Object[] listeners) {
+ if (listeners==null)
+ return false;
+ boolean found = false; // to verify class present at most once.
+ for (Object eventListener : listeners) {
+ if ( FullTextIndexEventListener.class == eventListener.getClass() ) {
+ assertFalse( found );
+ found = true;
+ }
+ if ( FullTextIndexCollectionEventListener.class == eventListener.getClass() ) {
+ assertFalse( found );
+ found = true;
+ }
+ }
+ return found;
+ }
+
+ private static class AnotherListener implements PostDeleteEventListener,
+ PostInsertEventListener, PostUpdateEventListener,
+ PostCollectionRecreateEventListener, PostCollectionRemoveEventListener,
+ PostCollectionUpdateEventListener {
+
+ //empty methods: just needing any implementation of these listeners.
+
+ public void onPostDelete(PostDeleteEvent event) {
+ }
+
+ public void onPostInsert(PostInsertEvent event) {
+ }
+
+ public void onPostUpdate(PostUpdateEvent event) {
+ }
+
+ public void onPostRecreateCollection(PostCollectionRecreateEvent event) {
+ }
+
+ public void onPostRemoveCollection(PostCollectionRemoveEvent event) {
+ }
+
+ public void onPostUpdateCollection(PostCollectionUpdateEvent event) {
+ }
+
+ }
+
+}
Property changes on: search/trunk/src/test/org/hibernate/search/test/configuration/EventListenerRegisterTest.java
___________________________________________________________________
Name: svn:keywords
+ Id
16 years, 4 months