HHH-5985 - Remove TransactionHelper in preference of IsolationDelegate

This commit is contained in:
Steve Ebersole 2011-03-04 21:46:06 -06:00
parent ddfcc44d76
commit 21cc90fbf4
13 changed files with 616 additions and 440 deletions

View File

@ -1,74 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.engine;
import org.hibernate.HibernateException;
import org.hibernate.jdbc.Work;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Allows work to be done outside the current transaction, by suspending it,
* and performing work in a new transaction
*
* @author Emmanuel Bernard
*/
public abstract class TransactionHelper {
// todo : remove this and just have subclasses use IsolationDelegate directly...
/**
* The work to be done
*/
protected abstract Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException;
/**
* Suspend the current transaction and perform work in a new transaction
*/
public Serializable doWorkInNewTransaction(final SessionImplementor session) throws HibernateException {
class WorkToDo implements Work {
Serializable generatedValue;
@Override
public void execute(Connection connection) throws SQLException {
String sql = null;
try {
generatedValue = doWorkInCurrentTransaction( connection, sql );
}
catch( SQLException e ) {
throw session.getFactory().getSQLExceptionHelper().convert(
e,
"could not get or update next value",
sql
);
}
}
}
WorkToDo work = new WorkToDo();
session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( work, true );
return work.generatedValue;
}
}

View File

