Работа с Spring Data JPA, Hibernate и multiple transaction manager: Не определен компонент с именем " transactionManager
редактировать: тем, кто может быть заинтересован в этом вопросе, я предоставляю анализ проблемы с соответствующим решением в конце вопроса.
я настраиваю модуль для веб-приложения, в котором я использую Spring 3.2, Hibernate 4.1, Spring Data JPA 1.3 и Apache CXF 2.5 (в частности, модуль JAX-RS). У меня есть следующая конфигурация (которая работает отлично, подробные опущены ради лаконичность):
@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();
}
моя проблема в том, что я должен полагаться на некоторые внешние модули, которые определяют свои собственные PlatformTransactionManager
, поэтому я работаю с большим количеством менеджера транзакций одновременно. Эта проблема легко решается транзакций.html#value (), так везде, где мне нужно использовать @Transactional
я квалифицировал аннотацию с именем менеджера транзакций, который я должен использовать для этой транзакции.
Я хотел бы изменить имя менеджера транзакций я определите в моем модуле что-то более значимое, чтобы соответствовать стандарту внешних модулей. Так, например, externalModule1
диспетчер определяет его как externalModule1TransactionManager
и я хотел бы иметь
@Bean(name = "myModuleransactionManager")
public JpaTransactionManager getTransactionManager() throws SQLException{
JpaTransactionManager manager = new JpaTransactionManager();
//...
return manager;
}
это кажется довольно простым, к сожалению, когда я делаю это изменение (и я меняю использование @Transactional#value()
соответственно, я получаю исключение.
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.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$bda5a73.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$044080.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
в частности, я хотел бы обратить внимание на
myModule.package.SomeOtherClass.someOtherMethod(SomeClass.java:114)
и
myModule.package.SomeClass.someMethod(SomeClass.java:64)
их коды выглядят как
@Transactional("myModuleransactionManager")
public ... someOtherMethod(){
...
}
и
public ... someMethod(){
...
}
Итак, в моем понимании эта конфигурация должна работать, почему не бросить исключение? Требуется ли стандартный диспетчер транзакций? Или это что-то из-за цены? Я нашел несколько вопросов, связанных с несколькими менеджерами транзакций в одном приложении (Пример 1, Пример 2) но принятый ответ на эти вопросы приводит к моему решению. Что я неправильно понял, и что я делаю? не так?
Спасибо всем, кто готов прочитать этот длинный вопрос до сих пор!
EDIT, чтобы предоставить полное объяснение, основанное на ответе Михаила: используя весенние данные JPA необходимо определить интерфейсы репозиториев для подключения к базе данных. someOtherMethod
действительно вызывает один из моих репозиториев, который определяется как:
@Repository("myRepository")
@Transactional(propagation = Propagation.NESTED, value = "myModuleransactionManager")
public interface MyRepository extends JpaRepository<MyEntity, Integer>
{
}
это снова выглядит хорошо, но глядя на JpaRepository
исходный код реализации (так, глядя на org.springframework.data.jpa.repository.support.SimpleJpaRepository
я обнаружил, что save
(а также другие методы обновления) аннотируется с @Transactional
. Код SimpleJpaRepository
@Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
поэтому при использовании Spring Data JPA диспетчер транзакций по умолчанию (тот, который называется transactionManager
) является обязательным. Плохо для моей цели, но по крайней мере теперь я знаю, что это!
5 ответов
похоже, ваш someOtherMethod
вызывает любой другой @Transactional
компонент (класс с save
метод). И я думаю, что это @Transactinal()
(пустая) аннотация (которая использует Боб по умолчанию с именем transactionManager
).
вы можете увидеть 2 TransactionInterceptor
позиции в stacktrace. Просьба представить некоторые подробности.
Я подозреваю, что вам просто нужно убедиться, что ваши репозитории используют правильно названный диспетчер транзакций в аннотации @EnableJpaRepositories.
то есть
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "fooEntityManagerFactory",
transactionManagerRef = "fooTransactionManager",
basePackages = {"com.sctrcd.multidsdemo.integration.repositories.foo"})
public class FooConfig {
//...
}
Мне потребовалось некоторое время, чтобы выяснить детали, поэтому я только что предоставил более полное объяснение того, как настроить репозитории Spring Data JPA для работы с несколькими источниками данных здесь:
и полный проект, демонстрирующий его здесь:
на самом деле, есть способ использовать именованный TransactionManager с Spring Data JPA. Это работает для меня:
<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>
Я нашел ваш вопрос очень интересный conceptully. И таким образом смог пересмотреть некоторые из моих давно забытых концепций. Похоже, это ограничение на стороне конфигурации java. Таким образом, вам придется прибегнуть к немного xml между, а затем дать transactionmanager что-то вроде
<tx:annotation-driven transaction-manager="myModuletransactionManager"/>
затем вы можете использовать свой transactionManager. По умолчанию SimpleJpaRepository также будет использовать только новый.
обновление: и кстати вы можете использовать это через Config и теперь кажется EnableTransactionManagement
Я квалифицировал @Transactional на своем уровне обслуживания. И мне кажется, что мы можем отключить управление транзакциями в хранилище данных Spring следующим образом:
<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"
/>
Не уверен на 100%, но ошибка ушла. Нашел здесь: https://github.com/spring-projects/spring-data-jpa/blob/master/src/test/resources/org/springframework/data/jpa/repository/support/disable-default-transactions.xml