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.TransactionContext;
import org.hibernate.engine.transaction.spi.TransactionCoordinator; import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.engine.transaction.spi.TransactionEnvironment; import org.hibernate.engine.transaction.spi.TransactionEnvironment;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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() { public void executeBatch() {
if ( currentBatch != null ) { if ( currentBatch != null ) {
currentBatch.execute(); 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.Batch;
import org.hibernate.engine.jdbc.batch.spi.BatchKey; import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.transaction.spi.TransactionCoordinator; import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import java.io.Serializable; import java.io.Serializable;
@ -88,9 +90,12 @@ public interface JdbcCoordinator extends Serializable {
public void coordinateWork(Work work); public void coordinateWork(Work work);
public <T> T coordinateWork(ReturningWork<T> work);
public void executeBatch(); public void executeBatch();
public void cancelLastQuery(); public void cancelLastQuery();
public void setTransactionTimeOut(int timeout); public void setTransactionTimeOut(int timeout);
} }

View File

@ -27,6 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.hibernate.jdbc.util.FormatStyle; import org.hibernate.jdbc.util.FormatStyle;
import org.hibernate.jdbc.util.Formatter;
/** /**
* Centralize logging for SQL statements. * Centralize logging for SQL statements.
@ -90,9 +91,13 @@ public class SQLStatementLogger {
*/ */
public void logStatement(String statement) { public void logStatement(String statement) {
// for now just assume a DML log for formatting // for now just assume a DML log for formatting
logStatement( statement, FormatStyle.BASIC.getFormatter() );
}
public void logStatement(String statement, Formatter formatter) {
if ( format ) { if ( format ) {
if ( logToStdout || log.isDebugEnabled() ) { if ( logToStdout || log.isDebugEnabled() ) {
statement = FormatStyle.BASIC.getFormatter().format( statement ); statement = formatter.format( statement );
} }
} }
log.debug( 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.jdbc.spi.SQLExceptionHelper;
import org.hibernate.engine.transaction.spi.IsolationDelegate; import org.hibernate.engine.transaction.spi.IsolationDelegate;
import org.hibernate.engine.transaction.spi.TransactionCoordinator; import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; 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" ); 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.jdbc.spi.SQLExceptionHelper;
import org.hibernate.engine.transaction.spi.IsolationDelegate; import org.hibernate.engine.transaction.spi.IsolationDelegate;
import org.hibernate.engine.transaction.spi.TransactionCoordinator; import org.hibernate.engine.transaction.spi.TransactionCoordinator;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; 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; package org.hibernate.engine.transaction.spi;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
/** /**
@ -41,4 +42,16 @@ public interface IsolationDelegate {
* @throws HibernateException Indicates a problem performing the work. * @throws HibernateException Indicates a problem performing the work.
*/ */
public void delegateWork(Work work, boolean transacted) throws HibernateException; 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; 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.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -31,22 +49,6 @@ import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.Properties; 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 * 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 Emmanuel Bernard
* @author <a href="mailto:kr@hbt.de">Klaus Richarz</a>. * @author <a href="mailto:kr@hbt.de">Klaus Richarz</a>.
*/ */
public class MultipleHiLoPerTableGenerator public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenerator, Configurable {
extends TransactionHelper private static final Logger log = LoggerFactory.getLogger( MultipleHiLoPerTableGenerator.class );
implements PersistentIdentifierGenerator, Configurable {
private static final Logger log = LoggerFactory.getLogger(MultipleHiLoPerTableGenerator.class);
public static final String ID_TABLE = "table"; public static final String ID_TABLE = "table";
public static final String PK_COLUMN_NAME = "primary_key_column"; public static final String PK_COLUMN_NAME = "primary_key_column";
@ -146,20 +145,28 @@ public class MultipleHiLoPerTableGenerator
return tableName; return tableName;
} }
public Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException { 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 ); IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );
SQLStatementLogger statementLogger = session
.getFactory()
.getServiceRegistry()
.getService( JdbcServices.class )
.getSqlStatementLogger();
int rows; int rows;
do { do {
SQL_STATEMENT_LOGGER.logStatement( query, FormatStyle.BASIC ); statementLogger.logStatement( query, FormatStyle.BASIC.getFormatter() );
PreparedStatement qps = conn.prepareStatement( query ); PreparedStatement qps = connection.prepareStatement( query );
PreparedStatement ips = null; PreparedStatement ips = null;
try { try {
ResultSet rs = qps.executeQuery(); ResultSet rs = qps.executeQuery();
boolean isInitialized = rs.next(); boolean isInitialized = rs.next();
if ( !isInitialized ) { if ( !isInitialized ) {
value.initialize( 0 ); value.initialize( 0 );
SQL_STATEMENT_LOGGER.logStatement( insert, FormatStyle.BASIC ); statementLogger.logStatement( insert, FormatStyle.BASIC.getFormatter() );
ips = conn.prepareStatement( insert ); ips = connection.prepareStatement( insert );
value.bind( ips, 1 ); value.bind( ips, 1 );
ips.execute(); ips.execute();
} }
@ -179,8 +186,8 @@ public class MultipleHiLoPerTableGenerator
qps.close(); qps.close();
} }
SQL_STATEMENT_LOGGER.logStatement( update, FormatStyle.BASIC ); statementLogger.logStatement( update, FormatStyle.BASIC.getFormatter() );
PreparedStatement ups = conn.prepareStatement( update ); PreparedStatement ups = connection.prepareStatement( update );
try { try {
value.copy().increment().bind( ups, 1 ); value.copy().increment().bind( ups, 1 );
value.bind( ups, 2 ); value.bind( ups, 2 );
@ -197,15 +204,14 @@ public class MultipleHiLoPerTableGenerator
return value; return value;
} }
};
public synchronized Serializable generate(final SessionImplementor session, Object obj)
throws HibernateException {
// maxLo < 1 indicates a hilo generator with no hilo :? // maxLo < 1 indicates a hilo generator with no hilo :?
if ( maxLo < 1 ) { if ( maxLo < 1 ) {
//keep the behavior consistent even for boundary usages //keep the behavior consistent even for boundary usages
IntegralDataTypeHolder value = null; IntegralDataTypeHolder value = null;
while ( value == null || value.lt( 1 ) ) { while ( value == null || value.lt( 1 ) ) {
value = (IntegralDataTypeHolder) doWorkInNewTransaction( session ); value = session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( work, true );
} }
return value.makeValue(); return value.makeValue();
} }
@ -213,7 +219,7 @@ public class MultipleHiLoPerTableGenerator
return hiloOptimizer.generate( return hiloOptimizer.generate(
new AccessCallback() { new AccessCallback() {
public IntegralDataTypeHolder getNextValue() { 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 * 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 * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * 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, * 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 * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,13 +20,11 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.id; package org.hibernate.id;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.jdbc.util.SQLStatementLogger;
/** /**
* An <tt>IdentifierGenerator</tt> that requires creation of database objects. * An <tt>IdentifierGenerator</tt> that requires creation of database objects.
@ -99,8 +97,6 @@ public interface PersistentIdentifierGenerator extends IdentifierGenerator {
*/ */
public Object generatorKey(); 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 * 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 * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * 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, * 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 * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,10 +20,24 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.id; 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.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -32,19 +46,6 @@ import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.Properties; 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 * An <tt>IdentifierGenerator</tt> that uses a database
* table to store the last generated value. It is not * table to store the last generated value. It is not
@ -70,8 +71,7 @@ import org.hibernate.type.Type;
* @see TableHiLoGenerator * @see TableHiLoGenerator
* @author Gavin King * @author Gavin King
*/ */
public class TableGenerator extends TransactionHelper public class TableGenerator implements PersistentIdentifierGenerator, Configurable {
implements PersistentIdentifierGenerator, Configurable {
/* COLUMN and TABLE should be renamed but it would break the public API */ /* COLUMN and TABLE should be renamed but it would break the public API */
/** The column parameter */ /** The column parameter */
public static final String COLUMN = "column"; public static final String COLUMN = "column";
@ -139,7 +139,63 @@ public class TableGenerator extends TransactionHelper
} }
protected IntegralDataTypeHolder generateHolder(SessionImplementor session) { 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 { public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
@ -165,66 +221,6 @@ public class TableGenerator extends TransactionHelper
return tableName; 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() { protected IntegralDataTypeHolder buildHolder() {
return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() ); return IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );
} }

View File

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

View File

@ -1,10 +1,10 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * 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 * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * 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, * 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 * copy, or redistribute it subject to the terms and conditions of the GNU
@ -20,39 +20,36 @@
* Free Software Foundation, Inc. * Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor * 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA * Boston, MA 02110-1301 USA
*
*/ */
package org.hibernate.id.enhanced; 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.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; 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 * Describes a table used to mimic sequence behavior
* *
* @author Steve Ebersole * @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 Logger log = LoggerFactory.getLogger( TableStructure.class );
private static final SQLStatementLogger SQL_STATEMENT_LOGGER = new SQLStatementLogger( false, false );
private final String tableName; private final String tableName;
private final String valueColumnName; private final String valueColumnName;
@ -87,88 +84,52 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
" where " + valueColumnName + "=?"; " where " + valueColumnName + "=?";
} }
/** @Override
* {@inheritDoc}
*/
public String getName() { public String getName() {
return tableName; return tableName;
} }
/** @Override
* {@inheritDoc}
*/
public int getInitialValue() { public int getInitialValue() {
return initialValue; return initialValue;
} }
/** @Override
* {@inheritDoc}
*/
public int getIncrementSize() { public int getIncrementSize() {
return incrementSize; return incrementSize;
} }
/** @Override
* {@inheritDoc}
*/
public int getTimesAccessed() { public int getTimesAccessed() {
return accessCounter; return accessCounter;
} }
/** @Override
* {@inheritDoc}
*/
public void prepare(Optimizer optimizer) { public void prepare(Optimizer optimizer) {
applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues(); applyIncrementSizeToSourceValues = optimizer.applyIncrementSizeToSourceValues();
} }
/** @Override
* {@inheritDoc}
*/
public AccessCallback buildCallback(final SessionImplementor session) { public AccessCallback buildCallback(final SessionImplementor session) {
return new AccessCallback() { return new AccessCallback() {
@Override
public IntegralDataTypeHolder getNextValue() { 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()
* {@inheritDoc} .getServiceRegistry()
*/ .getService( JdbcServices.class )
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException { .getSqlStatementLogger();
return new String[] {
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() ) {
sqlDropString.append( "if exists " );
}
sqlDropString.append( tableName ).append( dialect.getCascadeConstraintsString() );
if ( dialect.supportsIfExistsAfterTableName() ) {
sqlDropString.append( " if exists" );
}
return new String[] { sqlDropString.toString() };
}
/**
* {@inheritDoc}
*/
protected Serializable doWorkInCurrentTransaction(Connection conn, String sql) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType ); IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( numberType );
int rows; int rows;
do { do {
SQL_STATEMENT_LOGGER.logStatement( selectQuery, FormatStyle.BASIC ); statementLogger.logStatement( selectQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement selectPS = conn.prepareStatement( selectQuery ); PreparedStatement selectStatement = connection.prepareStatement( selectQuery );
try { try {
ResultSet selectRS = selectPS.executeQuery(); ResultSet selectRS = selectStatement.executeQuery();
if ( !selectRS.next() ) { if ( !selectRS.next() ) {
String err = "could not read a hi value - you need to populate the table: " + tableName; String err = "could not read a hi value - you need to populate the table: " + tableName;
log.error( err ); log.error( err );
@ -182,11 +143,11 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
throw sqle; throw sqle;
} }
finally { finally {
selectPS.close(); selectStatement.close();
} }
SQL_STATEMENT_LOGGER.logStatement( updateQuery, FormatStyle.BASIC ); statementLogger.logStatement( updateQuery, FormatStyle.BASIC.getFormatter() );
PreparedStatement updatePS = conn.prepareStatement( updateQuery ); PreparedStatement updatePS = connection.prepareStatement( updateQuery );
try { try {
final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1; final int increment = applyIncrementSizeToSourceValues ? incrementSize : 1;
final IntegralDataTypeHolder updateValue = value.copy().add( increment ); final IntegralDataTypeHolder updateValue = value.copy().add( increment );
@ -194,9 +155,9 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
value.bind( updatePS, 2 ); value.bind( updatePS, 2 );
rows = updatePS.executeUpdate(); rows = updatePS.executeUpdate();
} }
catch ( SQLException sqle ) { catch ( SQLException e ) {
log.error( "could not updateQuery hi value in: " + tableName, sqle ); log.error( "could not updateQuery hi value in: " + tableName, e );
throw sqle; throw e;
} }
finally { finally {
updatePS.close(); updatePS.close();
@ -207,5 +168,31 @@ public class TableStructure extends TransactionHelper implements DatabaseStructu
return value; return value;
} }
},
true
);
}
};
}
@Override
public String[] sqlCreateStrings(Dialect dialect) throws HibernateException {
return new String[] {
dialect.getCreateTableString() + " " + tableName + " ( " + valueColumnName + " " + dialect.getTypeName( Types.BIGINT ) + " )",
"insert into " + tableName + " values ( " + initialValue + " )"
};
}
@Override
public String[] sqlDropStrings(Dialect dialect) throws HibernateException {
StringBuffer sqlDropString = new StringBuffer().append( "drop table " );
if ( dialect.supportsIfExistsBeforeTableName() ) {
sqlDropString.append( "if exists " );
}
sqlDropString.append( tableName ).append( dialect.getCascadeConstraintsString() );
if ( dialect.supportsIfExistsAfterTableName() ) {
sqlDropString.append( " if exists" );
}
return new String[] { sqlDropString.toString() };
}
} }

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;
}