Logical Transactions in Spring - spring

Let's say I have this method:
#Transactional
public void registerUser(UserDto userDto) {
userService.create(userDto.name());
accountService.createAccount(userDto.email());
}
both create() and createAccount() are also transactional with REQUIRED propagation.
I know that they all map to the same physical transactions but does this create 3 different logical transactions or are they also all using the same logical transaction?

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)

Why does OpenEntityManagerInViewFilter change #Transactional propagation REQUIRES_NEW behavior?

Using Spring 4.3.12, Spring Data JPA 1.11.8 and Hibernate 5.2.12.
We use the OpenEntityManagerInViewFilter to ensure our entity relationships do not throw LazyInitializationException after an entity has been loaded. Often in our controllers we use a #ModelAttribute annotated method to load an entity by id and make that loaded entity available to a controller's request mapping handler method.
In some cases like auditing we have entity modifications that we want to commit even when some other transaction may error and rollback. Therefore we annotate our audit work with #Transactional(propagation = Propagation.REQUIRES_NEW) to ensure this transaction will commit successfully regardless of any other (if any) transactions which may or may not complete successfully.
What I've seen in practice using the OpenEntityManagerInviewFilter, is that when Propagation.REQUIRES_NEW transactions attempt to commit changes which occurred outside the scope of the new transaction causing work which should always result in successful commits to the database to instead rollback.
Example
Given this Spring Data JPA powered repository (the EmployeeRepository is similarly defined):
import org.springframework.data.jpa.repository.JpaRepository;
public interface MethodAuditRepository extends JpaRepository<MethodAudit,Long> {
}
This service:
#Service
public class MethodAuditorImpl implements MethodAuditor {
private final MethodAuditRepository methodAuditRepository;
public MethodAuditorImpl(MethodAuditRepository methodAuditRepository) {
this.methodAuditRepository = methodAuditRepository;
}
#Override #Transactional(propagation = Propagation.REQUIRES_NEW)
public void auditMethod(String methodName) {
MethodAudit audit = new MethodAudit();
audit.setMethodName(methodName);
audit.setInvocationTime(LocalDateTime.now());
methodAuditRepository.save(audit);
}
}
And this controller:
#Controller
public class StackOverflowQuestionController {
private final EmployeeRepository employeeRepository;
private final MethodAuditor methodAuditor;
public StackOverflowQuestionController(EmployeeRepository employeeRepository, MethodAuditor methodAuditor) {
this.employeeRepository = employeeRepository;
this.methodAuditor = methodAuditor;
}
#ModelAttribute
public Employee loadEmployee(#RequestParam Long id) {
return employeeRepository.findOne(id);
}
#GetMapping("/updateEmployee")
// #Transactional // <-- When uncommented, transactions work as expected (using OpenEntityManagerInViewFilter or not)
public String updateEmployee(#ModelAttribute Employee employee, RedirectAttributes ra) {
// method auditor performs work in new transaction
methodAuditor.auditMethod("updateEmployee"); // <-- at close of this method, employee update occurrs trigging rollback
// No code after this point executes
System.out.println(employee.getPin());
employeeRepository.save(employee);
return "redirect:/";
}
}
When the updateEmployee method is exercised with an invalid pin number updateEmployee?id=1&pin=12345 (pin number is limited in the database to 4 characters), then no audit is inserted into the database.
Why is this? Shouldn't the current transaction be suspended when the MethodAuditor is invoked? Why is the modified employee flushing when this Propagation.REQUIRES_NEW transaction commits?
If I wrap the updateEmployee method in a transaction by annotating it as #Transactional, however, audits will persist as desired. And this will work as expected whether or not the OpenEntityManagerInViewFilter is used.
While your application (server) tries to make two separate transactions you are still using a single EntityManager and single Datasource so at any given time JPA and the database see just one transaction. So if you want those things to be separated you need to setup two Datasources and two EntityManagers

JPA: Nested transactional method is not rolled back

