Spring JPA repository is not working with two transaction managers - spring

I am using two transaction managers in my code. One is marked as primary. Complete configuration is using Spring annotation.
Table that I am trying to update using NON-primary transaction manager is not able to save data in the database. If I mark the same transaction manager as primary then it starts inserting data into database.
In case of non primary transaction, I am getting below error
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:413)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:157)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy39.flush(Unknown Source)
at com.ashish.business.CreditCardManagementImpl.addCreditCardUser(CreditCardManagementImpl.java:44)
at com.ashish.business.CreditCardManagementImpl.addCrCardCustomerData(CreditCardManagementImpl.java:30)
My code is present in the following location
https://github.com/ashismo/repositoryForMyBlog/tree/master/spring/SpringJPARepoDistributedTransaction
In my code, com.ashish.appConfig package has three classes called AppConfig.java, CreditCardTransactionConfig.java and DebitCardTransactionConfig.java. Where AppConfig has included other two classes and CreditCardTransactionConfig.java has NON primary transaction manager configuration
I am running this code from junit file. And the above mentioned error is coming as soon as I am trying to do creditUserDetailRepository.flush();. If I do not have this line in my code then I am not getting any error but data is not getting inserted into the table.
Can someone help me please?

In your DebitCardTransactionConfig class you have annotated the debitEntityManagerFactory method with the #Primary annotation. That annotation means, that this instance should be used whenever there are multiple candidates for autowiring based on type. You are creating the PlatformTransactionManagers like this:
#Bean(name="creditTransactionManager")
#Qualifier("creditTransactionManager")
public PlatformTransactionManager creditTransactionManager(
EntityManagerFactory emf) {
//...
}
This means, that Spring should choose the EntityManagerFactory automatically. Hence, both transaction managers are handed the debit entity manager factory.
What you should do for a quick fix, is to change the declaration of the creditTransactionManager like this:
#Bean(name="creditTransactionManager")
#Qualifier("creditTransactionManager")
// #Primary
public PlatformTransactionManager creditTransactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(creditEntityManagerFactory().getObject());
return transactionManager;
}
As calling a different #Bean method in a #Configuration class means retrieving the instance from scope, this should work without problems. Unfortunately, I was unable to test this. See side notes.
SIDE NOTES
Here are some offtopic notes about your code.
Your CreditCardTransactionConfig, and DebitCardTransactionConfig classes are very simillar. Consider extracting a super class that would eliminate most of the boilerplate.
The bean name is taken from the name of the #Bean annotated method. You do not need to specify it in the #Bean and #Qualifier annotations.
While it is a good idea to share a repository so that anyone can clone your code and test it, I was unable to run your test as it expects a MySQL database. Consider using an in-memory database like H2 for experimenting.

Related

Spring Batch/Data JPA application not persisting data to db

I'm having really weird issue. I need to say that my code working perfectly fine in my local but not persisting some datas in our pod (k8 environment).
I have different datasources to work with in this batch. Everything running fine. Job Repository is map-based and using ResourcelessTransactionManager for it. I configured it like this
#Configuration
#EnableBatchProcessing
public class BatchConfigurer extends DefaultBatchConfigurer {
#Override
public void setDataSource(DataSource dataSource){
}
}
I also use different platformtransactionmanager then spring batch (issue). So I set my spring allow bean overriding to true in my properties. The platform transaction manager in my configurer is right binded one, I debugged it.
I have custom writer for one of my step. Updating records in multiple tables which in multiple dbs (different datasources, in brief)
public class MyWriter implements ItemWriter<MyDTO> {
#Autowired
private MyFirstRepo myfirstRepo; //table in first datasource
#Autowired
private MySecondRepo mySecondRepo; //table in second datasource
#Override
public void write(List<? extends MyDTO> myDtoList) throws Exception {
//some logic
mySecondRepo.delete(deletableEntity)
//some logic
mySecondRepo.saveAll(updatableEntities)
//some logic
myfirstRepo.saveAll(updatableEntities)
}
}
Since I have multiple datasources, I defined multiple transaction managers, and to give transaction manager to my step I defined chained transaction manager that includes that managers.
#Bean
public Step myStep(#Qualifier("chainedTransactionManager") ChainedTransactionManager chainedTransactionManager) {
return getCommonStepBuilder("myStep")
.transactionManager(chainedTransactionManager)
.<MyDTO,MyDTO>chunk(200)
.reader(myPaginingReader())
.writer(myWriter())
.taskExecutor(myTaskExecutor())
.throttleLimit(15)
.build();
}
chained transaction manager config (both of these transaction manager is JpaTransactionManager):
#Configuration
public class TransactionManagerConfig {
#Primary
#Bean(name = "chainedTransactionManager")
public ChainedTransactionManager transactionManager(
#Qualifier("firstTransactionManager") PlatformTransactionManager firstTransactionManager,
#Qualifier("secondTransactionManager")PlatformTransactionManager secondTransactionManager) {
return new ChainedTransactionManager(firstTransactionManager,secondTransactionManager);
}
}
So my first two jpa operations in writer working just fine( operations that made over MySecondRepo) but the last operation is not persisting data to db. It doesn't throws any errors, job completing succesfully but it doesn't update my records in table.
I must mention second time, it does update in my local actually. Just not updating in our app that lives on k8 environment (dockerized microservice). Which is making it so confusing. Any idea why is it happening?
Edit: I created another writer bean for myfirstRepo.saveAll(updatableEntities) (as jdbc batch item writer, executing same logic) and add two of these writer to composite one. Now it's working as expected. But I have a lot of concerns now since I don't know what caused it. Any idea?
Edit 2: I came across with this thread. I was using JdbcPagingItemReader, does entities fetched with this component are in managed state? Entites inside mySecondRepo.delete(deletableEntity) and
mySecondRepo.saveAll(updatableEntities) are fetched inside writer by using hibernate but myfirstRepo.saveAll(updatableEntities) entities are the ones that came from reader.
It all makes sense if it is the case but even it is then why it was working fine in local?
mySecondRepo.saveAll(updatableEntities) are fetched inside writer by using hibernate but myfirstRepo.saveAll(updatableEntities) entities are the ones that came from reader.
Fetching items in the item writer is the cause of your issue. This is incorrect, it is not expected to read items in the item writer. That's why items coming from the reader are saved, but not the ones fetched in the writer.
What you should know is that all writers in the composite are running in the scope of a single transaction, driven by the transaction manager of the step. So if you are writing data to multiple datasources, you need to make sure the transaction manager is coordinating the transaction between all datasources. ChainedTransactionManager is deprecated, you can use a JtaTransactionManager for your case.

