Mysql之处理多数据源

一 Sring+Mybatis处理动态数据源

1 创建一个普通maven项目,在pom文件中添加相关依赖

<!--Spring相关的依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>

<!--Mybatis相关依赖-->
<!--Mybatis框架依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
<!--Mybatis与Spring整合依赖-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.2</version>
</dependency>
<!--MySQL数据库连接驱动 版本不要过高-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.43</version>
</dependency>
<!--JDBC 数据库连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.1</version>
</dependency>

2 在pom文件中添加resource,指定编译Mybatis映射文件

<resource>
    <directory>src/main/java</directory>
    <includes>
        <include>**/*.xml</include>
    </includes>
</resource>

3 创建Mybatis整合多数据源的配置文件,applicationContext-mybatis.xml

4 创建Spring的核心配置文件applicationContext.xml,引入Mybatis配置文件

多数据源连接mysql mysql 多数据源_spring


5 在applicationContext-mybatis.xml配置3307|3308|3309|3310这四个数据源

<bean id="dataSource3307" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://192.168.235.128:3307/test"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>
<bean id="dataSource3308" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://192.168.235.128:3308/test"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>
<bean id="dataSource3309" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://192.168.235.128:3309/test"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="username" value="root"/>
   <property name="password" value="123456"/>
</bean>
<bean id="dataSource3310" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="jdbc:mysql://192.168.235.128:3310/test"/>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</bean>

6 在applicationContext-mybatis.xml配置一个sessionFactory,引用动态的数据源
动态数据源就是在运行的时候决定采用哪个数据源,在配置文件中不能写死

<!--数据库的sessionFactory-->
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dynamicDataSource"/>
</bean>

7 5.4.8 在com.multi.dynamic包下创建DynamicDataSource类继承AbstractRoutingDataSource
(1) 设计的思路是将所有数据源放到一个map集合中,通过指定map集合的key,可以动态获取不同的数据源
(2) 该类中有个抽象方法determineCurrentLookupKey需要实现

protected Object determineCurrentLookupKey() {
    return null;
}

(3) 定义几个常量,作为动态数据源对应的key

public static final String DATASOURCE_KEY_3307 = "3307";
public static final String DATASOURCE_KEY_3308 = "3308";
public static final String DATASOURCE_KEY_3309 = "3309";
public static final String DATASOURCE_KEY_3310 = "3310";

(4) 如何将指定的key和不同的数据源放到map集合中呢?
在applicationContext-mybatis.xml对动态数据源DynamicDataSource类进行配置

<!--动态数据源-->
<bean id="dynamicDataSource" class="com.bjpowernode.multi.dynamic.DynamicDataSource">
    <property name="targetDataSources">
        <map>
            <entry key="3307" value-ref="dataSource3307"/>
            <entry key="3308" value-ref="dataSource3308"/>
            <entry key="3309" value-ref="dataSource3309"/>
            <entry key="3310" value-ref="dataSource3310"/>
        </map>
    </property>
</bean>

(5) 如何为determineCurrentLookupKey()动态的指定key呢
在进行数据库操作前,我们提前设置使用哪个数据库,然后在指定的数据源的时候,会自动调用这个determineCurrentLookupKey()方法,得到指定的值。
直观的想法,可能是定义一个静态变量,但是静态变量存在线程安全问题,所以我们需要为每一个线程都维护一个变量,用于指定连接的数据库是哪个,所以我们这里使用线程的副本ThreadLocal类
(6) 在com.multi.dynamic包下创建ThreadLocalHolder类

public class ThreadLocalHolder {
    public static final ThreadLocal<String> holder = new ThreadLocal<String>();
    /**
     * 向当前线程变量的副本中放一个数据源的key
     */
    public static void setDataSourceKey(String dataSourceKey){
        holder.set(dataSourceKey);
    }

    /**
     * 从当前线程变量的副本中取出放入数据源的key
     */
    public static String getDataSourceKey(){
        return holder.get();
    }
}

