Spring Transactions With Supports Propagation - spring

I would like to understand the use of having a spring transaction with Propagation Supports. The java docs mention that if the method which has #Transactional(propagation = Propagation.SUPPORTS) is called from within a transaction it supports the transaction but if no transaction exists, the method is executed non-transactionally.
Isn't this already the behavior of spring transactions irrespective of Propagation.SUPPORTS?
public class ServiceBean {
#Transactional(propagation = Propagation.SUPPORTS)
public void methodWithSupportsTx() {
//perform some database operations
}
}
public class OtherServiceBean {
#Transactional(propagation = Propagation.REQUIRED)
public void methodWithRequiredTx() {
//perform some database operations
serviceBean.methodWithSupportsTx();
}
}
In the above code example, irrespective of whether methodWithSupportsTx() has #Transactional(propagation = Propagation.SUPPORTS) annotation it would be executed in a transaction depending on whether methodWithRequiredTx() has #Transactional annotation, right?
So what's the need/use of having a propagation level SUPPORTS?

From javadoc:
Note: For transaction managers with transaction synchronization, PROPAGATION_SUPPORTS is slightly different from no transaction at all, as it defines a transaction scope that synchronization will apply for. As a consequence, the same resources (JDBC Connection, Hibernate Session, etc) will be shared for the entire specified scope. Note that this depends on the actual synchronization configuration of the transaction manager.
So, it means that, for example, multiple invocations of Hibernate's SessionFactory.getCurrentSession() inside methodWithSupportsTx() would return the same session.

A required transaction will create a new transaction if none exists. Therefore a new transaction would be made when you call serviceBean.methodWithSupportsTx(). If your method is truly transactional you will see an error from spring if no transaction exists.

Related

Without JPA #Transaction and save() when is the commit done?

