views:

705

answers:

4

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.

+3  A: 

Hey Gene!

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

You can inject SessionContext like so:

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. SessionContext extends EJBContext: http://java.sun.com/j2ee/1.4/docs/api/javax/ejb/SessionContext.html Note it's only available in session EJBs.

@Resource is a standard JEE annotation, so you should probably check your setup in Eclipse. Here's an example of how to inject the SessionContext using @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.

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

eqbridges
Ed! crap, I've been unmasked.
Dr.Dredel
Well, if that's the right answer upvote me so that maybe I can catch up to you :-)
eqbridges
it's definitely *a right answer... I already did upvote you, I haven't selected it as *the right answer yet.
Dr.Dredel
Ed, I seem to have lost all your contact info except your home phone number. Could you please email me at y[mylastname]@gmail.com with your info?
Dr.Dredel
Eclipse doesn't recognize @Resource as an annotation, and setRollbackOnly() is not a member of SessionContext (when it imports from org.springframework.orm.hibernate3.SpringSessionContext). So... I'm closer, but not close enough :)
Dr.Dredel
+2  A: 

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

Ruggs
that does seem like the right way to go... I'm investigating SavepointManager now.
Dr.Dredel
+2  A: 

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

The problem you have here is that you're using @Transactional to 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.

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.

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

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

skaffman
A: 

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

JamesC