(7) 对DynamicDataSource类中的方法进行实现

protected Object determineCurrentLookupKey() {
    //在操作数据库前要确定是哪个数据源的key返回
    return ThreadLocalHolder.getDataSourceKey();
}

8 在applicationContext-mybatis.xml配置Mapper包扫描器,直接扫描mapper包即可

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
    <property name="basePackage" value="com.bjpowernode.multi.mapper"/>
</bean>

9 接口及映射

多数据源连接mysql mysql 多数据源_spring_02


10 service、model及Test

多数据源连接mysql mysql 多数据源_mybatis_03


11 在applicationContext.xml文件中扫描impl包

<context:component-scan base-package="com.multi.service.impl"/>

12 运行测试类,获取不同数据源用户

public class Test {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context =
                new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        UserService userService = context.getBean("userServiceImpl",UserService.class);
        ThreadLocalHolder.setDataSourceKey("3307");
        System.out.println("3307数据库:" +userService.getUserByIdFrom3307(4).getName());
        ThreadLocalHolder.setDataSourceKey("3308");
        System.out.println("3308数据库:" +userService.getUserByIdFrom3308(3).getName());
        ThreadLocalHolder.setDataSourceKey("3309");
        System.out.println("3309数据库:" +userService.getUserByIdFrom3309(4).getName());
        ThreadLocalHolder.setDataSourceKey("3310");
        System.out.println("3310数据库:" +userService.getUserByIdFrom3310(4).getName());
    }
}

二 SpringBoot+Mybatis处理动态数据源

1创建一个springBoot工程,在pom文件中添加依赖

<!-- 加载mybatis整合springboot -->
<dependency>
   <groupId>org.mybatis.spring.boot</groupId>
   <artifactId>mybatis-spring-boot-starter</artifactId>
   <!--在springboot的父工程中没有指定版本,我们需要手动指定-->
   <version>1.3.2</version>
</dependency>
<!-- MySQL的jdbc驱动包 -->
<dependency>
   <groupId>mysql</groupId>
   <!--在springboot的父工程中指定了版本,我们就不需要手动指定了-->
   <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--JDBC 数据库连接池-->
<dependency>
   <groupId>com.alibaba</groupId>
   <artifactId>druid</artifactId>
   <version>1.1.1</version>
</dependency>

2 在pom文件中添加resource,对mybatis映射文件编译

