HHH-6010 : Remove duplication in code involving Work and ReturningWork

This commit is contained in:
Gail Badner 2011-03-11 16:34:47 -08:00
parent 53bc7d1bb4
commit b30ff014ad
17 changed files with 356 additions and 234 deletions

View File

@ -25,6 +25,8 @@
package org.hibernate; package org.hibernate;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.hibernate.stat.SessionStatistics; import org.hibernate.stat.SessionStatistics;
@ -917,6 +919,17 @@ public interface Session extends Serializable {
*/ */
public void doWork(Work work) throws HibernateException; public void doWork(Work work) throws HibernateException;
/**
* Controller for allowing users to perform JDBC related work using the Connection
* managed by this Session, returning the result from calling <code>work.execute()</code>
* ({@link ReturningWork<T>.execute(Connection)}/
*
* @param work The work to be performed.
* @return the result from calling <code>work.execute()</code>.
* @throws HibernateException Generally indicates wrapped {@link java.sql.SQLException}
*/
public <T> T doReturningWork(ReturningWork<T> work) throws HibernateException;
/** /**
* Disconnect the session from its underlying JDBC connection. This is intended for use in cases where the * Disconnect the session from its underlying JDBC connection. This is intended for use in cases where the
* application has supplied the JDBC connection to the session and which require long-sessions (aka, conversations). * application has supplied the JDBC connection to the session and which require long-sessions (aka, conversations).

View File

@ -42,8 +42,9 @@ 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.WorkExecutorVisitable;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.WorkExecutor;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
/** /**
@ -184,32 +185,11 @@ public class JdbcCoordinatorImpl implements JdbcCoordinator {
} }
} }
public void coordinateWork(Work work) {
Connection connection = getLogicalConnection().getDistinctConnectionProxy();
try {
work.execute( connection );
getLogicalConnection().afterStatementExecution();
}
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);
}
}
}
@Override @Override
public <T> T coordinateWork(ReturningWork<T> work) { public <T> T coordinateWork(WorkExecutorVisitable<T> work) {
Connection connection = getLogicalConnection().getDistinctConnectionProxy(); Connection connection = getLogicalConnection().getDistinctConnectionProxy();
try { try {
T result = work.execute( connection ); T result = work.accept( new WorkExecutor<T>(), connection );
getLogicalConnection().afterStatementExecution(); getLogicalConnection().afterStatementExecution();
return result; return result;
} }

View File

@ -26,9 +26,7 @@ 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.WorkExecutorVisitable;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
@ -88,9 +86,7 @@ public interface JdbcCoordinator extends Serializable {
public void afterTransaction(); public void afterTransaction();
public void coordinateWork(Work work); public <T> T coordinateWork(WorkExecutorVisitable<T> work);
public <T> T coordinateWork(ReturningWork<T> work);
public void executeBatch(); public void executeBatch();

View File

@ -30,8 +30,8 @@ import org.hibernate.HibernateLogger;
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.WorkExecutorVisitable;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -59,7 +59,7 @@ public class JdbcIsolationDelegate implements IsolationDelegate {
} }
@Override @Override
public void delegateWork(Work work, boolean transacted) throws HibernateException { public <T> T delegateWork(WorkExecutorVisitable<T> work, boolean transacted) throws HibernateException {
boolean wasAutoCommit = false; boolean wasAutoCommit = false;
try { try {
// todo : should we use a connection proxy here? // todo : should we use a connection proxy here?
@ -72,69 +72,7 @@ public class JdbcIsolationDelegate implements IsolationDelegate {
} }
} }
work.execute( connection ); T result = work.accept( new WorkExecutor<T>(), connection );
if ( transacted ) {
connection.commit();
}
}
catch ( Exception e ) {
try {
if ( transacted && !connection.isClosed() ) {
connection.rollback();
}
}
catch ( Exception ignore ) {
LOG.unableToRollbackConnection(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.unableToReleaseIsolatedConnection(ignore);
}
}
}
catch ( SQLException sqle ) {
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 ) { if ( transacted ) {
connection.commit(); connection.commit();

View File

@ -34,8 +34,8 @@ import org.hibernate.HibernateLogger;
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.WorkExecutorVisitable;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -76,109 +76,7 @@ public class JtaIsolationDelegate implements IsolationDelegate {
} }
@Override @Override
public void delegateWork(Work work, boolean transacted) throws HibernateException { public <T> T delegateWork(WorkExecutorVisitable<T> work, boolean transacted) throws HibernateException {
TransactionManager transactionManager = transactionManager();
try {
// First we suspend any current JTA transaction
Transaction surroundingTransaction = transactionManager.suspend();
LOG.debugf("Surrounding JTA transaction suspended [%s]", surroundingTransaction);
boolean hadProblems = false;
try {
// then perform the requested work
if ( transacted ) {
doTheWorkInNewTransaction( work, transactionManager );
}
else {
doTheWorkInNoTransaction( work );
}
}
catch ( HibernateException e ) {
hadProblems = true;
throw e;
}
finally {
try {
transactionManager.resume( surroundingTransaction );
LOG.debugf("Surrounding JTA transaction resumed [%s]", 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 void doTheWorkInNewTransaction(Work work, TransactionManager transactionManager) {
try {
// start the new isolated transaction
transactionManager.begin();
try {
doTheWork( work );
// if everythign went ok, commit the isolated transaction
transactionManager.commit();
}
catch ( Exception e ) {
try {
transactionManager.rollback();
}
catch ( Exception ignore ) {
LOG.unableToRollbackIsolatedTransaction(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 );
}
}
private void doTheWorkInNoTransaction(Work work) {
doTheWork( work );
}
private void doTheWork(Work work) {
try {
// obtain our isolated connection
Connection connection = connectionProvider().getConnection();
try {
// do the actual work
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.unableToReleaseIsolatedConnection(ignore);
}
}
}
catch ( SQLException sqle ) {
throw sqlExceptionHelper().convert( sqle, "unable to obtain isolated JDBC connection" );
}
}
@Override
public <T> T delegateWork(ReturningWork<T> work, boolean transacted) throws HibernateException {
TransactionManager transactionManager = transactionManager(); TransactionManager transactionManager = transactionManager();
try { try {
@ -203,7 +101,7 @@ public class JtaIsolationDelegate implements IsolationDelegate {
finally { finally {
try { try {
transactionManager.resume( surroundingTransaction ); transactionManager.resume( surroundingTransaction );
LOG.debugf("Surrounding JTA transaction resumed [%s]", surroundingTransaction); LOG.debugf( "Surrounding JTA transaction resumed [%s]", surroundingTransaction );
} }
catch( Throwable t ) { catch( Throwable t ) {
// if the actually work had an error use that, otherwise error based on t // if the actually work had an error use that, otherwise error based on t
@ -219,7 +117,7 @@ public class JtaIsolationDelegate implements IsolationDelegate {
} }
} }
private <T> T doTheWorkInNewTransaction(ReturningWork<T> work, TransactionManager transactionManager) { private <T> T doTheWorkInNewTransaction(WorkExecutorVisitable<T> work, TransactionManager transactionManager) {
T result = null; T result = null;
try { try {
// start the new isolated transaction // start the new isolated transaction
@ -248,17 +146,17 @@ public class JtaIsolationDelegate implements IsolationDelegate {
return result; return result;
} }
private <T> T doTheWorkInNoTransaction(ReturningWork<T> work) { private <T> T doTheWorkInNoTransaction(WorkExecutorVisitable<T> work) {
return doTheWork( work ); return doTheWork( work );
} }
private <T> T doTheWork(ReturningWork<T> work) { private <T> T doTheWork(WorkExecutorVisitable<T> work) {
try { try {
// obtain our isolated connection // obtain our isolated connection
Connection connection = connectionProvider().getConnection(); Connection connection = connectionProvider().getConnection();
try { try {
// do the actual work // do the actual work
return work.execute( connection ); return work.accept( new WorkExecutor<T>(), connection );
} }
catch ( HibernateException e ) { catch ( HibernateException e ) {
throw e; throw e;
@ -280,6 +178,5 @@ public class JtaIsolationDelegate implements IsolationDelegate {
throw sqlExceptionHelper().convert( e, "unable to obtain isolated JDBC connection" ); throw sqlExceptionHelper().convert( e, "unable to obtain isolated JDBC connection" );
} }
} }
} }

View File

@ -24,8 +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.WorkExecutorVisitable;
import org.hibernate.jdbc.Work;
/** /**
* Contract for performing work in a manner that isolates it from any current transaction. * Contract for performing work in a manner that isolates it from any current transaction.
@ -33,16 +32,6 @@ import org.hibernate.jdbc.Work;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface IsolationDelegate { public interface IsolationDelegate {
/**
* 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?
*
* @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. * Perform the given work in isolation from current transaction.
* *
@ -53,5 +42,5 @@ public interface IsolationDelegate {
* *
* @throws HibernateException Indicates a problem performing the work. * @throws HibernateException Indicates a problem performing the work.
*/ */
public <T> T delegateWork(ReturningWork<T> work, boolean transacted) throws HibernateException; public <T> T delegateWork(WorkExecutorVisitable<T> work, boolean transacted) throws HibernateException;
} }

