Reputation: 6244
I've a particular flow calls in my Spring app.
I've a repository, and I call the method sell() as first call.
@Service
@Transactional
public class ServiceRepositoryImpl implements ServiceRepository {
@Inject
private SellRepository sellRepository;
@Override
public long sell(long id)
....
....
....
Sell sell = new Sell();
...
sellRepository.save(sell);
}
and my SellService:
@Service
public class SellRepositoryImpl implements SellRepository {
@PersistenceContext
private EntityManager entityManager;
@Inject
SellHelperRepository sellHelperRepository;
@Inject
private TransactionTemplate transactionTemplate;
@Override
public <S extends Sell> S save(S sell) throws Exception {
transactionTemplate.execute(new TransactionCallback<S>() {
@Override
public S doInTransaction(TransactionStatus status) {
try {
...
entityManager.persist(sell);
} catch (Throwable e) {
log.error("", e);
status.setRollbackOnly();
}
return vendita;
}
});
sellHelperRepository.createTickets(sell.getId());
return vendita;
}
this is my sellHelperRepository:
@Service
@Transactional
public class SellHelperRepositoryImpl implements SellHelperRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
@Async
public void createTickets(long sellID) {
Sell sell = entityManager.find(Sell.class, sellID);
try{
...
Ticket t = new Ticket();
ticketService.save(t);
}catch(Throwable e){
sell.setStatus("CANCELED");
}
}
and in the end my ticketService:
@Service
@Transactional
public class TicketRepositoryImpl implements TicketRepository {
@PersistenceContext
protected EntityManager entityManager;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Biglietto save(Ticket ticket) throws Exception {
entityManager.persist(ticket);
}
The problem is that in this chain of calls when I'm on SellRepositoryImpl, the object sell is not persisted asap I'm out of the transactiontemplate but it should be! So when I enter in SellHelperRepositoryImpl and I try to search the sell it doesn't find it!
This particular structure is needed because these methods are used also from others repository. I created many junit tests case to check if all works fine; for the rest of tests all works fine, a part for this particular chain of calls.
I think I'm missing something....
Thanks
Upvotes: 0
Views: 4280
Reputation: 7646
the problem with your code is that you are using the @Async
on the createTickets
method , which forces Spring to execute it in the different thread with the "fresh" transaction, so you need to flush/commit first transaction which was opened in the SellRepositoryImpl
class.
So you can go in three ways
@Async
from the createTickets
methodChange the Transaction Isolation Level from DEFAULT
to READ_UNCOMMITTED
in SellHelperRepositoryImpl
@Service
@Transactional(isolation=Isolation.READ_UNCOMMITTED)
public class SellHelperRepositoryImpl implements SellHelperRepository {
@PersistenceContext
private EntityManager entityManager;
@Override
@Async
public void createTickets(long sellID) {
Sell sell = entityManager.find(Sell.class, sellID);
try{
...
Ticket t = new Ticket();
ticketService.save(t);
}catch(Throwable e){
sell.setStatus("CANCELED");
}
}
Flush the manually managed transaction in save(S sell)
, see following code snippet.
@Override
public <S extends Sell> S save(S sell) throws Exception {
transactionTemplate.execute(new TransactionCallback<S>() {
@Override
public S doInTransaction(TransactionStatus status) {
try {
...
entityManager.persist(sell);
entityManager.getTransaction().commit();
status.flush();
} catch (Throwable e) {
log.error("", e);
status.setRollbackOnly();
}
return vendita;
}
});
sellHelperRepository.createTickets(sell.getId());
return vendita;
}
Upvotes: 2