<resources>
   <resource>
      <directory>src/main/java</directory>
      <includes>
         <include>**/*.xml</include>
      </includes>
   </resource>
</resources>

3 在SpringBoot的核心配置文件中,配置连接信息
那么这里我们涉及多数据源,所以需要配置多个连接信息,如果配置多个,SpringBoot会报错,所以我们只能对使用自定义配置,然后在程序中获取

#3307数据库的连接配置信息
spring.datasource.username3307=root
spring.datasource.password3307=123456
spring.datasource.driver3307=com.mysql.cj.jdbc.Driver
spring.datasource.url3307=jdbc:mysql://192.168.235.128:3307/test?useUnicode=true&characterEncoding=utf8&useSSL=false
#3308数据库的连接配置信息
spring.datasource.username3308=root
spring.datasource.password3308=123456
spring.datasource.driver3308=com.mysql.cj.jdbc.Driver
spring.datasource.url3308=jdbc:mysql://192.168.235.128:3308/test?useUnicode=true&characterEncoding=utf8&useSSL=false
#3309数据库的连接配置信息
spring.datasource.username3309=root
spring.datasource.password3309=123456
spring.datasource.driver3309=com.mysql.cj.jdbc.Driver
spring.datasource.url3309=jdbc:mysql://192.168.235.128:3309/test?useUnicode=true&characterEncoding=utf8&useSSL=false
#3310数据库的连接配置信息
spring.datasource.username3310=root
spring.datasource.password3310=123456
spring.datasource.driver3310=com.mysql.cj.jdbc.Driver
spring.datasource.url3310=jdbc:mysql://192.168.235.128:3310/test?useUnicode=true&characterEncoding=utf8&useSSL=false

4 在com.multi.config包下定义一个映射核心文件中自定义信息的实体类DataSourceConfigInfo

@Component
@ConfigurationProperties(prefix ="spring.datasource")
public class DataSourceConfigInfo {
    private String username3307;
    private String password3307;
    private String driver3307;
    private String url3307;
   
    private String username3308;
    private String password3308;
    private String driver3308;
    private String url3308;
    
    private String username3309;
    private String password3309;
    private String driver3309;
    private String url3309;
    private String username3310;
    private String password3310;
    private String driver3310;
    private String url3310;
//省略get|set
}

5 在自定义属性映射文件出现的警告处理

多数据源连接mysql mysql 多数据源_mybatis_04


点击红色圈的链接,到SpringBoot官网上,拷贝如下依赖到pom.xml文件中,这个警告不处理也不会影响程序的运行

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-configuration-processor</artifactId>
   <optional>true</optional>
</dependency>

6 将Spring+Mybatis实现中的applicationContext-mybatis.xml配置信息用SpringBoot注解替换

(1) 因为是基于动态数据源的,所以先将我们前面定义的动态数据源DynamicDataSource类以及ThreadLocal类拷贝到工程中

多数据源连接mysql mysql 多数据源_多数据源连接mysql_05


(2) 在com.bjpowernode.multi.config下创建MyDataSource类

@Configuration //相当于一个xml文件
@MapperScan(basePackages="com.bjpowernode.multi.mapper",sqlSessionTemplateRef = "sqlSessionTemplate")
public class MyDataSource {
    @Autowired//注入我们自定义属性的映射类
    private DataSourceConfigInfo dataSourceConfigInfo;

    /**
     <bean id="dataSource3307" class="com.alibaba.druid.pool.DruidDataSource">
     <property name="url" value="jdbc:mysql://192.168.235.128:3307/test"/>
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="username" value="root"/>
     <property name="password" value="123456"/>
     </bean>
     */
    @Bean//相当于xml中的bean
    public DruidDataSource dataSource3307(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceConfigInfo.getUrl3307());
        druidDataSource.setDriverClassName(dataSourceConfigInfo.getDriver3307());
        druidDataSource.setUsername(dataSourceConfigInfo.getUsername3307());
        druidDataSource.setPassword(dataSourceConfigInfo.getPassword3307());
        return druidDataSource;
    }
    /**
     <bean id="dataSource3308" class="com.alibaba.druid.pool.DruidDataSource">
     <property name="url" value="jdbc:mysql://192.168.235.128:3308/test"/>
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="username" value="root"/>
     <property name="password" value="123456"/>
     </bean>
     */
    @Bean//相当于xml中的bean
    public DruidDataSource dataSource3308(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceConfigInfo.getUrl3308());
        druidDataSource.setDriverClassName(dataSourceConfigInfo.getDriver3308());
        druidDataSource.setUsername(dataSourceConfigInfo.getUsername3308());
        druidDataSource.setPassword(dataSourceConfigInfo.getPassword3308());
        return druidDataSource;
    }
    /**
     <bean id="dataSource3309" class="com.alibaba.druid.pool.DruidDataSource">
     <property name="url" value="jdbc:mysql://192.168.235.128:3309/test"/>
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="username" value="root"/>
     <property name="password" value="123456"/>
     </bean>
     */
    @Bean//相当于xml中的bean
    public DruidDataSource dataSource3309(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceConfigInfo.getUrl3309());
        druidDataSource.setDriverClassName(dataSourceConfigInfo.getDriver3309());
        druidDataSource.setUsername(dataSourceConfigInfo.getUsername3309());
        druidDataSource.setPassword(dataSourceConfigInfo.getPassword3309());
        return druidDataSource;
    }
    /**
     <bean id="dataSource3310" class="com.alibaba.druid.pool.DruidDataSource">
     <property name="url" value="jdbc:mysql://192.168.235.128:3310/test"/>
     <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     <property name="username" value="root"/>
     <property name="password" value="123456"/>
     </bean>
     */
    @Bean//相当于xml中的bean
    public DruidDataSource dataSource3310(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceConfigInfo.getUrl3310());
        druidDataSource.setDriverClassName(dataSourceConfigInfo.getDriver3310());
        druidDataSource.setUsername(dataSourceConfigInfo.getUsername3310());
        druidDataSource.setPassword(dataSourceConfigInfo.getPassword3310());
        return druidDataSource;
    }
    /**
     * <!--数据库的sessionFactory-->
     <bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dynamicDataSource"/>
     </bean>
     */
    @Bean
    public SqlSessionFactoryBean sessionFactory(){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource());
  return sqlSessionFactoryBean;
    }
    /**
     * <!--动态数据源-->
     <bean id="dynamicDataSource" class="com.bjpowernode.multi.dynamic.DynamicDataSource">
         <property name="targetDataSources">
             <map>
                 <entry key="3307" value-ref="dataSource3307"/>
                 <entry key="3308" value-ref="dataSource3308"/>
                 <entry key="3309" value-ref="dataSource3309"/>
                 <entry key="3310" value-ref="dataSource3310"/>
             </map>
         </property>
     </bean>
     */
