TransactionManager from Spring or JNDI? (JBOSS + Spring 3 + Hibernate 4 + JTA) - java

We are using Spring with Hibernate to establish transactions with JTA. The PlatformTransactionManager is the JtaTransactionManager which is wired with the TransactionManager and UserTransaction from narayana.
#Bean
#Scope("prototype")
public TransactionManager jbossTransactionManager() {
return jtaPropertyManager.getJTAEnvironmentBean().getTransactionManager();
}
#Bean
#Scope("prototype")
public UserTransaction jbossUserTransaction() {
return jtaPropertyManager.getJTAEnvironmentBean().getUserTransaction();
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JtaTransactionManager(jbossUserTransaction(), jbossTransactionManager());
}
I have noted that JtaTransactionManager has the UT and TM I would want. On JBoss 6 EAP, I noted that my DataSource has been used as a WrapperDataSource and that this was related to a different TM. Specifically, it is using the TransactionManagerDelegate. This appears to be the transaction manager provided by JBoss via the JNDI names java:TransactionManager and java:jboss/TransactionManager. This is preventing my transactions from having transactional boundaries and I leak data on flush. If I remove my configuration and the UT and TM from the container, my transactions transact properly.
What is deciding to use this other TransactionManager? This appears to be the JCA from the container but I do not understand the mechanism for this decision.
Should I remove my UT and TM and surrender control to the
container to give these components to my app and rely on the JTA
platform as is or should I try to gain more control?

the container provides the datasource with a transaction manager from the JCA. This TransactionManager is a different instance than the one we had wired in from Spring. (Our bean had been instantiated from the arjuna environment bean). Using the JtaManager from Spring to get the transaction manager, via JNDI in the default locations, from the container ensured that we have the same transaction manager in the JTA platform used by Hibernate (JBoss App Server in this case).
Before we made this change, the application TransactionManager was in a transaction with Hibernate but the transactionManager on the datasource was not participating which caused the "leak".
Using the same instance has everything working together. This has also been proven out on WebLogic using the same approach.

Related

Multiple connection pools with shared TransactionManager for the same database in Spring?

I'm using Spring JPA to connect to a relational database. I want to have multiple connection pools connecting to the same database so scheduled tasks can't clog up the connection pool for user interactions.
This works fine if I define multiple DataSources like it is described in Spring JPA Hikari multiple connection pool with same datasource. Currently I define multiple different EntityManagerFactoryBeans and PlatformTransactionManagers, one for each DataSource. But I would like to be able to use the same physical transaction spanning calls to different JpaRepositories backed by different DataSources.
#Service
public class FooService {
// ..
#Transactional
public saveFoo(Foo foo) {
fooRepository.save(foo);
barService.somethingElse(foo);
}
}
#Service
public class BarService {
#Transactional(propagation = Propagation.MANDATORY)
public somethingElse(Foo foo) {
// BarRepository is backed by another DataSource as FooRepository,
// but both DataSources connect to the same database
barRepository.saveAndFlush(new FoobarEvent(foo));
}
}
The call to BarRepository fails for me with:
InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
It seems like multiple different DataSources are needed because the DataSources define the connection pool. But since the PlatformTransactionManager and the EntityManagerFactoryBean references the DataSource it's not really possible to have transactions span multiple repositories that are backed by different DataSources because Spring doesn't know that both DataSources point to the same database. Is there a way to configure Spring JPA to achieve what I want?
If your service is running through an interface from your controller, then Transactional method should come on the interface layer method.
And use javax.persistance.Transactional class for the annotation instead of org.springframework.transaction.annotation.Transactional
and specify #EnableTransactionManagement in your application class to enable transactions.

Atomikos or DataSource transaction manager with multiple datasource and local transaction

