TransactionRequiredException when deleting in threads - java

I'm having a problem when I trying to delete data from the DB using multiple threads with Hibernate.
Repo:
#Modifying
#Query("DELETE FROM Customer cus WHERE cus.customerId in :customerIds")
public void deleteByCustomerIds(#Param("customerIds") List<Long> customerIds);
Service:
public runDelete (List<Long> customerIds) {
List<List<Long>> partitions = Lists.partition(customerIds, 5000);
for(int i = 0; i < partitions.size(); i++ ) {
final int index = i;
Runnable thread = () -> deleteCustomersInBatches(partitions.get(index));
new Thread(thread).start();
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
private void deleteCustomerInBatches(List<Long> customerIds) {
for (List<Long> batch : Lists.partition(oldCalcIds, 1000)) {
customerRepo.deleteByCustomerIds(batch);
}
}
This is how code looks like, I have the #Transactional tag on the service layer where the repo call is being made.
at java.lang.Thread.run(Thread.java:748) Caused by:
javax.persistence.TransactionRequiredException: Executing an
update/delete query
at org.hibernate.jpa.spi.AbstractQueryImpl.executeUpdate(AbstractQueryImpl.java:54)
I keep getting this error. Any help is appreciated.

It's because you're invoking #Transactional method from within same bean.
#Transactional only works on methods invoked on proxies created by spring. It means, that when you create a #Service or other bean, method called from the outside will be transactional. If invoked from within bean, nothing will happen, as it doesn't pass through proxy object.
The easiest solution would be to move the method to another bean. If you really want to keep it within same component, then you need to invoke it, so that it gets wrapped in proxy by spring AOP. You can do this like that:
private YourClass self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(YourClass.class);
}
Then invoking method on self would result in opening a transaction.

Related

Asynchronous method not running in parallel

I have Service.class with start() and asychronous() method :
public ResponseEntity<Object> start() throws APICommandConstructionException, APICommunicationException, APIReplyParseException,
APIErrorResponse, IOException {
List<Company> companiesList = dbHandler.retrieveCompaniesList();
Company company = null;
for (int i = 0; i < companiesList.size(); i++) {
asychronousMethod(companiesList, i, company);
}
return new ResponseEntity<Object>("Start method has Finished", HttpStatus.OK);
}
#Async("threadPoolTaskExecutor")
public void asychronousMethod(List<Company> companiesList, int i, Company company) throws APICommandConstructionException, APIReplyParseException, APICommunicationException, APIErrorResponse, IOException {
company = companiesList.get(i);
company = utils.websiteScrap(company);
companiesRepository.save(company);
}
Everything that is inside a loop doesn't run in parallel, but it starts second loop after first finished. Why is that? How to do it parallel?
In brief: you shouldn't call explicitly methods with Spring annotations.
More detailed:
Spring creates special proxies that on back-stage do 'magic' for you. So if you have async annotation, that means that (depending on compile- and runtime-configuration of Spring) there was some hidden part of code that is not executed when you invoke this.asynchronousMethod.
How to fix:
First of all method should match public CompletableFuture<Void> - such way you know when thread is complete.
Second instead of this you need resolve self-instance as Spring proxy.
The simplest way over #Autowired:
#Autowired
MyClass zhis;
.... //in for loop:
future = zhis.asychronousMethod(companiesList, i, company);
P.s please see good example at https://spring.io/guides/gs/async-method/

Proper Hibernate nested transactions handling

I am sure that I am missing something, but I don't know exactly what...
Giving the following snippet:
#Service
public class MyClass {
private MyClass self;
private UserRepository userRepository;
private ApplicationContext applicationContext;
#PostConstruct
private void init() {
self = applicationContext.getBean(MyClass.class);
}
#Transactional
public void doA(User user) {
...
if (condition) {
self.doB(user);
throw new SecurityException();
}
user.setRandomField("x");
userRepository.save(user);
}
#Transactional(value = Transactional.TxType.REQUIRES_NEW)
public void doB(User user) {
...
userRepository.save(user);
}
}
What do I know about #Transactional is that if it is used, is redundant to call repository.save(entity).
What I am trying to do, is to process an entity from a transactional method, and if there is a breaking condition, call a new method (annotated with REQUIRES_NEW) that will update some fields of the entity and save it. The root method (doA) then throws an exception. FYI: the #Transactional(dontRollbackOn = SecurityException.class) is not an option in this situation.
For using this commiting mechanism, instead of creating a new bean just with one method I just injected the current bean into a variable just called self, therefore I can use the bean proxy for transaction management.
The odd thing is that if I am removing from doB the save call, when doA transaction is rollbacked because of the SecurityException, the changes performed by doB are rollbacked as well. But if I let it in there, this is working as expected.
Am I doing something wrong or am I missing something?
Thanks!
Try to do not pass User instance in the doB().
Pass an Id instead and read the User from the repo internally. I am not sure how the attached entity is handled between the different sessions.

How to ensure #Entity exists in #Async methods?

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

How to manually force a commit in a #Transactional method? [duplicate]

This question already has answers here:
How to flush data into db inside active spring transaction?
(4 answers)
Closed 7 years ago.
I'm using Spring / Spring-data-JPA and find myself needing to manually force a commit in a unit test. My use case is that I am doing a multi-threaded test in which I have to use data that is persisted before the threads are spawned.
Unfortunately, given that the test is running in a #Transactional transaction, even a flush does not make it accessible to the spawned threads.
#Transactional
public void testAddAttachment() throws Exception{
final Contract c1 = contractDOD.getNewTransientContract(15);
contractRepository.save(c1);
// Need to commit the saveContract here, but don't know how!
em.getTransaction().commit();
List<Thread> threads = new ArrayList<>();
for( int i = 0; i < 5; i++){
final int threadNumber = i;
Thread t = new Thread( new Runnable() {
#Override
#Transactional
public void run() {
try {
// do stuff here with c1
// sleep to ensure that the thread is not finished before another thread catches up
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
threads.add(t);
t.start();
}
// have to wait for all threads to complete
for( Thread t : threads )
t.join();
// Need to validate test results. Need to be within a transaction here
Contract c2 = contractRepository.findOne(c1.getId());
}
I've tried using the entity manager to, but get an error message when I do:
org.springframework.dao.InvalidDataAccessApiUsageException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead; nested exception is java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:293)
at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:33)
Is there any way to commit the transaction and continue it? I have been unable to find any method that allows me to call a commit().
I had a similar use case during testing hibernate event listeners which are only called on commit.
The solution was to wrap the code to be persistent into another method annotated with REQUIRES_NEW. (In another class) This way a new transaction is spawned and a flush/commit is issued once the method returns.
Keep in mind that this might influence all the other tests! So write them accordingly or you need to ensure that you can clean up after the test ran.
Why don't you use spring's TransactionTemplate to programmatically control transactions? You could also restructure your code so that each "transaction block" has it's own #Transactional method, but given that it's a test I would opt for programmatic control of your transactions.
Also note that the #Transactional annotation on your runnable won't work (unless you are using aspectj) as the runnables aren't managed by spring!
#RunWith(SpringJUnit4ClassRunner.class)
//other spring-test annotations; as your database context is dirty due to the committed transaction you might want to consider using #DirtiesContext
public class TransactionTemplateTest {
#Autowired
PlatformTransactionManager platformTransactionManager;
TransactionTemplate transactionTemplate;
#Before
public void setUp() throws Exception {
transactionTemplate = new TransactionTemplate(platformTransactionManager);
}
#Test //note that there is no #Transactional configured for the method
public void test() throws InterruptedException {
final Contract c1 = transactionTemplate.execute(new TransactionCallback<Contract>() {
#Override
public Contract doInTransaction(TransactionStatus status) {
Contract c = contractDOD.getNewTransientContract(15);
contractRepository.save(c);
return c;
}
});
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; ++i) {
executorService.execute(new Runnable() {
#Override //note that there is no #Transactional configured for the method
public void run() {
transactionTemplate.execute(new TransactionCallback<Object>() {
#Override
public Object doInTransaction(TransactionStatus status) {
// do whatever you want to do with c1
return null;
}
});
}
});
}
executorService.shutdown();
executorService.awaitTermination(10, TimeUnit.SECONDS);
transactionTemplate.execute(new TransactionCallback<Object>() {
#Override
public Object doInTransaction(TransactionStatus status) {
// validate test results in transaction
return null;
}
});
}
}
I know that due to this ugly anonymous inner class usage of TransactionTemplate doesn't look nice, but when for some reason we want to have a test method transactional IMHO it is the most flexible option.
In some cases (it depends on the application type) the best way to use transactions in Spring tests is a turned-off #Transactional on the test methods. Why? Because #Transactional may leads to many false-positive tests. You may look at this sample article to find out details. In such cases TransactionTemplate can be perfect for controlling transaction boundries when we want that control.

HibernateTemplate not getting the object when called through TaskExecutor

I have a web service DocGenerationServiceImpl that inserts (for every format) a record in the table using DocRepository and object representing the record as DocFileDO. In the for-loop, I can get the id of the record that was created in the table. For each record, I will call the executor's execute method where DocGenTask will search for the record given the id. However, for example, there are 3 formats, the DocGenTask is able to get only the last record. The first 2 it cannot find. Although it's using hibernateTemplate. Can please advise?
#RestfulService
#Controller
#RequestMapping("/docs")
public class DocGenerationServiceImpl {
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DocRepository docRepository;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
#ResponseBody
public String generatedDocFile(DOCParam param) {
for(String format : param.getFormatList()) {
DocFileDO docFileDO = new DocFileDO();
...
docRepository.saveDocFile(docFileDO);
log.debug("docFileDO id = " + docFileDO.getId());
DocGenTask task = new DocGenTask(docFileDO.getId());
task.setDocRepository(docRepository);
taskExecutor.execute(task);
}
}
}
#Repository
public class DocRepository {
#Autowired
private HibernateTemplate hibernateTemplate;
public DocFileDO saveDocFile(DocFileDO docFile) {
hibernateTemplate.save(docFile);
hibernateTemplate.flush();
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return hibernateTemplate.get(DocFileDO.class, docFileId);
}
}
public class DocGenTask implements Runnable {
public void run() {
generate();
}
private void generate() {
DocFileDO docFileObj = docRepository.getDocFile(docFileId);
}
}
A couple of things
Don't use HibernateTemplate it should be considered deprecated as of Hibernate 3.0.1 (which was released somewhere in 2006). Use the SessionFactory directly and use the getCurrentSession() method to get a hibernate Session to operate on.
You don't have transactions setup (judging from the snippets), to work with a databse you need proper transaction setup.
Your controller is doing much, all of this should be inside a service.
The first refactor your repository
#Repository
public class DocRepository {
#Autowired
private SessionFactory sf;
public DocFileDO saveDocFile(DocFileDO docFile) {
Session session = sf.getCurrentSession();
session.save(docFile);
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return sf.getCurrentSession().get(DocFileDO.class, docFileId);
}
}
Now your code will probably fail due to improper transaction setup. Add #Transactional to all the methods (or class) that need a transaction (like the saveDocFile method).
As mentioned you probably should move the code found in the controller to a service. The controller should be nothing more then a thin integration layer converting from the web to an internal representation of something and then kick off a service/business method somewhere. This service-/business-method is also your transactional unit-of-work it either all succeeds or all fails.

Categories

Resources