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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/832375/
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-08-11 19:59:35  来源:igfitidea点击:

is there a way to force a transactional rollback without encountering an exception?

javahibernatespring

提问by Yevgeny Simkin

I have a method that does a bunch of things; amongst them doing a number of inserts and updates. It's declared thusly...

我有一个方法可以做很多事情;其中包括进行一些插入和更新。它是这样宣布的......

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

It works exactly as it is supposed to and I have no problems with it. There are situations however when I want to force the rollback in spite of there not being an exception... at the moment, I'm forcing an exception when I encounter the right conditions, but it's ugly and I don't like it.

它完全按照预期工作,我没有任何问题。然而,在某些情况下,尽管没有异常,但我仍想强制回滚......目前,当我遇到正确的条件时,我会强制执行异常,但它很丑陋,我不喜欢它。

Can I actively call the rollback somehow? The exception calls it... I'm thinking maybe I can too.

我可以以某种方式主动调用回滚吗?异常调用它......我想也许我也可以。

采纳答案by skaffman

In Spring Transactions, you use TransactionStatus.setRollbackOnly().

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

The problem you have here is that you're using @Transactionalto demarcate your transactions. This has the benefit of being non-invasive, but it also means that if you want to manually interact with the transaction context, you can't.

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

If you want tight control of your transaction status, you have to use programmatic transactions rather than declarative annotations. This means using Spring's TransactionTemplate, or use its PlatformTransactionManager directly. See section 9.6 of the Spring reference manual.

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

With TransactionTemplate, you provide a callback object which implements TransactionCallback, and the code in this callback has access to the TransactionStatusobjects.

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

It's not as nice as @Transactional, but you get closer control of your tx status.

它不如 好@Transactional,但您可以更密切地控制您的 tx 状态。

回答by Edward Q. Bridges

Call setRollbackOnly()on the SessionContextif you're in an EJB.

打电话setRollbackOnly()SessionContext,如果你在一个EJB是。

You can inject SessionContextlike so:

你可以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()is a member of EJBContext. SessionContextextends EJBContext: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.htmlNote it's only available in session EJBs.

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

@Resourceis a standard Java EE annotation, so you should probably check your setup in Eclipse. Here's an exampleof how to inject the SessionContext using @Resource.

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

I suspect that this is probably not your solution, since it seems like you may not be working with EJBs -- explaining why Eclipse is not finding @Resource.

我怀疑这可能不是您的解决方案,因为您似乎没有使用 EJB —— 解释了为什么 Eclipse 没有找到@Resource.

If that's the case, then you will need to interact with the transaction directly -- see transaction template.

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

回答by Ruggs

You should have spring inject the transactional manager. Then you can just call the rollback method on it.

您应该让 spring 注入事务管理器。然后你就可以调用回滚方法就可以了。

回答by JARC

Throw an exception and use the framework as designed otherwise do not use declarative transaction management and follow skaffman advise above. Keep it simple.

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

回答by Urs

I have service methods annotated with @Transactional. When the validation fails, and I already have an entity attached to the current unit of work, I use sessionFactory.getCurrentSession().evict(entity)to make sure nothing is written to the database. That way I don't need to throw an exception.

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

回答by Jakub

We don't use EJB, but simple Spring and we have chosen AOP approach. We've implemented new annotation @TransactionalWithRollbackand used AOP to wrap those annotated methods with "around" advice. To implement the advice we use mentioned TransactionTemplate. This means a little work at the beginning, but as a result we can just annotate a method with @TransactionalWithRollbacklike we use @Transactionalin other cases. The main code looks clean and simple.

我们不使用 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>

回答by Melquiades Fagundez

This works for me:

这对我有用:

TransactionInterceptor.currentTransactionStatus().setRollbackOnly();

回答by myounism

Yes we can force rollback while using @Transactional (class level) without encountering an exception. We can simply throw an exception (any suitable one). Like

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

if(some condition matches){
 throw new DataIntegrityViolationException("Rollback Tnx.. Since ..." );
}