有没有办法强制事务回滚而不遇到异常?

时间:2009-05-07 00:11:25

标签: java hibernate spring

我有一种做一堆事情的方法;其中包括一些插入和更新。它如此宣布......

@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)
public int saveAll(){
 //do stuff;
}

它完全按照预期工作,我没有任何问题。然而,有些情况下我想强制回滚,尽管没有例外......目前,当我遇到合适的条件时,我强迫异常,但它很难看,我不喜欢它。

我能以某种方式主动调用回滚吗?这个例外叫它......我想也许我也可以。

8 个答案:

答案 0 :(得分:22)

在Spring Transactions中,您使用TransactionStatus.setRollbackOnly()

您遇到的问题是您使用@Transactional来划分交易。这具有非侵入性的好处,但它也意味着如果您想手动与事务上下文交互,则不能。

如果要严格控制事务状态,则必须使用编程事务而不是声明性注释。这意味着使用Spring的TransactionTemplate,或直接使用其PlatformTransactionManager。请参见Spring参考手册的第9.6节。

使用TransactionTemplate,您提供一个实现TransactionCallback的回调对象,此回调中的代码可以访问TransactionStatus个对象。

它不如@Transactional那么好,但你可以更好地控制你的tx状态。

答案 1 :(得分:16)

我们不使用EJB,而是简单的Spring,我们选择了AOP方法。 我们已经实现了新的注释@TransactionalWithRollback,并使用AOP用“around”建议包装带注释的方法。要实现我们使用的建议TransactionTemplate。这意味着一开始就做了一些工作,但结果我们可以使用@TransactionalWithRollback注释方法,就像我们在其他情况下使用@Transactional一样。主要代码看起来干净简单。

//
// Service class - looks nice
//
class MyServiceImpl implements MyService {
    @TransactionalWithRollback
    public int serviceMethod {
        // DO "read only" WORK
    }
}

//
// Annotation definition
//
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface TransactionalWithRollback {
}

//
// the around advice implementation
//
public class TransactionalWithRollbackInterceptor {
    private TransactionTemplate txTemplate;
    @Autowired private void setTransactionManager(PlatformTransactionManager txMan) {
        txTemplate = new TransactionTemplate(txMan);
    }

    public Object doInTransactionWithRollback(final ProceedingJoinPoint pjp) throws Throwable {
        return txTemplate.execute(new TransactionCallback<Object>() {
            @Override public Object doInTransaction(TransactionStatus status) {
                status.setRollbackOnly();
                try {
                    return pjp.proceed();
                } catch(RuntimeException e) {
                    throw e;
                } catch (Throwable e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

//
// snippet from applicationContext.xml:
//
<bean id="txWithRollbackInterceptor" class="net.gmc.planner.aop.TransactionalWithRollbackInterceptor" />

<aop:config>
    <aop:aspect id="txWithRollbackAspect" ref="txWithRollbackInterceptor">
        <aop:pointcut 
            id="servicesWithTxWithRollbackAnnotation" 
            expression="execution( * org.projectx..*.*(..) ) and @annotation(org.projectx.aop.TransactionalWithRollback)"/>
        <aop:around method="doInTransactionWithRollback" pointcut-ref="servicesWithTxWithRollbackAnnotation"/>
    </aop:aspect>
</aop:config>

答案 2 :(得分:15)

如果您在EJB中,请在setRollbackOnly()上调用SessionContext

您可以像这样注入SessionContext

public MyClass {
    @Resource
    private SessionContext sessionContext;

    @Transactional(propagation = Propagation.REQUIRED, 
                   isolation = Isolation.DEFAULT, 
                   readOnly = false)
    public int saveAll(){
        //do stuff;
        if(oops == true) {
             sessionContext.setRollbackOnly();
             return;
        }
    }

setRollbackOnly()EJBContext的成员。 SessionContext扩展EJBContexthttp://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html请注意,它仅在会话EJB中可用。

@Resource是标准的Java EE注释,因此您应该检查Eclipse中的设置。这是example如何使用@Resource注入SessionContext。

我怀疑这可能不是你的解决方案,因为看起来你可能没有使用EJB - 解释为什么Eclipse没有找到@Resource

如果是这种情况,那么您需要直接与交易进行交互 - 请参阅交易模板。

答案 3 :(得分:13)

这对我有用:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

答案 4 :(得分:3)

你应该让spring注入事务管理器。然后你就可以在它上面调用rollback方法。

答案 5 :(得分:2)

我有使用@Transactional注释的服务方法。当验证失败,并且我已经有一个附加到当前工作单元的实体时,我使用sessionFactory.getCurrentSession().evict(entity)来确保没有任何内容写入数据库。这样我就不需要抛出异常。

答案 6 :(得分:0)

是的,我们可以在使用@Transactional(类级别)时强制回滚而不会遇到异常。我们可以简单地抛出一个异常(任何合适的异常)。喜欢

var getParams = function (url) {
        var params = {};
        var parser = document.createElement('a');
        parser.href = url;
        var query = parser.search.substring(1);
        var vars = query.split('&');
        for (var i = 0; i < vars.length; i++) {
            var pair = vars[i].split('=');
            params[pair[0]] = decodeURIComponent(pair[1]);
        }
        return params;
    };
    var mylocation ={};
    var mylocation = getParams(window.location.href);
    console.log(mylocation.prodName);
    $('#product').html(mylocation.prodName);
    $('#item').html(mylocation.prodItem);

答案 7 :(得分:-3)

抛出异常并按设计使用框架,否则不要使用声明式事务管理并遵循上面的skaffman建议。保持简单。