精华内容
下载资源
问答
  • Spring Boot, Spring Cloud项目,变单数据源数据源配置问题

    应项目的需求,要把项目改成多数据源,将两张表单独抽取出来,放入另一个数据库中,由于spring cloud 架构不熟悉,也是花了点时间才做好。

    1、禁用srping boot自动配置的单数据源,
    启动类上加上注解@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)

    @SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
    @EnableDiscoveryClient
    @EnableFeignClients
    public class UserServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(UserServerApplication.class, args);
        }
    }
    

    2、修改配置文件,项目中用spring cloud config统一管理配置文件的
    在datasource节点下再加一个新数据源配置,如下所示

    datasource:
      user-server:
        url: jdbc:mysql://***?characterEncoding=utf-8&autoReconnect=true&f***
        username: ***
        password: ***
        driver-class-name: com.mysql.jdbc.Driver
        max-idle: 10
        max-wait: 10000
        min-idle: 5
        initial-size: 5
        validation-query: SELECT 1
        test-on-borrow: false
        test-while-idle: true
        time-between-eviction-runs-millis: 18800
      user-server-separate:
        url: jdbc:mysql://***?characterEncoding=utf-8&autoReconnect=true****
        username: ***
        password: ***
        driver-class-name: com.mysql.jdbc.Driver
        max-idle: 10
        max-wait: 10000
        min-idle: 5
        initial-size: 5
        validation-query: SELECT 1
        test-on-borrow: false
        test-while-idle: true
        time-between-eviction-runs-millis: 18800

    3、分包  两个数据库对应两个实体类的包,xml文件也一样,分两个文件夹,如下图所示
    这里写图片描述
    domain 和 domain_separate 存放不同库的实体类
    这里写图片描述
    mybatis和separate存放不同库对应的xml文件

    4、创建新的数据源
    这里写图片描述
    UcSeparateDataSourceConfig这是新配置的数据源,具体内容如下

    @Configuration
    @MapperScan(basePackages = {"com.baojun.user_server.application.domain_separate"}, sqlSessionFactoryRef = "ucSeparateSqlSessionFactory")
    public class UcSeparateDataSourceConfig {
        @Primary
        @Bean(name = "ucSeparateDataSource")
        @ConfigurationProperties(prefix = "datasource.user-server-separate")
        public DataSource dataSource() {
            return DataSourceBuilder.create().build();
        }
    
        @Primary
        @Bean(name = "ucSeparateTransactionManager")
        public DataSourceTransactionManager transactionManager(@Qualifier("ucSeparateDataSource") DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }
    
        @Primary
        @Bean(name = "ucSeparateSqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory(@Qualifier("ucSeparateDataSource") DataSource dataSource) throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource);
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:separate/*.xml"));
            return factoryBean.getObject();
        }
    
        @Bean(name = "ucSeparateSqlSessionTemplate")
        @Primary
        public SqlSessionTemplate sqlSessionTemplate(@Qualifier("ucSeparateSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory);
        }

    这里要注意包路径和xml文件的路径,
    @ConfigurationProperties(prefix = “datasource.user-server-separate”)前缀与配置文件中一致

    5、在需要更改数据源的地方,加入对应的注解
    @Resource(name=”ucSeparateSqlSessionTemplate”)这个就是新配置的数据源

    @Repository
    public class CarDaoImpl extends BaseDaoImpl<Car> implements CarDao {
        public CarDaoImpl() {
        super(Car.class);
        }
        @Resource(name="ucSeparateSqlSessionTemplate")
        public void sqlSessionTemplate(SqlSessionTemplate sessionTemplate) {
        setSqlSessionTemplate(sessionTemplate);
        }
    }

    http://blog.csdn.net/neosmith/article/details/61202084,这是本人参考的网上例子

    展开全文
  • 前言在开发过程中可能需要用到多个数据源,比如一个项目(MySQL)就是和(SQL Server)混合使用,就需要使用多数据源;如果业务场景比较复炸,可以使用动态数据源,灵活切换,典型的应用就是读写分离。下面分两个...

    前言

    在开发过程中可能需要用到多个数据源,比如一个项目(MySQL)就是和(SQL Server)混合使用,就需要使用多数据源;如果业务场景比较复炸,可以使用动态数据源,灵活切换,典型的应用就是读写分离。下面分两个模块来配置数据源,大家可以根据自己实际情况配置。

    多数据源

    禁用DataSourceAutoConfiguration

    如果DataSourceAutoConfiguration不禁用的话,就会报错,多个数据源,无法装配哪一个。在springBoot的主程序入口的注解

    @SpringBootApplication(exclude = {
            DataSourceAutoConfiguration.class
    })

    配置application.properties

    #datasource 这是自动装配的默认配置禁止,这里不能使用
    #spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    #spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    #spring.datasource.url=jdbc:mysql://127.0.0.1:3306/api_resources?#autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
    #spring.datasource.username=api
    #spring.datasource.password=api
    #db1
    spring.datasource.db1.url=jdbc:mysql://127.0.0.1:3306/api_resources?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
    spring.datasource.db1.username=api
    spring.datasource.db1.password=api
    spring.datasource.db1.driver-class-name=com.mysql.jdbc.Driver
    #db2
    spring.datasource.db2.url=jdbc:mysql://127.0.0.1:3306/ssm?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
    spring.datasource.db2.username=api
    spring.datasource.db2.password=api
    spring.datasource.db2.driver-class-name=com.mysql.jdbc.Driver

    配置dataSource

    配置两个数据源,分别为db1,和db2

    /**
     * Author: Starry.Teng
     * Email: tengxing7452@163.com
     * Date: 17-11-1
     * Time: 下午9:14
     * Describe: DataSource Config
     */
    @Configuration
    public class DataSourceConfig {
        @Autowired
        Environment env;
    
        @Bean(name = "ds1")
        public DataSource dataSource1() {
            DruidDataSource dataSource = new DruidDataSource();
    
            dataSource.setUrl(env.getProperty("spring.datasource.db1.url"));
            dataSource.setUsername(env.getProperty("spring.datasource.db1.username"));
            dataSource.setPassword(env.getProperty("spring.datasource.db1.password"));
            dataSource.setDriverClassName(env.getProperty("spring.datasource.db1.driver-class-name"));
            return dataSource;
        }
    
        @Bean(name = "ds2")
        public DataSource dataSource2() {
            DruidDataSource dataSource = new DruidDataSource();
    
            dataSource.setUrl(env.getProperty("spring.datasource.db2.url"));
            dataSource.setUsername(env.getProperty("spring.datasource.db2.username"));
            dataSource.setPassword(env.getProperty("spring.datasource.db2.password"));
            dataSource.setDriverClassName(env.getProperty("spring.datasource.db2.driver-class-name"));
            return dataSource;
        }
    
    }
    

    Mybatis配置

    为两个数据源分贝配置一个MybatisDb1Config类和MybatisDb2Config类,对数据源进行管理,注意他们所管理的包是不同的。

    /**
     * Author: Starry.Teng
     * Email: tengxing7452@163.com
     * Date: 17-11-1
     * Time: 下午9:15
     * Describe: MybatisDb1 Config
     */
    @Configuration
    @MapperScan(basePackages = {"cn.yjxxclub.demo.datasource.dao.db1"}, sqlSessionFactoryRef = "sqlSessionFactory1")
    public class MybatisDb1Config {
    
        @Qualifier("ds1")
        @Autowired
        DataSource dataSource;
    
        @Bean(name = "sqlSessionFactory1")
        public SqlSessionFactory sqlSessionFactory1() throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource);
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            factoryBean.setMapperLocations(resolver.getResources("classpath:mappers/db1/*.xml"));
            factoryBean.setTypeAliasesPackage("cn.yjxxclub.demo.datasource.model");
            return factoryBean.getObject();
    
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
            SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory1()); // 使用上面配置的Factory
            return template;
        }
    }
    /**
     * Author: Starry.Teng
     * Email: tengxing7452@163.com
     * Date: 17-11-1
     * Time: 下午9:26
     * Describe: MybatisDb2 Config
     */
    @Configuration
    @MapperScan(basePackages = {"cn.yjxxclub.demo.datasource.dao.db2"}, sqlSessionFactoryRef = "sqlSessionFactory2")
    public class MybatisDb2Config {
    
        @Qualifier("ds2")
        @Autowired
        DataSource dataSource;
    
        @Bean(name = "sqlSessionFactory2")
        public SqlSessionFactory sqlSessionFactory2() throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource);
            PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            factoryBean.setMapperLocations(resolver.getResources("classpath:mappers/db2/*.xml"));
            factoryBean.setTypeAliasesPackage("cn.yjxxclub.demo.datasource.model");
            return factoryBean.getObject();
    
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate2() throws Exception {
            SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory2());
            return template;
        }
    }
    

    编写Dao和Mapper

    Mapper.java和mapper.xml在basePackages和classpath对应放置即可,和springboot整合Mybtis一样,这里就不说了,然后正常启动即可。代码在此:https://github.com/tengxing/Multiple-dataSources

    数据源动态切换

    数据源注册

    /**
     * Author: Starry.Teng
     * Email: tengxing7452@163.com
     * Date: 17-11-1
     * Time: 下午9:14
     * Describe: DataSource Config
     */
    @Configuration
    public class DataSourceConfig {
        @Autowired
        Environment env;
    
        @Bean(name = "ds1")
        public DataSource dataSource1() {
            DruidDataSource dataSource = new DruidDataSource();
    
            dataSource.setUrl(env.getProperty("spring.datasource.db1.url"));
            dataSource.setUsername(env.getProperty("spring.datasource.db1.username"));
            dataSource.setPassword(env.getProperty("spring.datasource.db1.password"));
            dataSource.setDriverClassName(env.getProperty("spring.datasource.db1.driver-class-name"));
            return dataSource;
        }
    
        @Bean(name = "ds2")
        public DataSource dataSource2() {
            DruidDataSource dataSource = new DruidDataSource();
    
            dataSource.setUrl(env.getProperty("spring.datasource.db2.url"));
            dataSource.setUsername(env.getProperty("spring.datasource.db2.username"));
            dataSource.setPassword(env.getProperty("spring.datasource.db2.password"));
            dataSource.setDriverClassName(env.getProperty("spring.datasource.db2.driver-class-name"));
            return dataSource;
        }
    
        @Bean(name = "dynamicDS1")//注意这个bean是mybatis的sqlSessionFacatory所管理的dataSource
        public DataSource dataSource() {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            // 默认数据源
            dynamicDataSource.setDefaultTargetDataSource(dataSource1());
    
            // 配置多数据源
            Map<Object, Object> dsMap = new HashMap(5);
            dsMap.put("ds1", dataSource1());
            dsMap.put("ds2", dataSource2());
    
            dynamicDataSource.setTargetDataSources(dsMap);
    
            return dynamicDataSource;
        }
    
    }
    

    动态数据源编写

    需要继承AbstractRoutingDataSource类

    /**
     * Author: http://blog.csdn.net/neosmith/article/details/61202084
     * Date: 17-11-2
     * Time: 下午2:56
     * Describe: 动态数据源
     */
    public class DynamicDataSource extends AbstractRoutingDataSource {
        private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);
    
        @Override
        protected Object determineCurrentLookupKey() {
            log.debug("数据源为{}", DataSourceContextHolder.getDB());
    
            return DataSourceContextHolder.getDB();
        }
    }
    

    编写DataSourceContextHolder

    /**
     * Author: http://blog.csdn.net/neosmith/article/details/61202084
     * Date: 17-11-2
     * Time: 下午2:56
     * Describe: DataSource ContextHolder
     */
    public class DataSourceContextHolder {
        public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);
    
        /**
         * 默认数据源
         */
        public static final String DEFAULT_DS = "ds1";
    
        /**
         * 获取当前线程
         */
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
    
        // 设置数据源名
        public static void setDB(String dbType) {
            log.debug("切换到{}数据源", dbType);
            contextHolder.set(dbType);
        }
    
        // 获取数据源名
        public static String getDB() {
            return (contextHolder.get());
        }
    
        // 清除数据源名
        public static void clearDB() {
            contextHolder.remove();
        }
    }
    

    咋们到时候只要调用setDB()方法即可实现数据源的切换,下面使用Aop的方式进行动态切换。

    编写代理类

    /**
     * Author: http://blog.csdn.net/neosmith/article/details/61202084
     * Date: 17-11-2
     * Time: 下午3:01
     * Describe: 动态数据源代理类
     * 逻辑:对方法@DB注解的方法进行切面换数据源操作
     */
    @Aspect
    @Component
    @Order(value=-1) //保证该AOP在@Transactional之前执行
    public class DynamicDataSourceAspect {
    
        @Before("@annotation(DB)")
        public void beforeSwitchDS(JoinPoint point){
    
            //获得当前访问的class
            Class<?> className = point.getTarget().getClass();
    
            //获得访问的方法名
            String methodName = point.getSignature().getName();
            //得到方法的参数的类型
            Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
            String dataSource = DataSourceContextHolder.DEFAULT_DS;
            try {
                // 得到访问的方法对象
                Method method = className.getMethod(methodName, argClass);
    
                // 判断是否存在@DS注解
                if (method.isAnnotationPresent(DB.class)) {
                    DB annotation = method.getAnnotation(DB.class);
                    // 取出注解中的数据源名
                    dataSource = annotation.value();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            // 切换数据源
            DataSourceContextHolder.setDB(dataSource);
    
        }
        @After("@annotation(DB)")
        public void afterSwitchDS(JoinPoint point){
    
            DataSourceContextHolder.clearDB();
    
        }
    }
    

    自定义注解

    /**
     * Author: Starry.Teng
     * Email: tengxing7452@163.com
     * Date: 17-11-2
     * Time: 下午3:00
     * Describe:
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({
            ElementType.METHOD
    })
    public @interface DB {
        String value() default "ds1";
    }

    serviceImpl

        @DB("ds1")
        public Object getDs1(){
    
            Singer singer = singerMapper.findByName("陈奕迅");
            logger.info("/n"+singer);
            return null;
        }
    
        @DB("ds2")
        public Object getDs2(){
            Book book = bookMapper.findById(bookMapper.list().get(0).getId());
            logger.info(book+"/n");
            return null;
        }

    最后调用吧!!!项目在此:https://github.com/tengxing/DynamicDataSource

    两者比较

    类别多数据源动态数据源
    作用配置多个数据源,分模块开发通过灵活的手段对数据库进行灵活切库
    原理实例化多个SsqlSessionFactorySpring+AOP
    作用框架MybatisSpring
    应用场景解耦比较大的项目,模块化开发主从分布,读写分离

    知识点

    上面的两种方式都达到了”换数据源”目的,只是实现的原理和方式不一样而已,都是优虐,还是那句话,没有最好的方法,只有在特定的环境下最适合的解决方案。

    参考文章

    http://blog.csdn.net/neosmith/article/details/61202084
    http://www.cnblogs.com/hdwang/p/7041096.html
    http://blog.csdn.net/catoop/article/details/50575038
    https://github.com/zeq9069/koala/tree/master/src/main/java/org/kyrin/koala

    展开全文
  • SpringBoot配置数据源 1.创建项目 创建好项目以后,pom文件应该是这样的: <?xml version="1.0" encoding="UTF-8"?> <project xmlns=...

    SpringBoot配置数据源

    1.创建项目

    1557144041757

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    创建好项目以后,pom文件应该是这样的:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.fgr</groupId>
        <artifactId>datasource-dome</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>datasource-dome</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    2.编写DatasourceDomeApplication类

    package com.fgr.datasourcedome;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.CommandLineRunner;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    @SpringBootApplication
    @Slf4j
    public class DatasourceDomeApplication implements CommandLineRunner {
    
        @Autowired
        private DataSource dataSource;
    
        public static void main(String[] args) {
            SpringApplication.run(DatasourceDomeApplication.class, args);
        }
    
    
        @Override
        public void run(String... args) throws Exception {
            showConnection();
        }
    
        private void showConnection() throws SQLException {
            log.info(dataSource.toString());
            Connection connection = dataSource.getConnection();
            log.info(connection.toString());
            connection.close();
        }
    }
    

    3.运行项目查看输出信息:

    2019-05-06 20:16:55.045  INFO 3040 --- [           main] c.f.d.DatasourceDomeApplication          : HikariDataSource (null)
    2019-05-06 20:16:55.045  INFO 3040 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
    2019-05-06 20:16:55.141  INFO 3040 --- [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
    2019-05-06 20:16:55.143  INFO 3040 --- [           main] c.f.d.DatasourceDomeApplication          : HikariProxyConnection@720638621 wrapping conn0: url=jdbc:h2:mem:testdb user=SA
    

    这里可以发现我们只是直接注入了DataSource,并没有做任何的配置,springboot根据我们引入的H2数据库自动为我们配置了H2相关的数据连接。

    由于我们项目中加载了actuator也可以访问http://localhost:8080/actuator/beans查看加载了哪些类:

     "dataSource": {
                        "aliases": [],
                        "scope": "singleton",
                        "type": "com.zaxxer.hikari.HikariDataSource",
                        "resource": "class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]",
                        "dependencies": [
                            "spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties"
                        ]
                    },
    

    手动配置数据源

    pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.fgr</groupId>
        <artifactId>pure-spring-datasource-demo</artifactId>
        <version>1.0-SNAPSHOT</version>
        <properties>
            <spring.version>5.1.3.RELEASE</spring.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${spring.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>${spring.version}</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-dbcp2</artifactId>
                <version>RELEASE</version>
            </dependency>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <version>RELEASE</version>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    
    </project>
    

    applicationContext.xml

    <?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"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <context:component-scan base-package="dome" />
        <!--
        <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
              destroy-method="close">
            <property name="driverClassName" value="org.h2.Driver"/>
            <property name="url" value="jdbc:h2:mem:testdb"/>
            <property name="username" value="SA"/>
            <property name="password" value=""/>
        </bean>
        -->
    </beans>
    

    DataSourceDemo.java

    package dome;
    
    import org.apache.commons.dbcp2.BasicDataSourceFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Arrays;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement
    public class DataSourceDemo {
        @Autowired
        private DataSource dataSource;
    
        public static void main(String[] args) throws SQLException {
            ApplicationContext applicationContext =
                    new ClassPathXmlApplicationContext("applicationContext*.xml");
            showBeans(applicationContext);
            dataSourceDemo(applicationContext);
        }
    
        @Bean(destroyMethod = "close")
        public DataSource dataSource() throws Exception {
            Properties properties = new Properties();
            properties.setProperty("driverClassName", "org.h2.Driver");
            properties.setProperty("url", "jdbc:h2:mem:testdb");
            properties.setProperty("username", "sa");
            return BasicDataSourceFactory.createDataSource(properties);
        }
    
        @Bean
        public PlatformTransactionManager transactionManager() throws Exception {
            return new DataSourceTransactionManager(dataSource());
        }
    
        private static void showBeans(ApplicationContext applicationContext) {
            System.out.println(Arrays.toString(applicationContext.getBeanDefinitionNames()));
        }
    
        private static void dataSourceDemo(ApplicationContext applicationContext) throws SQLException {
            DataSourceDemo demo = applicationContext.getBean("dataSourceDemo", DataSourceDemo.class);
            demo.showDataSource();
        }
    
        public void showDataSource() throws SQLException {
            System.out.println(dataSource.toString());
            Connection conn = dataSource.getConnection();
            System.out.println(conn.toString());
            conn.close();
        }
    }
    

    运行以后输出的结果

    在这里插入图片描述

    还可以使用配置文件配置的方式注入bean:

    pom文件不变,applicationContext.xml将注释放开,DataSourceDemo.java改为:

    package dome;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Arrays;
    
    @Configuration
    @EnableTransactionManagement
    public class DataSourceDemo {
        @Autowired
        private DataSource dataSource;
    
        public static void main(String[] args) throws SQLException {
            ApplicationContext applicationContext =
                    new ClassPathXmlApplicationContext("applicationContext*.xml");
            showBeans(applicationContext);
            dataSourceDemo(applicationContext);
        }
    
        @Bean
        public PlatformTransactionManager transactionManager() throws Exception {
            return new DataSourceTransactionManager(dataSource);
        }
    
        private static void showBeans(ApplicationContext applicationContext) {
            System.out.println(Arrays.toString(applicationContext.getBeanDefinitionNames()));
        }
    
        private static void dataSourceDemo(ApplicationContext applicationContext) throws SQLException {
            DataSourceDemo demo = applicationContext.getBean("dataSourceDemo", DataSourceDemo.class);
            demo.showDataSource();
        }
    
        public void showDataSource() throws SQLException {
            System.out.println(dataSource.toString());
            Connection conn = dataSource.getConnection();
            System.out.println(conn.toString());
            conn.close();
        }
    }
    

    输出结果为:
    在这里插入图片描述

    展开全文
  • 本文介绍两种切换数据库的方法。 方法1:数据库信息都配置在spring xml中,适用于一般数据库切换。...方法2:将数据库信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。...

    鉴于很多同学反应,在看的过程中,自己搭建的demo,没有成功。
    首先附上本项目github地址,可以对照着看,看一下相关的配置是否正确配置。

    本文介绍两种动态切换数据库的方法。
    方法一:数据源信息配置在xml中,适用于一般数据库切换。执行完某操作,切换数据库,执行另一个操作。
    方法二:数据源信息配置在默认数据源中,适用于切换数据库操作同一方法,相当于批量执行方法。

    两种方法核心都是AbstractRoutingDataSource,由spring提供,用来动态切换数据源。我们需要继承它,来进行操作。本博客中,隐藏了部分spring相关的配置信息。

    源码分析可以看下一篇文章源码分析

    方法一:数据源信息都配置在xml中

    1…继承AbstractRoutingDataSource,重写determineCurrentLookupKey方法

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class MultiDataSource extends AbstractRoutingDataSource{
    
    	/* ThreadLocal,叫线程本地变量或线程本地存储。
    	 * ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
    	 * 这里使用它的子类InheritableThreadLocal用来保证父子线程都能拿到值。
    	 */
    	private static final ThreadLocal<String> dataSourceKey = new InheritableThreadLocal<String>();
    	
    	/**
    	 * 设置dataSourceKey的值
    	 * @param dataSource
    	 */
    	public static void setDataSourceKey(String dataSource) {
    		dataSourceKey.set(dataSource);
    	}
    	/**
    	 * 清除dataSourceKey的值
    	 */
    	public static void toDefault() {
    		dataSourceKey.remove();
    	}
    	/**
    	 * 返回当前dataSourceKey的值
    	 */
    	@Override
    	protected Object determineCurrentLookupKey() {
    		return dataSourceKey.get();
    	}
    }
    

    2… 配置xml,这里将上面创建的MultiDataSource注入到spring容器中,这里主要用到AbstractRoutingDataSource的两个属性defaultTargetDataSource和targetDataSources。defaultTargetDataSource默认目标数据源,targetDataSources(map类型)存放用来切换的数据源。配置完以后,其他地方用到数据源的话,都引用multiDataSource。

    <?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    
    
    	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
    		init-method="init" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    			<property name="url" value="第一个数据库的url" />
    			<property name="username" value="账号" />
    			<property name="password" value="密码" />
    			<property name="validationQuery" value="select 1"></property>
    			<property name="testOnBorrow" value="true"></property>
    	</bean>
    		<bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource"
    		init-method="init" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    		<property name="url" value="第二个数据库的url" />
    		<property name="username" value="账号" />
    		<property name="password" value="密码" />
    		<property name="validationQuery" value="select 1"></property>
    		<property name="testOnBorrow" value="true"></property>
    	</bean>
    	<bean id="multiDataSource" class="com.wbb.dataSource.MultiDataSource">
    		<property name="defaultTargetDataSource" ref="dataSource"></property>
    		<property name="targetDataSources">
    			<map>
    				<entry key="dataSource2" value-ref="dataSource2"></entry>
    			</map>
    		</property>
    	</bean>
    	<!-- mybatis -->
    	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    		<property name="dataSource" ref="multiDataSource"></property>
    		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
    		<property name="mapperLocations" value="classpath:com/wbb/mapper/*.xml"></property>
    	</bean>
    	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    		<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    		<property name="basePackage" value="com.wbb.mapper"></property>
    	</bean>
    
    	<!-- 添加事务管理 -->
    	<tx:annotation-driven transaction-manager="transactionManager" />
    
    	<bean id="transactionManager"
    		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="multiDataSource"></property>
    	</bean>
    
    </beans>
    

    3…手动切换数据源,切换完以后,记得再切回默认数据库。

    MultiDataSource.setDataSourceKey("dataSource2");//切换到dataSource2数据源
    XXX在该数据源下的操作XXX
    MultiDataSource.toDefault();//操作完以后,清除dataSourceKey的值,即切回默认数据源,原理后面会讲。
    

    4… 利用aop切换数据源,这里记得开启aop,配置文件中使用<aop:aspectj-autoproxy />
    4.1首先定义一个注解,来调用注解切换数据库

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DynamicRoutingDataSource {
    
    	String value() default "dataSource";//本文默认dataSource
    
    }
    

    4.2 这里使用@Before和@After,在调用目标方法前,进行aop拦截,通过解析注解上的值来切换数据源。在调用方法结束后,切回默认数据源。如果目标方法无返回值,也可以使用@Around,调用ProceedingJoinPoint的proceed()方法前切换数据源,调用proceed()方法后切回默认数据源。

    import java.lang.reflect.Method;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    import com.wbb.annotation.DynamicRoutingDataSource;
    import com.wbb.dataSource.MultiDataSource;
    
    @Aspect
    @Component
    public class HandlerDataSourceAop {
    
    	private static Logger logger = LoggerFactory.getLogger(HandlerDataSourceAop.class);
    	/**
    	 * @within匹配类上的注解
    	 * @annotation匹配方法上的注解
    	 */
    	@Pointcut("@within(com.wbb.annotation.DynamicRoutingDataSource)||@annotation(com.wbb.annotation.DynamicRoutingDataSource)")
    	public void pointcut(){}
    	
    	@Before(value = "pointcut()")
    	public void beforeOpt(JoinPoint joinPoint) {
    	/** 先查找方法上的注解,没有的话再去查找类上的注解
    	*-----------------------------------------------------------------------
     	* 这里使用的是接口的模式,注解在实现类上,所以不能使用如下方式获取目标方法的对象,
    	* 因为该方式获取的是该类的接口或者顶级父类的方法的对象.
    	* MethodSignature methodSignature = (MethodSignature)point.getSignature();
         * Method method = methodSignature.getMethod();
         * DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
         * 通过上面代码是获取不到方法上的注解的,如果真要用上面代码来获取,可以修改aop代理模式,修改为cglib代理
         * 在xml配置文件修改为<aop:aspectj-autoproxy proxy-target-class="true" /> ,
         * proxy-target-class属性true为cglib代理,默认false为jdk动态代理 。
         * ---------------------------------------------------------
         * 本文使用是jdk动态代理, 这里使用反射的方式获取方法
    	*/
    	//反射获取Method 方法一
    	Object target = joinPoint.getTarget();
    	Class<?> clazz = target.getClass();
    	Method[] methods = clazz.getMethods();
    	DynamicRoutingDataSource annotation = null;
    	for (Method method : methods) {
    		if (joinPoint.getSignature().getName().equals(method.getName())) {
    			annotation = method.getAnnotation(DynamicRoutingDataSource.class);
    			if (annotation == null) {
    				annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
    				if (annotation == null) {
    					return;
    				}
    			}
    		}
    	}
    	// 	反射获取Method 方法二
    	//		Object[] args = joinPoint.getArgs();
    	//		Class<?>[] argTypes = new Class[joinPoint.getArgs().length];
    	//		for (int i = 0; i < args.length; i++) {
    	//			argTypes[i] = args[i].getClass();
    	//		}
    	//		Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), argTypes);
    	//		DynamicRoutingDataSource annotation = method.getAnnotation(DynamicRoutingDataSource.class);
    	//		if (annotation == null) {
    	//			annotation = joinPoint.getTarget().getClass().getAnnotation(DynamicRoutingDataSource.class);
    	//			if (annotation == null) {
    	//				return;
    	//			}
    	//		}
    		
    		String dataSourceName = annotation.value();
    		MultiDataSource.setDataSourceKey(dataSourceName);
    		logger.info("切到" + dataSourceName + "数据库");
    	}
    	@After(value="pointcut()")
    	public void afterOpt(){
    		MultiDataSource.toDefault();
    		logger.info("切回默认数据库");
    	}
    }
    

    4.3 使用:只需要把@DynamicRoutingDataSource注解加到方法或者类上即可

    @DynamicRoutingDataSource("dataSource2")
    

    方法一到此就结束了

    方法二:数据源信息配置在数据库中

    1.xml的配置,这里只需要配置一个默认的数据源就行了,因为其他的数据源都是从该数据源的数据源表中读取。

    	<?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/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    			http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
    			http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
    	
    		<bean id="defaultDataSource" class="com.alibaba.druid.pool.DruidDataSource"
    			init-method="init" destroy-method="close">
    			<property name="driverClassName" value="com.mysql.jdbc.Driver" />
    			<property name="url" value="第一个数据库的url" />
    			<property name="username" value="账号" />
    			<property name="password" value="密码" />
    			<property name="validationQuery" value="select 1"></property>
    			<property name="testOnBorrow" value="true"></property>
    		</bean>
    	
    		<!--动态数据源相关-->
    	    <bean id="dynamicDataSource" class="com.wbb.dataSource.dynamic.DynamicDataSource">
    	        <property name="targetDataSources">
    	            <map key-type="java.lang.String">
    	                <entry key="defaultDataSource" value-ref="defaultDataSource"/>
    	            </map>
    	        </property>
    	        <property name="defaultTargetDataSource" ref="defaultDataSource"/>
    	    </bean>
    		<!-- mybatis -->
    		<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    			<property name="dataSource" ref="dynamicDataSource"></property>
    			<property name="configLocation" value="classpath:mybatis-config.xml"></property>
    			<property name="mapperLocations" value="classpath:com/wbb/mapper/*.xml"></property>
    		</bean>
    		<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    			<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    			<property name="basePackage" value="com.wbb.mapper"></property>
    		</bean>
    	
    		<!-- 添加事务管理 -->
    		<tx:annotation-driven transaction-manager="transactionManager" />
    	
    		<bean id="transactionManager"
    			class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    			<property name="dataSource" ref="dynamicDataSource"></property>
    		</bean>
    	
    	</beans>
    

    2.数据源表的设计

    DROP TABLE IF EXISTS `other_datasource`;
    CREATE TABLE `other_datasource` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `datasource_name` varchar(64) DEFAULT NULL,
      `database_ip` varchar(64) DEFAULT NULL,
      `database_port` varchar(64) DEFAULT NULL,
      `database_name` varchar(64) DEFAULT NULL,
      `database_username` varchar(64) DEFAULT NULL,
      `database_password` varchar(64) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
    

    这里写图片描述
    3.数据源表对应的bean

    /**
     * 数据源bean构建器
     */
    public class DataSourceBean {
    
    	private final String beanName;		//注册在spring中bean名字
    	private final String driverClassName; 
    	private final String url;
    	private final String username;
    	private final String password;
    	private final String validationQuery;
    	private final Boolean testOnBorrow;
    
    	public String getBeanName() {
    		return beanName;
    	}
    
    	public String getDriverClassName() {
    		return driverClassName;
    	}
    
    	public String getUrl() {
    		return url;
    	}
    
    	public String getUsername() {
    		return username;
    	}
    
    	public String getPassword() {
    		return password;
    	}
    
    	public String getValidationQuery() {
    		return validationQuery;
    	}
    
    	public Boolean getTestOnBorrow() {
    		return testOnBorrow;
    	}
    
    	public DataSourceBean(DataSourceBeanBuilder builder) {
    		this.beanName = builder.getBeanName();
    		this.driverClassName = builder.getDriverClassName();
    		this.url = builder.getUrl();
    		this.username = builder.getUsername();
    		this.password = builder.getPassword();
    		this.validationQuery = builder.getValidationQuery();
    		this.testOnBorrow = builder.getTestOnBorrow();
    	}
    
    	public static class DataSourceBeanBuilder {
    		private String beanName;
    		private String driverClassName = "com.mysql.jdbc.Driver";
    		private String url = "jdbc:mysql://%s:%s/%s";
    	    private String databaseIP;
    	    private String databasePort;
    	    private String databaseName;
    		private String username;
    		private String password;
    		private String validationQuery = "select 1";
    		private Boolean testOnBorrow = true;
    		public DataSourceBeanBuilder(String beanName, String databaseIP, String databasePort, String databaseName,
    				String username, String password) {
    			super();
    			this.beanName = beanName;
    			this.databaseIP = databaseIP;
    			this.databasePort = databasePort;
    			this.databaseName = databaseName;
    			this.username = username;
    			this.password = password;
    		}
    		public DataSourceBeanBuilder() {
    			super();
    		}
    		public DataSourceBeanBuilder driverClassName(String driverClassName) {
    			this.driverClassName = driverClassName;
    			return this;
    		}
    		public DataSourceBeanBuilder validationQuery(String validationQuery) {
    			this.validationQuery = validationQuery;
    			return this;
    		}
    		public DataSourceBeanBuilder testOnBorrow(Boolean testOnBorrow) {
    			this.testOnBorrow = testOnBorrow;
    			return this;
    		}
    		public String getUrl() {
    			return String.format(url,this.databaseIP,this.databasePort,this.databaseName);
    		}
    		public String getBeanName() {
    			return beanName;
    		}
    		public String getDriverClassName() {
    			return driverClassName;
    		}
    		public String getDatabaseIP() {
    			return databaseIP;
    		}
    		public String getDatabasePort() {
    			return databasePort;
    		}
    		public String getDatabaseName() {
    			return databaseName;
    		}
    		public String getUsername() {
    			return username;
    		}
    		public String getPassword() {
    			return password;
    		}
    		public String getValidationQuery() {
    			return validationQuery;
    		}
    		public Boolean getTestOnBorrow() {
    			return testOnBorrow;
    		}
    	}
    

    4.这里将操作抽离出,建立数据源操作类,方便操作

    /**
     * 数据源操作类
     */
    public class DataSourceContext {
    
    	private static ThreadLocal<DataSourceBean> threadLocal = new InheritableThreadLocal<DataSourceBean>();
    
    	/**
    	 * 获取数据源
    	 */
    	public static DataSourceBean getDataSource() {
    		return threadLocal.get();
    	}
    	/**
    	 * 设置数据源
    	 */
    	public static void setDataSource(DataSourceBean dataSourceBean) {
    		threadLocal.set(dataSourceBean);
    	}
    	/**
    	 * 清除数据源
    	 * 清除后,数据源为默认时间
    	 */
    	public static void toDefault() {
    		threadLocal.remove();
    	}
    }
    
    

    5.定义AbstractRoutingDataSource的继承类DynamicDataSource,同时实现ApplicationContextAware接口,因为该方法切换数据源,需要到spring上下文中生成和获取数据源bean。

    import java.lang.reflect.Field;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.support.BeanDefinitionBuilder;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    import com.alibaba.druid.pool.DruidDataSource;
    
    
    public class  DynamicDataSource extends AbstractRoutingDataSource implements ApplicationContextAware{
    
    	private ApplicationContext applicationContext ;
    
    	/**
    	 * 连接数据源前,调用该方法
    	 */
    	@Override
    	protected Object determineCurrentLookupKey() {
    		//1.获取手动设置的数据源参数DataSourceBean
    		DataSourceBean dataSourceBean = DataSourceContext.getDataSource();
    		if(dataSourceBean == null) {
    			return null;
    		}
    		try {
    			//2.获取AbstractRoutingDataSource的targetDataSources属性,该属性存放数据源属性
    			Map<Object, Object> targetSourceMap = getTargetSource();
    			synchronized(this) {
    				/*
    				 * 3.判断targetDataSources中是否已经存在要设置的数据源bean
    				 * 存在的话,则直接返回beanName
    				 * 
    				 */
    				if(!targetSourceMap.keySet().contains(dataSourceBean.getBeanName())) {
    					/*不存在,则进行以下几步
    					3.1 先在spring容器中创建该数据源bean
    					*/
    					Object dataSource = createDataSource(dataSourceBean);
    					//3.2 在创建后的bean,放入到targetDataSources Map中
    					targetSourceMap.put(dataSourceBean.getBeanName(), dataSource);
    					/*
    					 * 3.3 通知spring有bean更新
    					 * 主要更新AbstractRoutingDataSource的resolvedDefaultDataSource(Map)属性,
    					 * 更新完以后,AbstractRoutingDataSource的determineTargetDataSource()中,才能找到数据源
    					 * 代码如下:
    					 * Object lookupKey = determineCurrentLookupKey();
    					   DataSource dataSource = this.resolvedDataSources.get(lookupKey);
    					 */
    					super.afterPropertiesSet();
    				}
    			}
    			for(Map.Entry<Object, Object> entry : targetSourceMap.entrySet()) {
    				System.out.println(entry.getKey()+"-"+entry.getValue());
    			}
    			return dataSourceBean.getBeanName();
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    		return null;
    	}
    	/**
    	 * 根据数据源信息在spring中创建bean,并返回
    	 * @param dataSourceBean 数据源信息
    	 * @return
    	 * @throws IllegalAccessException
    	 */
    	public Object createDataSource(DataSourceBean dataSourceBean) throws IllegalAccessException {
    		//1.将applicationContext转化为ConfigurableApplicationContext
    		ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
    		//2.获取bean工厂并转换为DefaultListableBeanFactory
    		DefaultListableBeanFactory beanFactory =  (DefaultListableBeanFactory) context.getBeanFactory();
    		/*
    		 * 3.本文用的是DruidDataSource,所有在这里我们获取的是该bean的BeanDefinitionBuilder,
    		 * 通过BeanDefinitionBuilder来创建bean定义
    		 */
    		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DruidDataSource.class);
    		/**
    		 * 4.获取DataSourceBean里的属性和对应值,并将其交给BeanDefinitionBuilder创建bean的定义
    		 */
    		Map<String, Object> propertyKeyValues = getPropertyKeyValues(DataSourceBean.class, dataSourceBean);
    		for(Map.Entry<String,Object> entry : propertyKeyValues.entrySet()) {
    			beanDefinitionBuilder.addPropertyValue(entry.getKey(), entry.getValue());
    		}
    		//5.bean定义创建好以后,将其交给beanFactory注册成bean对象,由spring容器管理
    		beanFactory.registerBeanDefinition(dataSourceBean.getBeanName(), beanDefinitionBuilder.getBeanDefinition());
    		//6.最后获取步骤5生成的bean,并将其返回
    		return context.getBean(dataSourceBean.getBeanName());
    	}
    	//获取类属性和对应的值,放入Map中
        @SuppressWarnings("unused")
    	private <T> Map<String, Object> getPropertyKeyValues(Class<T> clazz, Object object) throws IllegalAccessException {
           Field[] fields = clazz.getDeclaredFields();
           Map<String,Object> map = new HashMap<>();
           for (Field field : fields) {
        	   field.setAccessible(true);
        	   map.put(field.getName(), field.get(object));
           }
           map.remove("beanName");
           return map;
        }
        //通过反射获取AbstractRoutingDataSource的targetDataSources属性
    	@SuppressWarnings("unchecked")
    	public Map<Object, Object> getTargetSource() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
    		Field field = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
    		field.setAccessible(true);
    		return (Map<Object, Object>) field.get(this);
    	}
    	@Override
    	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    			this.applicationContext = applicationContext;
    	}
     
    }
    
    

    6.测试用例测试一下

    	@Test
    	public void testDynamicDataSource() {
    		1.从表中获取需要切换的数据源信息
    		List<DataSourceDO> dataSourceDOList = dataSourceMapper.getAllDataSources();
    		2.循环遍历,切换数据源,执行操作,切完就得切回默认数据源
    		for (DataSourceDO dataSourceDO : dataSourceDOList) {
    			DataSourceBean dataSourceBean = new DataSourceBean(new DataSourceBeanBuilder(dataSourceDO.getDatasourceName(),
    					dataSourceDO.getDatabaseIp(), dataSourceDO.getDatabasePort(), dataSourceDO.getDatabaseName(),
    					dataSourceDO.getUsername(), dataSourceDO.getPassword()));
    			DataSourceContext.setDataSource(dataSourceBean);
    			XXX你的操作XXX
    			DataSourceContext.toDefault();
    		}
    	}
    

    方法二部分参考https://blog.csdn.net/yizhenn/article/details/53965552该博客的思想。

    源码分析可以看下一篇文章https://blog.csdn.net/u013034378/article/details/81661706

    讲到这里,本篇文章到此也就结束了,如果文章中有问题,或者有一些不够严谨完善的地方,希望大家体谅体谅。欢迎大家留言,交流交流。
    最后附上本项目github的地址

    展开全文
  • spring + druid 配置动态配置数据源以及多数据源切换功能实现 数据源连接池使用druid 其他的数据源基本原理相同 spring中配置默认数据源连接池如下: <!-- 数据源配置, 使用 BoneCP 数据库连接池 --> &...
  • Spring Boot配置MongoDB多数据源

    千次阅读 2018-07-18 19:34:56
    我们在之前的文章中已经学习了如何在Spring Boot中...本章我们来记录连接多个数据源的步骤,以两个数据源为例,多个数据源类推。 数据源配置文件配置两个数据源 我们之前的配置是配置在application.propertie...
  • springboot AOP 多数据源切换

    万次阅读 2019-11-07 15:08:21
    由于需要做多数据源切换,然后想到去年做过一次多数据源切换,但是之前是springmvc的,现在是springboot,两者有所不同,再次做个简单的记录。 第一步还是一样的,写一个DynamicDataSourceHolder,用来获取需要使用...
  • Spring Boot 动态数据源(多数据源自动切换)

    万次阅读 多人点赞 2016-01-24 20:04:43
    为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便。一配置二使用 1. 配置文件中配置多个...
  • 在ssh项目中,需要连接两个不同ip下的数据库,所以必须要给hibernate配置两个或多个数据源 因为我只有一台电脑,所以我配置的是sqlserver+MySQL两个数据源 首先hibernate配置是直接从myeclipse中添加的 右键-...
  • springboot动态数据源

    千次阅读 2019-06-25 17:42:01
    基于spring-boot2.0.0版本实现多数据源注册 ...二,实现了多数据源中,调用单个数据源的事务控制(同一个方法中只调用一个数据库的事务),同一个方法中调用不同数据源就是分布式事务,以下代码没有实...
  • 最近在配置spring框架时需要使用多数据源,其中遇到一些问题,特此记录 无事务数据源切换 见 http://blog.csdn.net/shadowsick/article/details/8878448  主要说一下带事务数据源切换的配置 首先在spring-database....
  • SpringBoot静态数据源指的是将多个数据源信息配置在配置文件中,在项目启动时加载配置文件中的多个数据源,并实例化多个数据源Bean,再通过分包/Aop达到切换数据源的目的 如果想要新增或者修改数据源,必须修改配置文件,...
  • 我们在很多的项目中经常会有用到多个数据源。比如数据库读写分离,读操作都去从库里读,写操作都往主库里写。那么这里主库和从库就是两个不同的数据源。再比如要做两个数据库之间的数据转换,从一个数据库读取数据写...
  • Spring配置多个数据源,并实现数据源的动态切换

    万次阅读 热门讨论 2017-10-11 14:52:15
    2.在spring配置文件中配置这两个数据源数据源1 <!-- initialSize初始化时建立物理连接的个数0 maxActive最大连接池数量8 minIdle最小连接池数量0--> <bean id="dataSource1" class="com.alibaba.druid
  • 项目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource/tree/dev 在 Spring Boot... 在这个项目中使用注解方式声明要使用的数据源,通过 AOP 查找注解,从而实现数据源的动态切换;该项目 Product
  • 为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便。一配置二使用 1. 启动类注册动态数据源...
  • 一个项目里一般情况下只会使用到一个...2、就是在项目里配置多数据源; 我现在就是要使用另一个数据库的数据,想到了以上两种方法,为了更加熟悉spring,我打算使用第二种方案; 我在百度上查了好多关于spring配置多
  • 1 . POM依赖 mysql mysql-connector-java org.mybatis.spring.boot mybatis-spring-boot-starter 1.1.1 com.alibaba ...2 . SpringBoot数据源配置 spring: datasource: type: com.alib
  • Springboot动态数据源

    千次阅读 2018-05-03 14:31:56
    一、Springboot 动态数据源 把文章下半部分的代码复制到项目包中,然后按下面步骤处理就能实现多数据源的切换。 1、启动类注册动态数据源 @SpringBootApplication @Import({DynamicDataSourceRegister.class}) ...
  • 【JAVA】JAVA数据源

    千次阅读 多人点赞 2018-11-16 15:09:19
    文章目录前言连接池、数据源、JNDI连接池、数据源连接池思想JNDI直连数据源 前言 我们在进行数据访问,声明模板和repository之前【JAVA】Spring对JDBC的支持,都需要配置数据源用来连接数据库。数据源又可以分为两大...
  • Springboot整合Mybatis多数据源配置

    万次阅读 热门讨论 2019-06-09 14:06:44
    数据源配置类: 次数据源配置类: 三、项目结构(重点是mapper结构和配置类里的路径对应) 四、启动类——启动类需要取消加载数据源自动配置 五、测试: controller: Service: Mapper: 数据...
  • SpringBoot中多数据源的配置

    万次阅读 2018-07-27 15:11:08
    在实际项目中,一个工程配置多个数据源很常见,工程可能会根据业务或者模块访问不同的数据库或表;今天笔者就springboot中配置多数据源作个详细的讲解 2.实现方案 注意:一个应用工程中有且只有一个启动类,其...
  • 数据源的配置

    万次阅读 2018-10-23 20:14:06
    页面查询实在太慢,所以准备把dau_baseinfo表迁移到clickhouse,此时就需要再引入一个数据源,即clickhouse对应的数据源 下面开始配置多数据源 第一步:创建一个DynamicDataSource的类,继...
  • Spring动态创建数据源

    千次阅读 2018-09-26 16:35:48
    Spring动态创建数据源 Spring内置了一个AbstractRoutingDataSource,可以根据Key找到对应的数据源。那么我们可以把多个数据源存放到Map中,然后根据key去切换数据源。 ...
  • Spring实现动态数据源切换

    万次阅读 2015-09-15 18:33:43
    如下流程是我在实现Spring数据源动态切换到过程中遇到的问题接处理方式。   1、编写数据源切换的类 //此类时用于切换数据源时所调用的类及方法 public class DataSourceContextHolder { public static final ...
  • JdbcTemplate动态多数据源配置

    千次阅读 2019-09-04 22:44:24
    数据源就是javax.sql.DataSource,所有实现了这个接口的DataSource就叫做数据源,现在比较常用阿里巴巴的DruidDataSource,支持监控多数据源下的sql运行状况,便于以此实现以sql核心的应用系统,比如BI报表系统、...
  • 网上关于动态数据源配置的博文一搜一大堆,都是拿来主义,往往把需要的人弄得不是太明白,也没有个具体的好用的简单的demo例子供人参考,本篇,我也是拿来主义,,我拿来核心的core,进行demo案列整理,我只挑重要的...
  • java项目配置多数据源

    千次阅读 2019-05-26 13:59:36
    有些web应用需要连接多个数据源,本文讲解一下如何使用多个数据源,大题思路是这样的,系统启动的时候创建多个数据源,然后具体执行sql的时候去切换数据源执行对应的sql。如何切换数据源呢?spring提供了一个...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 495,691
精华内容 198,276
关键字:

数据源为static