My application works with multi datasources and 2 databases Oracle and PostgreSQL (I dont need global transaction) .
I dont know which transaction manager to use. Both have some advantages and disadvantages.
Atomikos suppport global transaction which I dont need and log some information about transaction to file system which I want to avoid:
public void setEnableLogging(boolean enableLogging)
Specifies if disk logging should be enabled or not. Defaults to true.
It is useful for JUnit testing, or to profile code without seeing the
transaction manager's activity as a hot spot but this should never be
disabled on production or data integrity cannot be guaranteed.
advantages is that it use just one transaction manager
When using DataSourceTransactionManager I need one per dataSource
#Bean
#Primary
DataSourceTransactionManager transactionManager1() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource1());
return transactionManager;
}
#Bean
DataSourceTransactionManager transactionManager2() {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource2());
return transactionManager;
}
this is problem because I need to specify name of tm in annotation:
#Transactional("transactionManager1")
public void test() {
}
but I dont know it because in runtime I can switch in application which database to use.
is there some other options or I am missing something in this two transaction manager ?
You should solve this as option 2, using one DataSourceTransactionManager per data source. You will need to keep track of the transaction manager for each data source.
One thing additionally, if you need to be able to rollback transactions on both databases, you will have to set up a ChainedTransactionManager for both.

Spring default transactionManager not found when using JmsTransactionManager

I have a Spring Boot app, where I use JMS with Database. I'm trying to configure JmsTransactionManager to use with default TransactionManager (for JPA). I defined the bean in the #SpringBootApplication file (that means it has #Configuration and #EnableTransactionManagement):
#Bean(name="jmsTransactionManager")
public JmsTransactionManager jmsTransactionManager(ConnectionFactory connectionFactory) {
JmsTransactionManager jmsTransactionManager = new JmsTransactionManager();
jmsTransactionManager.setConnectionFactory(connectionFactory);
return jmsTransactionManager;
}
That's the only bean I configure by myself for JMS because other configuartion spring-boot does automatically, I just have properties in the application.yaml so I assume connectionFactory will be autowired. And I want it to use like this:
#Transactional(transactionManager = "jmsTransactionManager", propagation = Propagation.REQUIRES_NEW)
void do(){
sendJms();
saveDb();
}
#Transactional // uses default JPA TM
void sendDb(){
...
}
So the logic is that I will send to Jms first, then save something to DB, so I need two separate transactions but I want to close the DB transaction before JMS transaction. Maybe it's not correct to make calls like this in such situation, but I don't know how to do it else using declarative transaction management. And the problem is that when I'm defining JmsTransactionManagement the default one, that works with DB stops working, but without a JmsTransactionManagement transactions to db work:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'transactionManager' available: No matching TransactionManager bean found for qualifier 'transactionManager' - neither qualifier
match nor bean name match!
Am I missing something? I have spring-data-jpa in pom so default transactionManager configures by spring boot, but it can't find it, why? Unfortunately didn't find the answer on how to do something like that on the StackOverflow.
I am presuming that you are not using two phase commit (XA) transactions. Essentially in order to chain transactions between multiple transactional resources, both your jms ConnectionFactory and db Datasource have to be an XA resource implementation, and you have to use proper JTA TransactionManager. While it is not particularly hard thing to do, JTA is usually skipped by majority of java programmers as in normal historical situation (spring code deployed in JEE serer) JTA "just works" in background never to be directly accessed by java programmer. In standalone boot application you have to explicitly enable this functionality by providing proper JTA transaction manager and using XA implementation of your resources.
See: https://docs.spring.io/spring-boot/docs/2.0.x/reference/html/boot-features-jta.html
In short JMSTransactionManager and DBTrasnactionManager wont do as you need instance of JTATransactionManager.

Why is setting TransactionManager as JPATransactionManager in a Step not correct?

