All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate Maven / Gradle / Ivy

There is a newer version: 5.6.15.Final
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.resource.transaction.backend.jta.internal;

import jakarta.transaction.NotSupportedException;
import jakarta.transaction.SystemException;
import jakarta.transaction.Transaction;
import jakarta.transaction.TransactionManager;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.concurrent.Callable;

import org.hibernate.HibernateException;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.transaction.spi.IsolationDelegate;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.jdbc.WorkExecutorVisitable;

/**
 * An isolation delegate for JTA environments.
 *
 * @author Andrea Boriero
 */
public class JtaIsolationDelegate implements IsolationDelegate {
	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( JtaIsolationDelegate.class );

	private final JdbcConnectionAccess connectionAccess;
	private final SqlExceptionHelper sqlExceptionHelper;
	private final TransactionManager transactionManager;

	public JtaIsolationDelegate(
			JdbcConnectionAccess connectionAccess,
			SqlExceptionHelper sqlExceptionHelper,
			TransactionManager transactionManager) {
		this.connectionAccess = connectionAccess;
		this.sqlExceptionHelper = sqlExceptionHelper;
		this.transactionManager = transactionManager;
	}

	protected JdbcConnectionAccess jdbcConnectionAccess() {
		return this.connectionAccess;
	}

	protected SqlExceptionHelper sqlExceptionHelper() {
		return this.sqlExceptionHelper;
	}

	@Override
	public  T delegateWork(final WorkExecutorVisitable work, final boolean transacted) throws HibernateException {
		return doInSuspendedTransaction(new HibernateCallable() {
			@Override
			public T call() throws HibernateException {
				HibernateCallable workCallable = new HibernateCallable() {
					@Override
					public T call() throws HibernateException {
						return doTheWork(work);
					}
				};
				if ( transacted ) {
					return doInNewTransaction( workCallable, transactionManager );
				}
				else {
					return workCallable.call();
				}
			}
		});
	}

	@Override
	public  T delegateCallable(final Callable callable, final boolean transacted) throws HibernateException {
		return doInSuspendedTransaction(new HibernateCallable() {
			@Override
			public T call() throws HibernateException {
				HibernateCallable workCallable = new HibernateCallable() {
					@Override
					public T call() throws HibernateException {
						try {
							return callable.call();
						}
						catch (HibernateException e) {
							throw e;
						}
						catch (Exception e) {
							throw new HibernateException(e);
						}
					}
				};
				if ( transacted ) {
					return doInNewTransaction( workCallable, transactionManager );
				}
				else {
					return workCallable.call();
				}
			}
		});
	}

	private  T doInSuspendedTransaction(HibernateCallable callable) {
		try {
			// First we suspend any current JTA transaction
			Transaction surroundingTransaction = transactionManager.suspend();
			LOG.debugf( "Surrounding JTA transaction suspended [%s]", surroundingTransaction );

			boolean hadProblems = false;
			try {
				return callable.call();
			}
			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  T doInNewTransaction(HibernateCallable callable, TransactionManager transactionManager) {
		try {
			// start the new isolated transaction
			transactionManager.begin();

			try {
				T result = callable.call();
				// if everything went ok, commit the isolated transaction
				transactionManager.commit();
				return result;
			}
			catch (Exception e) {
				try {
					transactionManager.rollback();
				}
				catch (Exception ignore) {
					LOG.unableToRollbackIsolatedTransaction( e, ignore );
				}
				throw new HibernateException( "Could not apply work", e );
			}
		}
		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  T doTheWork(WorkExecutorVisitable work) {
		try {
			// obtain our isolated connection
			Connection connection = jdbcConnectionAccess().obtainConnection();
			try {
				// do the actual work
				return work.accept( new WorkExecutor(), 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)
					jdbcConnectionAccess().releaseConnection( connection );
				}
				catch (Throwable ignore) {
					LOG.unableToReleaseIsolatedConnection( ignore );
				}
			}
		}
		catch (SQLException e) {
			throw sqlExceptionHelper().convert( e, "unable to obtain isolated JDBC connection" );
		}
	}

	// Callable that does not throw Exception; in Java <8 there's no Supplier
	private interface HibernateCallable {
		T call() throws HibernateException;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy