web-dev-qa-db-fra.com

Annotation @EnableTransactionManagement avec 2 gestionnaires de transactions

J'utilise l'annotation @Configuration pour la configuration du ressort au lieu du fichier xml. Je configure 2 sources de données avec différentes fabriques de sessions et différents gestionnaires de transactions. Je suis coincé avec un problème ici pour l'annotation @EnableTransactionManagement. J'ai lu dans sa documentation que,

@EnableTransactionManagement est plus flexible; il va retomber à un recherche par type pour tout bean PlatformTransactionManager dans le fichier récipient. Ainsi, le nom peut être "txManager", "transactionManager" ou "tm": ça n'a pas d'importance.

Cela signifie que quel que soit le nom que je donne à method, il cherchera toujours la méthode qui retourne un objet PlatformTransactionManager tant que je dispose de 2 gestionnaires de transaction. Maintenant, le problème est que, lorsque je teste cette classe, cela me donne une erreur:

org.springframework.beans.factory.NoSuchBeanDefinitionException: aucun bean unique de type [org.springframework.transaction.PlatformTransactionManager] n'est défini: bean simple attendu mais trouvé 2

J'ai même essayé d'avoir 2 classes de configuration différentes mais en vain. Dans la configuration XML, ce n'était pas le cas. J'ai enregistré mes deux gestionnaires de transaction avec deux balises <tx:annotation-driven transaction-manager="" /> et cela a bien fonctionné. Mais pas capable de faire la même chose ici avec des annotations.

Que dois-je faire si je veux configurer 2 sources de données avec 2 gestionnaires de transactions différents dans la classe de configuration annotée Spring?

23
Mital Pritmani

Dans votre classe de configuration, utilisez l'annotation @EnableTransactionManagement.

Définissez un gestionnaire de transactions dans cette classe comme suit:

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

Dans votre classe/méthode qui exécute le (s) travail (s) transactionnel (s), annotez comme suit:

@Transactional("txName")

ou

@Transactional(value = "txName")

C’est ainsi que vous associeriez un gestionnaire de transactions qualifié par nom à l’endroit où vous en avez besoin. Vous pouvez maintenant avoir autant de gestionnaires de transactions que vous le souhaitez et l'utiliser en conséquence, où que vous soyez.

31
Angad

Juste au cas où quelqu'un rencontrerait ce problème, j'ai trouvé une 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;
}

De cette manière, vous pouvez utiliser un txManager spécifique défini dans une configuration XML. 

Si vous voulez définir le txManager utilisé au niveau service, vous devez supprimer l'annotation @EnableTransactionManagement de la classe @Configuration et spécifier le txManager dans les annotations @Transactional, par exemple.

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

Depuis le Java doc

Pour ceux qui souhaitent établir une relation plus directe entre
@EnableTransactionManagement et le bean exact du gestionnaire de transactions à utiliser, le Une interface de rappel TransactionManagementConfigurer peut être implémentée - notez le implémente la clause et la méthode @Override- annotée ci-dessous:

Votre classe @Configuration doit implémenter l'interface TransactionManagementConfigurer - implémentez la annotationDrivenTransactionManager qui retournera la référence à la transactionManager qui devrait être utilisée.

5
gkamal

Je ne sais pas pourquoi vous utilisez deux gestionnaires de transactions. Vous pouvez envisager d'utiliser le même TransactionManager pour plusieurs sources de données via AbstractRoutingDataSource. Se il vous plaît se référer

http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/

pour un échantillon sur son utilisation.

1
Aravind A

Certaines des autres réponses impliquent que l'utilisation de deux gestionnaires de transactions est en quelque sorte une erreur. Cependant, la configuration XML de Spring permet d'utiliser plusieurs gestionnaires de transactions, comme indiqué dans la documentation en ligne (ci-dessous). Malheureusement, il ne semble pas y avoir de moyen de faire en sorte que l'annotation @EnableTransactionManagement fonctionne de la même manière. En conséquence, j'utilise simplement une annotation @ImportResource pour charger un fichier XML incluant la ligne <tx:annotation-driven/>. Cela vous permet d'obtenir une configuration Java pour la plupart des choses, tout en utilisant toujours @Transactional avec un qualificatif facultatif du gestionnaire de transactions.

http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/transaction.html

La plupart des applications Spring n'ont besoin que d'un seul gestionnaire de transactions, mais il peut arriver que vous souhaitiez plusieurs gestionnaires de transactions indépendants dans une même application. L'attribut value de l'annotation @Transactional peut être utilisé pour spécifier éventuellement l'identité de la PlatformTransactionManager à utiliser. Il peut s'agir du nom du bean ou de la valeur de qualificateur du bean du gestionnaire de transactions. Par exemple, en utilisant la notation de qualificatif, le code Java suivant

0
Brian D

Essayez d'utiliser chaîné 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);
    }

}

Et placez l'annotation suivante sur votre classe de service:

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

Vous pouvez également l'utiliser dans les tests d'intégration:

@RunWith(SpringRunner.class)
@Transactional(transactionManager = "chainedRawAndAggregatedTransactionManager")
@Rollback
public class ExampleIntegrationTest extends AbstractIntegrationTest {
    ....
}

et il effectuera une restauration pour les deux gestionnaires de transactions de base de données.

0
Leonid Dashko