spring-boot+dynamic-datasource实现真正的动态数据源
⼀、前⾔
  现在有这样⼀个连接表。数据库连接信息是从连接表中获取,想对连接表中的所有数据源进⾏维护只有⽤动态数据库。在⽹上了很多动态数据库教程。都是⽤@Ds注解选择需要的数据源,意思是在编写代码时就要确定数据源,并不能实现需求。想要对对应的连接进⾏管理,只有通过连接id能创建⼀个数据源,并使其⽣效,下⾯进⼊正题。
⼆、准备
1.两个不同的数据源以及连接表
2.主要依赖
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.1</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- 动态数据源依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!-- 阿⾥云数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--aop依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3.实体类
@Data
@TableName("dm_connect")
@ApiModel(value ="Connect对象", description ="")
public class Connect extends Model<Connect>{
@ApiModelProperty(value ="主键")
@TableId(value ="id", type = IdType.AUTO)
private Integer id;
@ApiModelProperty(value ="ip地址")
@TableField
private String ipAddress;
@ApiModelProperty(value ="端⼝")
@TableField
private Integer port;
@ApiModelProperty(value ="账号")
@TableField
private String username;
@ApiModelProperty(value ="密码")
@TableField
private String password;
}
@TableName("information_schema.SCHEMATA")
@Data
public class Schemata {
@TableField("CATALOG_NAME")
private String catalogName;
@TableField("SCHEMA_NAME")
private String schemaName;
@TableField("DEFAULT_CHARACTER_SET_NAME")
private String defaultCharacterSetName;
@TableField("DEFAULT_COLLATION_NAME")
private String defaultCollationName;
@TableField("SQL_PATH")
private String sqlPath;
@TableField("DEFAULT_ENCRYPTION")
private String defaultEncryption;
}
三、出发
1.默认数据源配置
  默认数据源主要功能是保存连接信息
yml
spring:
datasource:
url: jdbc:mysql://*.*.*.227:3306/mysql_manager?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToN ull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: zxc4256101
driver-class-name: sql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource # 指定数据源类型
MybatisPlusConfig
@MapperScan("⾃⼰定义mapper扫描逻辑")
@Configuration
@EnableTransactionManagement
public class MybatisPlusConfig {
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
/**
*
* @return 数据源配置
* @throws SQLException
*/
@Bean
@Primary
@ConfigurationProperties(prefix ="spring.datasource.master")
public DataSource dataSourceMaster()throws SQLException {
DruidDataSource dataSource =new DruidDataSource();
dataSource.setUrl(url);
dataSource.setDriver(new DruidDriver().createDriver(driverClassName));
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setUseGlobalDataSourceStat(true);
dataSource.setPoolPreparedStatements(true);
dataSource.setValidationQuery("select 1");
dataSource.setValidationQuery("select 1");
dataSource.setTimeBetweenEvictionRunsMillis(6000);
return dataSource;
}
/**
* 将默认数据源添加进动态数据源
* @param dataSourceMasterspringboot aop
* @return
*/
@Bean("dynamicDataSource")
public DataSource dynamicDataSource(DataSource dataSourceMaster){
DynamicRoutingDataSource dynamicRoutingDataSource =new DynamicRoutingDataSource();
dynamicRoutingDataSource.addDataSource("master", dataSourceMaster);
return dynamicRoutingDataSource;
}
@Bean
@Primary
@ConfigurationProperties(prefix ="mybatis")
public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dynamicDataSource") DataSource dataSource,@Qualifier("MybatisPlusInter ceptor") MybatisPlusInterceptor mybatisPlusInterceptor){
MybatisSqlSessionFactoryBean sqlSessionFactoryBean =new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
//分页插件
Interceptor[] plugins =new Interceptor[1];
plugins[0]= mybatisPlusInterceptor;
sqlSessionFactoryBean.setPlugins(plugins);
return sqlSessionFactoryBean;
}
/**
*    myBatis-plus 分页查询配置
*/
@Bean(name="MybatisPlusInterceptor")
public MybatisPlusInterceptor MpPaginationInterceptor(){
MybatisPlusInterceptor interceptor =new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2.注解实现
//在⽅法上使⽤
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public@interface Dynamic {
/**
* 数据库名称
*/
String database()default"";
}
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class DynamicAspect {
public class DynamicAspect {
public static final String ID ="id";
public static final String TABLE_SCHEMA="tableSchema";
public static final String DS_NAME ="dynamic";
private final IConnectService connectService;
private final DynamicRoutingDataSource dynamicRoutingDataSource;
@Pointcut("@annotation(包名.annotation.Dynamic)")
public void pointcut(){
}
/**
* 进⼊⽅法前通过连接id定义⼀个数据源并设为默认
* @param joinPoint
*/
@Before("pointcut()")
public void before(JoinPoint joinPoint){
MethodSignature ms =(MethodSignature) Signature();
Method method = ms.getMethod();
Dynamic dynamic = Annotation(Dynamic.class);
ServletRequestAttributes attributes =(ServletRequestAttributes) RequestAttributes();
if(null!= attributes &&null!= Request().getParameter(ID)){
//获取请求头参数
Integer id = Integer.Request().getParameter(ID));
//数据库名称:information_schema<;请求<;注解
String database="information_schema";
//从请求中获取数据库名
if(StringUtils.Request().getParameter(TABLE_SCHEMA))){
Request().getParameter(TABLE_SCHEMA);
}
//从注解上获取数据库名称
if(StringUtils.isNotBlank(dynamic.database())){
database=dynamic.database();
}
//获取连接参数
Connect connect = ById(id);
if(null== connect){
throw new RuntimeException("连接不存在!");
}
//组装datasource
DruidDataSource dataSource =new DruidDataSource();
dataSource.setUrl("jdbc:mysql://"+ IpAddress()+":"+ Port()+"/"+ database);
dataSource.Username());
dataSource.Password());
//添加datasource到动态数据源并设为primary
dynamicRoutingDataSource.addDataSource("dynamic", dataSource);
dynamicRoutingDataSource.setPrimary("dynamic");
}
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint)throws Throwable {
try{
return joinPoint.proceed();
}catch(Throwable throwable){
//⽅法出错时重置数据源
throwable.printStackTrace();

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。