@ -36,6 +36,7 @@ import org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.engine.transaction.spi.TransactionEnvironment;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -204,6 +205,29 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
}
}
@Override
public <T> T coordinateWork(ReturningWork<T> work) {
Connection connection = getLogicalConnection().getDistinctConnectionProxy();
try {
T result = work.execute( connection );
getLogicalConnection().afterStatementExecution();
return result;
}
catch ( SQLException e ) {
throw sqlExceptionHelper().convert( e, "error executing work" );
}
finally {
try {
if ( ! connection.isClosed() ) {
connection.close();
}
}
catch (SQLException e) {
log.debug( "Error closing connection proxy", e );
}
}
}
public void executeBatch() {
if ( currentBatch != null ) {
currentBatch.execute();

View File

@ -26,6 +26,8 @@ package org.hibernate.engine.jdbc.spi;
import org.hibernate.engine.jdbc.batch.spi.Batch;
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import java.io.Serializable;
@ -88,9 +90,12 @@ public interface JdbcCoordinator extends Serializable {
public void coordinateWork(Work work);
public <T> T coordinateWork(ReturningWork<T> work);
public void executeBatch();
public void cancelLastQuery();
public void setTransactionTimeOut(int timeout);
}

View File

@ -27,6 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.jdbc.util.Formatter;
/**
* Centralize logging for SQL statements.
@ -90,9 +91,13 @@ public class SQLStatementLogger {
*/
public void logStatement(String statement) {
// for now just assume a DML log for formatting
logStatement( statement, FormatStyle.BASIC.getFormatter() );
}
public void logStatement(String statement, Formatter formatter) {
if ( format ) {
if ( logToStdout || log.isDebugEnabled() ) {
statement = FormatStyle.BASIC.getFormatter().format( statement );
statement = formatter.format( statement );
}
}
log.debug( statement );

View File

@ -34,6 +34,7 @@ import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.spi.SQLExceptionHelper;
import org.hibernate.engine.transaction.spi.IsolationDelegate;
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
@ -120,4 +121,68 @@ public class JdbcIsolationDelegate implements IsolationDelegate {
throw sqlExceptionHelper().convert( sqle, "unable to obtain isolated JDBC connection" );
}
}
@Override
public <T> T delegateWork(ReturningWork<T> work, boolean transacted) throws HibernateException {
boolean wasAutoCommit = false;
try {
// todo : should we use a connection proxy here?
Connection connection = connectionProvider().getConnection();
try {
if ( transacted ) {
if ( connection.getAutoCommit() ) {
wasAutoCommit = true;
connection.setAutoCommit( false );
}
}
T result = work.execute( connection );
if ( transacted ) {
connection.commit();
}
return result;
}
catch ( Exception e ) {
try {
if ( transacted && !connection.isClosed() ) {
connection.rollback();
}
}
catch ( Exception ignore ) {
log.info( "unable to rollback connection on exception [" + ignore + "]" );
}
if ( e instanceof HibernateException ) {
throw (HibernateException) e;
}
else if ( e instanceof SQLException ) {
throw sqlExceptionHelper().convert( (SQLException) e, "error performing isolated work" );
}
else {
throw new HibernateException( "error performing isolated work", e );
}
}
finally {
if ( transacted && wasAutoCommit ) {
try {
connection.setAutoCommit( true );
}
catch ( Exception ignore ) {
log.trace( "was unable to reset connection back to auto-commit" );
}
}
try {
connectionProvider().closeConnection( connection );
}
catch ( Exception ignore ) {
log.info( "Unable to release isolated connection [" + ignore + "]" );
}
}
}
catch ( SQLException sqle ) {
throw sqlExceptionHelper().convert( sqle, "unable to obtain isolated JDBC connection" );
}
}
}

View File

@ -37,6 +37,7 @@ import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.spi.SQLExceptionHelper;
import org.hibernate.engine.transaction.spi.IsolationDelegate;
import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
@ -181,5 +182,113 @@ public class JtaIsolationDelegate implements IsolationDelegate {
}
}
@Override
public <T> T delegateWork(ReturningWork<T> work, boolean transacted) throws HibernateException {
TransactionManager transactionManager = transactionManager();
try {
// First we suspend any current JTA transaction
Transaction surroundingTransaction = transactionManager.suspend();
if ( log.isDebugEnabled() ) {
log.debug( "surrounding JTA transaction suspended [" + surroundingTransaction + "]" );
}
boolean hadProblems = false;
try {
// then perform the requested work
if ( transacted ) {
return doTheWorkInNewTransaction( work, transactionManager );
}
else {
return doTheWorkInNoTransaction( work );
}
}
catch ( HibernateException e ) {
hadProblems = true;
throw e;
}
finally {
try {
transactionManager.resume( surroundingTransaction );
if ( log.isDebugEnabled() ) {
log.debug( "surrounding JTA transaction resumed [" + surroundingTransaction + "]" );
}
}
catch( Throwable t ) {
// if the actually work had an error use that, otherwise error based on t
if ( !hadProblems ) {
//noinspection ThrowFromFinallyBlock
throw new HibernateException( "Unable to resume previously suspended transaction", t );
}
}
}
}
catch ( SystemException e ) {
throw new HibernateException( "Unable to suspend current JTA transaction", e );
}
}
private <T> T doTheWorkInNewTransaction(ReturningWork<T> work, TransactionManager transactionManager) {
T result = null;
try {
// start the new isolated transaction
transactionManager.begin();
try {
result = doTheWork( work );
// if everything went ok, commit the isolated transaction
transactionManager.commit();
}
catch ( Exception e ) {
try {
transactionManager.rollback();
}
catch ( Exception ignore ) {
log.info( "Unable to rollback isolated transaction on error [" + e + "] : [" + ignore + "]" );
}
}
}
catch ( SystemException e ) {
throw new HibernateException( "Unable to start isolated transaction", e );
}
catch ( NotSupportedException e ) {
throw new HibernateException( "Unable to start isolated transaction", e );
}
return result;
}
private <T> T doTheWorkInNoTransaction(ReturningWork<T> work) {
return doTheWork( work );
}
private <T> T doTheWork(ReturningWork<T> work) {
try {
// obtain our isolated connection
Connection connection = connectionProvider().getConnection();
try {
// do the actual work
return work.execute( connection );
}
catch ( HibernateException e ) {
throw e;
}
catch ( Exception e ) {
throw new HibernateException( "Unable to perform isolated work", e );
}
finally {
try {
// no matter what, release the connection (handle)
connectionProvider().closeConnection( connection );
}
catch ( Throwable ignore ) {
log.info( "Unable to release isolated connection [" + ignore + "]" );
}
}
}
catch ( SQLException e ) {
throw sqlExceptionHelper().convert( e, "unable to obtain isolated JDBC connection" );
}
}
}

View File

@ -24,6 +24,7 @@
package org.hibernate.engine.transaction.spi;
import org.hibernate.HibernateException;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
/**
@ -41,4 +42,16 @@ public interface IsolationDelegate {
* @throws HibernateException Indicates a problem performing the work.
*/
public void delegateWork(Work work, boolean transacted) throws HibernateException;
/**
* Perform the given work in isolation from current transaction.
*
* @param work The work to be performed.
* @param transacted Should the work itself be done in a (isolated) transaction?
*
* @return The work result
*
* @throws HibernateException Indicates a problem performing the work.
*/
public <T> T delegateWork(ReturningWork<T> work, boolean transacted) throws HibernateException;
}

