2013-02-28 12 views
9

EDIT: dla każdego, kto może być zainteresowany tym zagadnieniem, przedstawię analizę problemu za pomocą powiązanego rozwiązania koniec pytania.Praca ze Spring Data JPA, Hibernate i wieloma menedżerami transakcji: Nie zdefiniowano fasoli o nazwie "transactionManager"

Konfiguruję moduł dla aplikacji internetowej, w której używam Spring 3.2, Hibernate 4.1, Spring Data JPA 1.3 i Apache CXF 2.5 (w szczególności moduł JAX-RS). Mam następującą konfigurację (który działa perfekcyjnie, szczegółowe zostały pominięte ze względu na zwięzłość):

@Bean(name = "entityManagerFactory") 
    public LocalContainerEntityManagerFactoryBean getEntityManagerFactory() throws SQLException{ 
    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean(); 
    //...  
    return factory; 
    } 

    @Bean(name = "transactionManager") 
    public JpaTransactionManager getTransactionManager() throws SQLException{ 
    JpaTransactionManager manager = new JpaTransactionManager(); 
    //...  
    return manager; 
    } 

    @Bean(name = "persistenceExceptionTranslator") 
    public PersistenceExceptionTranslator getPersistenceExceptionTranslator(){ 
    return new HibernateExceptionTranslator(); 
    } 

Moim problemem jest to, że muszę polegać na niektórych modułów zewnętrznych, które definiować własne PlatformTransactionManager, więc znajdę się praca z większą ilością menedżerów transakcji w tym samym czasie. Ten problem jest łatwo rozwiązany przez Transactional.html#value(), więc gdy potrzebuję użyć @Transactional, zakwalifikowałem adnotację z nazwą menedżera transakcji, którego muszę użyć dla tej transakcji.
Chciałbym zmienić nazwę menedżera transakcji zdefiniowanego w moim module na coś bardziej znaczącego, aby spełnić standard modułów zewnętrznych. Tak więc, na przykład, externalModule1 definiuje jego menedżer jako externalModule1TransactionManager i chciałbym mieć

@Bean(name = "myModuleransactionManager") 
    public JpaTransactionManager getTransactionManager() throws SQLException{ 
    JpaTransactionManager manager = new JpaTransactionManager(); 
    //...  
    return manager; 
    } 

To wydaje się dość proste, niestety kiedy robię tę zmianę (i zmienić wykorzystanie @Transactional#value() odpowiednio uzyskać wyjątek.

java.lang.RuntimeException: org.apache.cxf.interceptor.Fault: No bean named 'transactionManager' is defined 
    at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:110) 
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:323) 
    at org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:123) 
    at org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:207) 
    at org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:213) 
    at org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:154) 
    at org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:126) 
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:185) 
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:113) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621) 
    at org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:164) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) 
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918) 
    at java.lang.Thread.run(Thread.java:662) 
Caused by: org.apache.cxf.interceptor.Fault: No bean named 'transactionManager' is defined 
    at org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:155) 
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:121) 
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:167) 
    at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:94) 
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58) 
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:439) 
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:138) 
    at org.apache.cxf.workqueue.SynchronousExecutor.execute(SynchronousExecutor.java:37) 
    at org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:106) 
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:263) 
    ... 25 more 
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'transactionManager' is defined 
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:568) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1099) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:278) 
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198) 
    at org.springframework.transaction.interceptor.TransactionAspectSupport.determineTransactionManager(TransactionAspectSupport.java:246) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:100) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204) 
    at sun.proxy.$Proxy98.save(Unknown Source) 
    at myModule.package.SomeOtherClass.someOtherMethod(SomeOtherClass.java:114) 
    at myModule.package.SomeOtherClass$$FastClassByCGLIB$$2bda5a73.invoke(<generated>) 
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) 
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150) 
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) 
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) 
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631) 
    at myModule.package.SomeClass$$EnhancerByCGLIB$$37044080.myMethod(<generated>) 
    at myModule.package.SomeClass.someMethod(SomeClass.java:64) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:173) 
    at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:89) 
    ... 34 more 