Injecting an entitymanager in #ApplicationScoped bean

I am porting an existing JBOSS JEE application to Quarkus. I am using a number of HV custom validators that require injection.
For that purpose I've defined all custom validators that require injection as bean in my libraries like this:
#ApplicationScoped
public class SomeValidator implements ConstraintValidator<SomeValidation, AnObject> {
#Inject
public BeanUsingEntityManager bean;
Note: It is common code, so it should remain working on JBOSS as well
Next I defined a REST service. The REST service makes use of an application scoped bean like this.
#ApplicationScoped
public class ApplicationContext {
#PersistenceContext( unitName = "A" )
EntityManager em;
#Produces
#EnityManagerA // required qualifier to make datasource unique in JEE context (there are more)
public EntityManager produce() {
return em;
}
// NOTE: quarkus does not allow the #Produces on a field, which is allowed in JBOSS hence the method
#Produces
public BeanUsingEntityManager createBeanUsingEntityManager () {
// some logic that requires the entity manager.
}
}
Now the problem is simplified, but I keep on running into an error message.
Caused by: javax.enterprise.inject.CreationException: Synthetic bean instance for javax.persistence.EntityManager not initialized yet: javax_persistence_EntityManager_b60c51739990fc921960fc78caeb075a811a91a6
- a synthetic bean initialized during RUNTIME_INIT must not be accessed during STATIC_INIT
- RUNTIME_INIT build steps that require access to synthetic beans initialized during RUNTIME_INIT should consume the SyntheticBeansRuntimeInitBuildItem
at javax.persistence.EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.create(EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.zig:167)
at javax.persistence.EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.create(EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.zig:190)
at io.quarkus.arc.impl.AbstractSharedContext.createInstanceHandle(AbstractSharedContext.java:96)
at io.quarkus.arc.impl.AbstractSharedContext.access$000(AbstractSharedContext.java:14)
at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:29)
at io.quarkus.arc.impl.AbstractSharedContext$1.get(AbstractSharedContext.java:26)
at io.quarkus.arc.impl.LazyValue.get(LazyValue.java:26)
at io.quarkus.arc.impl.ComputingCache.computeIfAbsent(ComputingCache.java:69)
at io.quarkus.arc.impl.AbstractSharedContext.get(AbstractSharedContext.java:26)
at javax.persistence.EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.get(EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.zig:222)
at javax.persistence.EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.get(EntityManager_e1903961aa3b05f292293ca76e991dd812f3e90e_Synthetic_Bean.zig:238)
at nl.bro.gm.gmw.dispatch.resources.ApplicationContext_Bean.create(ApplicationContext_Bean.zig:131)
... 59 more
I'm new to Quarkus. So, not sure to how to handle this issue or even if I make the correct assumptions. I can imagine that Quarkus wants to give me a fresh entitymanager each request (which I understand), but that poses a problem for my application scoped beans.
What am I doing wrong here?
So, the full answer is that the EntityManager is created at the runtime init phase whereas the ValidatorFactory (and the ConstraintValidators) are created at static init time.
The Quarkus bootstrap goes static init -> runtime init.
So in your case, you can't access a #Singleton bean which uses the EntityManager during static init as it's not yet available.
Making your bean #ApplicationScoped will create a proxy and avoid this chicken and egg problem.
You will have only one BeanUsingEntityManager for your whole application.
The EntityManager is a bit different because we wrap it and you will get one new EntityManager/Session per transaction, which is what is expected as EntityManagers/Sessions are not thread safe.

