在springboot中使用休眠拦截器的问题

时间:2019-02-13 09:51:33

标签: java hibernate spring-boot interceptor

我想在春季启动时使用休眠拦截器,以便在事务提交后使用afterTransactionCompletion()方法执行某些操作。

我按照How to use Spring managed Hibernate interceptors in Spring Boot进行配置(我只需在spring.jpa.properties.hibernate.ejb.interceptor=com.lc.demo.inteceptor.MyInteceptor中添加application.properties

拦截器可以工作,但是当我尝试在方法afterTransactionCompletion()中获取事务状态时仍然存在问题,它始终为NOT_ACTIVE(我希望它可能是COMMITTED):

import static org.hibernate.resource.transaction.spi.TransactionStatus.COMMITTED;

import org.hibernate.EmptyInterceptor;
import org.hibernate.Transaction;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.springframework.stereotype.Component;


@Component
public class MyInteceptor extends EmptyInterceptor{

    private static final long serialVersionUID = -7992825362361127331L;

    @Override
    public void afterTransactionCompletion(Transaction tx) {
        //The status is always NOT_ACTIVE
        TransactionStatus status = tx.getStatus(); //
        if (tx.getStatus() == COMMITTED) {
            System.out.println("This is what I want to do");
        } else {
            System.out.println("This is what I do not want");
        }
    }

    @Override
    public void beforeTransactionCompletion(Transaction tx) {
        // The status is ACTIVE
        TransactionStatus status = tx.getStatus();
        System.out.println(status);
    }
}

我尝试对其进行调试,并发现在调用afterTransactionCompletion()之前,

在扩展org.hibernate.resource.jdbc.internal.LogicalConnectionProvidedImpl的{​​{1}}中,AbstractLogicalConnectionImplementor方法调用commit()方法,该方法调用afterCompletion()以设置交易状态resetConnection(boolean initiallyAutoCommit)

NOT_ACTIVE

有人可以帮我解决这个问题吗?非常感谢。 这是我的 /* * 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 <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.resource.jdbc.internal; import java.sql.Connection; import java.sql.SQLException; import org.hibernate.TransactionException; import org.hibernate.resource.jdbc.ResourceRegistry; import org.hibernate.resource.jdbc.spi.LogicalConnectionImplementor; import org.hibernate.resource.jdbc.spi.PhysicalJdbcTransaction; import org.hibernate.resource.transaction.spi.TransactionStatus; import org.jboss.logging.Logger; /** * @author Steve Ebersole */ public abstract class AbstractLogicalConnectionImplementor implements LogicalConnectionImplementor, PhysicalJdbcTransaction { private static final Logger log = Logger.getLogger( AbstractLogicalConnectionImplementor.class ); private TransactionStatus status = TransactionStatus.NOT_ACTIVE; protected ResourceRegistry resourceRegistry; @Override public PhysicalJdbcTransaction getPhysicalJdbcTransaction() { errorIfClosed(); return this; } protected void errorIfClosed() { if ( !isOpen() ) { throw new IllegalStateException( this.toString() + " is closed" ); } } @Override public ResourceRegistry getResourceRegistry() { return resourceRegistry; } @Override public void afterStatement() { log.trace( "LogicalConnection#afterStatement" ); } @Override public void afterTransaction() { log.trace( "LogicalConnection#afterTransaction" ); resourceRegistry.releaseResources(); } // PhysicalJdbcTransaction impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ protected abstract Connection getConnectionForTransactionManagement(); @Override public void begin() { try { if ( !doConnectionsFromProviderHaveAutoCommitDisabled() ) { log.trace( "Preparing to begin transaction via JDBC Connection.setAutoCommit(false)" ); getConnectionForTransactionManagement().setAutoCommit( false ); log.trace( "Transaction begun via JDBC Connection.setAutoCommit(false)" ); } status = TransactionStatus.ACTIVE; } catch( SQLException e ) { throw new TransactionException( "JDBC begin transaction failed: ", e ); } } @Override public void commit() { try { log.trace( "Preparing to commit transaction via JDBC Connection.commit()" ); getConnectionForTransactionManagement().commit(); status = TransactionStatus.COMMITTED; log.trace( "Transaction committed via JDBC Connection.commit()" ); } catch( SQLException e ) { status = TransactionStatus.FAILED_COMMIT; throw new TransactionException( "Unable to commit against JDBC Connection", e ); } afterCompletion(); } protected void afterCompletion() { // by default, nothing to do } protected void resetConnection(boolean initiallyAutoCommit) { try { if ( initiallyAutoCommit ) { log.trace( "re-enabling auto-commit on JDBC Connection after completion of JDBC-based transaction" ); getConnectionForTransactionManagement().setAutoCommit( true ); status = TransactionStatus.NOT_ACTIVE; } } catch ( Exception e ) { log.debug( "Could not re-enable auto-commit on JDBC Connection after completion of JDBC-based transaction : " + e ); } } @Override public void rollback() { try { log.trace( "Preparing to rollback transaction via JDBC Connection.rollback()" ); getConnectionForTransactionManagement().rollback(); status = TransactionStatus.ROLLED_BACK; log.trace( "Transaction rolled-back via JDBC Connection.rollback()" ); } catch( SQLException e ) { status = TransactionStatus.FAILED_ROLLBACK; throw new TransactionException( "Unable to rollback against JDBC Connection", e ); } afterCompletion(); } protected static boolean determineInitialAutoCommitMode(Connection providedConnection) { try { return providedConnection.getAutoCommit(); } catch (SQLException e) { log.debug( "Unable to ascertain initial auto-commit state of provided connection; assuming auto-commit" ); return true; } } @Override public TransactionStatus getStatus(){ return status; } protected boolean doConnectionsFromProviderHaveAutoCommitDisabled() { return false; } }

pom.xml

2 个答案:

答案 0 :(得分:0)

如果您使用的是Spring交易,则可以利用TransactionSynchronization并使用afterCommit()

  

默认空值afterCommit()

     

在事务提交后调用。可以执行进一步的操作   成功完成主要交易之后。

用法:

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization(){
           void afterCommit(){
                //do your thing
           }
})

您还可以探索TransactionSynchronizationAdapter-以类似的方式来实现自己的“ AfterCommitExecutor”,该实现实现Executor接口并扩展TransactionSynchronizationAdapter并覆盖afterCommit()方法。

答案 1 :(得分:0)

我使用了hovanessyan的答案,它很有效,现在让我完整地描述一下我在这里所做的事情:

我试图将其他人的代码迁移到springboot,代码使用带有persistence.xml的休眠模式,拦截器使用threadlocal来存储事务中的所有实体,提交事务后,选择一个“最佳”实体电子邮件用户,否则不执行任何操作并清除threadlocal,代码为:

    public class MyInterceptor extends EmptyInterceptor {

    private static final long serialVersionUID = -7992825362361127331L;

    //The MyThreadLocal used to store all the entities in a transaction, when the transaction
    //committed, the interceptor will choose the "best" entity to email user
    private static MyThreadLocal myThreadLocal;

    public static void setMyThreadLocal(MyThreadLocal mTL) {
        MyInterceptor.myThreadLocal = mTL;
    }

    @Override
    public void afterTransactionCompletion(Transaction tx) {
        TransactionStatus status = tx.getStatus();
        if (tx.getStatus() == COMMITTED) {
            MyThreadLocal.selectTheBestEntityToEmailUser();
        } else {
            MyThreadLocal.clear();
        }
    }

    @Override
    public void beforeTransactionCompletion(Transaction tx) {
        TransactionStatus status = tx.getStatus();
        MyThreadLocal.beforeTransactionCompletion();
    }

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        MyThreadLocal.resourceAdded((Entity) entity);
        return false;
    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
        Diff diff = new Diff(previousState, currentState, propertyNames);
        MyThreadLocal.resourceUpdated((Entity) entity, diff);
        return false;
    }

    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        MyThreadLocal.resourceRemoved((Entity) entity);
    }

    @Override
    public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {
        if (!(collection instanceof PersistentCollection)) {
            LOGGER.e("Unsupported collection type: {}", collection.getClass());
            return;
        }
        Entity owner = (Entity) ((PersistentCollection) collection).getOwner();
        String role = ((PersistentCollection) collection).getRole();
        MyThreadLocal.collectionResourceUpdated(owner, role);
    }
}