When a method has a #Transaction annatotion, I know the commit is done at the end of the method. But when I don't use #Transaction, it's not clear to me when the commit is done. In my example I don't use #Transaction, do the real change in another service and don't use someRepository .save(), but it still works:
#Service
public class ServiceA {
private final SomeRepository someRepository;
private final ServiceB serviceB;
public ServiceA(SomeRepository someRepository, ) {
this.someRepository = someRepository;
this.serviceB = serviceB;
}
// Called from controller
public void doStuff() {
var someEntity = someRepository.findById(1);
serviceB.makeChange(someEntity);
}
}
#Service
public class ServiceB {
public ServiceB() {}
public void makeChange(SomeEntity someEntity) {
someEntity.setName("Test"); // this is working and committed to the database
}
}
So actually I have 2 questions:
When I don't add a #Transaction annatotion to a method when is the commit done?
I don't even have to call someRepository.save(entity)? I thought that worked only when using the #Transaction annotation?
Context:
Spring Boot 2.2.6
"spring-boot-starter-data-jpa" as dependency
first one clarification: the #Transactional annotation does not mean there is a commit at end of the method. It means the method joins the transaction (or start a new one - this depends on the propagation attributes to be precise), so the commit (or rollback) will be performed at the end of the transaction, which can (and often does) involve multiple methods with various DB access.
Normally Spring (or another transaction manager) takes care of this (ie disabling auto-commit).
#Transactional missing
There is no transactional context so the commit is performed immediately as the database in modified. There is no rollback option and, if there is an error, the data integrity might be violated,
#Transactional defined
During the transactions the JPA entities are in managed-state, at the end of the transaction the state is automatically flushed to the DB (no need to call someRepository.save(entity)

Grails/Spring REQUIRES_NEW rolling back outer transaction

REQUIRES_NEW is rolling back all transactions:
I have a method marked #Transactional(propagation = REQUIRES_NEW)
within a bean that also has methods marked #Transactional(propagation = REQUIRED). These methods never call each other. They are called from another bean individually. However when my method that has been marked with REQUIRES_NEW fails, it rolls back the whole transaction, including the methods that are marked REQUIRED (which is problematic).
My understanding is that if factored this way Spring AOP will have a chance to intercept the REQUIRES_NEW method and start a new logical transaction.
The general idea looks like this:
#Transactional
class TransactionalBean{
#Transactional(propagation = REQUIRED)
public void methodA(){ //do transactional work }
#Transactional(propagation = REQUIRES_NEW)
public void methodB(){ //do transactional work }
}
And then the calling bean looks like so:
#Transactional
class CallingBean{
#Autowired
TransactionalBean
public void doWork(){
TransactionalBean.methodA();
TransactionalBean.methodB();
}
}
So if method A and B succeed, everything is fine. However when method B fails work done in method A gets rolled back. My understanding is that when methodB() is invoked that it 'should' get intercepted by AOP and start a new transaction and suspend the other transaction, but it's not working. How do I fix this? I'm using Grails 2.5, Hibernate, JPA
The problem is with the way the CallingBean was marked as #Transactional. What is happening is that the CallingBean is creating a transaction, let's say t1. The CallingBean is invoking the two transactional methods methodA and methodB. Since methodA requires a transaction, it will make use of the existing transaction t1. However, methodB will create a new transaction as the annotation requires a new one, let's say t2. The transactional boundary of methodA does not end when the control exits methodA, but is kept alive until methodB is completed since the transaction started at the calling bean. Now that the transaction t2 is failing in methodB, the exception would bubble up to the calling bean and the transaction t1 would fail.
In order to resolve this, you can do one of the following based on your needs.
Turn off the #Transactional annotation in the CallingBean class.
//#Transactional
class CallingBean{
...
}
Update the annotation to use noRollbackFor option
#Transactional(noRollbackFor={WhateverException.class})
class CallingBean{
...
}

spring transaction rollback with #Transaction annotation using jdbcTemplates

Here is an example where I am using JdbcTemplate. My question doMultipleCalls() fail at step 3 due to runTimeException, will everything gets rolledback.
I have #Transactional annotation only to doMultipleCalls but not to others. Is the same transaction session shared across all of them?. If not how to pass same transaction session across?
#Component
public class MyRespository {
#Autowired
JdbcTemplate template
#Transactional
public void doMutlipleCalls() {
callUpdate(); //1
callInsert(); //2
callDelete(); //3
}
callUpdate() {
template.query(...)
}
callInsert() {
template.query(...)
}
callDelete() {
template.query(..)
}
}
Is the same transaction session shared across all of them?
Yes. The transaction is begun prior to entering the doMultipleCalls() method and is completed once it returns.
When you use spring jdbc with transactional, it acts in the same manner as it does with jpa. Your actions are wrapped under the same transaction.
Check this article.

How to link JPA persistence context with single database transaction

Latest Spring Boot with JPA and Hibernate: I'm struggling to understand the relationship between transactions, the persistence context and the hibernate session and I can't easily avoid the dreaded no session lazy initialization problem.
I update a set of objects in one transaction and then I want to loop through those objects processing them each in a separate transaction - seems straightforward.
public void control() {
List<> entities = getEntitiesToProcess();
for (Entity entity : entities) {
processEntity(entity.getId());
}
}
#Transactional(value=TxType.REQUIRES_NEW)
public List<Entity> getEntitiesToProcess() {
List<Entity> entities = entityRepository.findAll();
for (Entity entity : entities) {
// Update a few properties
}
return entities;
}
#Transactional(value=TxType.REQUIRES_NEW)
public void processEntity(String id) {
Entity entity = entityRepository.getOne(id);
entity.getLazyInitialisedListOfObjects(); // throws LazyInitializationException: could not initialize proxy - no Session
}
However, I get a problem because (I think) the same hibernate session is being used for both transactions. When I call entityRepository.getOne(id) in the 2nd transaction, I can see in the debugger that I am returned exactly the same object that was returned by findAll() in the 1st transaction without a DB access. If I understand this correctly, it's the hibernate cache doing this? If I then call a method on my object that requires a lazy evaluation, I get a "no session" error. I thought the cache and the session were linked so that's my first confusion.
If I drop all the #Transactional annotations or if I put a #Transactional on the control method it all runs fine, but the database commit isn't done until the control method completes which is obviously not what I want.
So, I have a few questions:
How can I make the hibernate session align with my transaction scope?
What is a good pattern for doing the separation transactions in a loop with JPA and declarative transaction management?
I want to retain the declarative style (i.e. no xml), and don't want to do anything Hibernate specific.
Any help appreciated!
Thanks
Marcus
Spring creates a proxy around your service class, which means #Transactional annotations are only applied when annotated methods are called through the proxy (where you have injected this service).
You are calling getEntitiesToProcess() and processEntity() from within control(), which means those calls are not going through proxy but instead have the transactional scope of the control() method (if you aren't also calling control() from another method in the same class).
In order for #Transactional to apply, you need to do something like this
#Autowired
private ApplicationContext applicationContext;
public void control() {
MyService myService = applicationContext.getBean(MyService.class);
List<> entities = myService.getEntitiesToProcess();
for (Entity entity : entities) {
myService.processEntity(entity.getId());
}
}

Transactions via spring annotations - readonly or not?

I've got in my project code similar to this
#Transactional(readOnly = true)
public void tt() {
dd();
}
#Transactional()
public void dd() {
gg();
}
#Transactional(readOnly = true)
public void gg() {
}
Function dd is used both by other readonly transaction functions and not readonly functions. Assuming that transaction should extendend from execution of tt to gg - operations in dd will in be read-only transaction or not?
In this particular example, your question is moot.
The call to dd() from tt() will not pass the proxy boundary so no transactional advise will be applied to dd() (since it's a call inside the same instance). Same with the call to gg() from dd(). Consequently, only the call from outside to tt() would actually be transaction-advised (in your case, with readOnly=true) and that would be the transaction that would be used in the entire call-chain.
In the general case though, read the documentation hinted by #melihcelik - it explains the behavior.
Spring's AbstractPlatformTransactionManager has a property named validateExistingTransaction that controls this behavior. Javadoc states that:
When participating in an existing transaction (e.g. with PROPAGATION_REQUIRES or PROPAGATION_SUPPORTS encountering an existing transaction), this outer transaction's characteristics will apply even to the inner transaction scope. Validation will detect incompatible isolation level and read-only settings on the inner transaction definition and reject participation accordingly through throwing a corresponding exception.
Since default propagation for Spring #Transactional annotation is REQUIRED and default validation strategy is false, I expect Spring to use existing transaction created from tt method call in readonly mode.
If you want to have a read only transaction, then you have to annotate your method with:
#Transactional(propagation=Propagation.REQUIRES_NEW, readOnly=true)
use #Transactional(readoOnly = true) if you are performing a get/select and not making any changes, this means that no locks will be applied (which is more efficent).
For updates/inserts/deletions/saves/merges I use (when a lock is required) :
#Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)

Resources