Since the programmer is forced to catch all checked exception, I to throw checked exception in case of any problem. I would like to rollback on any of those expections. Writing rollbackFor=Exception.class
on every @Transactional
annotation is very error-prone, so I would like to tell spring, that: "whenever I write @Transactional
, I mean @Transactional(rollbackFor=Exception.class)
".
I know, that I could create a custom annotation, but that seems unnatural.
So is there a way to tell spring how it should handle checked excpetions globally?
@Transactional only rolls back transactions for unchecked exceptions. For checked exceptions and their subclasses, it commits data. So although an exception is raised here, because it's a checked exception, Spring ignores it and commits the data to the database, making the system inconsistent.
The @Transactional annotation makes use of the attributes rollbackFor or rollbackForClassName to rollback the transactions, and the attributes noRollbackFor or noRollbackForClassName to avoid rollback on listed exceptions. The default rollback behavior in the declarative approach will rollback on runtime exceptions.
RollbackException exception is thrown when the transaction has been marked for rollback only or the transaction has been rolled back instead of committed. This is a local exception thrown by methods in the UserTransaction , Transaction , and TransactionManager interfaces.
I know, that I could create a custom annotation, but that seems unnatural.
No, this is exactly the use case for a Custom Annotation. Here's a quote from Custom Shortcut Annotations in the Spring Reference:
If you find you are repeatedly using the same attributes with @Transactional on many different methods, then Spring's meta-annotation support allows you to define custom shortcut annotations for your specific use cases.
And here's a sample annotation for your use case:
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Transactional(rollbackFor=Exception.class) public @interface MyAnnotation { }
Now annotate your services and / or methods with @MyAnnotation
(you'll think of a better name). This is well-tested functionality that works by default. Why re-invent the wheel?
Approach with custom annotation looks good and straightforward, but if you actually don't want to use it, you can create a custom TransactionAttributeSource
to modify interpretation of @Transactional
:
public class RollbackForAllAnnotationTransactionAttributeSource extends AnnotationTransactionAttributeSource { @Override protected TransactionAttribute determineTransactionAttribute( AnnotatedElement ae) { TransactionAttribute target = super.determineTransactionAttribute(ae); if (target == null) return null; else return new DelegatingTransactionAttribute(target) { @Override public boolean rollbackOn(Throwable ex) { return true; } }; } }
And instead of <tx:annotation-driven/>
you configure it manually as follows (see source of AnnotationDrivenBeanDefinitionParser
):
<bean id = "txAttributeSource" class = "RollbackForAllAnnotationTransactionAttributeSource" /> <bean id = "txInterceptor" class = "org.springframework.transaction.interceptor.TransactionInterceptor"> <property name = "transactionManagerBeanName" value = "transactionManager" /> <property name = "transactionAttributeSource" ref = "txAttributeSource" /> </bean> <bean class = "org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor"> <property name="transactionAttributeSource" ref = "txAttributeSource" /> <property name = "adviceBeanName" value = "txInterceptor" /> </bean>
Also you need <aop:config/>
or <aop:aspectj-autoproxy />
.
However note that such overrides may be confusing for other developers who expect a normal behaviour of @Transactional
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With