I am working with Spring Batch and JPA and I experienced the TransactionManager bean conflict. I found a solution by setting the TransactionManager as JpaTransactionManager in a step. But according to this link (https://github.com/spring-projects/spring-batch/issues/961), it is not correct even though it works for me.
#Autowired
private JpaTransactionManager transactionManager;
private Step buildTaskletStep() {
return stepBuilderFactory.get("SendCampaignStep")
.<UserAccount, UserAccount>chunk(pushServiceConfiguration.getCampaignBatchSize())
.reader(userAccountItemReader)
.processor(userAccountItemProcessor)
.writer(userAccountItemWriter)
.transactionManager(transactionManager)
.build();
}
}
I tried the suggested solution of implementing the BatchConfigurer but it conflicts with me disabling the metadata tables using this code:
#Configuration
#EnableAutoConfiguration
#EnableBatchProcessing
public class BatchConfiguration extends DefaultBatchConfigurer {
#Override
public void setDataSource(DataSource dataSource) {
// override to do not set datasource even if a datasource exist.
// initialize will use a Map based JobRepository (instead of database)
}
}
What would be the problem using the first solution of setting the TransactionManager in a Step?
In Spring Batch, there are two places where a transaction manager is used:
In the proxy created around the JobRepository to create transactional methods when interacting with the job repository
In each step definition to drive the step's transaction
Typically, the same transaction manager is used in both places, but this is not a requirement. It is perfectly fine to use a ResourcelessTransactionManager with the job repository to not store any meta-data and a JpaTransactionManager in the step to persist data in a database.
By default, when you use #EnableBatchProcessing and you provide a DataSource bean, Spring Batch will create a DataSourceTransactionManager and set it in both places, because this is the most typical case. But nothing prevents you from using a different transaction manager for the step. In this case, you should accept that business data and technical meta-data can get out of sync when things go wrong.
That's why the expected way to provide a custom transaction manager is via a custom BatchConfigurer#getTransactionManager, in which case your custom transaction manager is set it in both places. This was not clearly documented, but it has been fixed since v4.1. Here is the section that mentions that: Configuring a JobRepository. This is also mentioned in the Javadoc of #EnableBatchProcessing:
In order to use a custom transaction manager, a custom BatchConfigurer should be provided.

Spring-Boot: How to restrict the visibility of Beans

I have two custom PlatformTransactionManager beans injected into the Spring framework with specific names as follows:
#Bean(name = "ubldbTransactionManager")
protected PlatformTransactionManager transactionManager(
#Qualifier("ubldbEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
#Bean(name = "bpdbTransactionManager")
public PlatformTransactionManager bpdbTransactionManager(
#Qualifier("bpdbEntityManagerFactory") EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
A 3rd-party library has a #Autowired protected PlatformTransactionManager transactionManager; dependency. So, the 3rd party library is not supposed to use none of the two TransactionManagers. However, as you see there is no Qualifier for the dependency injection in the external library and I get an error as follows:
Field transactionManager in org.camunda.bpm.spring.boot.starter.configuration.impl.DefaultDatasourceConfiguration required a single bean, but 2 were found:
- bpdbTransactionManager: defined by method 'bpdbTransactionManager' in class path resource [eu/nimble/service/bp/config/BusinessProcessDBConfig.class]
- ubldbTransactionManager: defined by method 'transactionManager' in class path resource [eu/nimble/service/bp/config/UBLDBConfig.class]
So, how can I restrict the visibility of the two Beans so that they would not be accessible by the 3rd-party library?
DefaultDatasourceConfiguration is provided to use default Spring beans e.g. DataSource named dataSource and PlatformTransactionManager named transcationManager. It's there to glue Camunda into a Spring Boot application which be default has a single data source.
Since you have created your own PlatformTransactionManager beans this disabled Spring Boot's default transaction manager bean named transcationManager (as per TransactionAutoConfiguration Spring Boot auto-configuration logic).
You most likely need to define one more transactionManager (and potentially dataSource) for Camunda's process engine, which requires it's own schema. Make sure to use the right bean name as below:
#Bean
public PlatformTransactionManager transactionManager() {
...
}
Starting from Spring 4 the bean name is the default qualifier when auto-wiring so the new transaction manager will be wired into DefaultDatasourceConfiguration as it matches the field name in the class.
Alternatively don't use DefaultDatasourceConfiguration and roll out your own configuration if Spring Boot defaults are not working for you.
Use #Qualifier annotation
The #Qualifier annotation is used to resolve the autowiring conflict, when there are multiple beans of same type.
#Bean
#Qualifier("ubldbTransactionManager")
protected PlatformTransactionManager transactionManager
and
#Bean
#Qualifier("bpdbTransactionManager")
public PlatformTransactionManager bpdbTransactionManager

Categories