Java 避免事务在Spring中回滚

Java 避免事务在Spring中回滚,java,spring,jpa,jdbc,Java,Spring,Jpa,Jdbc,假设我有下一个代码: @Autowired private IManager1 manager1; @Autowired private IManager2 manager2; @Autowired private IManager3 manager3; @Transactional public void run() { manager1.doStuff(); manager2.registerStuffDone(); manager3.doStuff()

假设我有下一个代码:

@Autowired
private IManager1 manager1;

@Autowired
private IManager2 manager2;

@Autowired
private IManager3 manager3;

@Transactional
public void run() {
     manager1.doStuff();
     manager2.registerStuffDone();

     manager3.doStuff();
     manager2.registerStuffDone();

     manager1.doMoreStuff();
     manager2.registerStuffDone();
}
如果启动任何异常,我希望回滚“doStuff()”方法完成的所有操作,但不希望回滚“registerStuffDone()”方法记录的数据

我一直在阅读@Transactional annotation的传播选项,但我不知道如何正确使用它们

每个经理在内部使用Hibernate提交更改:

@Autowired
private IManager1Dao manager1Dao;

@Transactional
public void doStuff() {
    manager1Dao.doStuff();
}
dao看起来是这样的:

@PersistenceContext
protected EntityManager entityManager;

public void doStuff() {
    MyObject whatever = doThings();
    entityManager.merge(whatever);
}
这是我的applicationContext配置:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSourcePool" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
</bean>

<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>


想法?

您需要两个事务,一个用于提交内容,另一个用于回滚内容

   @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor={Exception1.class, Exception2.class})
public void registerStuffDone()() {
   //code
}

然后,您的run方法将使用第一个事务并将回滚,但registerStuffDone方法将启动将提交的第二个事务。

您使用的是声明性事务,希望像程序感知一样进行控制。因此,您需要更多的实践和对Spring事务定义(如传播、隔离等)的深入理解

编程事务管理:这意味着您已经在编程的帮助下管理了事务。这给了你极大的灵活性,但很难保持
Vs
声明性事务管理:这意味着您将事务管理与业务代码分开。您只能使用注释或基于XML的配置来管理事务

也许,您可以通过编程事务管理来解决问题

/** DataSourceTransactionManager */
    @Autowired
    private PlatformTransactionManager txManager;

    public void run() {
    try {

         // Start a manual transaction.
         TransactionStatus status = getTransactionStatus();

         manager1.doStuff();
         manager2.registerStuffDone();

         manager3.doStuff();
         manager2.registerStuffDone();

         manager1.doMoreStuff();
         manager2.registerStuffDone();

        //your condition 
        txManager.commit(status);
        //your condition 
        txManager.rollback(status);

         } catch (YourException e) {

        }       
    }    

/**
     * getTransactionStatus
     *
     * @return TransactionStatus
     */
    private TransactionStatus getTransactionStatus() {
        DefaultTransactionDefinition dtd = new DefaultTransactionDefinition();
        dtd.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        dtd.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        dtd.setReadOnly(false);

        return txManager.getTransaction(dtd);
    }

注意:这并不意味着您需要始终使用编程事务管理之类的方法。我喜欢混合方式。对于简单的数据库服务,请使用声明性事务之类的简单方法,否则,只需在服务中使用编程事务控制即可轻松保存您的逻辑。

我尝试了传播。需要\u NEW,但它始终使用相同的事务。我添加了我的applicationContext配置,以防有什么影响到这个问题。我相信这会起作用。配置在我看来是正确的,为什么不切换登录并运行单元测试呢?你是对的!,这就成功了。在实际项目中,在主应用程序和示例管理器之间有另一个管理器。我试图将
@Transactional(propagation=propagation.REQUIRED\u NEW)
添加到这个中间方法中,但是就像我的manager2已经有了@Transactional注释一样,它覆盖了父方法中定义的所有内容。我真傻,我以前没检查过!谢谢。谢谢你的回答,很有启发性。不过,我可以在最后用注释来修复它。很高兴听到这个消息!伟大的