java Spring Transaction - 当一个数据库更新失败时自动回滚以前的数据库更新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3880590/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 03:49:30  来源:igfitidea点击:

Spring Transaction - automatic rollback of previous db updates when one db update failes

javahibernatespringtransactions

提问by Vaandu

I am writing a simple application (Spring + Hibernate + PostgreSql db). I am just trying to construct a sample object and persist in db.

我正在编写一个简单的应用程序(Spring + Hibernate + PostgreSql db)。我只是想构建一个示例对象并保留在 db 中。

I run a simple java class main method where i have loaded the applicationContext and have got reference to the service class as below

我运行了一个简单的 java 类 main 方法,我在其中加载了 applicationContext 并获得了对服务类的引用,如下所示

TestService srv = (TestService)factory.getBean("testService");  

Application Context - context :

应用程序上下文 - 上下文:

<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactoryVsm" />
</bean>

<bean id="testService" class="com.test.service.TestServiceImpl">
    <property name="testDao" ref="testDao"/>
</bean>

<bean id="testDao" class="com.test.dao.TestDaoImpl>
    <property name="sessionFactory" ref="sessionFactoryVsm"/>
</bean>


In TestService I have injected TestDao. In test service method I have constructed to employee objects emp1 and emp2 and calling dao twice to update.

在 TestService 中,我注入了 TestDao。在测试服务方法中,我构造了员工对象 emp1 和 emp2 并调用了 dao 两次进行更新。

TestDaoImpl code:

TestDaoImpl 代码:

public void saveOrUpdate(BaseDomainModel baseObject) {

    Session session = null;
    try {
        session = getHibernateTemplate().getSessionFactory().openSession();
        session.saveOrUpdate(baseObject);
        session.flush();
    } catch (Exception e) {
        logger.error("Generic DAO:saveOrUpdate::" + e);
        e.printStackTrace();
    } finally {
        if (session != null) {
            session.close();
        }
    }

}

When emp2 update fails emp1 should also fail. How do I do that. Please advice

当 emp2 更新失败时,emp1 也应该失败。我怎么做。请指教

Thanks in advance

提前致谢

Updated :

更新 :

Thanks Nanda. I tried Declarative transaction. But it is not working. emp1 gets persisted and not rolled back eveb second dao call fails. I have added transaction advice to the method.

谢谢南达。我尝试了声明式事务。但它不起作用。emp1 被持久化并且没有回滚 eveb 第二个 dao 调用失败。我在方法中添加了交易建议。

to test if the transaction advice is applied or not i changed the propagation to "NOT_SUPPORTED". but still emp1 gets persisted. the expectation is we should have got Transaction Not Supported type of exception. please advice .

为了测试是否应用了交易建议,我将传播更改为“NOT_SUPPORTED”。但 emp1 仍然存在。期望是我们应该得到 Transaction Not Supported 类型的异常。请指教 。

UPDATED

更新

@seanizer - Thanks for the update. I have even tried adding

@seanizer - 感谢您的更新。我什至尝试添加

@Transactional(propagation=Propagation.NOT_SUPPORTED)
public void saveEmp(Employee emp)

to that service method. But it didn't work. Moreover iterating the collection<BaseDomainModel>hold good only if i need to call one dao. If in case i have to call two different dao to persist obj1 and obj2- this may not help. Just to check if the transaction is getting applied I get @Transactional(propagation=Propagation.NOT_SUPPORTED). But still obj1 got persisted. I just doubt if the xml configuration/ annotation given is correct. please check

到那个服务方法。但它没有用。此外,collection<BaseDomainModel>仅当我需要调用一个 dao 时才迭代保持良好。如果我必须调用两个不同的 dao 来持久化 obj1 和 obj2 - 这可能无济于事。只是为了检查交易是否得到应用,我得到了 @Transactional(propagation=Propagation.NOT_SUPPORTED)。但 obj1 仍然存在。我只是怀疑给出的 xml 配置/注释是否正确。请检查

<bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactoryVsm" />
    </bean>

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
         <tx:method name="saveEmp" propagation="REQUIRED" rollback-for="Exception"/>
        <tx:method name="*"/>
    </tx:attributes>
 </tx:advice>   
 <aop:config>
    <aop:pointcut id="testServiceOperation" expression="execution(*com.test.service.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="testServiceOperation"/>
  </aop:config> 

I am using org.springframework.orm.hibernate3.HibernateTransactionManager for the transactionManager. Is this correct ?

我正在使用 org.springframework.orm.hibernate3.HibernateTransactionManager 作为 transactionManager。这个对吗 ?



Updated

