深入探讨Druid动态数据源的实现方式
作者:vker
Druid是一个高性能的实时分析数据库,它可以处理大规模数据集的快速查询和聚合操作,在Druid中,动态数据源是一种可以在运行时动态添加和删除的数据源,使用动态数据源,您可以在Druid中轻松地处理不断变化的数据集,本文讲给大家介绍一下Druid动态数据源该如何实现
一、应用场景
- 主从复制
- 读写分离
- 分库分表
主从复制与读写分离通常是一起使用的。
二、实现方式
1、技术栈
- SpringBoot 2.6.13
- Druid 1.2.16
- Spring JDBC
- MySQL 8.0
- AOP动态代理
2、Maven 依赖
下边依赖放入同一个 pom.xml
文件就可以
<properties> <java.version>11</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.6.13</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.16</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
3、数据库结构
数据库结构非常简单,主要实现思想,复杂的都是同一个操作方式。
(1)数据库:db_dynamic1
DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` bigint NOT NULL AUTO_INCREMENT, `username` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; BEGIN; INSERT INTO `tb_user` (`id`, `username`) VALUES (1, '张三'); INSERT INTO `tb_user` (`id`, `username`) VALUES (2, '李四'); INSERT INTO `tb_user` (`id`, `username`) VALUES (3, '王五'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;
(2)数据库:db_dynamic2
DROP TABLE IF EXISTS `tb_user`; CREATE TABLE `tb_user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; BEGIN; INSERT INTO `tb_user` (`id`, `username`) VALUES (1, 'Vker'); INSERT INTO `tb_user` (`id`, `username`) VALUES (2, 'Jack'); INSERT INTO `tb_user` (`id`, `username`) VALUES (3, 'Lucy'); COMMIT; SET FOREIGN_KEY_CHECKS = 1;
4、application.yml 配置文件
将下面配置文件的 url
, username
, password
换成你自己的
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: master: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/db_dynamic1?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false username: root password: root_root slave: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/db_dynamic2?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF8&useSSL=false username: root password: root_root initial-size: 1 min-idle: 1 max-active: 20 test-on-borrow: true
5、定义一个 DataSources 注解
这个注解主要用于多数据源的选择,默认是 master
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface DataSources { String value() default "master"; }
6、配置一下动态数据源
这个类主要用于配置多数据源,不要忘记 @Primary
注解,否则会报错。
@Component @Primary public class DynamicDataSources extends AbstractRoutingDataSource { public static ThreadLocal<String> name = new ThreadLocal<>(); @Resource DataSource master; @Resource DataSource slave; @Override protected Object determineCurrentLookupKey() { return name.get(); } @Override public void afterPropertiesSet() { Map<Object, Object> ds = new HashMap<>(); ds.put("master", master); ds.put("slave", slave); super.setTargetDataSources(ds); super.setDefaultTargetDataSource(master); super.afterPropertiesSet(); } }
7、写一个基于 DataSources 注解的动态代理
这个动态代理用于检查类或者方法上的 @DataSources
注解,取到注解中的 value
值,将值传入动态数据源配置类 DynamicDataSources
的 ThreadLocal
中,用于选择数据源。
@Component @Aspect public class DynamicDataSourcesAspect { Logger logger = LoggerFactory.getLogger(DynamicDataSourcesAspect.class); @Before("@annotation(dataSources)") public void before(DataSources dataSources) { String value = dataSources.value(); DynamicDataSources.name.set(value); logger.info("进入AOP代理: {}", value); } }
8、再来写一个读数据库配置信息的配置类
这个配置类主要将 application.yml
中的数据库配置信息的配置信息进行加载,然后使用数据源事务管理器 DataSourceTransactionManager
进行注册。
@Configuration public class DynamicDataSourcesConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.druid.master") public DataSource master() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.druid.slave") public DataSource slave() { return DruidDataSourceBuilder.create().build(); } @Bean public DataSourceTransactionManager dataSourceTransactionManager1(@Qualifier("master") DataSource master) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(master); return dataSourceTransactionManager; } @Bean public DataSourceTransactionManager dataSourceTransactionManager2(@Qualifier("slave") DataSource slave) { DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(slave); return dataSourceTransactionManager; } }
重点的都做完了,接下来就写一下基本的业务层代码吧,注释就不写了,我相信小伙伴们都能看懂。
1、实体类
public class User { private Long id; private String username; // 省略了 getter setter }
2、Dao 数据层
public interface UserMapper { List<User> selectAllUser(); }
@Repository public class UserMapperImpl implements UserMapper { @Resource private JdbcTemplate jdbcTemplate; @Override public List<User> selectAllUser() { String sql = "select * from tb_user"; return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class)); } }
3、Service 服务层
public interface UserService { List<User> list(); }
@Service public class UserServiceImpl implements UserService { @Resource private UserMapper userMapper; @Override public List<User> list() { return userMapper.selectAllUser(); } }
4、Controller 控制层
这里说一下,你可以将注解 @DataSources
注解放到你想使用不同数据源的方法上,如下,默认 master
可以不写。
@RestController public class UserController { @Resource private UserService userService; @GetMapping("list1") public List<User> list1() { return userService.list(); } @GetMapping("list2") @DataSources("slave") public List<User> lis2t() { return userService.list(); } }
5、最后来看一下启动类
需要把 DataSourceAutoConfiguration
排除掉哦
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class DynamicDatasourceDemoApplication { public static void main(String[] args) { SpringApplication.run(DynamicDatasourceDemoApplication.class, args); } }
6、测试结果
list1接口
list2接口
好了,以上就是动态数据源的实现方式了,欢迎小伙伴们留言,下期我们看看,如何实现 主从复制
和 读写分离
。
以上就是深入探讨Druid动态数据源的实现方式的详细内容,更多关于Druid动态数据源的资料请关注脚本之家其它相关文章!