@Bean
    public DynamicDataSource dynamicDataSource(){
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> targetDataSources = new ConcurrentHashMap<Object,Object>();
        targetDataSources.put(DynamicDataSource.DATASOURCE_KEY_3307,dataSource3307());
        targetDataSources.put(DynamicDataSource.DATASOURCE_KEY_3308,dataSource3308());
        targetDataSources.put(DynamicDataSource.DATASOURCE_KEY_3309,dataSource3309());
        targetDataSources.put(DynamicDataSource.DATASOURCE_KEY_3310,dataSource3310());
        dynamicDataSource.setTargetDataSources(targetDataSources);
        return dynamicDataSource;
    }
    /**
     * <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sessionFactory"/>
        <property name="basePackage" value="com.bjpowernode.multi.mapper"/>
       </bean>
        注意:
        在SpringBoot下,不能简单的配置MapperScannerConfigurer这个bean
        在SpringBoot下,需要SqlSessionTemplate + @MapperScan注解 配合实现原来xml的功能
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate() throws Exception{
        SqlSessionTemplate sqlSessionTemplate =new SqlSessionTemplate(sessionFactory().getObject());
        return sqlSessionTemplate;
    }
}

7 mapper、service、model

多数据源连接mysql mysql 多数据源_多数据源连接mysql_06


8 在Application添加测试代码进行测试

注意:操作数据库前需要指定数据源

@SpringBootApplication
public class Application {
   public static void main(String[] args) {
      ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
      UserService userService = context.getBean("userServiceImpl",UserService.class);
      ThreadLocalHolder.setDataSourceKey(DynamicDataSource.DATASOURCE_KEY_3307);
      System.out.println("3307数据库:" +userService.getUserByIdFrom3307(4).getName());
      ThreadLocalHolder.setDataSourceKey(DynamicDataSource.DATASOURCE_KEY_3308);
      System.out.println("3308数据库:" +userService.getUserByIdFrom3308(3).getName());
      ThreadLocalHolder.setDataSourceKey(DynamicDataSource.DATASOURCE_KEY_3309);
      System.out.println("3309数据库:" +userService.getUserByIdFrom3309(4).getName());
      ThreadLocalHolder.setDataSourceKey(DynamicDataSource.DATASOURCE_KEY_3310);
      System.out.println("3310数据库:" +userService.getUserByIdFrom3310(4).getName());
   }
}