View File

@ -40,7 +40,7 @@ import org.hibernate.event.EventSource;
import org.hibernate.hql.ast.HqlSqlWalker; import org.hibernate.hql.ast.HqlSqlWalker;
import org.hibernate.hql.ast.SqlGenerator; import org.hibernate.hql.ast.SqlGenerator;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.AbstractWork;
import org.hibernate.persister.entity.Queryable; import org.hibernate.persister.entity.Queryable;
import org.hibernate.sql.InsertSelect; import org.hibernate.sql.InsertSelect;
import org.hibernate.sql.Select; import org.hibernate.sql.Select;
@ -137,7 +137,7 @@ public abstract class AbstractStatementExecutor implements StatementExecutor {
" from " + persister.getTemporaryIdTableName(); " from " + persister.getTemporaryIdTableName();
} }
private static class TemporaryTableCreationWork implements Work { private static class TemporaryTableCreationWork extends AbstractWork {
private final Queryable persister; private final Queryable persister;
private TemporaryTableCreationWork(Queryable persister) { private TemporaryTableCreationWork(Queryable persister) {
@ -209,7 +209,7 @@ public abstract class AbstractStatementExecutor implements StatementExecutor {
} }
}; };
private static class TemporaryTableDropWork implements Work { private static class TemporaryTableDropWork extends AbstractWork {
private final Queryable persister; private final Queryable persister;
private final SessionImplementor session; private final SessionImplementor session;

View File

@ -43,7 +43,8 @@ import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.id.enhanced.AccessCallback; import org.hibernate.id.enhanced.AccessCallback;
import org.hibernate.id.enhanced.OptimizerFactory; import org.hibernate.id.enhanced.OptimizerFactory;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.AbstractReturningWork;
import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -147,7 +148,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera
} }
public synchronized Serializable generate(final SessionImplementor session, Object obj) { public synchronized Serializable generate(final SessionImplementor session, Object obj) {
final ReturningWork<IntegralDataTypeHolder> work = new ReturningWork<IntegralDataTypeHolder>() { final WorkExecutorVisitable<IntegralDataTypeHolder> work = new AbstractReturningWork<IntegralDataTypeHolder>() {
@Override @Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException { public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass ); IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass );

View File

@ -40,7 +40,7 @@ import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.AbstractReturningWork;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -144,7 +144,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
.getService( JdbcServices.class ) .getService( JdbcServices.class )
.getSqlStatementLogger(); .getSqlStatementLogger();
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
new ReturningWork<IntegralDataTypeHolder>() { new AbstractReturningWork<IntegralDataTypeHolder>() {
@Override @Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException { public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = buildHolder(); IntegralDataTypeHolder value = buildHolder();

View File

@ -50,7 +50,7 @@ import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.AbstractReturningWork;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -465,7 +465,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab
@Override @Override
public IntegralDataTypeHolder getNextValue() { public IntegralDataTypeHolder getNextValue() {
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
new ReturningWork<IntegralDataTypeHolder>() { new AbstractReturningWork<IntegralDataTypeHolder>() {
@Override @Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException { public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() ); IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() );

View File

@ -39,7 +39,7 @@ import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.id.IdentifierGenerationException; import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.IdentifierGeneratorHelper; import org.hibernate.id.IdentifierGeneratorHelper;
import org.hibernate.id.IntegralDataTypeHolder; import org.hibernate.id.IntegralDataTypeHolder;
import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.AbstractReturningWork;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
/** /**
@ -115,7 +115,7 @@ public class TableStructure implements DatabaseStructure {
@Override @Override
public IntegralDataTypeHolder getNextValue() { public IntegralDataTypeHolder getNextValue() {
return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork(
new ReturningWork<IntegralDataTypeHolder>() { new AbstractReturningWork<IntegralDataTypeHolder>() {
@Override @Override
public IntegralDataTypeHolder execute(Connection connection) throws SQLException { public IntegralDataTypeHolder execute(Connection connection) throws SQLException {
final SqlStatementLogger statementLogger = session final SqlStatementLogger statementLogger = session

View File

@ -125,7 +125,10 @@ import org.hibernate.event.SaveOrUpdateEventListener;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.loader.criteria.CriteriaLoader; import org.hibernate.loader.criteria.CriteriaLoader;
import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery; import org.hibernate.loader.custom.CustomQuery;
@ -1975,8 +1978,29 @@ public final class SessionImpl
persistenceContext.setReadOnly(entity, readOnly); persistenceContext.setReadOnly(entity, readOnly);
} }
public void doWork(Work work) throws HibernateException { public void doWork(final Work work) throws HibernateException {
transactionCoordinator.getJdbcCoordinator().coordinateWork( work ); WorkExecutorVisitable<Void> realWork = new WorkExecutorVisitable<Void>() {
@Override
public Void accept(WorkExecutor<Void> workExecutor, Connection connection) throws SQLException {
workExecutor.executeWork( work, connection );
return null;
}
};
doWork( realWork );
}
public <T> T doReturningWork(final ReturningWork<T> work) throws HibernateException {
WorkExecutorVisitable<T> realWork = new WorkExecutorVisitable<T>() {
@Override
public T accept(WorkExecutor<T> workExecutor, Connection connection) throws SQLException {
return workExecutor.executeReturningWork( work, connection );
}
};
return doWork( realWork );
}
private <T> T doWork(WorkExecutorVisitable<T> work) throws HibernateException {
return transactionCoordinator.getJdbcCoordinator().coordinateWork( work );
} }
public void afterScrollOperation() { public void afterScrollOperation() {

View File

@ -0,0 +1,53 @@
/*
* 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;
/**
* An abstract implementation of {@link ReturningWork} that accepts a {@link WorkExecutor}
* visitor for executing a discrete piece of work and returning a result.
*
* This class is intended to be used for work that returns a value when executed.
*
* @author Gail Badner
*/
public abstract class AbstractReturningWork<T> implements ReturningWork<T>, WorkExecutorVisitable<T> {
/**
* Accepts a {@link WorkExecutor} visitor for executing the discrete work
* encapsulated by this work instance using the supplied connection.
*
* @param executor The visitor that executes the work
* @param connection The connection on which to perform the work.
*
* @return the valued returned by {@link #execute(java.sql.Connection)}.
*
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
*/
public T accept(WorkExecutor<T> executor, Connection connection) throws SQLException {
return executor.executeReturningWork( this, connection );
}
}

View File

@ -0,0 +1,57 @@
/*
* 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;
/**
* An abstract implementation of {@link Work} that accepts a {@link WorkExecutor}
* visitor for executing a discrete piece of work.
*
* This class is intended to be used for work that does not return a value when
* executed.
*
* @author Gail Badner
*/
public abstract class AbstractWork implements Work, WorkExecutorVisitable<Void> {
/**
* Accepts a {@link WorkExecutor} visitor for executing the discrete work
* encapsulated by this work instance using the supplied connection.
*
* Because {@link Work} does not return a value when executed
* (via {@link Work#execute(java.sql.Connection)}, this method
* always returns null.
*
* @param connection The connection on which to perform the work.
*
* @return null
*
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
*/
public Void accept(WorkExecutor<Void> executor, Connection connection) throws SQLException {
return executor.executeWork( this, connection );
}
}

View File

@ -0,0 +1,74 @@
/*
* 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 visitor used for executing a discrete piece of work encapsulated in a
* {@link Work} or {@link ReturningWork} instance..
*
* @author Gail Badner
*/
public class WorkExecutor<T> {
/**
* Execute the discrete work encapsulated by a {@link Work} instance
* using the supplied connection.
*
* Because {@link Work} does not return a value when executed
* (via {@link Work#execute(java.sql.Connection)}, this method
* always returns null.
*
* @param work The @link ReturningWork} instance encapsulating the discrete work
* @param connection The connection on which to perform the work.
*
* @return null>.
*
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
*/
public <T> T executeWork(Work work, Connection connection) throws SQLException {
work.execute( connection );
return null;
}
/**
* Execute the discrete work encapsulated by a {@link ReturningWork} instance
* using the supplied connection, returning the result of
* {@link ReturningWork#execute(java.sql.Connection)}
*
* @param work The @link ReturningWork} instance encapsulating the discrete work
* @param connection The connection on which to perform the work.
*
* @return the valued returned by <code>work.execute(connection)</code>.
*
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
*/
public <T> T executeReturningWork(ReturningWork<T> work, Connection connection) throws SQLException {
return work.execute( connection );
}
}

View File

@ -0,0 +1,53 @@
/*
* 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;
/**
* This interface provides a way to execute unrelated "work" objects using
* polymorphism.
*
* Instances of this interface can accept a {@link WorkExecutor} visitor
* for executing a discrete piece of work, and return an implementation-defined
* result.
*
* @author Gail Badner
*/
public interface WorkExecutorVisitable<T> {
/**
* Accepts a {@link WorkExecutor} visitor for executing a discrete
* piece of work, and returns an implementation-defined result..
*
* @param executor The visitor that executes the work.
* @param connection The connection on which to perform the work.
*
* @return an implementation-defined result
*
* @throws SQLException Thrown during execution of the underlying JDBC interaction.
* @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException.
*/
public T accept(WorkExecutor<T> executor, Connection connection) throws SQLException;
}

View File

@ -21,6 +21,7 @@ import java.sql.Statement;
import junit.framework.Test; import junit.framework.Test;
import org.hibernate.JDBCException; import org.hibernate.JDBCException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work; import org.hibernate.jdbc.Work;
import org.hibernate.testing.junit.functional.FunctionalTestCase; import org.hibernate.testing.junit.functional.FunctionalTestCase;
import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite; import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite;
@ -108,6 +109,52 @@ public class GeneralWorkTest extends FunctionalTestCase {
session.close(); session.close();
} }
public void testGeneralReturningUsage() throws Throwable {
Session session = openSession();
session.beginTransaction();
Person p = new Person( "Abe", "Lincoln" );
session.save( p );
session.getTransaction().commit();
session = openSession();
session.beginTransaction();
long count = session.doReturningWork(
new ReturningWork<Long>() {
public Long execute(Connection connection) throws SQLException {
// in this current form, users must handle try/catches themselves for proper resource release
Statement statement = null;
long personCount = 0;
try {
statement = connection.createStatement();
ResultSet resultSet = null;
try {
resultSet = statement.executeQuery( "select count(*) from T_JDBC_PERSON" );
resultSet.next();
personCount = resultSet.getLong( 1 );
assertEquals( 1L, personCount );
}
finally {
releaseQuietly( resultSet );
}
}
finally {
releaseQuietly( statement );
}
return personCount;
}
}
);
session.getTransaction().commit();
session.close();
assertEquals( 1L, count );
session = openSession();
session.beginTransaction();
session.delete( p );
session.getTransaction().commit();
session.close();
}
private void releaseQuietly(Statement statement) { private void releaseQuietly(Statement statement) {
if ( statement == null ) { if ( statement == null ) {
return; return;