UPD 1: Upon further research I think the following information may be useful:
I obtain datasource through JNDI lookup on WildFly 9.0.2, then 'wrap' it into in instance of HikariDataSource (e. g. return new HikariDataSource(jndiDSLookup(dsName))).
the transaction manager that ends up being used is JTATransactionManager.
I do not configure the transaction manager in any way.
ORIGINAL QUESTION:
I am experiencing an issue with JPA/Hibernate and (maybe) Spring-Boot where DB changes introduced in a transactional method of one class called from a transactional method of another class are committed even though the changes in the caller method are rolled back (as they should be).
Here are my transactional services
StuffService:
#Service
#Transactional(rollbackFor = IOException.class)
public class StuffService {
#Inject private BarService barService;
#Inject private StuffRepository stuffRepository;
public Stuff updateStuff(Stuff stuff) {
try {
if (null != barService.doBar(stuff)) {
stuff.setSomething(SOMETHING);
stuff.setSomethingElse(SOMETHING_ELSE);
return stuffRepository.save(stuff);
}
} catch (FirstCustomException e) {
logger.error("Blah", e);
throw new SecondCustomException(e.getMessage());
}
throw new SecondCustomException("Blah 2");
}
// other methods
}
and BarService:
#Service
#Transactional
public class BarService {
#Inject private EntityARepository entityARepository;
#Inject private EntityBRepository entityBRepository;
/*
* updates existing entity A and persists new entity B.
*/
public EntityA doBar(Stuff stuff) throws FirstCustomException {
EntityA a = entityARepository.findOne(/* some criteria */);
a.setSomething(SOMETHING);
EntityB b = new EntityB();
b.setSomething(SOMETHING);
b.setSomethingElse(SOMETHING_ELSE);
entityBRepository.save(b);
return entityARepository.save(a);
}
// other methods
}
EntityARepository and EntityBRepository are very similar Spring-Boot repositories defined like this:
public interface EntityARepository extends JpaRepository<EntityA, Long>{
EntityA findOne(/* some criteria */);
}
FirstCustomException extends Throwable
SecondCustomException extends RuntimeException
Stuff entity is versioned, and every once in a while it is concurrently updated by StuffService.updateStuff(). In that case changes to one of the stuff instances are rolled back, as expected, but everything that happens in the barService.doBar() ends up being committed.
This puzzles me quite a lot since transaction propagation on both methods should be REQUIRED (the default one) and both methods belong to different classes, hence #Transactional should apply for both.
I did see Transaction is not completely rolled back after server throws OptimisticLockException1
But it did not really answer my question.
Can anyone please give me an idea of what's going on?
Thank you.
This isn't a 'nested' transaction - these services are operating in completely independent transactions. If you want the rollback of one to affect the other, you need to have them take part in the same transaction rather than start its own.
Or if your issue is that there is a problem with the version of 'stuff' passed into the doBar method and you want it verified, you will need to do something with the stuff instance that would cause an optimistic lock check, and so result in an exception if it is stale. see EntityManager.lock

The Spring annotation #Transactional(Propagation.REQUIRED) over a method, how does it behave?

I have read the official documentation of Spring about the
#Transactional(Propagation.REQUIRED)
annotation but still have some doubts. I will show you an example about how I thinks it behaves:
First Service
public class MyServiceImpl implements MyService{
#AutoWired
private OtherService otherService;
#Transactional(Propagation.REQUIRED)
public void saveItem(Item item){.....}
#Transactional(Propagation.REQUIRED)
public void updateItem(Item item){....}
}
#Transactional(Propagation.REQUIRED)
public void deleteItem(Item item){
otherService.checkItem(item);
...........
}
}
Second Service
public class OtherServiceImpl implements OtherService {
#Transactional(Propagation.REQUIRED)
public void checkItem(Item item){.....}
}
Making calls to MyServiceImpl class from a Spring Controller:
If I make one call to saveItem(), a new physical and logical transaction will be created, right?
If I make two calls to this service from the controller, one to saveItem() and the next to updateItem(),Spring will create for each method two physical different transactions, right?
If I make a call to deleteItem(), only one physical transaction will be created because it will be opened a transaction when deleteItem is called but the inner call from this method to otherService.checkItem() will reuse the first physical transaction, right?
REQUIRED means that one transaction is needed for running the method, so if one is not already ongoing at the beggining of the method then a new one is created (REQUIRED is the default propagation mode):
1) not necessarilly, if this was called from a method that already had an ongoing transaction
2) depends if the controller is transactional. It should not be by convention, only the service layer should define the scope of the transactions. so in the usual case of a non transactional controller you would have two transactions.
3) depends if where the call was made a transaction was already ongoing. if so then both methods would join a new transaction, if not delete item would create a transaction and otherService keep using it.

Spring Transactions With Supports Propagation

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.

Resources