Preface
Spring do transaction management through PlatformTransactionManager interface.This interface has implementation for different kinds of datasources, for example for DataSourceTransactionManager, HibernateTransactionManager, HibernateTransactionManager, JmsTransactionManager, JpaTransactionManager and others.
In a typical JPA application, the JpaTransactionManager is being used.
In SpringBoot, All the JpaRepository are annotated with @Transactional annotation.
This annotation used by spring to do the Declarative transaction management.
So, the Developer uses this annotation to control many things, included the transaction propagation (REQUIRED, REQUIRED_NEW, SUPPORTED, etc), the isolation level (READ_COMMITED, READ_UNCOMMITED, etc.) and the rollback exception, timeout and more.
And behind the since, the platform-specific transaction manager along with the databasource do the actual work.
@Transactionl
So, many people says, use the @Transactional annotation on the service layer, you might need to use it on DAO (now called Repository) layer, but do not use it on the controller layer.In spring boot, as I said, all the out-of-the-box methods that you get by implementing the JpaRepository are already have the @Transactional annotation (see SimpleJpaRepository). And I think hence the @Transactional on the implementation class itself, then all your repo methods might have the annotation as well.
But this is not the problem, the problem in the Service layer.
@Transactionl in the Service layer
What if I didn't put the Transactional annotation on my service methods?Then your method will not run in a transaction, so If you have a service method that do the following
public void serviceMethod(){
insert1();
insert2();
}
insert1() might succeed and inserts data into the database, while insert2() fails.
In this case you should put the @Transactional annotation on your service method if you want to have all or none.
@Transactionl and noRollbackFor
The rollback is controlled by RuleBasedTransactionAttributesee http://docs.spring.io/autorepo/docs/spring/current/spring-framework-reference/html/transaction.html#transaction-declarative-rolling-back
In its default configuration, the Spring Framework’s transaction infrastructure code only marks a transaction for rollback in the case of runtime, unchecked exceptions; that is, when the thrown exception is an instance or subclass ofAlso,RuntimeException
. (Error
s will also - by default - result in a rollback). Checked exceptions that are thrown from a transactional method do not result in rollback in the default configuration.
When the Spring Framework’s transaction infrastructure catches an exception and is consults configured rollback rules to determine whether to mark the transaction for rollback, the strongest matching rule wins
For rollbackFor and noRollbackFor;
I think spring take the exception thrown and check to see how it is close (in inheritance hierarchy) to the declared exceptions (in rollbackFor and noRollbackFor). If it is closer to an exception declared in rollbackFor more than one declared in noRollbackFor, then the transaction will be rollbacked. and vice versa.
ex:
We have this heirarcy:
class Exception1{}
class Exception2 extends 1{}
class Exception3 extends 2{}
@Transactional(rollbackFor=Exception1.class, noRollbackFor=Exception2 .class)
public void myMethod(){
throw new Exception3 ();
}
In this case, the transaction will NOT be rollbacked, because the thrown exception (Exception3) is closest to exception in noRollbackFor (Exception2) than the exception in rollbackFor (Exception1)
And if the closest exception declared in rollbackFor, the spring will rollback the tx, otherwise it will not rollback it:
in RuleBasedTransactionAttribute: return !(winner instanceof NoRollbackRuleAttribute);
@Transactionl and lazy evaluation
lazy evaluation is important aspect in web development, it facilities things, lazy evaluation includes not only evaluate lazy associations in the Controller layer, but also evaluation lazy Streams. (Thanks to OpenEntityManagerInViewFilter).But if you choose to annotate your service method with @Transactional, lazy transaction data will not be available in the Controller layer (and exceptions will be thrown as well).
Here's an example issue.
So, some people says, at service layer get all the data you want and then send it to the controller layer as eager so you don't need the service layer's transaction anymore.
I think for finder services, you might not need to have the @Transactional annotation,
But in case of services that updates the database, you do need to use the @Transactional annotation to control the things not to go messy!
@Transactionl on controller and service layer
If you have a finder method that returns a lazy object (java 8 stream or lazy association as in hibernate), the if you have the @Transactional annotation on the service layer, then the lazy parts of object will not be available to the controller layer.
However, if you put the @Transactionl annotation on the controller layer as well (which many people don't recommend) then the lazy part will be available to the controller layer unless you make the transaction propagation on the service layer as REQUIRED_NEW, in this case the service layer will suspend the controller's transaction and start/end it's own transaction and will send back the object to the controller out of its transaction and the problem will still exists.
Conclusion
For now, I'll - mostly, because some times I do not need it - use @Transactionl on Service methods that do update (Add/update/delete) the database.However for finder service methods (that query the database), it is better to have the @Transational(readOnly=true) on it, but since I didn't understand - till now- much more, I'll choose not to use this annotation so I can deal with my laziness objects in the controller layer)
Interesting classes to see the source code of:
1. TransactionInterceptor: class source code to see what is happening internally. (very interesting)
2. TransactionAspectSupport:
a. method: invokeWithinTransaction
b. method: completeTransactionAfterThrowing (it shows
3. AbstractPlateformTransactoinManager
a. method: commit (it shows the rollback if done if "setRollbackOnly" method is being called
The post above is based on my thoughts and is might not be valid.
No comments:
Post a Comment