View File

@ -23,6 +23,24 @@
*/
package org.hibernate.id;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SQLStatementLogger;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
@ -31,22 +49,6 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.TransactionHelper;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
/**
*
* A hilo <tt>IdentifierGenerator</tt> that returns a <tt>Long</tt>, constructed using
@ -78,11 +80,8 @@ import org.hibernate.type.Type;
* @author Emmanuel Bernard
* @author <a href="mailto:kr@hbt.de">Klaus Richarz</a>.
*/
public class MultipleHiLoPerTableGenerator
extends TransactionHelper
implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger(MultipleHiLoPerTableGenerator.class);
public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger( MultipleHiLoPerTableGenerator.class );
public static final String ID_TABLE = "table";
public static final String PK_COLUMN_NAME = "primary_key_column";
@ -146,66 +145,73 @@ public class MultipleHiLoPerTableGenerator
return tableName;
}
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );
int rows;
do {
SQL_STATEMENT_LOGGER.logStatement( query, FormatStyle.BASIC );
PreparedStatement qps = conn.prepareStatement( query );
PreparedStatement ips = null;
try {
ResultSet rs = qps.executeQuery();
boolean isInitialized = rs.next();
if ( !isInitialized ) {
value.initialize( 0 );
SQL_STATEMENT_LOGGER.logStatement( insert, FormatStyle.BASIC );
ips = conn.prepareStatement( insert );
value.bind( ips, 1 );
ips.execute();
}
else {
value.initialize( rs, 0 );
}
rs.close();
}
catch (SQLException sqle) {
log.error("could not read or init a hi value", sqle);
throw sqle;
}
finally {
if (ips != null) {
ips.close();
}
qps.close();
}
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
final ReturningWork<IntegralDataTypeHolder> work = new ReturningWork<IntegralDataTypeHolder>() {
@Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );
SQLStatementLogger statementLogger = session
.getFactory()
.getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
int rows;
do {
statementLogger.logStatement( query, FormatStyle.BASIC.getFormatter() );
PreparedStatement qps = connection.prepareStatement( query );
PreparedStatement ips = null;
try {
ResultSet rs = qps.executeQuery();
boolean isInitialized = rs.next();
if ( !isInitialized ) {
value.initialize( 0 );
statementLogger.logStatement( insert, FormatStyle.BASIC.getFormatter() );
ips = connection.prepareStatement( insert );
value.bind( ips, 1 );
ips.execute();
}
else {
value.initialize( rs, 0 );
}
rs.close();
}
catch (SQLException sqle) {
log.error("could not read or init a hi value", sqle);
throw sqle;
}
finally {
if (ips != null) {
ips.close();
}
qps.close();
}
SQL_STATEMENT_LOGGER.logStatement( update, FormatStyle.BASIC );
PreparedStatement ups = conn.prepareStatement( update );
try {
value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 );
rows = ups.executeUpdate();
}
catch (SQLException sqle) {
log.error("could not update hi value in: " + tableName, sqle);
throw sqle;
}
finally {
ups.close();
}
} while ( rows==0 );
statementLogger.logStatement( update, FormatStyle.BASIC.getFormatter() );
PreparedStatement ups = connection.prepareStatement( update );
try {
value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 );
rows = ups.executeUpdate();
}
catch (SQLException sqle) {
log.error("could not update hi value in: " + tableName, sqle);
throw sqle;
}
finally {
ups.close();
}
} while ( rows==0 );
return value;
}
return value;
}
};
public synchronized Serializable generate(final SessionImplementor session, Object obj)
throws HibernateException {
// maxLo < 1 indicates a hilo generator with no hilo :?
if ( maxLo < 1 ) {
//keep the behavior consistent even for boundary usages
IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 1 ) ) {
value = (IntegralDataTypeHolder) doWorkInNewTransaction( session );
value = session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( work, true );
}
return value.makeValue();
}
@ -213,7 +219,7 @@ public class MultipleHiLoPerTableGenerator
return hiloOptimizer.generate(
new AccessCallback() {
public IntegralDataTypeHolder getNextValue() {
return (IntegralDataTypeHolder) doWorkInNewTransaction( session );
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( work, true );
}
}
);

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,13 +20,11 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.jdbc.util.SQLStatementLogger;
/**
* An <tt>IdentifierGenerator</tt> that requires creation of database objects.
@ -99,8 +97,6 @@ public interface PersistentIdentifierGenerator extends IdentifierGenerator {
*/
public Object generatorKey();
static final SQLStatementLogger SQL_STATEMENT_LOGGER = new SQLStatementLogger( false, false );
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,10 +20,24 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SQLStatementLogger;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
@ -32,19 +46,6 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.TransactionHelper;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
/**
* An <tt>IdentifierGenerator</tt> that uses a database
* table to store the last generated value. It is not
@ -70,8 +71,7 @@ import org.hibernate.type.Type;
* @see TableHiLoGenerator
* @author Gavin King
*/
public class TableGenerator extends TransactionHelper
implements PersistentIdentifierGenerator, Configurable {
public class TableGenerator implements PersistentIdentifierGenerator, Configurable {
/* COLUMN and TABLE should be renamed but it would break the public API */
/** The column parameter */
public static final String COLUMN = "column";
@ -139,7 +139,63 @@ public class TableGenerator extends TransactionHelper
}
protected IntegralDataTypeHolder generateHolder(SessionImplementor session) {
return (IntegralDataTypeHolder) doWorkInNewTransaction( session );
final SQLStatementLogger statementLogger = session
.getFactory()
.getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
new ReturningWork<IntegralDataTypeHolder>() {
@Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = buildHolder();
int rows;
do {
// The loop ensures atomicity of the
// select + update even for no transaction
// or read committed isolation level
statementLogger.logStatement( query, FormatStyle.BASIC.getFormatter() );
PreparedStatement qps = connection.prepareStatement( query );
try {
ResultSet rs = qps.executeQuery();
if ( !rs.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName;
log.error(err);
throw new IdentifierGenerationException(err);
}
value.initialize( rs, 1 );
rs.close();
}
catch (SQLException e) {
log.error("could not read a hi value", e);
throw e;
}
finally {
qps.close();
}
statementLogger.logStatement( update, FormatStyle.BASIC.getFormatter() );
PreparedStatement ups = connection.prepareStatement(update);
try {
value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 );
rows = ups.executeUpdate();
}
catch (SQLException sqle) {
log.error("could not update hi value in: " + tableName, sqle);
throw sqle;
}
finally {
ups.close();
}
}
while (rows==0);
return value;
}
},
true
);
}
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
@ -165,66 +221,6 @@ public class TableGenerator extends TransactionHelper
return tableName;
}
/**
* Get the next value.
*
* @param conn The sql connection to use.
* @param sql n/a
*
* @return Prior to 3.5 this method returned an {@link Integer}. Since 3.5 it now
* returns a {@link IntegralDataTypeHolder}
*
* @throws SQLException
*/
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
IntegralDataTypeHolder value = buildHolder();
int rows;
do {
// The loop ensures atomicity of the
// select + update even for no transaction
// or read committed isolation level
sql = query;
SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
PreparedStatement qps = conn.prepareStatement(query);
try {
ResultSet rs = qps.executeQuery();
if ( !rs.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName;
log.error(err);
throw new IdentifierGenerationException(err);
}
value.initialize( rs, 1 );
rs.close();
}
catch (SQLException sqle) {
log.error("could not read a hi value", sqle);
throw sqle;
}
finally {
qps.close();
}
sql = update;
SQL_STATEMENT_LOGGER.logStatement( sql, FormatStyle.BASIC );
PreparedStatement ups = conn.prepareStatement(update);
try {
value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 );
rows = ups.executeUpdate();
}
catch (SQLException sqle) {
log.error("could not update hi value in: " + tableName, sqle);
throw sqle;
}
finally {
ups.close();
}
}
while (rows==0);
return value;
}
protected IntegralDataTypeHolder buildHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
}

