Spring Data JPA - How to have multiple transaction boundaries with same TransactionManager - spring

I have a service which has to perform the following steps in sequence.
1) insert database records
2) commit
3) call external service (This service need to see the inserts in step 1)
4) more inserts
5) commit
Currently the external service is not able to see the inserted rows.
Please suggest how to make the commit happen before the external call. I am using Spring JPA/Hibernate.
Thanks

You need to ensure both operations run in their own transaction and that T1 is committed before T2 executes. You also need to be aware of this discussion:
Spring #Transaction method call by the method within the same class, does not work?
Given the above something like this should work:
#Service
public class ClientService{
#Autowired
private RecordsService recordsService;
#Autowired
private ExternalService externalService;
public void insert(){
recordsService.insertRecords();
externalService.insertRecords();
}
}
#Service
public class RecordsService{
#Transactional
public void insertRecords(){
}
}
#Service
public class ExternalService{
#Transactional
public void insertRecords(){
}
}

Related

#Transactional has no effect on JpaRepository

I have a parent transaction at controller layer, but I want to start a new transaction when I call a repository, to achieve this I tried annotating Repository interface as below
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface EventRepo extends JpaRepository<Event, Integer>{ }
However this seems to not start a new transaction upon calls to EventRepo#save. Why?
Here is my service layer.
public interface IApplicationService {
void save(Event event);
}
#Service
public class ApplicationService implements IApplicationService {
#Autowired
private EventRepo eventRepo;
#Override
public void save(Event event) {
eventRepo.save(event);
}
}
It is in turn called from controller layer
#RequestMapping(value="/{indicator}", method=RequestMethod.POST)
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
#ResponseBody
public String processRequest(#PathVariable Integer indicator) {
Event event = new Event("Student1");
service.save(event);
if(indicator != 0) {
throw new RuntimeException();
}
return "Success";
}
However everything works perfectly if I annotate Service interface with #Transactional
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface IApplicationService {
void save(Event event);
}
When I say working what is mean is, if I run the below curl commands I will see 2 rows in h2 db for Event entity
curl -X POST http://localhost:8080/1
curl -X POST http://localhost:8080/0
I understand it is good to control transactions at Service layer then repository or controller layer, constructing situation this way makes it easy to demonstrate the problem.
Spring boot starter version is 2.5.6
below dependencie have versions managed by springboot starter
spring-boot-starter-data-jpa
spring-boot-starter-web
lombok
h2
Here is a thread that suggests it should be ok to annotate Repository layer although discourages it.
#Transactional on a JpaRepository
In this Spring article we can read the following:
Additionally, we can get rid of the #Transactional annotation for the method as the CRUD methods of the Spring Data JPA repository implementation are already annotated with #Transactional.
To me, this means that whatever #Transactional annotation you add to your EventRepo will be overridden by the #Transactional annotation mentioned above in the CRUD methods. Having said that, I really doubt #Transactional annotation has any effect in JpaRepository methods. It would have in your own custom methods, but it seems to me that it has none in the inherited methods.
In order to apply your own transactional settings in EventRepo#save override the save method:
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface EventRepo extends JpaRepository<Event, Integer>{
#Override
Event save(Event event);
}
Explanation
Spring ignores your #Transactional annotation because it cannot find the save method in the EventRepo proxy and applies the default transaction settings from the parent CrudRepository interface.
Further reading: How Does Spring #Transactional Really Work?

How to call #Scheduled method in only one instance using #PostConstruct

There is job that needs to be done on cron schedule
The same logic as in the job must be performed at the start of the spring boot application, therefore #PostConstruct method is used
Shedlock is used, since it is planned to run the application in multiple instances
The question is: how to make the logic from the #PostConstruct method be called in only one instance and not called in others?
An example of my code:
#Component
#AllArgsConstructor
public class TestJob {
private TestService testService;
#PostConstruct
public void init() {
testService.upload();
}
#Scheduled(cron = "${cron}")
#SchedulerLock(name = "uploadJob", lockAtMostFor = "${lockAtMostFor}")
public void execute() {
testService.upload();
}
}
It should work if you put the #PostConstruct method to a different service and call the execute() method.
The reason is that ShedLock by default uses Spring AOP which wraps each method marked by #SchedulerLock in an aspect that does the locking. And Spring AOP usually does not get applied if you call another method in the same class.

