Spring Boot JPA 多数据源错误

Posted

技术标签:

【中文标题】Spring Boot JPA 多数据源错误【英文标题】:Spring Boot JPA multiple datasource error 【发布时间】:2017-06-12 09:56:19 【问题描述】:

我想将我的 spring boot 应用程序连接到 2 个数据库。所以根据教程,我创建了 2 个配置类。

配置类 1

@Configuration
@EnableTransactionManagement
@PropertySource( "classpath:database-configs.properties" )
@EnableJpaRepositories(
        basePackages = "com.dialog.pod.ideabiz_admin.data_access_objects",
        entityManagerFactoryRef = "adminEntityManagerFactory",
        transactionManagerRef = "adminTransactionManager")
public class IdeabizAdminConfig 


    @Autowired
    private Environment env;


    @Bean
    PlatformTransactionManager adminTransactionManager() 
        return new JpaTransactionManager(adminEntityManagerFactory().getObject());
    

    @Bean
    LocalContainerEntityManagerFactoryBean adminEntityManagerFactory() 

        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
       // jpaVendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(adminDataSource());
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        factoryBean.setPackagesToScan("com.dialog.pod.ideabiz_admin.models");
        factoryBean.setJpaPropertyMap(jpaProperties());
        factoryBean.setPersistenceUnitName("adminDataSource");
        return factoryBean;
    

    @Bean
    DataSource adminDataSource() 

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("admin.jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("admin.jdbc.url"));
        dataSource.setUsername(env.getProperty("admin.jdbc.username"));
        dataSource.setPassword(env.getProperty("admin.jdbc.password"));

        return dataSource;
    


    private Map<String, Object> jpaProperties() 
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.implicit_naming_strategy","org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl");
        props.put("hibernate.physical_naming_strategy","com.dialog.pod.PhysicalNamingStrategyImpl");
        return props;
    



配置类 2

@Configuration
@EnableTransactionManagement
@PropertySource( "classpath:database-configs.properties" )
@EnableJpaRepositories(
        basePackages = "com.dialog.pod.ideabiz_log_summary.data_access_objects",
        entityManagerFactoryRef = "sumLogEntityManagerFactory",
        transactionManagerRef = "sumLogTransactionManager")
public class IdeabizLogSummaryConfig 


    @Autowired
    private Environment env;


    @Bean
    PlatformTransactionManager sumLogTransactionManager() 
        return new JpaTransactionManager(sumLogEntityManagerFactory().getObject());
    



    @Bean
    LocalContainerEntityManagerFactoryBean sumLogEntityManagerFactory() 


        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
       // jpaVendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(adminDataSource());
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        factoryBean.setPackagesToScan("com.dialog.pod.ideabiz_log_summary.models");
        factoryBean.setJpaPropertyMap(jpaProperties());
        factoryBean.setPersistenceUnitName("sumLogDataSource");
        return factoryBean;
    



    @Bean
    DataSource adminDataSource() 

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("sumlog.jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("sumlog.jdbc.url"));
        dataSource.setUsername(env.getProperty("sumlog.jdbc.username"));
        dataSource.setPassword(env.getProperty("sumlog.jdbc.password"));

        return dataSource;
    

    private Map<String, Object> jpaProperties() 
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.implicit_naming_strategy","org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl");
        props.put("hibernate.physical_naming_strategy","com.dialog.pod.PhysicalNamingStrategyImpl");
        return props;
    



应用类

@SpringBootApplication
@EnableAutoConfiguration (exclude =   DataSourceAutoConfiguration.class )
@Configuration
@ComponentScan
public class PodApiApplication 

    public static void main(String[] args) 
        SpringApplication.run(PodApiApplication.class, args);
    

    @Bean
    public WebMvcConfigurer corsConfigurer() 
        return new WebMvcConfigurerAdapter() 
            @Override
            public void addCorsMappings(CorsRegistry registry) 
                registry.addMapping("/").allowedOrigins("*");
            
        ;
    





当我尝试运行应用程序时出现以下错误。

***************************
APPLICATION FAILED TO START
***************************

Description:

Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration required a single bean, but 2 were found:
    - adminEntityManagerFactory: defined by method 'adminEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_admin/IdeabizAdminConfig.class]
    - sumLogEntityManagerFactory: defined by method 'sumLogEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_log_summary/IdeabizLogSummaryConfig.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed


Process finished with exit code 1

我将@Primary 放在第一个配置类中(根据另一个教程)。当我在第一个配置类中执行该数据源时。问题是当我这样做时,第一个数据源也适用于所有 jparepositories。

我是 Spring Boot 的新手。我已经尝试解决这个问题超过 5 个小时 :( 。提前感谢您提供的任何帮助。

完整日志

2017-01-27 00:52:39.713  INFO 6704 --- [           main] com.dialog.pod.PodApiApplication         : Starting PodApiApplication on DESKTOP-4B89ITN with PID 6704 (started by y2ksh in H:\Spring MVC Workspace\platform-overview-dashboard\PODApi)
2017-01-27 00:52:39.718  INFO 6704 --- [           main] com.dialog.pod.PodApiApplication         : No active profile set, falling back to default profiles: default
2017-01-27 00:52:39.938  INFO 6704 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6e171cd7: startup date [Fri Jan 27 00:52:39 IST 2017]; root of context hierarchy
2017-01-27 00:52:41.712  INFO 6704 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'adminDataSource' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=ideabizAdminConfig; factoryMethodName=adminDataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/dialog/pod/ideabiz_admin/IdeabizAdminConfig.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=ideabizLogSummaryConfig; factoryMethodName=adminDataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/dialog/pod/ideabiz_log_summary/IdeabizLogSummaryConfig.class]]
2017-01-27 00:52:42.804  INFO 6704 --- [           main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration' of type [class org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration$$EnhancerBySpringCGLIB$$3cc0fc3] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2017-01-27 00:52:43.594  INFO 6704 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8081 (http)
2017-01-27 00:52:43.609  INFO 6704 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-01-27 00:52:43.609  INFO 6704 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.6
2017-01-27 00:52:43.810  INFO 6704 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2017-01-27 00:52:43.810  INFO 6704 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 3891 ms
2017-01-27 00:52:44.247  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Mapping servlet: 'dispatcherServlet' to [/]
2017-01-27 00:52:44.253  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'metricFilter' to: [/*]
2017-01-27 00:52:44.254  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]
2017-01-27 00:52:44.254  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2017-01-27 00:52:44.254  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2017-01-27 00:52:44.254  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'requestContextFilter' to: [/*]
2017-01-27 00:52:44.255  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'webRequestLoggingFilter' to: [/*]
2017-01-27 00:52:44.255  INFO 6704 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'applicationContextIdFilter' to: [/*]
2017-01-27 00:52:44.367  INFO 6704 --- [           main] o.s.j.d.DriverManagerDataSource          : Loaded JDBC driver: com.mysql.jdbc.Driver
2017-01-27 00:52:44.403  INFO 6704 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'adminDataSource'
2017-01-27 00:52:44.435  INFO 6704 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: adminDataSource
    ...]
2017-01-27 00:52:44.634  INFO 6704 --- [           main] org.hibernate.Version                    : HHH000412: Hibernate Core 5.0.11.Final
2017-01-27 00:52:44.636  INFO 6704 --- [           main] org.hibernate.cfg.Environment            : HHH000206: hibernate.properties not found
2017-01-27 00:52:44.641  INFO 6704 --- [           main] org.hibernate.cfg.Environment            : HHH000021: Bytecode provider name : javassist
2017-01-27 00:52:44.725  INFO 6704 --- [           main] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations 5.0.1.Final
2017-01-27 00:52:45.302  INFO 6704 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2017-01-27 00:52:46.178  INFO 6704 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'adminDataSource'
2017-01-27 00:52:46.189  INFO 6704 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'sumLogDataSource'
2017-01-27 00:52:46.190  INFO 6704 --- [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [
    name: sumLogDataSource
    ...]
2017-01-27 00:52:46.228  INFO 6704 --- [           main] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
2017-01-27 00:52:46.291  INFO 6704 --- [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'sumLogDataSource'
2017-01-27 00:52:46.695  INFO 6704 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2017-01-27 00:52:46.947  INFO 6704 --- [           main] o.h.h.i.QueryTranslatorFactoryInitiator  : HHH000397: Using ASTQueryTranslatorFactory
2017-01-27 00:52:47.496  INFO 6704 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6e171cd7: startup date [Fri Jan 27 00:52:39 IST 2017]; root of context hierarchy
2017-01-27 00:52:47.560  WARN 6704 --- [           main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping]: Factory method 'requestMappingHandlerMapping' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'openEntityManagerInViewInterceptor' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration$JpaWebConfiguration$JpaWebMvcConfiguration.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available: expected single matching bean but found 2: adminEntityManagerFactory,sumLogEntityManagerFactory
2017-01-27 00:52:47.562  INFO 6704 --- [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'sumLogDataSource'
2017-01-27 00:52:47.563  INFO 6704 --- [           main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'adminDataSource'
2017-01-27 00:52:47.566  INFO 6704 --- [           main] o.apache.catalina.core.StandardService   : Stopping service Tomcat
2017-01-27 00:52:47.586  INFO 6704 --- [           main] utoConfigurationReportLoggingInitializer : 

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2017-01-27 00:52:47.592 ERROR 6704 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Method requestMappingHandlerMapping in org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$EnableWebMvcConfiguration required a single bean, but 2 were found:
    - adminEntityManagerFactory: defined by method 'adminEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_admin/IdeabizAdminConfig.class]
    - sumLogEntityManagerFactory: defined by method 'sumLogEntityManagerFactory' in class path resource [com/dialog/pod/ideabiz_log_summary/IdeabizLogSummaryConfig.class]


Action:

Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed


Process finished with exit code 1

【问题讨论】:

你能用包含@Primary 注释的代码版本更新你的问题吗? (您将需要使用它。) @gyoder 你的意思是春季启动版本吗? 1.4 不,具有@Primary 属性的配置类会导致将相同的数据源应用于所有存储库 @gyoder 配置类 1 【参考方案1】:

将其中一个 bean 标记为主要

    @Primary
    @Bean
    public EntityManagerFactory entityManagerFactory() 

【讨论】:

【参考方案2】:

您的 Spring Boot 应用程序通过 @WebMvcAutoConfiguration 隐式激活 Spring Web Mvc,这由在您的类路径中包含 Servlet.class 等触发。这个 @WebMvcAutoConfiguration 需要一个 EntityManagerFactory 类型的 bean。但是,您已经注册了两个这样的 bean adminEntityManagerFactory 和 sumLogEntityManagerFactory。因此,您必须在相应的 @Bean 方法之上通过 @Primary 告诉 Spring Web Mvc 哪个是您的首选。或者,如果您不需要 Web Mvc 自动配置,请通过 @EnableAutoConfiguration(exclude=WebMvcAutoConfiguration) 将其关闭并手动配置 Web Mvc。

【讨论】:

你的解释很好,如果格式好的话会更好。【参考方案3】:

找到了解决办法。您需要为配置类中的每个 bean 命名。

新配置 1 类

@Configuration
@EnableTransactionManagement
@PropertySource( "classpath:database-configs.properties" )
@EnableJpaRepositories(
        basePackages = "com.dialog.pod.ideabiz_admin.data_access_objects",
        entityManagerFactoryRef = "adminEntityManagerFactory",
        transactionManagerRef = "adminTransactionManager")
public class IdeabizAdminConfig 


    @Autowired
    private Environment env;


    @Bean(name = "adminTransactionManager")
    PlatformTransactionManager adminTransactionManager() 
        return new JpaTransactionManager(adminEntityManagerFactory().getObject());
    

    @Bean(name = "adminEntityManagerFactory")
    LocalContainerEntityManagerFactoryBean adminEntityManagerFactory() 

        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
       // jpaVendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(adminDataSource());
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        factoryBean.setPackagesToScan("com.dialog.pod.ideabiz_admin.models");
        factoryBean.setJpaPropertyMap(jpaProperties());
        factoryBean.setPersistenceUnitName("adminDataSource");
        return factoryBean;
    

    @Bean(name = "adminDataSource")
    DataSource adminDataSource() 

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("admin.jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("admin.jdbc.url"));
        dataSource.setUsername(env.getProperty("admin.jdbc.username"));
        dataSource.setPassword(env.getProperty("admin.jdbc.password"));

        return dataSource;
    


    private Map<String, Object> jpaProperties() 
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.implicit_naming_strategy","org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl");
        props.put("hibernate.physical_naming_strategy","com.dialog.pod.PhysicalNamingStrategyImpl");
        return props;
    



新配置 2 类

@Configuration
@EnableTransactionManagement
@PropertySource( "classpath:database-configs.properties" )
@EnableJpaRepositories(
        basePackages = "com.dialog.pod.ideabiz_log_summary.data_access_objects",
        entityManagerFactoryRef = "sumLogEntityManagerFactory",
        transactionManagerRef = "sumLogTransactionManager")
public class IdeabizLogSummaryConfig 


    @Autowired
    private Environment env;


    @Primary
    @Bean(name = "sumLogTransactionManager")
    PlatformTransactionManager sumLogTransactionManager() 
        return new JpaTransactionManager(sumLogEntityManagerFactory().getObject());
    



    @Primary
    @Bean(name = "sumLogEntityManagerFactory")
    LocalContainerEntityManagerFactoryBean sumLogEntityManagerFactory() 


        HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
       // jpaVendorAdapter.setGenerateDdl(true);

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();

        factoryBean.setDataSource(summaryLogDataSource());
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
        factoryBean.setPackagesToScan("com.dialog.pod.ideabiz_log_summary.models");
        factoryBean.setJpaPropertyMap(jpaProperties());
        factoryBean.setPersistenceUnitName("sumLogDataSource");
        return factoryBean;
    


    @Primary
    @Bean(name = "summaryLogDataSource")
    DataSource summaryLogDataSource() 

        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("sumlog.jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("sumlog.jdbc.url"));
        dataSource.setUsername(env.getProperty("sumlog.jdbc.username"));
        dataSource.setPassword(env.getProperty("sumlog.jdbc.password"));

        return dataSource;
    

    private Map<String, Object> jpaProperties() 
        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.implicit_naming_strategy","org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl");
        props.put("hibernate.physical_naming_strategy","com.dialog.pod.PhysicalNamingStrategyImpl");
        return props;
    



----------------------------------------------- -------------------------------------------------- -------------------------------------------

事实证明,我对两个数据源 bean 使用了相同的方法名称。这就是为什么所有 jparepos 只使用一个数据源的原因。感谢 @Javatar81 解释 bean 命名的工作原理

【讨论】:

名称不应该有所不同,因为 bean 的默认名称由方法名称给出。这次您正确使用了@Primary,这就是它起作用的原因。您能否检查一下它是否在没有定义名称属性的情况下仍然有效?

以上是关于Spring Boot JPA 多数据源错误的主要内容,如果未能解决你的问题,请参考以下文章

spring boot jpa 多数据源配置

Spring Boot,Spring Data JPA多数据源支持配置

Spring-boot-route整合JPA操作数据库+多数据源切换

Spring Boot Jpa多数据源配置

Spring Boot学习进阶笔记-多数据源配置(JdbcTemplateSpring-data-jpa)

使用 Spring JPA 的单向多对多映射