方式一
其实网上有包如下:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>4.3.0</version>
</dependency>
applicaiton.yml配置如下:
spring:
datasource:
dynamic:
primary: ds1
strict: false
# 自定义负载均衡策略。slave组下有;多个个数据源,当用户使用 slave 切换数据源时会使用负载均衡算法。系统自带了两个负载均衡算法 LoadBalanceDynamicDataSourceStrategy 轮询,是默认的。 RandomDynamicDataSourceStrategy 随机的。
strategy: com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy
datasource:
ds1:
url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalIn
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
ds2:
url: jdbc:mysql://127.0.0.1:3306/demo1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
使用的时候就简单了,直接在service中的方法上使用
@DS("ds2")
public void saveTest() {
this.save(TestEntity.builder()
.name("动态数据源测试"+ ThreadLocalRandom.current().nextInt(1000))
.build());
}
方式二:自定义aop实现
我这里使用的springboot 3.2.4
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
application.yml,内容如下:
spring:
datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=
# username: root
# password: 123456
# type: com.alibaba.druid.pool.DruidDataSource
master:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=
username: root
password: 123456
slave:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/demo1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=
username: root
password: 123456
# mybatis
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: org.demo.entity
#typeEnumsPackage: org.springblade.dashboard.entity.enums
global-config:
#打开在线执行sql语句
enable-sql-runner: true
# 关闭MP3.0自带的banner
banner: false
db-config:
#主键类型 0:"数据库ID自增", 1:"不操作", 2:"用户输入ID",3:"数字型snowflake", 4:"全局唯一ID UUID", 5:"字符串型snowflake";
id-type: assign_id
#字段策略
insert-strategy: not_null
update-strategy: not_null
where-strategy: not_null
#驼峰下划线转换
table-underline: true
# 逻辑删除配置
# 逻辑删除全局值(1表示已删除,这也是Mybatis Plus的默认配置)
logic-delete-value: 1
# 逻辑未删除全局值(0表示未删除,这也是Mybatis Plus的默认配置)
logic-not-delete-value: 0
导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.5</version>
</dependency>
先创建一个自定义数据源注解DataSource.java,内容如下:
package org.demo.RW;
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)
public @interface DataSource {
DataSourceType value() default DataSourceType.MASTER;
}
继续创建数据源类型DataSourceType.java,内容如下:
package org.demo.RW;
public enum DataSourceType {
MASTER, SLAVE
}
接下来重点,springbootJDBC提供了一个抽象类 AbstractRoutingDataSource.java 用于数据源管理,只需要实现这个类,创建类DynamicDataSource.java ,内容如下:
package org.demo.RW;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<DataSourceType> CONTEXT_HOLDER = new ThreadLocal<>();
@Override
protected Object determineCurrentLookupKey() {
return getDataSourceType();
}
public static void setDataSourceType(DataSourceType dataSourceType) {
CONTEXT_HOLDER.set(dataSourceType);
}
public static DataSourceType getDataSourceType() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceType() {
CONTEXT_HOLDER.remove();
}
}
继续创建配置文件DataSourceConfig.java ,内容如下:
package org.demo.RW;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@MapperScan(basePackages = "org.demo.mapper")
public class DataSourceConfig {
@Bean("masterDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean("slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
@Qualifier("slaveDataSource")DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, masterDataSource);
targetDataSources.put(DataSourceType.SLAVE, slaveDataSource);
DynamicDataSource dynamicDataSource = new DynamicDataSource();
dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.afterPropertiesSet();
return dynamicDataSource;
}
@Bean
@Primary
public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
/*SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();*/
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dynamicDataSource);// 这里是重点,DynamicDataSource是我们创建的数据源管理类,否则不生效
// // 设置mapper.xml位置
// PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
// sessionFactory.setMapperLocations(
// resolver.getResources("classpath*:mapper/**/*.xml"));
// sessionFactory.setTypeAliasesPackage("org.demo.entity");
// sessionFactory.afterPropertiesSet();
return sessionFactory.getObject();
}
/*
// 启动报错JdbcTemplate。时再打开注释,不报错不需要打开注释
@Bean
public JdbcTemplate jdbcTemplate(DynamicDataSource dynamicDataSource) {
return new JdbcTemplate(dynamicDataSource);
}
// 事物管理
@Bean
public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
return new DataSourceTransactionManager(dynamicDataSource);
}*/
}
接下来创建DataSourceAspect.java 进行aop拦截,内容如下:
package org.demo.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.demo.RW.DataSource;
import org.demo.RW.DynamicDataSource;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@EnableAspectJAutoProxy(proxyTargetClass = true)
@Aspect
@Component
@Order(1)
@Slf4j
public class DataSourceAspect {
//任务层级目录下的service
@Around("execution(* org.demo..service..*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSource targetDataSource = method.getAnnotation(DataSource.class);
if (targetDataSource != null) {
DynamicDataSource.setDataSourceType(targetDataSource.value());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSourceType();
}
}
}
接下来创建测试ITestService.java 与实现TestServiceImpl.java ,内容如下:
package org.demo.a.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.demo.entity.TestEntity;
import java.util.List;
public interface ITest1Service extends IService<TestEntity> {
List<TestEntity> getList();
void saveTest();
}
## 实现
package org.demo.a.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.demo.RW.DataSource;
import org.demo.RW.DataSourceType;
import org.demo.a.service.ITest1Service;
import org.demo.entity.TestEntity;
import org.demo.mapper.TestMapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
@Service
public class Test1ServiceImpl extends ServiceImpl<TestMapper, TestEntity> implements ITest1Service {
@Override
// @DataSource(DataSourceType.SLAVE)
public List<TestEntity> getList() {
return this.list();
}
@Override
@DataSource(DataSourceType.SLAVE)
public void saveTest() {
this.save(TestEntity.builder()
.name("动态数据源测试"+ ThreadLocalRandom.current().nextInt(1000))
.build());
}
}
然后在接口里调用,Ok了。