当前位置: 首页 > 知识库问答 >
问题:

在弹靴中使用Hibernate拦截器的问题

章茂
2023-03-14

我想在Spring引导中使用hibernate拦截器,以便在提交事务后使用postTransactionComplments()方法执行某些操作。

我遵循如何在Spring Boot中使用Spring管理的Hibernate拦截器进行配置(我只是在application.properties中添加Spring.jpa.properties.Hibernate.ejb.interceptor=com.lc.demo.inteceptor.MyInteceptor

拦截器可以工作,但当我尝试在方法afterTransactionCompletion()中获取事务状态时,仍然存在问题,它总是未处于活动状态(我希望它可以提交):

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中。冬眠资源jdbc。内部的LogicalConnectionProvidedImpl扩展了AbstractLogicalConnectionImplementorcommit()方法调用afterCompletion()方法,该方法调用重置连接(布尔初始自动提交)以设置事务状态未处于活动状态

    /*
 * 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

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lc</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

共有3个答案

华永新
2023-03-14

而我也建议使用Spring TransactionSynchronization。如果无法使用(或不希望使用),有两件事需要注意:

这意味着实际上可以使用此方法来识别事务在提交之前是否正常,并将其保存在某个临时状态(理想情况下是Threadlocal)。

在事务回滚的情况下,事务的状态不是“NOT_ACTIVE”,而是“MARKED_ROLLBACK”,因此状态“NOT_ACTIVE”与实际调用的前事务完成相结合,可以用来确定事务是否成功。

亢保赫
2023-03-14

我使用了Hovanesyan的答案,它是有效的,现在让我完整地描述一下我在这里做了什么:

我试图将其他人的代码迁移到springboot,代码使用Hibernatepersistence.xml拦截器使用线程本地存储事务中的所有实体,当事务提交时,选择一个“最佳”实体给用户发电子邮件,否则什么都不做,清除线程本地,代码是:

    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()方法中,事务状态始终不是活动的,现在我使用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
}

新的智能接收器还需要通过AOP进行全局配置:

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

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

现在看来一切都很好,我会继续测试。

胡昊
2023-03-14

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

默认的void afterCommit()

在事务提交后调用。可以在主事务成功提交后立即执行进一步的操作。

使用方法:

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

您还可以探索TransactionSynChronizationAdapter-以类似的方式,您可以实现您自己的AfterCommission Execator,它实现了Execator接口,并扩展了TransactionSynChronizationAdapter并覆盖了postCommit()方法。

 类似资料:
  • 在我的应用程序中,消息从一个队列移动到另一个队列,我想在日志中添加消息ID。我试图找出是否可以在实际处理之前拦截消息,并在MDC字段中设置消息ID,以便可以跨所有队列跟踪此消息。到目前为止,我还没有取得成功。是否存在任何现有解决方案?

  • 问题内容: 我尝试了一些来自hibernate 4.0拦截器的 代码,该代码为使用会话级拦截器提供了以下代码: 但是,我同时检查了hibernate-core 4.0源代码和onliehibernate 4.0 java- doc ,该类没有方法,但是hibernate 3.6 java- doc确实具有此方法。 有人知道该方法移到哪里吗?如果已弃用,为什么文档仍将其保留在教程文档中?在4.0中如

  • 我使用Spring Boot v2.0.3和Hibernate 5.2。我想创建一个Hibernate拦截器,在我的一个实体字段中生成一个值,基于我数据库中的一个唯一数字,然后保存。 我获取了一些在线资源,并最终扩展了Hibernate空拦截器,以覆盖onSave方法。 在我的申请中。yml配置文件,我添加了以下键: 当我尝试保存票证时,我可以看到方法onSave被正确调用,但是由于拦截器完全由H

  • 问题内容: 如何在Hibernate拦截器中获取Hibernate会话? 我正在尝试使用Hibernate通过组织ID透明地强制执行数据访问。我设置了一个全局筛选器,以按组织ID筛选所有查询。现在,在保存/更新之前,我需要使用实体拦截器在所有实体上设置组织ID。 组织ID来自HttpSession 我已经在Hibernate会话中将Organizational Id设置为Filter属性,我想在我

  • 是否可以在Spring Boot中集成Spring托管Hibernate拦截器(http://docs.jboss.org/Hibernate/orm/4.3/manual/en-us/html/ch14.html)? 我正在使用Spring Data JPA和Spring Data REST并需要一个Hibernate拦截器来处理实体上特定字段的更新。 对于标准的JPA事件,不可能获得旧值,因此

  • 1. 前言 拦截器这个名词定义的非常形象,就像导弹要攻击目标的时候,可能会被先进的反导系统拦截,此处的反导系统就是一种拦截器。 我们开发的应用,对外暴露的是控制器中定义的 API 方法,我们可以在 API 方法的外围放置拦截器,所有对 API 的访问都可以通过拦截器进行过滤。 OK,那么这样的拦截有什么意义吗,其实已经很明显了,反导系统可以保护目标的安全并识别对目标的攻击行为。同理,拦截器可以跟踪