更新

I have created my exception class myRuntimeExp extending from RuntimeException and throwing the same from Dao method to the service method. but still the rollback is not happening. I just doubt if I have correctly given the configurations in the applnContext.xml. Can someone help me how to check if the transaction advice / annotation is being applied to the method or not? is there any way of running it in a debugging mode and check

我创建了从 RuntimeException 扩展的异常类 myRuntimeExp 并将其从 Dao 方法抛出到服务方法。但仍然没有发生回滚。我只是怀疑我是否在 applnContext.xml 中正确给出了配置。有人可以帮我检查交易建议/注释是否应用于该方法吗?有没有办法在调试模式下运行它并检查

Issue :

问题 :

I was using

我正在使用

session = getHibernateTemplate().getSessionFactory().openSession();

But it should be current session and it is working fine.

但它应该是当前会话并且工作正常。

session = getHibernateTemplate().getSessionFactory().getCurrentSession();

采纳答案by Sean Patrick Floyd

If you use declarative transaction management, you can lose most of this boilerplate:

如果您使用声明式事务管理,您可能会丢失大部分样板文件:

TestDaoImpl:

TestDaoImpl:

private SessionFactory sessionFactory;

public void setSessionFactory(SessionFactory f){
    this.sessionFactory = f;
}

public void saveOrUpdate(BaseDomainModel baseObject) {
    Session session = sessionFactory.getCurrentSession();
    session.saveOrUpdate(baseObject);
}

And you can control the transaction handling from the service layer using @Transactional(or xml configuration)

您可以使用@Transactional(或 xml 配置)从服务层控制事务处理

TestServiceImpl:

测试服务实现:

private TestDao testDao;

public void setTestDao(TestDao  d){
    this.testDao = d;
}

@Transactional // one transaction for multiple operations
public void someServiceMethod(Collection<BaseDomainModel> data){
     for(BaseDomainModel baseObject : data)
         testDao.saveOrUpdate(baseObject);
}

Reference:

参考:

回答by virgium03

By default, Spring only rolls back for unchecked exception. You have to provide the rollback-forattributeand specify what exception you are trying to catch.

默认情况下,Spring 只会回滚未经检查的异常。您必须提供rollback-for属性并指定您要捕获的异常。



From the Spring documentation:

从 Spring文档

However, please note that the Spring Framework's transaction infrastructure code will, by default, onlymark a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass of RuntimeException. (Errorswill also - by default - result in a rollback.) Checked exceptions that are thrown from a transactional method will notresult in the transaction being rolled back.

但是,请注意,Spring Framework 的事务基础结构代码默认只会在运行时、未检查异常的情况下将事务标记为回滚;也就是说,当抛出的异常是RuntimeException的实例或子类时。(错误也会 - 默认情况下 - 导致回滚。)从事务方法抛出的已检查异常不会导致事务被回滚。

回答by virgium03

Here, get these snippets and hope they will help you:

在这里,获取这些片段,希望它们对您有所帮助:

<bean id="abstractService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
    <props>
        <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
        <prop key="add*">PROPAGATION_REQUIRED, -Exception</prop>
        <prop key="update*">PROPAGATION_REQUIRED, -Exception</prop>
        <prop key="modify*">PROPAGATION_REQUIRED, -Exception</prop>
        <prop key="delete*">PROPAGATION_REQUIRED, -Exception</prop>
        <prop key="save*">PROPAGATION_REQUIRED, -Exception</prop>
    </props>
</property>
</bean>

<bean id="persistenceServiceTarget" class="com.blahblah.server.service.impl.PersistenceServiceImpl">
<property name="persistenceDAO" ref="persistenceDAO" />
</bean>
<bean id="persistenceService" parent="abstractService">
     <property name="target" ref="persistenceServiceTarget" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="abstractDAO"
class="org.springframework.orm.hibernate3.support.HibernateDaoSupport"
abstract="true">
<property name="sessionFactory">
    <ref bean="webSessionFactory" />
</property>
</bean>

<bean id="persistenceDAO" class="com.blahblah.server.dao.impl.PersistenceDAOImpl"
parent="abstractDAO">
</bean>

These should be the things that you need. They don't have to be in the same file, maybe split them between services and daos.

这些应该是你需要的东西。它们不必在同一个文件中,也许可以在服务和 daos 之间拆分它们。

回答by Scarlett

another point is to check if the database support the transaction

还有一点就是检查数据库是否支持事务

回答by nanda

You can put the sessionFactory in TestServiceImpl and open the session there.

您可以将 sessionFactory 放在 TestServiceImpl 中并在那里打开会话。