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

net.leanix.dropkit.persistence.TransactionHandler Maven / Gradle / Ivy

package net.leanix.dropkit.persistence;

import com.google.inject.Inject;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import javax.inject.Singleton;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.context.internal.ManagedSessionContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A helper class used to wrap the execution of code within a hibernate transaction or session.
 * 
 */
@Singleton
public class TransactionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(TransactionHandler.class);

    protected final SessionFactory sessionFactory;

    @Inject
    public TransactionHandler(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public boolean hasSession() {
        return ManagedSessionContext.hasBind(sessionFactory);
    }

    public Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    /**
     * Execute a block of code within a hibernate session. If the hibernate session for the current thread is already opened before,
     * the bock of code uses this session. Otherwise, a new session is opened, the code is executed and the session will be closed at the end.
     * @param supplier
     * @return
     * @throws Exception
     */
    public  T runInSession(Supplier supplier) {

        if (hasSession()) {
            // this can happened, if the code block is called from a resource that have the @UnitOfWork annotation.
            LOG.debug("using existing session");
            return supplier.get();
        }

        Session session = openSession();
        LOG.debug("opened Hibernate session {}", System.identityHashCode(session));

        try {
            return supplier.get();
        } catch (Throwable t) {
            LOG.debug("we got a throwable here", t);
            throw t;
        } finally {
            closeSession(session);
            LOG.debug("closed Hibernate session {}", System.identityHashCode(session));
        }
    }

    /**
     * Executes a block of code within a transaction.
     *
     * @param supplier
     * @return
     * @throws Exception
     */
    public  K runInTransaction(Supplier supplier) {
        Session session = openSession();
        Transaction transaction = session.beginTransaction();

        boolean success = false;
        K result = null;
        try {
            result = supplier.get();
            success = true;
        } catch (Throwable e) {
            throw e;
        } finally {
            if (success) {
                try {
                    transaction.commit();
                } catch (Throwable t) {
                    LOG.error("can not commit transaction", t);
                } finally {
                    // close session anyway to avoid db connection leaks
                    closeSession(session);
                }
            } else {
                try {
                    transaction.rollback();
                } catch (Throwable t) {
                    LOG.error("can not rollback transaction", t);
                } finally {
                    // if you make rollback, clear is needed
                    session.clear();
                    // close session anyway to avoid db connection leaks
                    closeSession(session);
                }
            }
        }

        return result;
    }

    /**
     * Calls the callable within a transaction.
     * If the {@code callable} throws an {@link Exception} the exception is catched
     * and rethrown as {@link RuntimeException} with the exception as the cause.
     *
     * @param callable
     * @param 
     * @return
     * @deprecated Please use {@linkplain #runInTransaction(Supplier)} instead, which provides a more clear exception handling.

    */
    public  V callInTransaction(Callable callable) {
        return runInTransaction(() -> {
            try {
                return callable.call();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * Executes a runnable within a transaction.
     * 
     * @param runnable
     */
    public void executeInTransaction(Runnable runnable) {
        runInTransaction(() -> {
            runnable.run();
            return null;
        });
    }

    /**
     * Opens a new session and binds the factory to ManagedSessionContext.
     *
     * @return
     */
    private synchronized Session openSession() {
        Session openedSession = sessionFactory.openSession();
        ManagedSessionContext.bind(openedSession);
        return openedSession;
    }

    /**
     * Closes the session and removes the binding.
     *
     * Calling this is a must, so best call it in a finally block.
     */
    private synchronized void closeSession(Session openedSession) {
        openedSession.close();
        ManagedSessionContext.unbind(sessionFactory);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy