编辑:对于可能对此问题感兴趣的任何人,我在问题的最后提供了相关解决方案的问题分析。
我正在为Web应用程序配置模块,其中我使用的是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
的外部模块,所以我发现自己同时使用更多的事务管理器。 Transactional.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$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
特别是,我想把注意力集中在
上myModule.package.SomeOtherClass.someOtherMethod(SomeClass.java:114)
和
myModule.package.SomeClass.someMethod(SomeClass.java:64)
他们的代码看起来像
@Transactional("myModuleransactionManager")
public ... someOtherMethod(){
...
}
和
public ... someMethod(){
...
}
所以,根据我的理解,这个配置应该有效,为什么会抛出这个异常呢?是否需要标准的命名事务管理器?或者它是由cxf引起的?我在同一个应用程序(example 1,example2中找到了与多个事务管理器相关的一些问题,但这些问题中的已接受答案驱动到我的解决方案。我误解了什么,我做错了什么? 感谢所有愿意阅读这个长期问题的人,直到这里!
编辑根据Michail的回答提供完整的解释:使用Spring Data 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
(以及其他更新方法)注释了{ {1}}。来自@Transactional
SimpleJpaRepository
因此,在使用Spring Data JPA时,默认的事务管理器(名为 @Transactional
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
)是必需的。对我的目标不好,但至少我现在知道就是这样!
答案 0 :(得分:8)
您的someOtherMethod
似乎调用了任何其他@Transactional
组件(某些类save
方法)。我认为它有@Transactinal()
(空)注释(使用名为transactionManager
的默认bean)。
您可能会在堆栈跟踪中看到2个TransactionInterceptor
个职位。请提供一些有关它的详细信息。
答案 1 :(得分:4)
我怀疑您只需要确保您的存储库在@EnableJpaRepositories注释中使用正确命名的事务管理器。
即。
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "fooEntityManagerFactory",
transactionManagerRef = "fooTransactionManager",
basePackages = {"com.sctrcd.multidsdemo.integration.repositories.foo"})
public class FooConfig {
//...
}
我花了一些时间来弄清楚细节,所以我刚刚提供了一个更全面的解释,说明如何配置Spring Data JPA存储库来处理多个数据源:
一个完整的项目在这里展示:
答案 2 :(得分:1)
实际上,有一种方法可以将命名的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>
答案 3 :(得分:0)
我发现你的问题非常有趣。因此能够修改我遗忘的一些概念。看起来它是java配置端的限制。所以你将不得不在两者之间使用一些xml,然后giv transactionmanager 之类的东西
<tx:annotation-driven transaction-manager="myModuletransactionManager"/>
然后你可以使用你的transactionManager。默认 SimpleJpaRepository 也将仅使用新的。
更新或者通过Config也可以使用此功能,现在似乎EnableTransactionManagement
答案 4 :(得分:0)
我已在我的服务层验证了@Transactional。在我看来,我们可以在Spring Data存储库中禁用事务管理,如下所示:
<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
答案 5 :(得分:0)
我已经做过几次了,所以这里是如何用 Java 代码(没有 xml)完全做到这一点。但是,我确实使用 Lombok,我强烈推荐它。我只关注提出的问题,因此如果您以前从未这样做过,请阅读 Spring 文档以配置有关 JPA 方言和 spring 数据源驱动程序类的其他详细信息。
说明:当调用像 findAll()
或 save()
这样的 JPA 嵌入方法时,TransactionInterceptor 将寻找默认的 "transactionManager"
这是通过 Hibernate 和 JPA 连接多个数据库所需要的。
application.properties
中定义变量first.datasource.url=my/database/url/example
first.datasource.username=username-example
first.datasource.password=password-example
second.datasource.url=my/database/url/example
second.datasource.username=username-example
second.datasource.password=password-example
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "firstEntityManagerFactory", basePackages = {
"be.company.appname.repository.firstdatabase", "be.company.appname.config.firstdatabase"
})
public class FirstDatabaseConfig {
@Value("${first.datasource.url}")
private String url;
@Value("${first.datasource.username}")
private String username;
@Value("${first.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driver;
@Primary
@Bean(name = "firstDataSourceProperties")
@ConfigurationProperties("first.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
@Primary
@Bean(name = "firstDataSource")
@ConfigurationProperties("first.datasource.configuration")
public DataSource dataSource(@Qualifier("firstDataSourceProperties") DataSourceProperties dataSourceProperties) {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setRemoveAbandonedOnBorrow(true);
dataSource.setRemoveAbandonedOnMaintenance(true);
dataSource.setInitialSize(10);
dataSource.setMaxTotal(20);
dataSource.setValidationQuery("select 1 from MY_SCHEMA.TABLE");
dataSource.setValidationQueryTimeout(900_000);
dataSource.setTestWhileIdle(true);
dataSource.setLogAbandoned(true);
dataSource.setTestOnReturn(true);
dataSource.setTestOnBorrow(true);
dataSource.setDefaultAutoCommit(false);
return dataSource;
}
@Primary
@Bean(name = "firstEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
EntityManagerFactoryBuilder builder,
@Qualifier("firstDataSource") DataSource firstDataSource
) {
return builder
.dataSource(firstDataSource)
.packages("be.company.appname.model.firstdatabase")
.persistenceUnit("first")
.build();
}
@Primary
@Bean(name = "firstTransactionManager")
public PlatformTransactionManager transactionManager(
@Qualifier("firstEntityManagerFactory") EntityManagerFactory firstEntityManagerFactory
) {
return new JpaTransactionManager(firstEntityManagerFactory);
}
}
关于此的一些注意事项:
@Primary
!您只需要在 1 个 databaseConfigs 上使用它!对于您的第二个数据库,您可以使用相同的代码并进行明显的更改,包括名称更改(例如 firstEntityManagerFactory 变为 secondEntityManagerFactory 等)、更改适当的变量、更改 ValidationQuery 并定义正确的包。"be.company.appname.config.firstdatabase"
声明中删除了 basePackages = {}
。只有指向存储库包的指针才可以。firstEntityManagerFactory
bean 中定义的包中创建您的 databaseObject。@Entity
@Table(name = "USER")
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MyUser {
@Id
@Column(name = "ID")
private Long id;
@Column(name = "USERNAME")
private String userName;
@Column(name = "UID")
private String uid;
@Column(name = "FIRST_NAME")
private String firstName;
@Column(name = "LAST_NAME")
private String lastName;
@Table
是您的数据库表的确切名称@Column
是您的数据库表列的确切名称。您自己的字段变量不必匹配,但我出于习惯这样做(例如声明 @Column(name = "USERNAME") private String name;
也可以@Repository
@Transactional(value = "firstTransactionManager")
public interface MyUserRepository extends JpaRepository<MyUser, Long> {
List<MyUser> findAll();
}
是什么导致提问者的异常出现?例如:
我调用 MyUserRepository.findById(1L)
时没有在我的存储库中声明该方法。它是 JPA 默认嵌入的已知简写。查看您自己的 JPA shorthand queries 的详细信息。
如果未在存储库中声明,您的应用程序将寻找默认 transactionManager
绕过存储库接口。但是通过声明该方法,您的应用程序将知道寻找您自己的自定义 firstTransactionManager
注意: BasicDataSource
的创建可能因您使用的数据库而异。我正在使用 DB2Dialect
连接到 AS400。