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 = Propag
You should have spring inject the transactional manager. Then you can just call the rollback method on it.
Yes we can force rollback while using @Transactional (class level) without encountering an exception. We can simply throw an exception (any suitable one). Like
if(some condition matches){
throw new DataIntegrityViolationException("Rollback Tnx.. Since ..." );
}
We don't use EJB, but simple Spring and we have chosen AOP approach.
We've implemented new annotation @TransactionalWithRollback
and 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 @TransactionalWithRollback
like we use @Transactional
in other cases. The main code looks clean and simple.
//
// 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>
Throw an exception and use the framework as designed otherwise do not use declarative transaction management and follow skaffman advise above. Keep it simple.
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.
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 Java EE 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.