Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 767 Vote(s) - 3.58 Average
  • 1
  • 2
  • 3
  • 4
  • 5
@EnableTransactionManagement annotation with 2 transaction managers

#1
I am using `@Configuration` annotation for configuration of spring instead of xml file. I am configuring 2 datasources with different session factory and different transaction managers. I am stuck with a problem here for `@EnableTransactionManagement` annotation. I read in its documentation that,

> `@EnableTransactionManagement` is more flexible; it will fall back to a
> by-type lookup for any `PlatformTransactionManager` bean in the
> container. Thus the name can be "txManager", "transactionManager", or
> "tm": it simply does not matter.

This means whatever name I give to method, it will always search for the method which returns `PlatformTransactionManager` object while I have 2 transactionmanagers. Now the problem is, when I test this class, it gives me error:

>`org.springframework.beans.factory.NoSuchBeanDefinitionException`: No unique bean of type [`org.springframework.transaction.PlatformTransactionManager`] is defined: expected single bean but found 2

I even tried to have 2 different Configuration classes but in vain. In xml configuration, this was not the case. I registered my both transaction managers with two `<tx:annotation-driven transaction-manager="" />` tag and it worked fine. But not able to do same here with annotations.

What should I do if I want to configure 2 datasources with 2 different transaction managers in Spring annotated configuration class?
Reply

#2
From the [java doc][1]

>For those that wish to establish a more direct relationship between
`@EnableTransactionManagement` and the exact transaction manager bean to be used, the
`TransactionManagementConfigurer` callback interface may be implemented - notice the
implements clause and the `@Override`-annotated method below:


Your `@Configuration` class needs to implement `TransactionManagementConfigurer` interface - implement the `annotationDrivenTransactionManager` which will return the reference to the `transactionManager` that should be used.

[1]:

[To see links please register here]

Reply

#3
I am not sure why you are using two TransactionManagers . You could consider using the same TransactionManager for multiple datasource via the AbstractRoutingDataSource . Please refer

[To see links please register here]


for a sample on its usage.
Reply

#4
Some of the other answers imply that using two transaction managers is in some way wrong; however, Spring's XML configuration allows for using multiple transaction managers as stated in the online documentation (below). Unfortunately, there does not seem to be a way to make the `@EnableTransactionManagement` annotation work in a similar manner. As a result, I simply use an `@ImportResource` annotation to load an XML file that includes the `<tx:annotation-driven/>` line. This allows you to get a Java configuration for most things but still make use of `@Transactional` with an optional Transaction Manager qualifier.

[To see links please register here]


>Most Spring applications only need a single transaction manager, but there may be situations where you want multiple independent transaction managers in a single application. The value attribute of the `@Transactional` annotation can be used to optionally specify the identity of the `PlatformTransactionManager` to be used. This can either be the bean name or the qualifier value of the transaction manager bean. For example, using the qualifier notation, the following Java code
Reply

#5
In your configuration class, use `@EnableTransactionManagement` annotation.

Define a transaction manager in this class as:

@Bean(name="txName")
public HibernateTransactionManager txName() throws IOException{
HibernateTransactionManager txName= new HibernateTransactionManager();
txName.setSessionFactory(...);
txName.setDataSource(...);
return txName;
}


There on, in your class/method that executes transactional job(s), annotate as follows:

@Transactional("txName")

or

@Transactional(value = "txName")

This is how you would tie a name qualified transaction manager to wherever you need it. You can now have as many transaction managers as you want and use it accordingly wherever you need.
Reply

#6
Just in case anyone runs into this problem, I found a solution:

@Configuration
@EnableTransactionManagement
@DependsOn("myTxManager")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements TransactionManagementConfigurer {

@Autowired
private PlatformTransactionManager myTxManager;

...

@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return this.myTxManager;
}

In this way, you can use a specific txManager defined in an xml configuration.

In case you want to define the *txManager* used on service-level, you shall **remove** the ` @EnableTransactionManagement` annotation from the `@Configuration` class and specify the *txManager* in the `@Transactional` annotations, e.g.

@Service
@Transactional(value="myTxManager", readOnly = true)
public class MyServiceImpl implements MyService { ... }
Reply

#7
Try to use **chained TransactionalManager**

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class ChainedDBConfig {

@Bean("chainedTransactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("database1TransactionManager") final PlatformTransactionManager db1PlatformTransactionManager,
@Qualifier("database2TransactionManager") final PlatformTransactionManager db2PlatformTransactionManager) {

return new ChainedTransactionManager(db1PlatformTransactionManager, db2PlatformTransactionManager);
}

}


And place the following annotation on your service class:

@Transactional(transactionManager = "chainedTransactionManager")
public class AggregateMessagesJobIntegrationTest {
...
}

You can also use it inside the integration tests:

@RunWith(SpringRunner.class)
@Transactional(transactionManager = "chainedRawAndAggregatedTransactionManager")
@Rollback
public class ExampleIntegrationTest extends AbstractIntegrationTest {
....
}
and it will do a rollback for both DB transaction managers.
Reply

#8
I have to use JPA and Reactive Mongo in one project. What works at last was:

- create a `@Configuraition` class to explicitly create a JPA transaction manager, like [here](

[To see links please register here]

):

```java
private Environment env;
@Bean
@Primary
public LocalContainerEntityManagerFactoryBean dbEntityManager() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dbDatasource());
em.setPackagesToScan(new String[]{"projectone.mysql"});
em.setPersistenceUnitName("dbEntityManager");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);

HashMap<String, Object> properties = new HashMap<>();

properties.put("hibernate.dialect",env.getProperty("hibernate.dialect"));
properties.put("hibernate.show-sql",env.getProperty("jdbc.show-sql"));


em.setJpaPropertyMap(properties);
return em;
}
@Primary
@Bean
public DataSource dbDatasource() {
DriverManagerDataSource dataSource
= new DriverManagerDataSource();
dataSource.setDriverClassName(
env.getProperty("spring.datasource.driverClassName"));
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
@Primary
@Bean
public PlatformTransactionManager jpaTransactionManager() {
JpaTransactionManager transactionManager
= new JpaTransactionManager();
transactionManager.setEntityManagerFactory(
dbEntityManager().getObject());
return transactionManager;
}

}
```

**Notice that the bean name `jpaTransactionManager`, which would be the txManager name used in JPA `@Transactional`.**


- create `MongoConfiguration` to explicitly create a Mongo transaction manager(a lot of beans to define)
- in `@Transactional`, call them with name. The default one `transactionManger` will not work. You have to distinguish, like `jpaTransactionManager` and `reactiveMongoTransactionManger`.

```
@Transactional(value="jpaTransactionManager")
public void xxx() {
...
}
```

Note that JPA transaction methods cannot Reactor types as return value(Mono/Flux). Spring will force methods returning Mono/Flux to use ReactiveTransactionManager, it will cause confusion.

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through