View File

@ -23,37 +23,38 @@
*/
package org.hibernate.id.enhanced;
import java.sql.Types;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import java.util.Collections;
import java.util.Map;
import java.io.Serializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.cfg.Environment;
import org.hibernate.engine.TransactionHelper;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SQLStatementLogger;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.id.Configurable;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.type.Type;
import org.hibernate.dialect.Dialect;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.LockOptions;
import org.hibernate.LockMode;
import org.hibernate.cfg.ObjectNameNormalizer;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.mapping.Table;
import org.hibernate.type.Type;
import org.hibernate.util.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
/**
* An enhanced version of table-based id generation.
@ -128,7 +129,7 @@ import org.hibernate.util.StringHelper;
*
* @author Steve Ebersole
*/
public class TableGenerator extends TransactionHelper implements PersistentIdentifierGenerator, Configurable {
public class TableGenerator implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger( TableGenerator.class );
public static final String CONFIG_PREFER_SEGMENT_PER_ENTITY = "prefer_entity_table_as_segment_value";
@ -176,9 +177,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
private Optimizer optimizer;
private long accessCount = 0;
/**
* {@inheritDoc}
*/
@Override
public Object generatorKey() {
return tableName;
}
@ -283,9 +282,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
return accessCount;
}
/**
* {@inheritDoc}
*/
@Override
public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
identifierType = type;
@ -456,93 +453,96 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
return "insert into " + tableName + " (" + segmentColumnName + ", " + valueColumnName + ") " + " values (?,?)";
}
/**
* {@inheritDoc}
*/
@Override
public synchronized Serializable generate(final SessionImplementor session, Object obj) {
final SQLStatementLogger statementLogger = session
.getFactory()
.getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
return optimizer.generate(
new AccessCallback() {
@Override
public IntegralDataTypeHolder getNextValue() {
return ( IntegralDataTypeHolder ) doWorkInNewTransaction( session );
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
new ReturningWork<IntegralDataTypeHolder>() {
@Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
int rows;
do {
statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement selectPS = connection.prepareStatement( selectQuery );
try {
selectPS.setString( 1, segmentValue );
ResultSet selectRS = selectPS.executeQuery();
if ( !selectRS.next() ) {
value.initialize( initialValue );
PreparedStatement insertPS = null;
try {
statementLogger.logStatement( insertQuery, FormatStyle.BASIC.getFormatter() );
insertPS = connection.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
value.bind( insertPS, 2 );
insertPS.execute();
}
finally {
if ( insertPS != null ) {
insertPS.close();
}
}
}
else {
value.initialize( selectRS, 1 );
}
selectRS.close();
}
catch ( SQLException e ) {
log.error( "could not read or init a hi value", e );
throw e;
}
finally {
selectPS.close();
}
statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement updatePS = connection.prepareStatement( updateQuery );
try {
final IntegralDataTypeHolder updateValue = value.copy();
if ( optimizer.applyIncrementSizeToSourceValues() ) {
updateValue.add( incrementSize );
}
else {
updateValue.increment();
}
updateValue.bind( updatePS, 1 );
value.bind( updatePS, 2 );
updatePS.setString( 3, segmentValue );
rows = updatePS.executeUpdate();
}
catch ( SQLException e ) {
log.error( "could not updateQuery hi value in: " + tableName, e );
throw e;
}
finally {
updatePS.close();
}
}
while ( rows == 0 );
accessCount++;
return value;
}
},
true
);
}
}
);
}
/**
* {@inheritDoc}
*/
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
int rows;
do {
SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
selectPS.setString( 1, segmentValue );
ResultSet selectRS = selectPS.executeQuery();
if ( !selectRS.next() ) {
value.initialize( initialValue );
PreparedStatement insertPS = null;
try {
SQL_STATEMENT_LOGGER.logStatement( insertQuery, FormatStyle.BASIC );
insertPS = conn.prepareStatement( insertQuery );
insertPS.setString( 1, segmentValue );
value.bind( insertPS, 2 );
insertPS.execute();
}
finally {
if ( insertPS != null ) {
insertPS.close();
}
}
}
else {
value.initialize( selectRS, 1 );
}
selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read or init a hi value", sqle );
throw sqle;
}
finally {
selectPS.close();
}
SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
final IntegralDataTypeHolder updateValue = value.copy();
if ( optimizer.applyIncrementSizeToSourceValues() ) {
updateValue.add( incrementSize );
}
else {
updateValue.increment();
}
updateValue.bind( updatePS, 1 );
value.bind( updatePS, 2 );
updatePS.setString( 3, segmentValue );
rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
log.error( "could not updateQuery hi value in: " + tableName, sqle );
throw sqle;
}
finally {
updatePS.close();
}
}
while ( rows == 0 );
accessCount++;
return value;
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
new StringBuffer()
@ -565,9 +565,7 @@ public class TableGenerator extends TransactionHelper implements PersistentIdent
};
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Middleware LLC.
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,39 +20,36 @@
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*
*/
package org.hibernate.id.enhanced;
import java.io.Serializable;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SQLStatementLogger;
import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.util.FormatStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.engine.TransactionHelper;
import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.jdbc.util.SQLStatementLogger;
/**
* Describes a table used to mimic sequence behavior
*
* @author Steve Ebersole
*/
public class TableStructure extends TransactionHelper implements DatabaseStructure {
public class TableStructure implements DatabaseStructure {
private static final Logger log = LoggerFactory.getLogger( TableStructure.class );
private static final SQLStatementLogger SQL_STATEMENT_LOGGER = new SQLStatementLogger( false, false );
private final String tableName;
private final String valueColumnName;
@ -87,55 +84,98 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
" where " + valueColumnName + "=?";
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return tableName;
}
/**
* {@inheritDoc}
*/
@Override
public int getInitialValue() {
return initialValue;
}
/**
* {@inheritDoc}
*/
@Override
public int getIncrementSize() {
return incrementSize;
}
/**
* {@inheritDoc}
*/
@Override
public int getTimesAccessed() {
return accessCounter;
}
/**
* {@inheritDoc}
*/
@Override
public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
}
/**
* {@inheritDoc}
*/
@Override
public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() {
@Override
public IntegralDataTypeHolder getNextValue() {
return ( IntegralDataTypeHolder ) doWorkInNewTransaction( session );
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
new ReturningWork<IntegralDataTypeHolder>() {
@Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
final SQLStatementLogger statementLogger = session
.getFactory()
.getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
int rows;
do {
statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement selectStatement = connection.prepareStatement( selectQuery );
try {
ResultSet selectRS = selectStatement.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 );
}
value.initialize( selectRS, 1 );
selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read a hi value", sqle );
throw sqle;
}
finally {
selectStatement.close();
}
statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement updatePS = connection.prepareStatement( updateQuery );
try {
final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
final IntegralDataTypeHolder updateValue = value.copy().add( increment );
updateValue.bind( updatePS, 1 );
value.bind( updatePS, 2 );
rows = updatePS.executeUpdate();
}
catch ( SQLException e ) {
log.error( "could not updateQuery hi value in: " + tableName, e );
throw e;
}
finally {
updatePS.close();
}
} while ( rows == 0 );
accessCounter++;
return value;
}
},
true
);
}
};
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
dialect.getCreateTableString() + " " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
@ -143,9 +183,7 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
};
}
/**
* {@inheritDoc}
*/
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
@ -157,55 +195,4 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
}
return new String[] { sqlDropString.toString() };
}
/**
* {@inheritDoc}
*/
protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
int rows;
do {
SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC );
PreparedStatement selectPS = conn.prepareStatement( selectQuery );
try {
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 );
}
value.initialize( selectRS, 1 );
selectRS.close();
}
catch ( SQLException sqle ) {
log.error( "could not read a hi value", sqle );
throw sqle;
}
finally {
selectPS.close();
}
SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC );
PreparedStatement updatePS = conn.prepareStatement( updateQuery );
try {
final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
final IntegralDataTypeHolder updateValue = value.copy().add( increment );
updateValue.bind( updatePS, 1 );
value.bind( updatePS, 2 );
rows = updatePS.executeUpdate();
}
catch ( SQLException sqle ) {
log.error( "could not updateQuery hi value in: " + tableName, sqle );
throw sqle;
}
finally {
updatePS.close();
}
} while ( rows == 0 );
accessCounter++;
return value;
}
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
/**
* A discrete piece of work following the lines of {@link Work} but returning a result.
*
* @author Steve Ebersole
*/
public interface ReturningWork<T> {
/**
* Execute the discrete work encapsulated by this work instance using the supplied connection.
*
* @param connection The connection on which to perform the work.
*
* @return The work result
*
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
*/
public T execute(Connection connection) throws SQLException;
}