SpringBatch #Async method not working in persistent layer (JPAItemWriter) - spring-boot

SpringBoot Application with SpringBatch and JPA, without #Async all working fine but with #Async added in the REST API the job is completed but JPAItemWriter is not persisting the objects in the DB. looks like Transactional problem as am getting this exception.
Transaction Manager [org.sringframework.batch.support.transaction.ResourcelessTransactionManager] does not support transaction suspension.
I tried with different approaches like changing Propagation.Requires_New (Required, Supports) but nothing works, tried searching all the forums but no luck.
#Service
public class SampleWriter extends JpaItemWriter<TestEntity> {
#Autowired
EntityManager entityManager;
#Transactional(readOnly=false, propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED)
#Override
public void write(List<TestEntity> entities) {
this.doWrite(entityManager, entities);
}
}

It seems that you are configuring and using Spring Batch in a wrong way.
Firstly, it is using the default BatchConfigurer which the TransactionManager is ResourcelessTransactionManager that is mainly use for testing or acts as a "no-op" TransactionManager for the batch job that does not require any transaction which is definitely not your case now.
Secondly, Spring Batch will internally take care of managing the transaction boundary for processing each chunk , you do not need to extend JpaItemWriter and control the transaction behaviour using #Transactional by yourself.
So , read this section for how to configure Spring Batch , especially the part related to BatchConfigurer
On the other hand if you are using Spring-boot , it should already configure a JpaBatchConfigurer for you.

Related

Async Transactional Saves do not work in Spring after upgrade

We upgraded Spring Boot from 1.5.8 to 2.6.2. It has introduced a problem that has us perplexed: Transactional saves are not processing from inside spawned threads.
We use JPA managed entities on a Mysql database and make calls down to the CrudRepository to save them.
Transactions inside the main thread work fine. However, when called from an asynchronous operation things go awry:
both async and sync calls go through the Spring SimpleJpaRepository.save() method. But the entityManager returns the object to persist with a null id in the case of the async operation.
I followed the flow through in both types of calls and can see that the save propagates down to the org.hibernate.internal.SessionImpl service.
From there it makes its way to AbstractSaveEventListener.class and that is where the discrepancy appears to be. In the performSaveOrReplicate method (hibernate-core:5.6.3), inTrx boolean is false in the async workflow whereas it is true in the synch one. Because of that the shouldDelayIdentityInserts flag gets set and an id does not appear to be generated for any entities in this thread.
We have tried different things to get this to work. For example, we used the transactionTemplate to have some specific control here, but that has not changed the behavior.
We were originally creating this async process by using the ApplicationEventPublisher to create an event. We also tried using completablefuture and other constructs with the same result as well as annotating the method with #Async and calling it directly.
The issue was that, with the upgrade to Spring Boot 2.6, Spring Batch implements a new Transaction Manager.
What we didn't realize is that this transaction manager was being autowired into our other services and did not work in this threaded context. You do not want to share a Batch processing Tx Manager with your API/misc services. Declaring a specific Transaction Manager there to keep them separate solved the issue.
Here is an example marking a PlatformTransactionManager with the Primary annotation to test its usage explicitly.
#Primary
#Bean
public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory(dataSource).getObject());
return transactionManager;
}

How to use ObjectDB properly in spring boot application?

I have a spring boot application with objectdb embedded database.
I am manually handling connection and transaction operations as described at http://www.objectdb.com/java/jpa/persistence/overview
Below is a sample code that I am using: (taken from objecdb documentation):
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("myDbFile.odb");
EntityManager em = emf.createEntityManager();
try {
em.getTransaction().begin();
// Operations that modify the database should come here.
em.getTransaction().commit();
}
finally {
if (em.getTransaction().isActive())
em.getTransaction().rollback();
}
It works but the code has become uggly since I had to use try catch finally blocks in order to properly close connections.
I want to refactore my application so that database operations are done in JpaRepositories or Dao classes with #Transactional methods (as described in http://spring.io/guides/gs/accessing-data-jpa/)
I had a research on the web but could not find any solution that works.
What I am looking for is a very simple spring boot sample application with:
spring-boot-starter-data-jpa
Objectdb (embedded)
Maven
Uses annotation based configuration (no xml file)
A dummy entity class (e.g: Customer(id,firstname) )
A JpaRepository class or dao class with list() and #Transactional persist(Customer) methods
Note: I already tried this post but could not make it work.
ObjectDB support answered my question
https://www.objectdb.com/forum/2328#item-6

