it-swarm.dev

Adnotacja @EnableTransactionManagement z 2 menedżerami transakcji

Używam adnotacji @Configuration do konfiguracji wiosny zamiast pliku xml. Konfiguruję 2 źródła danych z różnymi fabrykami sesji i różnymi menedżerami transakcji. Mam problem z adnotacją @EnableTransactionManagement. Przeczytałem w jego dokumentacji, że

@EnableTransactionManagement jest bardziej elastyczny; wróci do wyszukiwania po typie każdej zmiennej PlatformTransactionManager w kontenerze . Tak więc nazwą może być „txManager”, „transactionManager” lub „Tm”: to po prostu nie ma znaczenia.

Oznacza to, że dowolna nazwa, jaką podaję metodzie, będzie zawsze szukać metody, która zwraca obiekt PlatformTransactionManager, gdy mam 2 menedżerów transakcji. Teraz problem polega na tym, że kiedy testuję tę klasę, daje mi błąd:

org.springframework.beans.factory.NoSuchBeanDefinitionException: Nie zdefiniowano unikalnego komponentu bean typu [org.springframework.transaction.PlatformTransactionManager]: oczekiwany pojedynczy komponent bean, ale znaleziono 2

Próbowałem nawet mieć dwie różne klasy konfiguracji, ale na próżno. W konfiguracji xml tak nie było. Zarejestrowałem oba moje menedżery transakcji za pomocą dwóch tagów <tx:annotation-driven transaction-manager="" /> i działało to dobrze. Ale nie mogę zrobić tego tutaj z adnotacjami.

Co powinienem zrobić, jeśli chcę skonfigurować 2 źródła danych z dwoma różnymi menedżerami transakcji w klasie konfiguracji z adnotacjami Spring?

23
Mital Pritmani

W swojej klasie konfiguracyjnej użyj adnotacji @EnableTransactionManagement.

Zdefiniuj menedżera transakcji w tej klasie jako:

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

Tam w twojej klasie/metodzie, która wykonuje transakcyjne zadania, adnotuj w następujący sposób:

@Transactional("txName")

lub

@Transactional(value = "txName")

W ten sposób możesz powiązać menedżera transakcji z kwalifikowaną nazwą do dowolnego miejsca. Możesz teraz mieć tyle menedżerów transakcji, ile chcesz i używać ich odpowiednio wszędzie, gdzie potrzebujesz.

31
Angad

Na wypadek, gdyby ktoś napotkał ten problem, znalazłem rozwiązanie:

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

@Autowired
private PlatformTransactionManager myTxManager;

...

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

W ten sposób można użyć określonego txManager zdefiniowanego w konfiguracji xml. 

Jeśli chcesz zdefiniować txManager używany na poziomie usługi, powinieneś usunąć adnotację @EnableTransactionManagement z klasy @Configuration i określić txManager w adnotacjach @Transactional, np.

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

Z Java doc

Dla tych, którzy chcą ustanowić bardziej bezpośredni związek między
@EnableTransactionManagement i dokładny komponent bean menedżera transakcji, który ma zostać użyty, interfejs TransactionManagementConfigurer może zostać zaimplementowany - zauważ klauzulę implementacji i metodę @Override- z adnotacją poniżej:

Twoja klasa @Configuration musi zaimplementować interfejs TransactionManagementConfigurer - zaimplementuj zmienną annotationDrivenTransactionManager, która zwróci odniesienie do zmiennej transactionManager, która powinna zostać użyta.

5
gkamal

Nie jestem pewien, dlaczego używasz dwóch menedżerów transakcji. Można rozważyć użycie tego samego TransactionManager dla wielu źródeł danych poprzez AbstractRoutingDataSource. Proszę odnieś się

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

po próbkę jego użycia.

1
Aravind A

Niektóre inne odpowiedzi sugerują, że użycie dwóch menedżerów transakcji jest w jakiś sposób błędne; jednak konfiguracja XML Springa pozwala na użycie wielu menedżerów transakcji, jak określono w dokumentacji online (poniżej). Niestety, nie ma sposobu, aby adnotacja @EnableTransactionManagement działała w podobny sposób. W rezultacie po prostu używam adnotacji @ImportResource, aby załadować plik XML zawierający linię <tx:annotation-driven/>. Pozwala to uzyskać konfigurację Java dla większości rzeczy, ale nadal korzystać z @Transactional z opcjonalnym kwalifikatorem Menedżera transakcji.

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

Większość aplikacji Spring wymaga tylko jednego menedżera transakcji, ale mogą zaistnieć sytuacje, w których chcesz mieć wielu niezależnych menedżerów transakcji w jednej aplikacji. Atrybut wartości adnotacji @Transactional może być użyty do opcjonalnego określenia tożsamości PlatformTransactionManager, która ma być użyta. Może to być nazwa komponentu bean lub wartość kwalifikująca komponentu bean menedżera transakcji. Na przykład, używając notacji kwalifikatora, następującego kodu Java

0
Brian D

Spróbuj użyć połączony menedżer transakcji

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);
    }

}

I umieść następującą adnotację na swojej klasie usług:

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

Możesz również użyć go w testach integracji:

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

i zrobi wycofanie dla obu menedżerów transakcji DB.

0
Leonid Dashko