项目中使用的是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版本
<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>
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数据源作为主数据源
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;
}
}
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异常,大概率是druid 驱动的问题,在使用druid-spring-boot-starter时就会出现这个问题将依赖改为
>
>com.alibaba >
>druid >
>1.2.8 >
>
问题解决
参考文章 https://www.cnblogs.com/java5wanping/p/16194529.html