dynamic-dadasource集成shardingjdbc动态数据源、读写分离

项目场景:

项目中使用的是dynamic-datasource进行数据源管理,最近数据库压力越来越大,想使用sharding jdbc做一个轻量的读写分离,在配置时也遇到了一些问题,因此记录一下问题,
我这里主要遇到了两个问题:依赖冲突问题,以及datasouce注入失败引发的空指针问题。

github:dynamic-dadasource集成shardingjdbc动态数据源
gitee: dynamic-dadasource集成shardingjdbc动态数据源


基本依赖:
sharding jdbc使用的是5.1.1版本
dynamic-datasource使用 3.2.0版本
mybatis-plus使用 3.5.1版本


基本配置

pom依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.8</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
        <groupId>org.apache.shardingsphere</groupId>
        <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
        <version>5.1.1</version>
    </dependency>
        <dependency>
            <groupId>org.apache.shardingsphere</groupId>
            <artifactId>shardingsphere-jdbc-core-spring-namespace</artifactId>
            <version>5.1.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.2.0</version>

        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.3.18</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.3.7.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.apache.tomcat</groupId>
                <artifactId>tomcat-dbcp</artifactId>
                <version>10.0.16</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    

yml配置

server:
  port: 8181

#mybatis-plus配置
mybatis-plus:
  mapper-locations: classpath:**/mapper/*.xml
  configuration:
    # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 返回类型为Map,显示null对应的字段
#    call-setters-on-nulls: true

spring:
  main:
    allow-bean-definition-overriding: true

  shardingsphere:
    datasource:
      names: master,slave
      common:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: *****#数据库密码
      #主数据库
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
         #配置主库地址
        url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
        username: root
        password: *****#数据库密码
      slave:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        #配置从库地址
        url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
        username: root
        password: *****#数据库密码
    rules:
      readwrite-splitting:
        data-sources:
          master_slave:
            type: Static
            props:
              write-data-source-name: master#指定写数据库
              read-data-source-names: slave #指定读数据库
              load-balancer-name: round_robin #指定负载均衡模式
        load-balancers:
        #这里的round_robin  同上 load-balancer-name的值
          round_robin:
            type: ROUND_ROBIN
            props:
              workId: 1
    props:
      sql-show: true
  datasource:
    dynamic:
      strict: true
      #指定默认主数据源
      primary: master
      datasource:
      #默认数据源
        master:
          driver-class-name: com.mysql.cj.jdbc.Driver
          type: com.zaxxer.hikari.HikariDataSource
          #配置默认数据源地址
          url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
          username: root
          password: ****#数据库密码

在yml中,dynamic-datasource 中不指定默认主数据源,可能会发生 please check the setting of primary 异常
在下面的动态数据源中直接指定数据源 dynamic. datasource数据源也可以不用配置,直接以sharing jdbc数据源作为主数据源


DataSourceConfiguration配置

package com.dzd.test.common;


import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.provider.AbstractDataSourceProvider;
import com.baomidou.dynamic.datasource.provider.DynamicDataSourceProvider;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DataSourceProperty;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Map;

/**
 * 动态数据源配置:
 *
 * 使用{@link com.baomidou.dynamic.datasource.annotation.DS}注解,切换数据源
 *
 * @DS(DataSourceConfiguration.SHARDING_DATA_SOURCE_NAME)
 *
 * @author wanping
 * @date 2022/4/25 上午10:36
 */
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class DataSourceConfiguration {
    /**
     * 分表数据源名称
     */
    public static final String SHARDING_DATA_SOURCE_NAME = "sharding";
    /**
     * 动态数据源配置项
     */
    @Autowired
    private DynamicDataSourceProperties dynamicDataSourceProperties;
    /**
     * shardingjdbc有四种数据源,需要根据业务注入不同的数据源
     *
     * 

1. 未使用分片, 脱敏的名称(默认): shardingDataSource; *

2. 主从数据源: masterSlaveDataSource; *

3. 脱敏数据源:encryptDataSource; *

4. 影子数据源:shadowDataSource * * shardingjdbc默认就是shardingDataSource * 如果需要设置其他的可以使用 * @Resource(value="") 设置 */ @Lazy @Resource DataSource shardingDataSource; /** * 将shardingDataSource放到了多数据源(dataSourceMap)中 * 注意有个版本的bug,3.1.1版本 不会进入loadDataSources 方法,这样就一直造成数据源注册失败 */ @Bean public DynamicDataSourceProvider dynamicDataSourceProvider() { Map<String, DataSourceProperty> datasourceMap = dynamicDataSourceProperties.getDatasource(); return new AbstractDataSourceProvider() { @Override public Map<String, DataSource> loadDataSources() { Map<String, DataSource> dataSourceMap = createDataSourceMap(datasourceMap); // 将 shardingjdbc 管理的数据源也交给动态数据源管 dataSourceMap.put(SHARDING_DATA_SOURCE_NAME, shardingDataSource); return dataSourceMap; } }; } /** * 将动态数据源设置为首选的 * 当spring存在多个数据源时, 自动注入的是首选的对象 * 设置为主要的数据源之后,就可以支持shardingjdbc原生的配置方式了 * * @return */ @Primary @Bean public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) { DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource(); dataSource.setPrimary(SHARDING_DATA_SOURCE_NAME); dataSource.setStrict(dynamicDataSourceProperties.getStrict()); dataSource.setStrategy(dynamicDataSourceProperties.getStrategy()); /** * 动态多数据源提供者, * 在DynamicRoutingDataSource需要要DynamicDataSourceProvider加载进去, * 否则会导致datasource注入失败,抛出空指针异常 */ dataSource.setProvider(dynamicDataSourceProvider); dataSource.setP6spy(dynamicDataSourceProperties.getP6spy()); dataSource.setSeata(dynamicDataSourceProperties.getSeata()); return dataSource; } }

问题描述

创建 dataSource 空指针异常

Error creating bean with name ‘dataSource’ defined in class path resource [com/xx/test/common/DataSourceConfiguration.class]:
Invocation of init method failed; nested exception is java.lang.NullPointerException

Error creating bean with name 'sqlSessionFactory' defined in class path resource [com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.class]:
 Unsatisfied dependency expressed through method 'sqlSessionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: 
 Error creating bean with name 'dataSource' defined in class path resource [com/dzd/test/common/DataSourceConfiguration.class]: 
 Invocation of init method failed; nested exception is java.lang.NullPointerException

解决办法

在设置数据源时加入 == dataSource.setProvider(dynamicDataSourceProvider) ==

@Primary
    @Bean
    public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(SHARDING_DATA_SOURCE_NAME);
        dataSource.setStrict(dynamicDataSourceProperties.getStrict());
        dataSource.setStrategy(dynamicDataSourceProperties.getStrategy());
        /**
         * 动态多数据源提供者,
         * 在DynamicRoutingDataSource需要要DynamicDataSourceProvider加载进去,
         * 否则会导致datasource注入失败,抛出空指针异常
         */
        dataSource.setProvider(dynamicDataSourceProvider);

        dataSource.setP6spy(dynamicDataSourceProperties.getP6spy());
        dataSource.setSeata(dynamicDataSourceProperties.getSeata());
        return dataSource;
    }

Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required

出现 Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required异常,大概率是druid 驱动的问题,在使用druid-spring-boot-starter时就会出现这个问题将依赖改为

		>
            >com.alibaba>
            >druid>
            >1.2.8>
        >

问题解决


参考文章 https://www.cnblogs.com/java5wanping/p/16194529.html

你可能感兴趣的:(mybatis,java,mysql)