W szczególności chciałbym skupić uwagę na

myModule.package.SomeOtherClass.someOtherMethod(SomeClass.java:114) 

i

myModule.package.SomeClass.someMethod(SomeClass.java:64) 

Ich kody wyglądać

@Transactional("myModuleransactionManager") 
public ... someOtherMethod(){ 
    ... 
} 

i

public ... someMethod(){ 
    ... 
} 

Tak, w moim rozumieniu to konfiguracja powinna działać, dlaczego to rzucić ten wyjątek? Czy wymagany jest standardowy nazwany menedżer transakcji? Czy jest to coś ze względu na cxf? Znalazłem kilka pytań związanych z wieloma menedżerami transakcji w tej samej aplikacji (example 1, example2), ale zaakceptowana odpowiedź w tych pytaniach prowadzi do mojego rozwiązania. Co źle zrozumiałem i robię źle?
Dziękuję wszystkim, którzy są gotowi przeczytać to długie pytanie do tej pory!

EDIT, aby zapewnić całkowite wyjaśnienie w oparciu o Michail za odpowiedź: using Spring Data JPA istnieje konieczność określenia repozytoriów interfejsy do łączenia się z bazą danych. someOtherMethod rzeczywiście dzwoni jeden z moich repozytorium, który jest zdefiniowany jako:

@Repository("myRepository") 
@Transactional(propagation = Propagation.NESTED, value = "myModuleransactionManager") 
public interface MyRepository extends JpaRepository<MyEntity, Integer> 
{ 

} 

