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
方法在删除这些“仪式”代码后会变得非常干净。
序言 - 使用 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
方法在删除这些“仪式”代码后会变得非常干净。