Propagate spring transaction to sibling call - java

Consider I have following spring beans
CompositeService:
#Service
public class CompositeService {
#Resource
private ServiceA serviceA;
#Resource
private ServiceB serviceB;
public ResultBean compositeMethod() {
ResultBean result = new ResultBean();
result.setA(serviceA.getA());
result.setB(serviceB.getB());
return result;
}
}
ServiceA:
#Service
public class ServiceA {
#Transactional
#Cacheable
A getA() {
// calls DAO layer and makes a query to the database
}
}
ServiceB:
#Service
public class ServiceB {
#Transactional
#Cacheable
B getB() {
// calls DAO layer and makes a query to the database
}
}
The cacheable aspect has a higher order
The problem with this code is that it will start two transactions (and take two connections from the pool) in case of cache-miss in both services.
Can I configure Spring to use the same transaction in this use case?
I.E. propagate the transaction from ServiceA to the CompositeService and after that down to ServiceB?
I cannot set the CompositeService as transactional because I do not want to start a transaction (and borrow a connection from pool) in case of cache-hit both in ServiceA and ServiceB

Spring will only propagate a transaction if everything is under the same transaction. So the short answer is that you should annotate your CompositeService with #Transactional.
#Service
public class CompositeService {
#Transactional
public ResultBean compositeMethod() {
ResultBean result = new ResultBean();
result.setA(serviceA.getA());
result.setB(serviceB.getB());
return result;
}
}
Generally this is fast enough as it only does a checkout from the underlying connection pool. However if you experience latency or don't always need a connection you can wrap your actual DataSource in a LazyConnectionDataSourceProxy. This will obtain a Connection when first needed.

What you can do is you can annotate the compositeMethod with #Transactional as well. The default propagation level of a transaction is set to Required
Support a current transaction, create a new one if none exists.
So even though its not exactly what you asked, the transaction demarcation starts with the compositeMethod, but it should be exactly the semantics you want.

Related

Spring #Transactional different behavior on Service and non-Service methods?

I have a Spring Boot 2.3 REST application with a standard architecture (controller -> service -> repository). For auditing purposes, I inserted a thin layer (some kind of a Mediator), so that I persist all the requests to some specific service method regardless they are successfully persisted or an exception is thrown and the transaction is rollbacked. Example:
#Component
public class Mediator {
private final Service service;
private final AuditService auditService;
public Mediator(Service service, AuditService auditService) {
this.service = service;
this.auditService = auditService;
}
#Transactional
public void saveReport(Report report) {
try {
service.saveReport(report);
auditService.saveReport(report);
} catch (Exception exception) {
auditService.saveReport(report, exception);
throw exception;
}
}
}
Thus I encountered a weird situation: if I place the #Transactional on the Mediator's method (example above), all the operations in the JPA Repositories are successfully persisted. If I place the #Transactional on the ServiceImpl method instead (example below) and no longer on the Mediator, one of the delete queries is not ran, although the rest of the queries are executed just fine. Suppose my ServiceImpl looks something like:
#Service
public class ServiceImpl implements Service {
private final RepositoryA repositoryA;
private final RepositoryB repositoryB;
public ServiceImpl(RepositoryA repositoryA, RepositoryB repositoryB) {
this.repositoryA = repositoryA;
this.repositoryB = repositoryB;
}
#Transactional
public void saveReport(Report report) {
repositoryA.save(report.getA());
repositoryB.save(report.getB());
repositoryB.delete(report.getSomethingElse());
}
}
The only visible difference between the two approaches with respect to the Transactions is that in the first scenario, I have this for each Mediator call:
o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(909894553<open>)] for JPA transaction
while in the second scenario, I have this:
tor$SharedEntityManagerInvocationHandler : Creating new EntityManager for shared EntityManager invocation
I guess there is a difference between annotating directly a bean's method with #Transactional (what I do in the Mediator) and annotating a bean's (that is the implementation of the interface injected) method with the same thing (what we usually do by annotating a ServiceImpl method), but I am not sure and cannot spot the reason for this weird behaviour. Does anyone have an idea why this happens?
I guess that this difference in behavior is due to Spring OpenSessionInView which is enabled by default.
You must set in application.yml
spring:
jpa:
open-in-view: false
Please see OSIV