Spring #Transactional timeout not timing out

I have set transaction timeout in my application as #Transactional(propagation=Propagation.REQUIRED,timeout=30)
ActiveMQXAConnectionFactory and Oracle XA Datasource are two resources of my Distributed transaction. after reading a message from queue my transaction begins and while processing the application is taking more than 30 seconds and still transaction is not timed out. Only when committing the transaction its throwing timeout exception. I wanted immediately after 30 seconds the transaction should time out and throw the exception and make that thread available to consume another message from queue. Is this possible?
Without seeing your configuration it will be hard to say. If you are just adding an #Transactional, it is not going to do anything. You going to need both an EntityManager and a TransactionManager, then you need to turn on annotation based transaction management, and Spring needs to be controlling your datasource if I recall correctly.
Another, probably unnecessary side note, #Transactional will only work on public methods. Spring will proxy your method in order to manage the transaction and Spring can only proxy public methods. Also, it will only be able to work on calls from another class to that method, if you are calling that method from another method inside the same class, Spring cannot proxy either, thus no transaction management. Spring is sneakily deceptive here.
#Service
public class A{
#Autowired
Datasource datasource;
#Transactional
public void save(){
datasource.doStuff();
}
public void callSave(){
save();
}
}
#Service
public class B{
#Autowired
A a;
public void callSave(){
a.save();
}
}
Here, if a.save() is called from a.callSave(), no proxy will occur, thus you will have no transaction management. But in the exact same application, if you call b.callSave(), you will have transaction management, since Spring can then proxy the method call to a.save().
Are you using Spring Boot or vanilla Spring? We can probably give you more of a direction if you divulge that.
Hopefully that helped a bit!

Using PersistenceContext in a Quartz Job

