I am inserting the data in one method(has #Transactional(propagation = Propagation.Required)) but in the other method(has #Transactional(propagation = Propagation.Required)) if I try to get the same data it is giving null.
Both methods are wrote in service layer with #Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
How to get the data which is inserted in the same transaction.
Something like :-
#Service
public class Service{
#Transactional
public void method(){
mapper.insert(); //insert to DB(Using Mapper interface)
ServiceLayer.method2()
}
}
#Service
public void ServiceLayer{
#Transactional
public static void method2(){
result = mapper.select() //Select inserted data - returning null
}
}
In order to persist the changes made to the current session you can invoke entityManager.flush();
It may be worked, but it's not a solution.
In your case, your Transaction from Service.method() created a transaction that is not committed yet. That's why you can't fetch it.
I found the answer...after removing #transactional from ServiceLayer.method2() it's worked fine.
Related
I'm trying to update a few columns in a table and the updation happens inside a forEach.
I want to handle each iteration as an individual transaction and any rollback inside the forEach should only rollback on the transactions that occurred on the specific iteration (not all previous iterations).
Moreover, I don't want an exception to trigger the rollback. Rather, it has to be triggered programmatically. For that, I'm making use of this - TransactionInterceptor.currentTransactionStatus().setRollbackOnly();
This is what I tried so far:
#Service
public class MyService {
#Transactional
public void processLabResults() {
arrayList.forEach(i -> {
proccessDiagnosis();
});
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
proccessDiagnosis() {
boolean isDispositionUpdated = updateDisposition(); // calls JPA Repository to update
if(isUpdated) {
boolean isSomethingElseUpdated = updatedSomethingElse(); // calls JPA Repository to update
if(!isSomethingElseUpdated) {
TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); //Should rollback only the transactions that happened in the current iteration
}
}
}
}
If I executed the above, it rolls back all the previous transactions that are not part of the current iteration as well. If I remove # Transactional annotation from the processLabResults method, I'm getting No transaction aspect-managed TransactionStatus in scope error and no rollback happens.
Any help would be appreciated. Thanks!
This answer helps me resolve the issue. I moved the proccessDiagnosis() function to a different service file.
Service.java
#Service
public class MyService {
#AutoWired
AnotherService anotherService;
#Transactional
public void processLabResults() {
arrayList.forEach(i -> {
anotherService.proccessDiagnosis();
});
}
}
AnotherService.java
#Service
public class AnotherService {
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void proccessDiagnosis() {
boolean isDispositionUpdated = updateDisposition(); // calls JPA Repository to update
if(isUpdated) {
boolean isSomethingElseUpdated = updatedSomethingElse(); // calls JPA Repository to update
if(!isSomethingElseUpdated) {
TransactionInterceptor.currentTransactionStatus().setRollbackOnly(); //Should rollback only the transactions that happened in the current iteration
}
}
}
}
I have a service class like this.
#Service
public class ServiceAImpl implements ServiceA {
#Autowired
private ServiceARepository repo;
#Autowired
private Publisher publisher;
#Transactional
public String save(Model model) {
//entity list creation code goes here
List<EntityA> entityList = repo.save(entityList);
repo.flush();
...
//savedIdList is comma separated list of entityList EntityA.id
publisher.publish(savedIdList);
return responseObject.toString();
}
}
When controller call to this service its create the Entity list and save. After that its call to publish method in another class with the saved ids. This save method annotated with #Transactional.
#Service
public class Publisher {
#Autowired
private AsyncPublisher asyPublisher;
#Autowired
PublishedDataRepository repo;
public void publish(String savedIdList) throws Exception {
savePublishData(..., savedIdList);
}
#Transactional
private void savePublishData(..., String savedIdList) throws Exception {
SearchPublishedData publishData = new SearchPublishedData();
...
publishData.setIdList(savedIdList);
publishData = repo.saveAndFlush(publishData);
asyPublisher.callPublisher(..., savedIdList, publishData.getId());
}
}
In publisher class its save a record to the publisher table and again it call to the async publisher class. In this class there is a method with #Async and its implemented with ThreadPoolTaskExecutor. In this async method what it going to do is get the previously saved data from its ids using EntityManager native query.
This is a sample java class code. Actually in this native query there are few tables join with this previously saved table and getting the data.
#Service
public class AsyncPublisher {
#PersistenceContext
private EntityManager entityManager;
#Async("threadPoolTaskExecutor") //This is created as ThreadPoolTaskExecutor
public void callPublisher(..., String ids, Long savedId) {
try {
String query = getQuery(ids);
List<Object[]> results = entityManager.createNativeQuery(query).getResultList();
... ///Other codes goes here, but results is empty
} catch (Exception e) {
logg error
}
}
private String getQuery(String ids) throws Exception {
StringBuilder query = new StringBuilder();
query.append("SELECT * FROM EntityA_table WHERE id IN (").append(ids).append(" ) ");
//This is a sample query, actually in this query there are 2 or more tables joined
return query.toString();
}
}
My problem is when I retrieve data from EntityManager native query time to time its not getting the data. But when I check the database with those ids those data available in database.
Anyone can give me the reason for this.
I think this saving block is annotated with #Transactional and it going to commit data to the Database at the end of the method execution and but before it save to the database EntityManager native query execution happens in another thread and its execute before the data commit. Is this correct? If not please explain someone what is happening here.
Also is there any way to avoid this data not retrieve scenario?
Thank you.
I am a new user of spring and hibernate.
I have a service class, which is called by a controller.
The call flow is like this:
controll calls myService.create method, then
myService.create calls myService.persistEnity method.
within the myService.persistEnity method, there is a dao object which persist entity to database.
The issue I found is:
if I attach the #transactional annotation to "myService.create" method, entities will be saved to databases.
however,
if I attach the #transactional annotation to "myService.persistEnity" method entities are not saved to databases, and I don't see any error message.
My question is:
why would this happen? (Is this because of the call flow? i.e. #transactional has to be attached to the first method of service class that is invoked by controller class in order to work?)
Thanks.
psudo Code is as below:
#org.springframework.stereotype.Service("myService")
public class MyService {
#Autowired
private MyDao dao;
// if the #Transactional is only put here as this, entities will NOT be saved to database.
#Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public MyEntity persistEnity(MyEntity toBeSaved) {
MyEntity entity = dao.persistEntity(toBeSaved);
return entity;
}
// if the #Transactional is only put here, entities will be saved to database.
// #Transactional(value = "transactionManager", readOnly = false, propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public ServiceObject create(ServiceObject serviceObject, User user) {
MyEntity entity = convertToEntity(serviceObject);
entity = persistEnity(dao);
return convertTOServiceObject(entity);
}
...
}
Because method convertToEntity creates an object type MyEntity in create, and if this method is transactional, the object is being created attached to the session. But if the method is not transactional, you create the new object out of session and send it to persistEnity, but it's not attached so dao cannot save it without new attaching to session.
Leave transactional on method where an object is created or attach it into session in persistEnity method.
I want to create an entity, and within the transaction trigger an #Async method to perform some changes on the same entity. The changes should also be persisted async.
Problem: as I have to fire the async method within the transaction, I could use the autogenerated #Id from the entity. BUT the async method then would have to first fetch the entity by that Id, and most often this does not exist yet.
Only if I put some Thread.sleep() as first statement inside the async method, it can mostly be ensured that the entity has been persisted by the outer transaction.
But that solution is not very nice. Question: how can I ensure inside the async method that it should wait for the entity to exist in DB?
#Service
public class OuterService {
#Service
private SyncService service;
#Transactional
public void process() {
service.mySyncMethod();
//etc
}
}
#Service
public class SyncService {
#Transactional
public void mySnycMethod() {
Entity entity = new MyEntity();
//fill entity
dao.save(entity);
asyncService.performLongRunningTask(entity.getId());
}
}
#Service
public class AsycnService {
#Async
#Transactional
public voi performLongRunningTask(Long id) {
//problem: is mostly == null because this is executed before the outer transaction completes
//only works if I put like Thread.sleep(5000) in between. but how can I really ensure the entity exists before executing this async lookup?
MyEntity entity = dao.findOne(id);
//perform long running task
//change some fields in entity accordingly
dao.save(entity);
}
}
You could register a hook on transaction commit using the TransactionSynchronizationManager.registerSynchronization() and implementing the afterCommit() method.
#Transactional
public void mySnycMethod() {
Entity entity = new MyEntity();
// fill entity
dao.save(entity);
// performLongRunningTask will start after the transaction has been
// commited
TransactionSynchronizationManager
.registerSynchronization(new TransactionSynchronizationAdapter() {
#Override
public void afterCommit() {
asyncService.performLongRunningTask(entity.getId());
}
});
}
But note what the Javadocs say about using the TransactionSynchronizationManager in your application:
To be used by resource management code but not by typical application
code
I am making an updating native query within my Spring Data JPA Repository.
#Query(nativeQuery=true, value="update t_table set change = 0 where id = ?1")
#Modifying(clearAutomatically=false)
void disableChange(int id);
The value is updated in a correct way as I can see with my database client but next transactions are not recognizing the change until the server is redeployed.
My service looks like this:
#Service("my.service.class.service")
public final class MyServiceClassImpl implements MyServiceClass
{
#Autowired
private ClientRepository clientRepository;
#Override
#Secured("MY_ROLE")
#Transactional(propagation=Propagation.REQUIRES_NEW, rollbackFor=MyException.class)
public void myMethod() throws PlayTheGuruException
{
//
myMethod();
//
}
private void myMethod() throws MyException {
//
clientRepository.disableChange(22);
//
}
}
May I need to annotate myMethod with transactional and mandatory propagation or something like that?
Is the native query the problem?
This issue appears only with updating queries.
Looks like a stale value from L2 cache, if L2 cache is enabled for the entity in question.
Try to remove affected entity from L2 cache manually:
#Autowired EntityManagerFactory emf;
...
clientRepository.disableChange(22);
emf.getCache().evict(..., 22);