How does rollback() in JDBC Template work?

I am learning JDBS template and wonder if there is a way to rollback operation.
It is easy to do it using JDBC, just
conn.setAutoCommit(false);
// DoinП stuff
con.rollback();
But is there a way do the same using JDBS template?
You should use the spring-boot-starter-data-jdbc like in this guide
Thanks to this spring-boot-starter-data-jdbc and to the spring boot autoconfiguration mechanism, a jdbc template bean and a transaction manager bean will be created for you.
Then you can use #Transactional annotation on non private method to wrap your code into a transaction :
#Transactional
public void methodA() {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
}
or you can use a transacionTemplate to handle the transaction programatically
#Service
public class ServiceA {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public void methodA() {
transactionTemplate.execute(new TransactionCallback<>() {
public Object doInTransaction(TransactionStatus status) {
jdbcTemplate.update(...);
jdbcTemplate.update(...);
return jdbcTemplate.query(...);
}
});
}
}
If you want to execute multiple statements and want to be sure all or none executed, you can use transactions.
First, you have to create a session and begin the transaction.
After, you can execute statements and commit the transaction.
But, in case of problems, you can rollback (undo previous statements).
With spring you can use #Transactional annotation
#Transactional
public void transaction() { // Spring will begin transaction
doTransactionalStatements(); // Spring will rollback in case of RuntimeException
} // Spring will commit transaction

How to pass a JPA entity to an Asynchronous EJB method

While processing a request, I would like to 'kick off' separate task in a separate transaction based on the data received and stored in the database.
The main advantage is that I can return the result to my request before all additional processing is done, also if additional processing fails, this will not affect the original processing of the request (as they are in different transactions). This additional processing might require adding extra information to the data.
I have the following setup in mind. Where using #Asynchronous the additional task is created.
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
}
}
#Stateless
public class Bean2
{
#Asynchronous // This causes a new transaction to happen
public void asyncActOnData(MyInput input){
// Act upon data and store result in DB
}
}
This seems like a clean way, however this causes JPA Entity to become detached, possibly during processing in Bean2.
Currently I don't plan on changing the data after the persist call (and asnyc), but as the application grows I feel it would be safer to allow this to avoid mistakes.
What is the correct way to kick off the separate asynchronous task based on the persisted data?
I am using: Java EE 6 with Eclipselink 2.5.2 for JPA.
You can continue to make use of the detached instance provided that:
You're not planning to perform further persistence operations;
All associated entities (if any) were available when asyncActOnData was invoked.
However, if you need to perform further persistence operations you can do the following:
#Stateless
public class Bean1
{
#EJB
Bean2 bean2;
#PersistenceContext
private EntityManager entityManager;
public MyResult doSomething(MyInput input) {
MyEntity myEntity = new MyEntity();
// Fill data based on input
entityManager.persist(myEntity);
// Flush the persistence context and detach the entity
// An entity is not permitted to be in more than one
// persistence context at a time. This should prevent
// a race condition with the merge in bean2.
entityManager.flush();
entityManager.detach(myEntity);
Future<Result> futureResult = bean2.asyncActOnData(myEntity);
// Perhaps do some more work and return result
....
return futureResult.get();
}
}
#Stateless
public class Bean2 {
#PersistenceContext
private EntityManager entityManager;
#Asynchronous
public Future<Result> asyncActOnData(MyInput input) {
// this will ensure that the database still matches input
// and add input into the persistence context
input = entityManager.merge(input);
...
return new javax.ejb.AsyncResult<Result>(result);
}
}
You will find it useful to read §3.2 of the "Java™ Persistence API, Version 2.1" specification.

How to run two Spring transactions in a single hibernate Session?

