2013-02-28 13 views
9

EDIT: a chiunque sia interessato a questo problema, fornisco l'analisi del problema con la relativa soluzione al fine della domanda.Utilizzo di Spring Data JPA, Hibernate e gestore di transazioni multiple: Nessun bean denominato 'transactionManager' è definito

Sto configurando un modulo per un'applicazione Web in cui sto utilizzando Spring 3.2, Hibernate 4.1, Spring Data JPA 1.3 e Apache CXF 2.5 (in particolare il modulo JAX-RS). Ho la seguente configurazione (che sta lavorando benissimo, dettagliato sono omessi per brevità):

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

Il mio problema è che devo fare affidamento su alcuni moduli esterni che definiscono la propria PlatformTransactionManager, così mi ritrovo lavorare con più gestori delle transazioni allo stesso tempo. Questo problema è facilmente risolto da Transactional.html#value(), quindi dovunque io abbia bisogno di usare @Transactional ho qualificato l'annotazione con il nome del gestore delle transazioni che devo usare per quella transazione.
Vorrei cambiare il nome del gestore transazioni che definisco nel mio modulo con qualcosa di più significativo, per soddisfare lo standard dei moduli esterni. Così, ad esempio, externalModule1 definisce il suo manager come externalModule1TransactionManager e vorrei avere

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

Questo sembra abbastanza facile, purtroppo, quando faccio questo cambiamento (e modificare l'utilizzo di @Transactional#value() conseguenza ottengo un'eccezione.

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 

In particolare, vorrei focalizzare l'attenzione su

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

e

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

i loro codici sembrano

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

e

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

Quindi, nella mia comprensione questa configurazione dovrebbe funzionare, perché lo fa buttare tale eccezione? È richiesto un gestore di transazioni denominato standard? O è qualcosa dovuto a cxf? Ho trovato alcune domande relative al gestore di transazioni multiple all'interno della stessa applicazione (example 1, example2) ma la risposta accettata in tali domande guida alla mia soluzione. Che cosa ho frainteso e sto sbagliando?
Grazie a tutti coloro che sono disposti a leggere questa lunga domanda fino a qui!

EDIT per fornire una spiegazione completa in base alla risposta di Michail: usando Spring Data JPA v'è la necessità di definire le interfacce repository per la connessione al database. someOtherMethod è infatti chiamando uno dei miei repository che è definita come:

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

} 

Questo sembra di nuovo bene, ma guardando JpaRepository codice sorgente implementazione (così, guardando org.springframework.data.jpa.repository.support.SimpleJpaRepository ho scoperto che il save (così come altri metodi di aggiornamento) è annotato con @Transactional.Codice da SimpleJpaRepository

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

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

Pertanto, quando si utilizza Primavera dati JPA il gestore delle transazioni di default (quella denominata transactionManager) è obbligatoria. Male per il mio obiettivo, ma almeno ora so che è tutto!

risposta

8

Sembra che il tuo someOtherMethod chiamate qualsiasi altro @Transactional componente (qualche classe con save metodo). E penso che abbia l'annotazione @Transactinal() (vuota) (che usa il bean predefinito chiamato transactionManager).

Si possono vedere 2 posizioni TransactionInterceptor nello stacktrace. Si prega di fornire alcuni dettagli a riguardo.

+0

Michail, la tua risposta mi ha spinto nella giusta direzione per trovare la causa alla radice del mio problema. La mia configurazione è perfettamente a posto, il problema risiede in [Spring Data Jpa] (http://www.springsource.org/spring-data/jpa). Ho esteso la tua risposta per fornire la spiegazione di questa affermazione. – ThanksForAllTheFish

0

Ho trovato la tua domanda molto interessante concettualmente. E così è stato in grado di rivedere alcuni dei miei concetti a lungo dimenticati. Sembra che sia una limitazione sul lato java config. Quindi, si dovrà ricorrere a un po 'di XML in mezzo e poi giv TransactionManager qualcosa come

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

allora si può dare utilizzare il transactionManager. Default SimpleJpaRepository utilizzerà anche il nuovo solo.

Aggiornamento: O dal modo in cui è possibile utilizzare questo attraverso Config anche ora sembra EnableTransactionManagement

+0

Ho già annotato la mia classe di configurazione Spring con '@ EnableTransactionManagement', che non aiuta. Inoltre, il codice sorgente di 'SimpleJpaRepository' usa chiaramente' @ Transaction'.Gli stati della documentazione Spring: 'È possibile omettere l'attributo transaction manager nel tag se il nome bean del PlatformTransactionManager che si desidera collegare ha il nome transactionManager. Se il fagiolo PlatformTransactionManager che si desidera alla dipendenza-Inject ha un altro nome, quindi è necessario utilizzare attribuisce esplicitamente la transazione-manager, come nella precedente example.' – ThanksForAllTheFish

+0

Così, quando '@ Transaction' viene utilizzato senza il valore di attributo I capire che non c'è modo di usare altro che lo standard denominato transactionManager. Ora sono andato avanti e ho delle cose da fare, ma appena avrò commesso tali cose, avrò una piccola idea che voglio provare! Probabilmente già più tardi oggi. Quindi, aggiornerò questa discussione. – ThanksForAllTheFish

+0

No, cioè per l'override solo il nome transactionManager fagioli di default, credo –

4

ho il sospetto che basta per garantire che i repository utilizzare il gestore delle transazioni di nome correttamente nel @EnableJpaRepositories annotazione.

cioè

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

Mi c'è voluto un po 'per capire i dettagli, così ho appena fornito una spiegazione più completa di come configurare i repository primavera dati JPA di lavorare con più origini dati qui:

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

E un progetto completo che dimostra qui:

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

+0

Ho finito per usare un singolo gestore di transazioni (chiamato 'transactionManager' quindi Spring JPA è felice), con più origini dati. In termini di codice, ho utilizzato molte classi astratte per definire comportamenti di base e classi di configurazione specifiche che estendono quelle astratte. Detto questo, se ora riesco a capire come ho fatto, posso creare un progetto di esempio su github per condividere la mia implementazione. Può essere utile – ThanksForAllTheFish

1

In realtà, esiste un modo per utilizzare TransactionManager denominato con Spring Data JPA. Questo funziona per me:

<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

Mi sono qualificato @Transactional al mio livello di servizio. E mi sembra che siamo in grado di disabilitare la gestione delle transazioni nel data repository primavera come segue:

<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" 
/> 

Non sono sicuro del 100%, ma l'errore è scomparso. Trovato qui: https://github.com/spring-projects/spring-data-jpa/blob/master/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml

Problemi correlati