Spring @transactional 与休眠问题 - 没有活动事务无效

Spring @transactional with hibernate issue - not valid without an active transaction

序言 - 使用 Spring

我对 spring @Transactional 注释的用途感到困惑。我从我读过的几篇博客文章中想到,它可以让我简化事务管理并只写这个,它会自动处理 connection/commit/rollback:

public class DaoImpl implements Dao {
   @Autowired
   private SessionFactory sessionFactory;

    @Transactional
    public void saveOrUpdateOne(final AdditionalDataItem item) {
        Session session = sessionFactory.getCurrentSession();
            session.saveOrUpdate(p_item);
    }
}

然而,这给了我一个例外:“调用方法 'saveOrUpdate' 在没有活动事务的情况下无效”

如果我改为将保存方法更改为此,一切都有效 - 所以我的问题是,@Transactional 在做什么?

@Override
@Transactional
public void saveOrUpdateOne(final AdditionalDataItem p_item) {
    Session session = null;
    Transaction trans = null;
    try {
        session = sessionFactory.getCurrentSession();
        trans = session.beginTransaction();
        TransactionStatus status = trans.getStatus();
        session.saveOrUpdate(p_item);
        trans.commit();
    } catch (Exception e) {
        LOGGER.error("Exception saving data: {}", e.getMessage());
        if (trans != null) {
            try {
                trans.rollback();
            } catch (RuntimeException rbe) {
                LOGGER.error("Couldn’t roll back transaction", rbe);
            }
        }
    } finally {
        if (session != null && session.isOpen()) {
            try {
                session.close();
            } catch (HibernateException ne) {
                LOGGER.error("Couldn’t close session", ne);
            }
        }
    }
}

作为参考,我将 Java 11 与 Spring Framework 5.3.7 和 hibernate 5.5.7 一起使用,并具有适当的 dao、会话工厂和 tx 管理器 bean:

    <bean id="sessionFactory" 
      class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
    <property name="dataSource" ref="${sessionFactory.datasource}" />
    <property name="configLocation" value="${sessionFactory.configLocation}" />
</bean>

<bean id="txManager" 
  class="org.springframework.orm.hibernate5.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="Dao" class="com.xxxxx.dao.DaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

如果你想更新 2 个表,如果其中一个失败,另一个将自动回滚,则使用@Transactional 你可以使用上面的方法 您可以使用存储库 class

的 bean 调用保存或更新

因为你还没有启用@Transactional,调用session.saveOrUpdate()的时候也没有开始交易,所以报错'not valid without an active transaction'.

要启用 @Transactional,您必须使用 @EnableTransactionManagement 或添加 <tx:annotation-driven/>,以防您使用 XML 配置。它基本上为您执行以下操作 (source) :

@EnableTransactionManagement and <tx:annotation-driven/> are responsible for registering the necessary Spring components that power annotation-driven transaction management, such as the TransactionInterceptor and the proxy- or AspectJ-based advice that weaves the interceptor into the call stack when JdbcFooRepository's @Transactional methods are invoked.

您的工作示例之所以有效,是因为您自己手动管理事务。这与 @Transactional 无关,因为您从未启用它。

以工作代码为例,@Transactional为您做的是,您不再需要手动编写以下交易代码,因为它们都将封装在TransactionInterceptor和基于 AOP 围绕 @Transactional 方法执行:

public Object invoke() {
    Session session = null;
    Transaction trans = null;
    try {
        session = sessionFactory.getCurrentSession();
        trans = session.beginTransaction();
        TransactionStatus status = trans.getStatus();
             
        /***********************************************/
         Here it will invoke your @Transactional Method
        /************************************************/

        trans.commit();
    } catch (Exception e) {
        LOGGER.error("Exception saving data: {}", e.getMessage());
        if (trans != null) {
            try {
                trans.rollback();
            } catch (RuntimeException rbe) {
                LOGGER.error("Couldn’t roll back transaction", rbe);
            }
        }
    } finally {
        if (session != null && session.isOpen()) {
            try {
                session.close();
            } catch (HibernateException ne) {
                LOGGER.error("Couldn’t close session", ne);
            }
        }
    }
}

所以你可以看到你的@Transactional方法在删除这些“仪式”代码后会变得非常干净。