I know, that there is a way to descend to a low level - get connection and perform two transaction's by hand in a single hibernate session.
But the question is - how to invoke second nested transaction in the same Session via #Transactional annotations (not using "low level hacks" or handwrited custom transaction management)?
Some possible code:
#Service
public class DoubleTransaction {
#Autowired
private SessionFactory sf;
#Autowired
private NestedTeHandler nestedHandler;
#Transactional
void invokeTransaction() {
Session cs = sf.getCurrentSession();
Session nestedCs = nestedHandler.invokeNested(sf);
System.out.println(cs == nestedCs);
}}
#Service
public class NestedTeHandler {
#Transactional
Session invokeNested(SessionFactory sf) {
return sf.getCurrentSession();
}}
You might be able to do that with
#org.springframework.transaction.annotation.Transactional(propagation = Propagation.NESTED)
on NestedTeHandler.invokeNested. See documenation at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html.
See also this question: Differences between requires_new and nested propagation in Spring transactions.

Pass Injected EntityManager to a web session persisted object

Is it secure to pass a Injected EntityManager created on an EJB, to a method that will return an Object, and after, persist that Object on a Web Session for web clients use it?
Like in this example:
the EJB
#Stateless(mappedName = "MyService")
#LocalBean
public class MyService implements MyServiceLocal {
#PersistenceContext(unitName="primary")
private EntityManager em;
/**
* Default constructor.
*/
public MyService() {
}
#Override
public Service newServiceX(User user) {
return new ServiceX(user,em); // here, passing the EntityManager
}
}
After, I persist this Service in a web client (using struts):
The base action
public class YAction extends ActionSupport implements SessionAware{
#Inject
private MyServiceLocal service;
public String execute(){
Service x = service.newServiceX();
persistInCookie("ServiceX",x);
}
public void persistInCookie(String, Object){
// persist
}
}
And after, using another Action:
// another Action that
class XAction{
public String useService(){
getService().doSomething();
}
protected Service getService(){
Service service = (Service) getSessionMap().get("ServiceX");
return service;
}
}
the POJO class ServiceX using the EntityManager:
public class ServiceX extends Service{
EntityManager em;
public ServiceX(User user, EntityManager em){
this.em = em;
}
public void doSomething(){
// do something with the EntityManager passed by the EJB
}
}
First, the action that would be call is the Y action to persist the Service on the Session, next, the X action will return the Service persisted on the Session and try to use it.
I believe that the EJB Stateless Session Bean can close My EntityManager and this ServiceX POJO class can't use it. This can happen? I found similar question HERE, but in this question, the EntityManager is passed to a helper class. In my case is different because I want to persist this Object on a session cookie, and use later.
I don't think It is a good idea to store a EntityManager in SessionMap. What is more, I don't even think that it is a good idea to perform EntityManager operations outside the EJB container.
Have read about transaction-boundaries in JPA?
By default, EJB container is using CMT (Container Managed Transactions). In this case, container uses entitymanager-per-request pattern which means that the transaction begins and ends when one of the business methods of MyService starts and ends (transaction is committed or rollbacked in case of RuntimeException). For whole transaction time, EntityManager is connected with the same PersistenceContext. After the transaction is ended the container closes EntityManager which means that the EntityManager is disconnected with recent PersistenceContext:
// transaction begins
Service x = service.newServiceX();
// transaction ends
This might be crucial if you were going to do some update/insert operations outside the transaction.
Now, when you call EntityManager operation (like find) outside the transaction, for every each operation the EntityManager will create new PersistentContext. This may cause some issues, as two entities that represent the same record will be treated as different entities:
// each operation occurs in a separate persistence context, and returns
// a new detached instance
Magazine mag1 = em.find(Magazine.class, magId);
Magazine mag2 = em.find(Magazine.class, magId);
assertTrue(mag2 != mag1);
Some more articles to read:
Persistent Context
Transactions and Concurrency
Entity Lifecycle Management

Categories

Resources