`
不爱不见
  • 浏览: 283875 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

spring和springboot 多数据源配置和问题解决

 
阅读更多
先说下遇到的问题

No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: pgDataSource,DataSource
1
这个是我在springboot项目中配置双数据源的时候出现的问题,看到这个问题,第一反应就是代码中有某个位置使用的@Autowired注入了DataSource,然而我并未在项目中找到这样的代码。
直到读到下面这部分的异常信息

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceInitializer': Invocation of init method failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: postgresqlDataSource,bpmDataSource
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:408) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1570) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:220) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:332) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerPostProcessor.postProcessAfterInitialization(DataSourceInitializerPostProcessor.java:62) ~[spring-boot-autoconfigure-1.3.5.RELEASE.jar:1.3.5.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1583) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545) ~[spring-beans-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    ... 40 common frames omitted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
才发现是dataSourceInitializer这个类导致的

@PostConstruct
    public void init() {
        if (!this.properties.isInitialize()) {
            logger.debug("Initialization disabled (not running DDL scripts)");
            return;
        }
        if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
        if (this.dataSource == null) {
            logger.debug("No DataSource found so not initializing");
            return;
        }
        runSchemaScripts();
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这是异常的代码。this.applicationContext.getBean(DataSource.class);在这位置通过类型来获取数据源,然后就出现了上述的异常。

下面介绍的是springboot的注释的方法配置多数据源


@Configuration
public class GlobalDataConfiguration {
    @Bean(name = "bpmDataSource")
    @Primary
    @ConfigurationProperties(prefix = "bpm.datasource")
    public DataSource primaryDataSource() {
        return new com.alibaba.druid.pool.DruidDataSource();
    }

    @Bean(name = "postgresqlDataSource")
    @ConfigurationProperties(prefix = "workbench.pgsql")
    public DataSource secondaryDataSource() {
        DataSource dataSource = new com.alibaba.druid.pool.DruidDataSource();
        return dataSource;
    }

    @Bean(name = "mysqlNamedParameterJdbcTemplate")
    public NamedParameterJdbcTemplate primaryJdbcTemplate(@Qualifier("bpmDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }

    @Bean(name = "postgresqlNamedParameterJdbcTemplate")
    public NamedParameterJdbcTemplate secondaryJdbcTemplate(@Qualifier("postgresqlDataSource") DataSource dataSource) {
        return new NamedParameterJdbcTemplate(dataSource);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
bpm.datasource.url=
bpm.datasource.username=
bpm.datasource.password=

workbench.pgsql.url=
workbench.pgsql.username=
workbench.pgsql.password=
1
2
3
4
5
6
7
切换到这个配置之后上述问题解决了。

    if (this.applicationContext.getBeanNamesForType(DataSource.class, false,
                false).length > 0) {
            this.dataSource = this.applicationContext.getBean(DataSource.class);
        }
1
2
3
4
但是这个代码中的的数据源也是两个,很不理解,继续跟踪代码进行分析。手下判断的length是数据源的总量这个没有问题。
那么问题就发生在了getBean这个方法上了。
下一步进入到DefaultListableBeanFactory这个类

@Override
    public <T> T getBean(Class<T> requiredType) throws BeansException {
        return getBean(requiredType, (Object[]) null);
    }

    @Override
    public <T> T getBean(Class<T> requiredType, Object... args) throws BeansException {
        Assert.notNull(requiredType, "Required type must not be null");
        String[] beanNames = getBeanNamesForType(requiredType);
        if (beanNames.length > 1) {
            ArrayList<String> autowireCandidates = new ArrayList<String>();
            for (String beanName : beanNames) {
                if (!containsBeanDefinition(beanName) || getBeanDefinition(beanName).isAutowireCandidate()) {
                    autowireCandidates.add(beanName);
                }
            }
            if (autowireCandidates.size() > 0) {
                beanNames = autowireCandidates.toArray(new String[autowireCandidates.size()]);
            }
        }
        if (beanNames.length == 1) {
            return getBean(beanNames[0], requiredType, args);
        }
        else if (beanNames.length > 1) {
            Map<String, Object> candidates = new HashMap<String, Object>();
            for (String beanName : beanNames) {
                candidates.put(beanName, getBean(beanName, requiredType, args));
            }
            String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
            if (primaryCandidate != null) {
                return getBean(primaryCandidate, requiredType, args);
            }
            String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
            if (priorityCandidate != null) {
                return getBean(priorityCandidate, requiredType, args);
            }
            throw new NoUniqueBeanDefinitionException(requiredType, candidates.keySet());
        }
        else if (getParentBeanFactory() != null) {
            return getParentBeanFactory().getBean(requiredType, args);
        }
        else {
            throw new NoSuchBeanDefinitionException(requiredType);
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
通过上面代码我们可以发现当同类型的实现存在多个的时候beanNames.length >1,再往下走进入determinePrimaryCandidate这个方法,我们可以看到

isPrimary(candidateBeanName, beanInstance)
1
这句验证代码

protected boolean isPrimary(String beanName, Object beanInstance) {
        if (containsBeanDefinition(beanName)) {
            return getMergedLocalBeanDefinition(beanName).isPrimary();
        }
        BeanFactory parentFactory = getParentBeanFactory();
        return (parentFactory instanceof DefaultListableBeanFactory &&
                ((DefaultListableBeanFactory) parentFactory).isPrimary(beanName, beanInstance));
    }
1
2
3
4
5
6
7
8
在这个位置我们发现了问题的根本原因了,我们用springboot配置的时候使用了@Primary的注解,然后spring会默认的加载有这个注解的对象。当存在多个对象而且还无法区分默认加载那一个对象的时候就会出现本文中的问题了。当然了,本着可配置一定可编程的原则,反之亦是通用的。我们可以在数据源的配置xml中加上

primary="true"
1
这个问题就很开心的解决掉了

这个是最终的多数据库连接的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd   
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
    default-autowire="byName">

    <bean id="bpmDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close"  primary="true" >
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="${datasource.url}" />
        <property name="username" value="${datasource.username}" />
        <property name="password" value="${datasource.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="40" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
    </bean>

    <bean id="mysqlTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="bpmDataSource" />
        </property>
    </bean>

    <bean id="mysqlNamedParameterJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg index="0" ref="bpmDataSource" />
    </bean>

    <context:component-scan base-package="com.demo.sop.service" />

    <!-- pgsql -->
    <bean id="postgresqlDataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="${pgsql.url}" />
        <property name="username" value="${pgsql.username}" />
        <property name="password" value="${pgsql.password}" />
        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="1" />
        <property name="minIdle" value="1" />
        <property name="maxActive" value="40" />
        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="60000" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <!-- 配置监控统计拦截的filters -->
        <property name="filters" value="stat,log4j" />
    </bean>
    <bean id="postgresqlTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref local="postgresqlDataSource" />
        </property>
    </bean>
    <tx:annotation-driven transaction-manager="postgresqlTransactionManager" />

    <bean id="postgresqlNamedParameterJdbcTemplate"
        class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate">
        <constructor-arg index="0" ref="postgresqlDataSource" />
    </bean>

</beans>


转子:
     http://blog.csdn.net/xupeng874395012/article/details/69419791
分享到:
评论

相关推荐

    springboot多数据源配置

    在Spring Boot应用中,多数据源配置是一项重要的技术实践,特别是在大型系统中,可能需要连接到多个数据库以实现数据隔离、读写分离或是分布式事务管理。Spring Boot以其强大的自动化配置能力,使得设置多数据源变得...

    SpringBoot配置多数据源实现动态切换数据源

    多数据源配置允许我们灵活地管理这些数据。 SpringBoot实现多数据源主要依靠Spring的`@Configuration`和`@DataSourceConfiguration`注解,以及Spring JDBC的`DataSource`接口。下面是一个基本的配置示例: ```java...

    springboot多数据源即分布式事务解决方案

    SpringBoot作为一款轻量级的框架,提供了便捷的多数据源配置和分布式事务管理方案,使得开发者能够高效地管理和操作不同的数据库。本文将详细探讨SpringBoot如何实现多数据源以及分布式事务。 首先,我们要理解什么...

    springboot多数据源配置(sqlserver,mysql)

    在Spring Boot应用中,多数据源配置是一项关键的技术,它允许我们连接并操作多个数据库,例如MySQL和SQL Server。在本场景中,我们将探讨如何在Spring Boot中设置和管理这样的多数据源配置。 首先,我们需要了解`...

    springboot多数据源即分布式事务解决方案,添加对多线程的支持

    1. 数据源配置:包含不同数据源的配置文件,如上面提到的`application.yml`。 2. 事务管理器配置:定义并配置了多数据源对应的事务管理器。 3. 业务逻辑层:使用`@Transactional`注解标记事务边界,可能还使用了`@...

    springboot2.0多数据源集成hibernate配置hibernateDaoSupport示例

    对于多数据源,我们需要创建多个数据源配置,例如: ```yaml spring: datasource: primary: url: jdbc:mysql://localhost:3306/main_db username: root password: password driver-class-name: ...

    springboot 多数据源

    在Spring Boot应用中,多数据源的配置与管理是一个常见的需求,特别是在大型系统中,可能需要连接多个数据库,比如主数据库用于高频读写操作,从数据库用于数据分析或备份。本示例将详细介绍如何使用Spring Boot的...

    SpringBoot多数据源案例

    你需要为每个数据源配置一个事务管理器,并确保在事务开始时选择正确的数据源。 6. **测试与实践**: 为了验证多数据源的配置是否正确,可以编写单元测试或集成测试,模拟对两个数据源的增删改查操作,确保数据被...

    SpringBoot多数据源框架【整合配置及应用】.docx

    在 SpringBoot 中,我们可以使用 `spring.datasource` 配置数据源参数,其中包括了多数据源配置。 ``` spring: datasource: dynamic: primary: master datasource: master: driver-class-name: org.sqlite....

    Springboot+mybatis+druid多数据源配置(oracle+mybatis)

    本篇将详细讲解如何在Spring Boot项目中结合Mybatis和Druid实现多数据源配置,以支持Oracle和MySQL两种数据库,并进行数据库的分离调试。 首先,我们要理解Spring Boot的自动配置机制。Spring Boot通过扫描`@...

    springboot实现多数据源而且加上事务不会使aop切换数据源失效

    在Spring Boot应用中,我们...以上就是使用Spring Boot、MyBatis和AOP实现多数据源切换并保证事务有效性的详细步骤。这个实例适用于简单的读写分离场景,如果涉及到更复杂的分布式事务需求,还需要进一步扩展和调整。

    Spring Boot+Jpa多数据源配置Demo(支持不同数据库)

    Spring Boot结合JPA(Java Persistence API)和JdbcTemplate,为开发者提供了灵活且强大的多数据源配置能力。本示例将详细介绍如何在Spring Boot项目中实现这样的配置,以支持不同类型的数据库。 首先,我们要理解...

    springboot + mybatis 多数据源demo

    总结,Spring Boot结合MyBatis的多数据源配置为企业级应用提供了强大的数据库管理能力。通过灵活的配置和智能的路由策略,开发者可以轻松地处理复杂的数据源切换需求,提升系统的健壮性和可扩展性。实践这个示例,将...

    springboot多数据源demo

    综上所述,"springboot多数据源demo"项目涵盖了Spring Boot中实现多数据源的关键步骤,包括数据源配置、JdbcTemplate或JPA的创建、事务管理以及数据源的动态切换。这个示例对于学习和实践Spring Boot的多数据源处理...

    SpringBoot多数据源成果源代码.rar

    综上所述,"SpringBoot多数据源成果源代码"项目提供了Spring Boot环境下多数据源配置和应用的实践经验,对于理解和掌握这一技术有极大的帮助。通过深入研究源代码,开发者可以学到如何在实际项目中灵活地管理和使用...

    springboot多数据源整合

    1. **数据源配置**:在Spring Boot中,我们可以通过`application.yml`或`application.properties`文件来配置多个数据源。每个数据源需要指定相应的数据库连接信息,包括URL、用户名、密码以及驱动类名。例如: ```...

    SpringBoot+gradle+mybatis多数据源动态配置

    本项目结合了SpringBoot、Gradle和MyBatis,旨在实现多数据源的动态配置,这对于处理多个数据库或者分库分表的场景尤其有用。以下是对这个项目的详细解析: **SpringBoot** SpringBoot是由Pivotal团队提供的全新...

    SpringBoot 多数据源

    1. **配置多数据源**:在SpringBoot中,我们可以使用`@ConfigurationProperties`注解来自定义数据源配置。例如,为两个数据库分别配置名为`primary`和`secondary`的数据源: ```java @Configuration @...

Global site tag (gtag.js) - Google Analytics