简体   繁体   中英

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

Preamble - using Spring

I am confused as to the purpose of the spring @Transactional annotation. I thought from a few blog posts I've read that it would allow me to simplify transaction management and just write this, and it would handle connection/commit/rollback automagically:

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

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

However this gives me an exception: " Calling method 'saveOrUpdate' is not valid without an active transaction"

If I instead change the save method to this, it all works - so my question is, what is @Transactional doing?

@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);
            }
        }
    }
}

For reference, I'm using Java 11 with Spring Framework 5.3.7 and hibernate 5.5.7 and have appropriate dao, session factory and tx manager beans:

    <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 如果其中一个失败,另一个将自动回滚,您可以使用上面的方法,并且可以使用 Repository 类的 bean 调用 save 或 update

You do not enable @Transactional yet and so it gives you the error 'not valid without an active transaction' .

To enable it , you have to use @EnableTransactionManagement or add <tx:annotation-driven/> in case you are using XML configuration . It basically does the following for you ( 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.

Your working example works because you manually manage the transaction by yourself .It is nothing to do with @Transactional since you never enable it.

Take the working codes as an example , what @Transactional does for you is that you no longer need to manually write the following transaction codes as all of them will be encapsulated in the TransactionInterceptor and execute around your @Transactional method based on AOP :

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);
            }
        }
    }
}

So you can see that your @Transactional method will become very clean after removing these "ceremony" codes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM