diff --git a/hibernate-core/src/main/java/org/hibernate/Session.java b/hibernate-core/src/main/java/org/hibernate/Session.java index 0a16a70e48..e3b30415b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/Session.java +++ b/hibernate-core/src/main/java/org/hibernate/Session.java @@ -25,6 +25,8 @@ package org.hibernate; import java.io.Serializable; import java.sql.Connection; + +import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.Work; import org.hibernate.stat.SessionStatistics; @@ -917,6 +919,17 @@ public interface Session extends Serializable { */ 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 work.execute() + * ({@link ReturningWork.execute(Connection)}/ + * + * @param work The work to be performed. + * @return the result from calling work.execute(). + * @throws HibernateException Generally indicates wrapped {@link java.sql.SQLException} + */ + public T doReturningWork(ReturningWork work) throws HibernateException; + /** * 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). diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 39bf9ae961..cc5125ac17 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -42,8 +42,9 @@ 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.hibernate.jdbc.WorkExecutorVisitable; +import org.hibernate.jdbc.WorkExecutor; + 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 - public T coordinateWork(ReturningWork work) { + public T coordinateWork(WorkExecutorVisitable work) { Connection connection = getLogicalConnection().getDistinctConnectionProxy(); try { - T result = work.execute( connection ); + T result = work.accept( new WorkExecutor(), connection ); getLogicalConnection().afterStatementExecution(); return result; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java index b693081470..6c7b966562 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcCoordinator.java @@ -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.BatchKey; 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.WorkExecutorVisitable; import java.io.Serializable; import java.sql.Connection; @@ -88,9 +86,7 @@ public interface JdbcCoordinator extends Serializable { public void afterTransaction(); - public void coordinateWork(Work work); - - public T coordinateWork(ReturningWork work); + public T coordinateWork(WorkExecutorVisitable work); public void executeBatch(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java index 516b1f7287..be97d17a79 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java @@ -30,8 +30,8 @@ import org.hibernate.HibernateLogger; 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.jdbc.WorkExecutorVisitable; +import org.hibernate.jdbc.WorkExecutor; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.jboss.logging.Logger; @@ -59,7 +59,7 @@ public class JdbcIsolationDelegate implements IsolationDelegate { } @Override - public void delegateWork(Work work, boolean transacted) throws HibernateException { + public T delegateWork(WorkExecutorVisitable work, boolean transacted) throws HibernateException { boolean wasAutoCommit = false; try { // todo : should we use a connection proxy here? @@ -72,69 +72,7 @@ public class JdbcIsolationDelegate implements IsolationDelegate { } } - work.execute( 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 delegateWork(ReturningWork 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 ); + T result = work.accept( new WorkExecutor(), connection ); if ( transacted ) { connection.commit(); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java index 571669b867..5ff3b2f93c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java @@ -34,8 +34,8 @@ import org.hibernate.HibernateLogger; 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.jdbc.WorkExecutorVisitable; +import org.hibernate.jdbc.WorkExecutor; import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; import org.jboss.logging.Logger; @@ -76,109 +76,7 @@ public class JtaIsolationDelegate implements IsolationDelegate { } @Override - public void delegateWork(Work 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 delegateWork(ReturningWork work, boolean transacted) throws HibernateException { + public T delegateWork(WorkExecutorVisitable work, boolean transacted) throws HibernateException { TransactionManager transactionManager = transactionManager(); try { @@ -203,7 +101,7 @@ public class JtaIsolationDelegate implements IsolationDelegate { finally { try { transactionManager.resume( surroundingTransaction ); - LOG.debugf("Surrounding JTA transaction resumed [%s]", 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 @@ -219,7 +117,7 @@ public class JtaIsolationDelegate implements IsolationDelegate { } } - private T doTheWorkInNewTransaction(ReturningWork work, TransactionManager transactionManager) { + private T doTheWorkInNewTransaction(WorkExecutorVisitable work, TransactionManager transactionManager) { T result = null; try { // start the new isolated transaction @@ -248,17 +146,17 @@ public class JtaIsolationDelegate implements IsolationDelegate { return result; } - private T doTheWorkInNoTransaction(ReturningWork work) { + private T doTheWorkInNoTransaction(WorkExecutorVisitable work) { return doTheWork( work ); } - private T doTheWork(ReturningWork work) { + private T doTheWork(WorkExecutorVisitable work) { try { // obtain our isolated connection Connection connection = connectionProvider().getConnection(); try { // do the actual work - return work.execute( connection ); + return work.accept( new WorkExecutor(), connection ); } catch ( HibernateException e ) { throw e; @@ -280,6 +178,5 @@ public class JtaIsolationDelegate implements IsolationDelegate { throw sqlExceptionHelper().convert( e, "unable to obtain isolated JDBC connection" ); } } - } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/IsolationDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/IsolationDelegate.java index e744b0d00a..2bed27d2f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/IsolationDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/spi/IsolationDelegate.java @@ -24,8 +24,7 @@ package org.hibernate.engine.transaction.spi; import org.hibernate.HibernateException; -import org.hibernate.jdbc.ReturningWork; -import org.hibernate.jdbc.Work; +import org.hibernate.jdbc.WorkExecutorVisitable; /** * 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 */ 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. * @@ -53,5 +42,5 @@ public interface IsolationDelegate { * * @throws HibernateException Indicates a problem performing the work. */ - public T delegateWork(ReturningWork work, boolean transacted) throws HibernateException; + public T delegateWork(WorkExecutorVisitable work, boolean transacted) throws HibernateException; } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java b/hibernate-core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java index 96d47adcf3..3098d400e4 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/ast/exec/AbstractStatementExecutor.java @@ -40,7 +40,7 @@ import org.hibernate.event.EventSource; import org.hibernate.hql.ast.HqlSqlWalker; import org.hibernate.hql.ast.SqlGenerator; 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.sql.InsertSelect; import org.hibernate.sql.Select; @@ -137,7 +137,7 @@ public abstract class AbstractStatementExecutor implements StatementExecutor { " from " + persister.getTemporaryIdTableName(); } - private static class TemporaryTableCreationWork implements Work { + private static class TemporaryTableCreationWork extends AbstractWork { private final 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 SessionImplementor session; diff --git a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java index 390b9a1366..c4c86e7700 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/MultipleHiLoPerTableGenerator.java @@ -43,7 +43,8 @@ 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.AbstractReturningWork; +import org.hibernate.jdbc.WorkExecutorVisitable; import org.hibernate.mapping.Table; import org.hibernate.type.Type; import org.jboss.logging.Logger; @@ -147,7 +148,7 @@ public class MultipleHiLoPerTableGenerator implements PersistentIdentifierGenera } public synchronized Serializable generate(final SessionImplementor session, Object obj) { - final ReturningWork work = new ReturningWork() { + final WorkExecutorVisitable work = new AbstractReturningWork() { @Override public IntegralDataTypeHolder execute(Connection connection) throws SQLException { IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( returnClass ); diff --git a/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java index a63d3d3416..cfe9a4104d 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/TableGenerator.java @@ -40,7 +40,7 @@ import org.hibernate.engine.jdbc.internal.FormatStyle; 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.AbstractReturningWork; import org.hibernate.mapping.Table; import org.hibernate.type.Type; import org.jboss.logging.Logger; @@ -144,7 +144,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab .getService( JdbcServices.class ) .getSqlStatementLogger(); return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( - new ReturningWork() { + new AbstractReturningWork() { @Override public IntegralDataTypeHolder execute(Connection connection) throws SQLException { IntegralDataTypeHolder value = buildHolder(); diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java index 845b1d7e06..5197d8e332 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableGenerator.java @@ -50,7 +50,7 @@ import org.hibernate.id.IntegralDataTypeHolder; import org.hibernate.id.PersistentIdentifierGenerator; import org.hibernate.internal.util.StringHelper; 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.type.Type; import org.jboss.logging.Logger; @@ -465,7 +465,7 @@ public class TableGenerator implements PersistentIdentifierGenerator, Configurab @Override public IntegralDataTypeHolder getNextValue() { return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( - new ReturningWork() { + new AbstractReturningWork() { @Override public IntegralDataTypeHolder execute(Connection connection) throws SQLException { IntegralDataTypeHolder value = IdentifierGeneratorHelper.getIntegralDataTypeHolder( identifierType.getReturnedClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java index d7bdd3d084..b478ea3e61 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/TableStructure.java @@ -39,7 +39,7 @@ 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.AbstractReturningWork; import org.jboss.logging.Logger; /** @@ -115,7 +115,7 @@ public class TableStructure implements DatabaseStructure { @Override public IntegralDataTypeHolder getNextValue() { return session.getTransactionCoordinator().getTransaction().createIsolationDelegate().delegateWork( - new ReturningWork() { + new AbstractReturningWork() { @Override public IntegralDataTypeHolder execute(Connection connection) throws SQLException { final SqlStatementLogger statementLogger = session diff --git a/hibernate-core/src/main/java/org/hibernate/impl/SessionImpl.java b/hibernate-core/src/main/java/org/hibernate/impl/SessionImpl.java index 1b8429f9ee..4f9f196b3b 100644 --- a/hibernate-core/src/main/java/org/hibernate/impl/SessionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/impl/SessionImpl.java @@ -125,7 +125,10 @@ import org.hibernate.event.SaveOrUpdateEventListener; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; 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.WorkExecutor; import org.hibernate.loader.criteria.CriteriaLoader; import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomQuery; @@ -1975,8 +1978,29 @@ public final class SessionImpl persistenceContext.setReadOnly(entity, readOnly); } - public void doWork(Work work) throws HibernateException { - transactionCoordinator.getJdbcCoordinator().coordinateWork( work ); + public void doWork(final Work work) throws HibernateException { + WorkExecutorVisitable realWork = new WorkExecutorVisitable() { + @Override + public Void accept(WorkExecutor workExecutor, Connection connection) throws SQLException { + workExecutor.executeWork( work, connection ); + return null; + } + }; + doWork( realWork ); + } + + public T doReturningWork(final ReturningWork work) throws HibernateException { + WorkExecutorVisitable realWork = new WorkExecutorVisitable() { + @Override + public T accept(WorkExecutor workExecutor, Connection connection) throws SQLException { + return workExecutor.executeReturningWork( work, connection ); + } + }; + return doWork( realWork ); + } + + private T doWork(WorkExecutorVisitable work) throws HibernateException { + return transactionCoordinator.getJdbcCoordinator().coordinateWork( work ); } public void afterScrollOperation() { diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractReturningWork.java b/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractReturningWork.java new file mode 100644 index 0000000000..d7d1191c6e --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractReturningWork.java @@ -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 implements ReturningWork, WorkExecutorVisitable { + /** + * 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 executor, Connection connection) throws SQLException { + return executor.executeReturningWork( this, connection ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractWork.java b/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractWork.java new file mode 100644 index 0000000000..22c6d20f7b --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/AbstractWork.java @@ -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 { + /** + * 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 executor, Connection connection) throws SQLException { + return executor.executeWork( this, connection ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/WorkExecutor.java b/hibernate-core/src/main/java/org/hibernate/jdbc/WorkExecutor.java new file mode 100644 index 0000000000..ba2d6b3b6c --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/WorkExecutor.java @@ -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 { + + /** + * 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 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 work.execute(connection). + * + * @throws SQLException Thrown during execution of the underlying JDBC interaction. + * @throws org.hibernate.HibernateException Generally indicates a wrapped SQLException. + */ + public T executeReturningWork(ReturningWork work, Connection connection) throws SQLException { + return work.execute( connection ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/jdbc/WorkExecutorVisitable.java b/hibernate-core/src/main/java/org/hibernate/jdbc/WorkExecutorVisitable.java new file mode 100644 index 0000000000..e6f5c2d391 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/jdbc/WorkExecutorVisitable.java @@ -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 { + /** + * 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 executor, Connection connection) throws SQLException; +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/jdbc/GeneralWorkTest.java b/hibernate-core/src/test/java/org/hibernate/test/jdbc/GeneralWorkTest.java index c0379391fb..acb1648090 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jdbc/GeneralWorkTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jdbc/GeneralWorkTest.java @@ -21,6 +21,7 @@ import java.sql.Statement; import junit.framework.Test; import org.hibernate.JDBCException; import org.hibernate.Session; +import org.hibernate.jdbc.ReturningWork; import org.hibernate.jdbc.Work; import org.hibernate.testing.junit.functional.FunctionalTestCase; import org.hibernate.testing.junit.functional.FunctionalTestClassTestSuite; @@ -108,6 +109,52 @@ public class GeneralWorkTest extends FunctionalTestCase { 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() { + 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) { if ( statement == null ) { return;