但是在afterTransactionCompletion()方法中,事务状态始终为NOT_ACTIVE,现在我使用TransactionSynchronization接口只是为了替换afterTransactionCompletion()方法:

    public class MyInterceptor extends EmptyInterceptor implements TransactionSynchronization {

    //the mothod of TransactionSynchronization interface
    @Override
    public void afterCompletion(int status) {
        if (status == STATUS_COMMITTED) {
            MyThreadLocal.selectTheBestEntityToEmailUser();
        } else {
            MyThreadLocal.clear();
        }
    }

    //the old code which works not well
    @Override
    public void afterTransactionCompletion(Transaction tx) {
        TransactionStatus status = tx.getStatus();
        if (tx.getStatus() == COMMITTED) {
            MyThreadLocal.selectTheBestEntityToEmailUser();
        } else {
            MyThreadLocal.clear();
        }
    }

   ...... other codes
}

并且新的Inteceptor也需要由AOP全局配置:

@Component
@Aspect
public class InterceptorInit{
    @Autowired
    private MyInteceptor mI;

    @Before("@annotation(org.springframework.transaction.annotation.Transactional)")
    public void registerTransactionSyncrhonization() {
        TransactionSynchronizationManager.registerSynchronization(mI);
    }
}

现在看来一切正常,我将继续测试。