【Kotlin + Spring Boot 4 从零到架构师】第 50 篇:多数据源集成
本系列定位:零基础入门,从 Kotlin 语法一路到 Spring Boot 4 高级架构(DDD + Modulith),适合 Java 开发者转型,也适合纯新手系统学习。
本篇你将学到
- 多数据源的应用场景
- 多数据源配置策略
@Primary/@Qualifier的使用- 主库(PostgreSQL)+ 分析库(ClickHouse)的双数据源实战
学完本篇,你将能让 mini-shop 同时连接多个数据库,各司其职。
一、为什么需要多数据源
1.1 典型场景
| 场景 | 数据源 A | 数据源 B |
|---|---|---|
| 读写分离 | 主库(写) | 从库(读) |
| 业务 + 分析 | PostgreSQL(业务) | ClickHouse(统计分析) |
| 多租户 | 主租户库 | 独立租户库 |
| 迁移过渡 | 旧数据库 | 新数据库 |
1.2 mini-shop 场景
PostgreSQL(主库):商品、订单、用户 —— 高频读写 ClickHouse(分析库):订单统计、销量报表 —— 低频大量查询下面是 mini-shop 多数据源架构总览:
二、配置两个数据源
2.1 添加依赖
dependencies{implementation("org.springframework.boot:spring-boot-starter-data-jpa")runtimeOnly("org.postgresql:postgresql")runtimeOnly("com.clickhouse:clickhouse-jdbc:0.7.0")}2.2 application.yml
spring:datasource:# 主数据源(PostgreSQL)—— 业务数据url:jdbc:postgresql://localhost:5432/mini_shopusername:postgrespassword:postgres123driver-class-name:org.postgresql.Driverhikari:pool-name:PrimaryPool# 自定义分析数据源配置analytics:datasource:url:jdbc:clickhouse://localhost:8123/mini_shop_analyticsusername:defaultpassword:""driver-class-name:com.clickhouse.jdbc.ClickHouseDriverhikari:pool-name:AnalyticsPoolmaximum-pool-size:52.3 主数据源配置
packagecom.example.minishop.configimportorg.springframework.boot.autoconfigure.jdbc.DataSourcePropertiesimportorg.springframework.boot.context.properties.ConfigurationPropertiesimportorg.springframework.boot.jdbc.autoconfigure.DataSourcePropertiesasBootDataSourcePropertiesimportorg.springframework.context.annotation.Beanimportorg.springframework.context.annotation.Configurationimportorg.springframework.context.annotation.Primaryimportorg.springframework.data.jpa.repository.config.EnableJpaRepositoriesimportjavax.sql.DataSource@Configuration@EnableJpaRepositories(basePackages=["com.example.minishop.product","com.example.minishop.order","com.example.minishop.user"],entityManagerFactoryRef="primaryEntityManagerFactory",transactionManagerRef="primaryTransactionManager")classPrimaryDataSourceConfig{@Bean@Primary@ConfigurationProperties("spring.datasource")funprimaryDataSourceProperties():DataSourceProperties{returnDataSourceProperties()}@Bean@PrimaryfunprimaryDataSource():DataSource{returnprimaryDataSourceProperties().initializeDataSourceBuilder().build()}}2.4 分析数据源配置
packagecom.example.minishop.configimportorg.springframework.boot.context.properties.ConfigurationPropertiesimportorg.springframework.context.annotation.Beanimportorg.springframework.context.annotation.Configurationimportorg.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplateimportjavax.sql.DataSource@ConfigurationclassAnalyticsDataSourceConfig{@Bean@ConfigurationProperties("spring.analytics.datasource")funanalyticsDataSourceProperties():DataSourceProperties{returnDataSourceProperties()}@Bean("analyticsDataSource")funanalyticsDataSource():DataSource{returnanalyticsDataSourceProperties().initializeDataSourceBuilder().build()}@Bean("analyticsJdbcTemplate")funanalyticsJdbcTemplate(@Qualifier("analyticsDataSource")dataSource:DataSource):NamedParameterJdbcTemplate{returnNamedParameterJdbcTemplate(dataSource)}}2.5 使用分析数据源
@ServiceclassAnalyticsService(@Qualifier("analyticsJdbcTemplate")privatevalanalyticsJdbc:NamedParameterJdbcTemplate){/** * 查询每日销售统计 */fungetDailySalesStats(startDate:String,endDate:String):List<DailySalesStat>{valsql=""" SELECT toDate(created_at) AS date, count() AS order_count, sum(total_amount) AS total_revenue, avg(total_amount) AS avg_order_value FROM orders WHERE created_at BETWEEN :start AND :end AND status = 'PAID' GROUP BY date ORDER BY date """.trimIndent()valparams=mapOf("start"tostartDate,"end"toendDate)returnanalyticsJdbc.query(sql,params){rs,_->DailySalesStat(date=rs.getString("date"),orderCount=rs.getLong("order_count"),totalRevenue=rs.getBigDecimal("total_revenue"),avgOrderValue=rs.getBigDecimal("avg_order_value"))}}}dataclassDailySalesStat(valdate:String,valorderCount:Long,valtotalRevenue:BigDecimal,valavgOrderValue:BigDecimal)下面是双数据源配置与注入的完整流程:
三、关键注意事项
3.1 @Primary 的作用
当 Spring 容器中有多个DataSourceBean 时,@Primary标记的 Bean 是默认选择。Liquibase、Spring Data JPA 自动配置默认会用@Primary的数据源。
@Bean@Primary// ← 主数据源,Spring Boot 自动配置用这个funprimaryDataSource():DataSource{...}3.2 @Qualifier 精确指定
// 不用 @Primary 时,必须用 @Qualifier 指定@ServiceclassAnalyticsService(@Qualifier("analyticsDataSource")// ← 精确指定分析数据源privatevalanalyticsDataSource:DataSource)3.3 ClickHouse autoCommit 陷阱
ClickHouse 的 JDBC 驱动要求autoCommit = true(与 PostgreSQL 不同):
spring:analytics:datasource:hikari:auto-commit:true# ClickHouse 必须为 true3.4 Liquibase 只在主数据源
spring:liquibase:change-log:classpath:db/changelog/db.changelog-master.yaml# Liquibase 默认用 @Primary 数据源,不需要额外配置四、@Primary vs @Qualifier 决策
| 方式 | 特点 | 场景 |
|---|---|---|
@Primary | 全局默认选择 | 主数据源(90% 用这个) |
@Qualifier("name") | 精确指定 | 分析数据源、特定场景 |
原则:主数据源用
@Primary,其他数据源用@Qualifier显式注入。
下面是数据源选择决策流程图:
本篇小结
| 知识点 | 核心内容 |
|---|---|
| 多数据源 | 业务库 + 分析库 / 读写分离 |
@Primary | 标记主数据源(默认选择) |
@Qualifier | 精确指定注入哪个数据源 |
@ConfigurationProperties | 绑定各数据源配置 |
@EnableJpaRepositories | 指定 Repository 用哪个数据源 |
NamedParameterJdbcTemplate | 非 JPA 的数据源操作 |
| ClickHouse autoCommit | 必须为true |
| Liquibase | 只在主数据源 |
下篇预告
第 51 篇:Kotlin 协程在 Spring Boot 中的应用
Spring Boot 4 支持协程 Controller!下一篇学习
suspend函数在 Web 层、数据访问层的实际应用,以及虚拟线程的对比。
如果本篇内容对你有帮助,欢迎点赞收藏!有任何疑问,欢迎在评论区交流。