To znowu wygląda dobrze, ale patrząc na JpaRepository kodu źródłowego realizacji (tak, patrząc na org.springframework.data.jpa.repository.support.SimpleJpaRepository odkryłem, że save (jak również inne metody aktualizacji) jest opatrzone przypisami @Transactional.Kod z SimpleJpaRepository

@Transactional 
    public <S extends T> S save(S entity) { 

     if (entityInformation.isNew(entity)) { 
      em.persist(entity); 
      return entity; 
     } else { 
      return em.merge(entity); 
     } 
    } 

Dlatego, kiedy wiosna dane przy użyciu WZP domyślnego menedżera transakcji (jednej nazwie transactionManager) jest obowiązkowe. Źle dla mojego celu, ale przynajmniej teraz wiem, że to jest to!

Odpowiedz

8

Wygląda jak Twój someOtherMethod dzwoni do dowolnego innego składnika @Transactional (jakaś klasa z metodą save). I myślę, że ma on adnotację @Transactinal() (która używa domyślnego komponentu bean o nazwie transactionManager).

Możesz zobaczyć 2 TransactionInterceptor pozycji w Stacktrace. Podaj szczegółowe informacje na ten temat.

+0

Michail, twoja odpowiedź popchnęła mnie we właściwym kierunku, aby znaleźć podstawową przyczynę mojego problemu. Moja konfiguracja jest całkowicie poprawna, problem występuje w [Spring Data Jpa] (http://www.springsource.org/spring-data/jpa). Rozszerzyłem twoją odpowiedź, aby podać wyjaśnienie tego stwierdzenia. – ThanksForAllTheFish

0

Znalazłem twoje pytanie bardzo interesujące koncepcyjnie. W ten sposób mogłem zrewidować niektóre z moich zapomnianych pojęć. Wygląda na to, że jest to ograniczenie po stronie konfiguracji Java. Więc trzeba będzie uciekać się do kawałka xml między a następnie giv transactionmanager coś podobnego

<tx:annotation-driven transaction-manager="myModuletransactionManager"/> 

następnie można dać używać transactionManager. Domyślnie SimpleJpaRepository użyje również tylko nowego.

Aktualizacja: lub poprzez sposób można wykorzystać to przez Config również teraz wydaje EnableTransactionManagement

+0

Mam już moją klasę konfiguracji Spring z adnotacją '@ EnableTransactionManagement', co nie pomaga. Co więcej, kod źródłowy 'SimpleJpaRepository' wyraźnie używa' @ Transaction'.Dokumentacja Spring stwierdza: 'Możesz pominąć atrybut transaction-manager w znaczniku , jeśli nazwa komponentu bean platformy PlatformTransactionManager ma nazwę transactionManager. Jeśli komponent bean PlatformTransactionManager, który ma być zależny, ma dowolną inną nazwę, należy użyć atrybutu menedżera transakcji jawnie, tak jak w poprzednim przykładzie. " – ThanksForAllTheFish

+0

Tak więc, gdy' @ Transakcja' jest używana bez atrybutu wartości I rozumiem, że nie ma możliwości użycia innego niż standardowy nazwany menedżer transakcji. Poszedłem dalej i mam kilka rzeczy do zrobienia, ale jak tylko popełnię takie rzeczy, będę miał mały pomysł, który chcę spróbować! Prawdopodobnie już później. Następnie, w końcu zaktualizuję ten wątek. – ThanksForAllTheFish

+0

Nie, to jest dla nadpisywania tylko domyślnej nazwy komponentu bean menedżera transakcji, uważam, że –

4

Podejrzewam, że po prostu trzeba upewnić się, że repozytoria używać poprawnie nazwie menedżera transakcji w @EnableJpaRepositories adnotacji.

tj

@Configuration 
@EnableTransactionManagement 
@EnableJpaRepositories(
     entityManagerFactoryRef = "fooEntityManagerFactory", 
     transactionManagerRef = "fooTransactionManager", 
     basePackages = {"com.sctrcd.multidsdemo.integration.repositories.foo"}) 
public class FooConfig { 
    //... 
} 

Zajęło mi trochę czasu, aby dowiedzieć się szczegółów, więc mam tylko pod warunkiem, pełniejsze wyjaśnienie jak skonfigurować repozytoria Wiosna danych WZP do pracy z wieloma datasources tutaj:

Multiple jpa:repositories in xml config, how to configure with @EnableJPARepositories using Spring java config?

oraz pełny projekt wykazując je tutaj:

https://github.com/gratiartis/multids-demo

+0

Skończyło się na użyciu pojedynczego menedżera transakcji (o nazwie 'transactionManager', dzięki czemu Spring JPA jest szczęśliwy), z wieloma źródłami danych. W ujęciu kodowym użyłem wielu klas abstrakcyjnych do zdefiniowania podstawowych zachowań i określonych klas konfiguracji, które rozszerzają te abstrakcyjne. To powiedziawszy, jeśli mogę teraz dowiedzieć się, jak to zrobiłem, mogę utworzyć przykładowy projekt na Github, aby udostępnić moją implementację. Może być użyteczne. – ThanksForAllTheFish

1

W rzeczywistości istnieje sposób użycia nazwanego TransactionManager z wiosennym JPA danych. Działa to dla mnie:

<bean id="myTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
    <property name="entityManagerFactory" ref="myEntityManagerFactory" /> 
</bean> 
<tx:annotation-driven transaction-manager="myTransactionManager"/> 

<jpa:repositories base-package="com.xxx.yyy" entity-manager-factory-ref="myEntityManagerFactory" transaction-manager-ref="myTransactionManager"> 
</jpa:repositories> 
0

Mam kwalifikacje @Transactional w moim warstwie usług. I wydaje mi się, że możemy wyłączyć obsługę transakcji w wiosennym repozytorium danych w następujący sposób:

<jpa:repositories base-package="ru.xdsoft.conn.thanksapp.thanks.dao.repository" 
        entity-manager-factory-ref="thanksEntityManagerFactory" 
        enable-default-transactions="false" 
/> 
<jpa:repositories base-package="ru.xdsoft.conn.thanksapp.orgstruct.dao" 
        entity-manager-factory-ref="orgStructEntityManagerFactory" 
        enable-default-transactions="false" 
/> 

Nie wiesz o 100%, ale błędu nie ma. Znalazłem go tutaj: https://github.com/spring-projects/spring-data-jpa/blob/master/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml

Powiązane problemy