Spring Boot JPA #Transactional #Service does not update, but #Transactional in controller does

I have a very basic Spring Boot/JPA stack app, with a controller, service layer, and repository that does not persist updates as I understand it should.
A trivial Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
protected Customer() {}
public Customer(String name) { this.name = name; }
// standard getters,setters //
}
A trivial Repository:
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {}
A simple Service layer:
// If the service is #Transactional and the controller is not, the update does NOT occur
#Transactional
#Service
public class CustomerService {
private static final Logger LOG = getLogger(CustomerService.class);
#Autowired
private CustomerRepository customerRepository;
boolean updateCustomerName(Long id, String name) {
Customer customer = customerRepository.findOne(id);
if (customer == null) { return false; }
// Modifies the entity
customer.setName(name);
// No explicit save()
return true;
}
}
And a REST controller that uses it all:
// If the controller is #Transactional and the service is not, the update occurs
#RestController
#RequestMapping("/mvc")
public class CustomerController {
#Autowired
private CustomerService customerService;
#RequestMapping(path = "{id}", method = RequestMethod.PUT)
public ResponseEntity updateCustomerName(#PathVariable Long id, #RequestParam("name") String name) {
customerService.updateCustomerName(id,name);
return ResponseEntity.noContent().build();
}
}
These are wired together with a simple one-liner SpringBootApplication
I have SQL debug logs enabled and see the selects, update, etc.
With the code above: When the service method is invoked by the controller, the modified entity is not persisted. SQL logs show the select of the entity but no update.
There is also no update if nothing is marked #Transactional
However, simply by moving the #Transactional annotation from the service class to the controller class, the SQL update does occur.
If I add an explicit customerRepository.save(customer) to the service method, the update also occurs. But my understanding is that the ORM should automatically save modified persistent entities.
I'm sure the issue has something to do with the EntityManager lifecycle in the web request, but I'm puzzled. Do I need to do additional configuration?
Complete example at https://github.com/monztech/SO-41515160
EDIT: This was solved, see below. Per the Spring spec #Transactional does not work in package-private methods and mistakenly did not make the update service method public.
The update will occur if the method is public and the service class has the #Transactional annotation.
I do have another question, however. Why is the #Transactional annotation necessary? (the update does not occur without it) Shouldn't the entity manager still persist the object because of the open session in view mechanism that Spring uses, independent of any transaction?
Make your updateCustomerName method public.

Using the default PROPAGATION_REQUIRED with #Transactional behaviour

I know that when you put say #Transactional on a class each method inside will be executed as part of one single transaction if you call them one after the other in sequence because the default spring transactional behaviour is PROPAGATION_REQUIRED. See docs http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/transaction.html#tx-propagation . So assume I have a class like:
#Service
#Transactional
public class ActorServiceImpl implements ActorService
{
public void method1(){}
public void method2(){}
}
However, I want to know what happens if method1 throws an exception and the transaction has to be rolled back then I guess method2 will never be executed because the transaction has already committed due to the exception in method1?

Order of Spring #Transactional and Spring Security #PreAuthorize

So I have something like the following:
public interface MyService {
#PreAuthorize("hasPermission(T(Name).OBJ, T(Action).GET)")
MyObj getObj(String id);
}
#Service
public class MyServiceImpl implements MyService {
#Override
#Transactional
public MyObj getObj(String id){
return dao.get(id);
}
}
#Controller
public class MyController {
#Resource(name="myServiceImpl")
private MyService service;
public MyObj getObj(String id){
return service.getObj(id);
}
}
When the method getObj(id) is called, everything is wrapped in a transaction first, then authorization is checked. Is is possible to keep this configuration and first get Spring to check for authorization, then create the transaction if the user is authorized?
I've spent a good deal searching for an answer and could not find anything.
You can use order attribute when configuring #Transactional:
<tx:annotation-driven order="100"/>
Experiment with lower values to move transaction aspect after the authorization one. Looks like <security:global-method-security/> also has this setting. The security aspect needs to have a higher value (lower priority) to be executed first.
See also
Table 10.2. settings
7.2.4.7 Advice ordering

Resources