Spring Boot: Why isn't entity being scanned in createQuery when using multiple datasources?

I am using Spring Boot 2.3.0. I have 2 data sources one for oracle and one for h2 defined in application.properties.
I have to 2 #Configuration classes for the data configurations. Both classes implement:
DataSource
PlatformTransactionManager
LocalContainerEntityManagerFactoryBean
In LocalContainerEntityManagerFactoryBean I set up:
setDataSource
setPackagesToScan
setJpaVendorAdapter
The application starts up properly, I can even do .findAll on the table in the H2 database, however
as soon as I start executing custom methods in the repository implementation, such as this:
#Transactional(readOnly = true)
private Optional<List<Foo>> findFooByState(Optional<Integer> id, Foo.State state) {
CriteriaBuilder cp = em.getCriteriaBuilder();
CriteriaQuery<Foo> cqFoo= cp.createQuery(Foo.class);
Root<Foo> fooRoot = cqFoo.from(Foo.class);
[...]
Spring throws an exception such as:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: Not an entity: class foo.Foo;
nested exception is java.lang.IllegalArgumentException: Not an entity: class foo.Foo] with root cause
Package foo is added in setPackagesToScan as I wrote earlier.
I have tried various things with #Transactional, e.g. remove it, add the name of the transaction manager specified in the DataSource in it, move the #Transactional to the #GetMapping, but none of it helped.
Does anybody have any clue what am I doing wrong?
Thanks,
I had a similar problem. Most probably you haven`t configured JPA repositories base packages to pick up different entities for different data sources. You can have a look at my guide on how to configure two data sources
in Spring Boot application. Hope it will help!
Ok, I was lame. I had to:
setPersistenceUnitName in the LocalContainerEntityManagerFactoryBean instantiation method
I had to use the proper #PersistenceContext with the proper unitName

In Spring 3.2, should we use #Transactional annotation for db activities?

I use spring 3.2 and Hibernate 4 in my project. When i query table i get a "No Session found for current thread" message. I try to use #Transactional annotation(it get success) but i don't want to put #Transactional to every service implementation.
Is there an another way?
In other words "How can i do a simple "insert" operation without using #Transaction?"
Thx...
You should not have #Transactional on you DAO methods, in fact you should never be accessing your DAO methods directly, you should be using an #Service. A service will use zero or more DAO classes to perform operations, only after all operations are completed will the transaction be committed.
#Repository
public class CustomerDao() {
// dao methods here, they are not transactional but will be run within a sevice transaction
}
#Service
#Transactional
public class CustomerService() {
private final CustomerDao customerDao;
#Autowired
public CustomerService(CustomerDao customerDao) {
this.customerDao = customerDao;
}
//service methods here (they are all transactional because we have annotated the class)
}
#Transactional is used for making a java code call in transaction so that in case any exception occurred during the process then all database changes will be rolled back. In ideal scenario every service which you think should be independent should have #Transactional annotation. Hibernate also want each database calls in transaction thats why they have implemented in such a way that Transaction will be required for each database query to be successful. I am not sure why you wanted your service to be out of transaction still they would like to fire database calls.

Spring+hibernate+jpa how does it work?

In the new project that I have joined, they keep using the terms Hibernate and JPA interchangeably. So, I tried to dive down into the code and try to understand how this whole thing works (I am new to Spring, JPA and Hibernate world). I'll try to put the code here for better understanding:
1) There is a #Configuration class where they have the following:
#Resource
private HibernateJpaVendorAdapter hibernateOracleJpaVendorAdapter;
LocalContainerEntityManagerFactoryBean entityManager =
new LocalContainerEntityManagerFactoryBean();
entityManager.setJpaVendorAdapter(hibernateOracleJpaVendorAdapter);
entityManager.setPersistenceUnitName("abc");
.
.
So, in this configuration class, we are returning an EntityManagerFactory.
2) Then there is a persistor class marked #Persistor, where a method of repository is invoked (for example, for a save operation):
blahblahRepository.save(blahblahEntity, abcdef);
3) Finally there is a repository class which is annotated #Repository. Then again, they have this piece of code:
#PersistenceContext(unitName = "same as the name in persistence.xml")
protected EntityManager entityManager;
The "save" method wraps around the persist method of JPA:
getEntityManager().persist(entityObject);
My questions are as follows:
1) There is no word about Hibernate other than in the hibernateJpaVendorAdapter. I searched the entire workspace and it showed just 3 occurences of the word hibernate, all in the configuration file.
2) From whatever knowledge I have, one should use either an EntityManagerFactory or an EntityManager but we are doing both?
Hibernate is one of the implementations of the JPA spec. Since your project chose Hibernate as its JPA implementation, it uses the JPA API, which delegates the Hibernate. Just like when you use the JDBC API, which delegates to a specific Oracle or PostgreSQL driver.
EntityManagerFactory, as its name indicates, is a factory for EntityManager. I don't see why you wouldn't use both. EntityManager is the main interface of the JPA API, used to execute all database operations (find, persist, merge, query, etc.). EntityManagerFactory must be configured before asking it to create an EntityManager.

Resources