We're using Spring 3.1, JPA (via Hibernate) and Quartz. Typically we interact with the DB via #PersistenceContext annotation on Service beans, and either SpringMVC controllers, or GraniteDS-managed service invocation.
I'm working on writing a Quartz job that needs to interact with the database. I've tried everything I can find to get this working. I tried passing in a Spring-managed component (annotated with #PersistenceContext and #Transactional) via the jobMap, the call to entityManager.persist(o) executes, but nothing happens in the database. I also tried similar to this answer, creating a factory class to call autowireBean() on the job object. I set up the job class like so:
public class CreateAlertJob implements Job {
#PersistenceContext
EntityManager entityManager;
#Override
#Transactional
public void execute(JobExecutionContext context) throws JobExecutionException {
SomeEntity entity = new SomeEntity();
entityManager.persist(entity);
}
}
Same result, the method executes but the database is unaltered. I found this blog post which references a GitHub project. There he is using JpaInterceptor to establish a Hibernate session, but this uses the DAO pattern and I'd like to stick with using #PersistenceContext.
Clearly there is something about the Quartz thread that is preventing this from working properly? I'm about out of ideas and considering making a web service call to a SpringMVC controller just to get this working.
Since your CreateAlertJob is not created by Spring, #Transactional in it doesn't take effect.
You have the following options:
Delegate actual work to Spring bean and put #Transactional there
Use programmatic transaction management
Use AspectJ-based AOP implementation instead of Spring default implementation (but it would be overkill for such a simple problem)

How do you use Spring Data JPA outside of a Spring Container?

I'm trying to wire up Spring Data JPA objects manually so that I can generate DAO proxies (aka Repositories) - without using a Spring bean container.
Inevitably, I will be asked why I want to do this: it is because our project is already using Google Guice (and on the UI using Gin with GWT), and we don't want to maintain another IoC container configuration, or pull in all the resulting dependencies. I know we might be able to use Guice's SpringIntegration, but this would be a last resort.
It seems that everything is available to wire the objects up manually, but since it's not well documented, I'm having a difficult time.
According to the Spring Data user's guide, using repository factories standalone is possible. Unfortunately, the example shows RepositoryFactorySupport which is an abstract class. After some searching I managed to find JpaRepositoryFactory
JpaRepositoryFactory actually works fairly well, except it does not automatically create transactions. Transactions must be managed manually, or nothing will get persisted to the database:
entityManager.getTransaction().begin();
repositoryInstance.save(someJpaObject);
entityManager.getTransaction().commit();
The problem turned out to be that #Transactional annotations are not used automatically, and need the help of a TransactionInterceptor
Thankfully, the JpaRepositoryFactory can take a callback to add more AOP advice to the generated Repository proxy before returning:
final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(emf.createEntityManager());
factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
#Override
public void postProcess(ProxyFactory factory) {
factory.addAdvice(new TransactionInterceptor(xactManager, new AnnotationTransactionAttributeSource()));
}
});
This is where things are not working out so well. Stepping through the debugger in the code, the TransactionInterceptor is indeed creating a transaction - but on the wrong EntityManager. Spring manages the active EntityManager by looking at the currently executing thread. The TransactionInterceptor does this and sees there is no active EntityManager bound to the thread, and decides to create a new one.
However, this new EntityManager is not the same instance that was created and passed into the JpaRepositoryFactory constructor, which requires an EntityManager. The question is, how do I make the TransactionInterceptor and the JpaRepositoryFactory use the same EntityManager?
Update:
While writing this up, I found out how to solve the problem but it still may not be the ideal solution. I will post this solution as a separate answer. I would be happy to hear any suggestions on a better way to use Spring Data JPA standalone than how I've solve it.
The general principle behind the design of JpaRepositoryFactory and the according Spring integration JpaRepositoryFactory bean is the following:
We're assuming you run your application inside a managed JPA runtime environment, not caring about which one.
That's the reason we rely on injected EntityManager rather than an EntityManagerFactory. By definition the EntityManager is not thread safe. So if dealt with an EntityManagerFactory directly we would have to rewrite all the resource managing code a managed runtime environment (just like Spring or EJB) would provide you.
To integrate with the Spring transaction management we use Spring's SharedEntityManagerCreator that actually does the transaction resource binding magic you've implemented manually. So you probably want to use that one to create EntityManager instances from your EntityManagerFactory. If you want to activate the transactionality at the repository beans directly (so that a call to e.g. repo.save(…) creates a transaction if none is already active) have a look at the TransactionalRepositoryProxyPostProcessor implementation in Spring Data Commons. It actually activates transactions when Spring Data repositories are used directly (e.g. for repo.save(…)) and slightly customizes the transaction configuration lookup to prefer interfaces over implementation classes to allow repository interfaces to override transaction configuration defined in SimpleJpaRepository.
I solved this by manually binding the EntityManager and EntityManagerFactory to the executing thread, before creating repositories with the JpaRepositoryFactory. This is accomplished using the TransactionSynchronizationManager.bindResource method:
emf = Persistence.createEntityManagerFactory("com.foo.model", properties);
em = emf.createEntityManager();
// Create your transaction manager and RespositoryFactory
final JpaTransactionManager xactManager = new JpaTransactionManager(emf);
final JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
// Make sure calls to the repository instance are intercepted for annotated transactions
factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() {
#Override
public void postProcess(ProxyFactory factory) {
factory.addAdvice(new TransactionInterceptor(xactManager, new MatchAlwaysTransactionAttributeSource()));
}
});
// Create your repository proxy instance
FooRepository repository = factory.getRepository(FooRepository.class);
// Bind the same EntityManger used to create the Repository to the thread
TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
try{
repository.save(someInstance); // Done in a transaction using 1 EntityManger
} finally {
// Make sure to unbind when done with the repository instance
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
}
There must be be a better way though. It seems strange that the RepositoryFactory was designed to use EnitiyManager instead of an EntityManagerFactory. I would expect, that it would first look to see if an EntityManger is bound to the thread and then either create a new one and bind it, or use an existing one.
Basically, I would want to inject the repository proxies, and expect on every call they internally create a new EntityManager, so that calls are thread safe.

Resources