当前位置: 代码网 > it编程>编程语言>Java > SpringBoot实现多数据源配置的四种方案

SpringBoot实现多数据源配置的四种方案

2026年07月26日 Java 我要评论
数据库环境:本地 mysql / postgresql / 阿里云 rds mysql方案一:手动配置多 datasource + druid适用场景:固定 2~3 个数据源,按 mapper 包路径

数据库环境:本地 mysql / postgresql / 阿里云 rds mysql

方案一:手动配置多 datasource + druid

适用场景:固定 2~3 个数据源,按 mapper 包路径隔离

1.1 pom.xml

<?xml version="1.0" encoding="utf-8"?>
<project xmlns="http://maven.apache.org/pom/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
         xsi:schemalocation="http://maven.apache.org/pom/4.0.0
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>3.2.5</version>
    </parent>
    <groupid>com.example</groupid>
    <artifactid>multi-ds-manual</artifactid>
    <version>1.0.0</version>
    <properties>
        <java.version>17</java.version>
        <druid.version>1.2.21</druid.version>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupid>org.mybatis.spring.boot</groupid>
            <artifactid>mybatis-spring-boot-starter</artifactid>
            <version>3.0.3</version>
        </dependency>
        <!-- druid(spring boot 3 用这个) -->
        <dependency>
            <groupid>com.alibaba</groupid>
            <artifactid>druid-spring-boot-3-starter</artifactid>
            <version>${druid.version}</version>
        </dependency>
        <!-- mysql 驱动 -->
        <dependency>
            <groupid>com.mysql</groupid>
            <artifactid>mysql-connector-j</artifactid>
            <version>8.3.0</version>
        </dependency>
        <!-- postgresql 驱动 -->
        <dependency>
            <groupid>org.postgresql</groupid>
            <artifactid>postgresql</artifactid>
            <version>42.7.3</version>
        </dependency>
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

1.2 application.yml

server:
  port: 8080
spring:
  datasource:
    type: com.alibaba.druid.pool.druiddatasource
    # ============ 数据源1:本地 mysql ============
    primary:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://localhost:3306/db_primary?useunicode=true&characterencoding=utf-8&usessl=false&servertimezone=asia/shanghai&allowpublickeyretrieval=true
      username: root
      password: root123456
      initial-size: 5
      min-idle: 5
      max-active: 30
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall,slf4j
    # ============ 数据源2:postgresql ============
    secondary:
      driver-class-name: org.postgresql.driver
      url: jdbc:postgresql://localhost:5432/db_secondary?currentschema=public&stringtype=unspecified
      username: postgres
      password: pg123456
      initial-size: 3
      min-idle: 3
      max-active: 20
      max-wait: 60000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall,slf4j
    # ============ 数据源3:阿里云 rds mysql ============
    third:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://rm-bp1xxxxxxxxxxxx.mysql.rds.aliyuncs.com:3306/db_cloud?useunicode=true&characterencoding=utf-8&usessl=true&servertimezone=asia/shanghai
      username: cloud_user
      password: cloud@2024!
      initial-size: 10
      min-idle: 10
      max-active: 50
      max-wait: 30000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      filters: stat,wall,slf4j
      keep-alive: true
      phy-timeout-millis: 1800000
logging:
  level:
    com.example.mapper: debug

1.3 启动类

package com.example;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourceautoconfigure;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;

// 关键:排除 druid 自动配置,否则会和手动配置冲突
@springbootapplication(exclude = {druiddatasourceautoconfigure.class})
public class manualdsapplication {
    public static void main(string[] args) {
        springapplication.run(manualdsapplication.class, args);
    }
}

1.4 数据源配置类

primarydatasourceconfig.java

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import org.apache.ibatis.session.sqlsessionfactory;
import org.mybatis.spring.sqlsessionfactorybean;
import org.mybatis.spring.sqlsessiontemplate;
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 org.springframework.core.io.support.pathmatchingresourcepatternresolver;
import org.springframework.jdbc.datasource.datasourcetransactionmanager;
import org.springframework.transaction.platformtransactionmanager;

import javax.sql.datasource;

@configuration
@mapperscan(
    basepackages = "com.example.mapper.primary",
    sqlsessionfactoryref = "primarysqlsessionfactory",
    sqlsessiontemplateref = "primarysqlsessiontemplate"
)
public class primarydatasourceconfig {

    @primary
    @bean("primarydatasource")
    @configurationproperties(prefix = "spring.datasource.primary")
    public datasource primarydatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @primary
    @bean("primarysqlsessionfactory")
    public sqlsessionfactory primarysqlsessionfactory(
            @qualifier("primarydatasource") datasource ds) throws exception {
        sqlsessionfactorybean bean = new sqlsessionfactorybean();
        bean.setdatasource(ds);
        bean.setmapperlocations(new pathmatchingresourcepatternresolver()
                .getresources("classpath:mapper/primary/*.xml"));
        org.apache.ibatis.session.configuration cfg =
                new org.apache.ibatis.session.configuration();
        cfg.setmapunderscoretocamelcase(true);
        cfg.setlogimpl(org.apache.ibatis.logging.slf4j.slf4jimpl.class);
        bean.setconfiguration(cfg);
        return bean.getobject();
    }

    @primary
    @bean("primarytransactionmanager")
    public platformtransactionmanager primarytxmanager(
            @qualifier("primarydatasource") datasource ds) {
        return new datasourcetransactionmanager(ds);
    }

    @primary
    @bean("primarysqlsessiontemplate")
    public sqlsessiontemplate primarysqlsessiontemplate(
            @qualifier("primarysqlsessionfactory") sqlsessionfactory sf) {
        return new sqlsessiontemplate(sf);
    }
}

secondarydatasourceconfig.java

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import org.apache.ibatis.session.sqlsessionfactory;
import org.mybatis.spring.sqlsessionfactorybean;
import org.mybatis.spring.sqlsessiontemplate;
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.core.io.support.pathmatchingresourcepatternresolver;
import org.springframework.jdbc.datasource.datasourcetransactionmanager;
import org.springframework.transaction.platformtransactionmanager;

import javax.sql.datasource;

@configuration
@mapperscan(
    basepackages = "com.example.mapper.secondary",
    sqlsessionfactoryref = "secondarysqlsessionfactory",
    sqlsessiontemplateref = "secondarysqlsessiontemplate"
)
public class secondarydatasourceconfig {

    @bean("secondarydatasource")
    @configurationproperties(prefix = "spring.datasource.secondary")
    public datasource secondarydatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @bean("secondarysqlsessionfactory")
    public sqlsessionfactory secondarysqlsessionfactory(
            @qualifier("secondarydatasource") datasource ds) throws exception {
        sqlsessionfactorybean bean = new sqlsessionfactorybean();
        bean.setdatasource(ds);
        bean.setmapperlocations(new pathmatchingresourcepatternresolver()
                .getresources("classpath:mapper/secondary/*.xml"));
        org.apache.ibatis.session.configuration cfg =
                new org.apache.ibatis.session.configuration();
        cfg.setmapunderscoretocamelcase(true);
        cfg.setlogimpl(org.apache.ibatis.logging.slf4j.slf4jimpl.class);
        bean.setconfiguration(cfg);
        return bean.getobject();
    }

    @bean("secondarytransactionmanager")
    public platformtransactionmanager secondarytxmanager(
            @qualifier("secondarydatasource") datasource ds) {
        return new datasourcetransactionmanager(ds);
    }

    @bean("secondarysqlsessiontemplate")
    public sqlsessiontemplate secondarysqlsessiontemplate(
            @qualifier("secondarysqlsessionfactory") sqlsessionfactory sf) {
        return new sqlsessiontemplate(sf);
    }
}

thirddatasourceconfig.java

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import org.apache.ibatis.session.sqlsessionfactory;
import org.mybatis.spring.sqlsessionfactorybean;
import org.mybatis.spring.sqlsessiontemplate;
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.core.io.support.pathmatchingresourcepatternresolver;
import org.springframework.jdbc.datasource.datasourcetransactionmanager;
import org.springframework.transaction.platformtransactionmanager;

import javax.sql.datasource;

@configuration
@mapperscan(
    basepackages = "com.example.mapper.third",
    sqlsessionfactoryref = "thirdsqlsessionfactory",
    sqlsessiontemplateref = "thirdsqlsessiontemplate"
)
public class thirddatasourceconfig {

    @bean("thirddatasource")
    @configurationproperties(prefix = "spring.datasource.third")
    public datasource thirddatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @bean("thirdsqlsessionfactory")
    public sqlsessionfactory thirdsqlsessionfactory(
            @qualifier("thirddatasource") datasource ds) throws exception {
        sqlsessionfactorybean bean = new sqlsessionfactorybean();
        bean.setdatasource(ds);
        bean.setmapperlocations(new pathmatchingresourcepatternresolver()
                .getresources("classpath:mapper/third/*.xml"));
        org.apache.ibatis.session.configuration cfg =
                new org.apache.ibatis.session.configuration();
        cfg.setmapunderscoretocamelcase(true);
        cfg.setlogimpl(org.apache.ibatis.logging.slf4j.slf4jimpl.class);
        bean.setconfiguration(cfg);
        return bean.getobject();
    }

    @bean("thirdtransactionmanager")
    public platformtransactionmanager thirdtxmanager(
            @qualifier("thirddatasource") datasource ds) {
        return new datasourcetransactionmanager(ds);
    }

    @bean("thirdsqlsessiontemplate")
    public sqlsessiontemplate thirdsqlsessiontemplate(
            @qualifier("thirdsqlsessionfactory") sqlsessionfactory sf) {
        return new sqlsessiontemplate(sf);
    }
}

1.5 druid 监控配置

package com.example.config;

import com.alibaba.druid.support.http.statviewservlet;
import com.alibaba.druid.support.http.webstatfilter;
import org.springframework.boot.web.servlet.filterregistrationbean;
import org.springframework.boot.web.servlet.servletregistrationbean;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;

import java.util.hashmap;
import java.util.map;

@configuration
public class druidmonitorconfig {

    @bean
    public servletregistrationbean<statviewservlet> statviewservlet() {
        servletregistrationbean<statviewservlet> bean =
                new servletregistrationbean<>(new statviewservlet(), "/druid/*");
        map<string, string> params = new hashmap<>();
        params.put("loginusername", "admin");
        params.put("loginpassword", "admin123");
        params.put("allow", "");
        params.put("resetenable", "false");
        bean.setinitparameters(params);
        return bean;
    }

    @bean
    public filterregistrationbean<webstatfilter> webstatfilter() {
        filterregistrationbean<webstatfilter> bean =
                new filterregistrationbean<>(new webstatfilter());
        bean.addurlpatterns("/*");
        bean.addinitparameter("exclusions", ".js,.gif,.jpg,.png,.css,.ico,/druid/*");
        return bean;
    }
}

1.6 entity / mapper / xml

entity

package com.example.entity;

import lombok.data;
import java.time.localdatetime;

@data
public class user {
    private long id;
    private string username;
    private string email;
    private localdatetime createtime;
}

@data
public class order {
    private long id;
    private long userid;
    private string orderno;
    private java.math.bigdecimal amount;
    private integer status;
    private localdatetime createtime;
}

@data
public class product {
    private long id;
    private string productname;
    private java.math.bigdecimal price;
    private integer stock;
}

mapper 接口

package com.example.mapper.primary;

import com.example.entity.user;
import org.apache.ibatis.annotations.param;
import java.util.list;

public interface usermapper {
    list<user> selectall();
    user selectbyid(@param("id") long id);
    int insert(user user);
}

package com.example.mapper.secondary;

import com.example.entity.order;
import org.apache.ibatis.annotations.param;
import java.util.list;

public interface ordermapper {
    list<order> selectall();
    list<order> selectbyuserid(@param("userid") long userid);
    int insert(order order);
}

package com.example.mapper.third;

import com.example.entity.product;
import org.apache.ibatis.annotations.param;
import java.util.list;

public interface productmapper {
    list<product> selectall();
    product selectbyid(@param("id") long id);
    int insert(product product);
}

mapper xml

resources/mapper/primary/usermapper.xml

<?xml version="1.0" encoding="utf-8"?>
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.primary.usermapper">
    <select id="selectall" resulttype="com.example.entity.user">
        select id, username, email, create_time from t_user
    </select>
    <select id="selectbyid" resulttype="com.example.entity.user">
        select id, username, email, create_time from t_user where id = #{id}
    </select>
    <insert id="insert" usegeneratedkeys="true" keyproperty="id">
        insert into t_user(username, email, create_time)
        values(#{username}, #{email}, now())
    </insert>
</mapper>

resources/mapper/secondary/ordermapper.xml

<?xml version="1.0" encoding="utf-8"?>
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.secondary.ordermapper">
    <select id="selectall" resulttype="com.example.entity.order">
        select id, user_id, order_no, amount, status, create_time from t_order
    </select>
    <select id="selectbyuserid" resulttype="com.example.entity.order">
        select id, user_id, order_no, amount, status, create_time
        from t_order where user_id = #{userid}
    </select>
    <insert id="insert" usegeneratedkeys="true" keyproperty="id">
        insert into t_order(user_id, order_no, amount, status, create_time)
        values(#{userid}, #{orderno}, #{amount}, #{status}, now())
    </insert>
</mapper>

resources/mapper/third/productmapper.xml

<?xml version="1.0" encoding="utf-8"?>
<!doctype mapper public "-//mybatis.org//dtd mapper 3.0//en"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.third.productmapper">
    <select id="selectall" resulttype="com.example.entity.product">
        select id, product_name, price, stock from t_product
    </select>
    <select id="selectbyid" resulttype="com.example.entity.product">
        select id, product_name, price, stock from t_product where id = #{id}
    </select>
    <insert id="insert" usegeneratedkeys="true" keyproperty="id">
        insert into t_product(product_name, price, stock)
        values(#{productname}, #{price}, #{stock})
    </insert>
</mapper>

1.7 service & controller

package com.example.service;

import com.example.entity.;
import com.example.mapper.primary.usermapper;
import com.example.mapper.secondary.ordermapper;
import com.example.mapper.third.productmapper;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;
import java.util.list;

@service
public class businessservice {

    @autowired
    private usermapper usermapper;
    @autowired
    private ordermapper ordermapper;
    @autowired
    private productmapper productmapper;

    public list<user> getusers() { return usermapper.selectall(); }
    public list<order> getorders() { return ordermapper.selectall(); }
    public list<product> getproducts() { return productmapper.selectall(); }

    @transactional(transactionmanager = "primarytransactionmanager")
    public void adduser(user user) { usermapper.insert(user); }

    @transactional(transactionmanager = "secondarytransactionmanager")
    public void addorder(order order) { ordermapper.insert(order); }

    @transactional(transactionmanager = "thirdtransactionmanager")
    public void addproduct(product product) { productmapper.insert(product); }
}
package com.example.controller;

import com.example.service.businessservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.;
import java.util.;

@restcontroller
@requestmapping("/api")
public class testcontroller {

    @autowired
    private businessservice service;

    @getmapping("/test")
    public map<string, object> test() {
        map<string, object> result = new linkedhashmap<>();
        result.put("本地mysql_用户", service.getusers());
        result.put("postgresql_订单", service.getorders());
        result.put("阿里云rds_商品", service.getproducts());
        return result;
    }
}

1.8 建表 sql

-- ========== 本地 mysql: db_primary ==========
create database if not exists db_primary default charset utf8mb4;
use db_primary;
create table t_user (
    id bigint auto_increment primary key,
    username varchar(50) not null,
    email varchar(100),
    create_time datetime default current_timestamp
);
insert into t_user(username, email) values('张三','zhangsan@test.com');
insert into t_user(username, email) values('李四','lisi@test.com');
-- ========== postgresql: db_secondary ==========
-- create database db_secondary;
create table t_order (
    id bigserial primary key,
    user_id bigint not null,
    order_no varchar(64) not null,
    amount numeric(10,2) default 0,
    status int default 0,
    create_time timestamp default current_timestamp
);
insert into t_order(user_id, order_no, amount, status)
values(1, 'ord20240101001', 99.90, 1);
insert into t_order(user_id, order_no, amount, status)
values(2, 'ord20240101002', 199.00, 0);
-- ========== 阿里云 rds mysql: db_cloud ==========
create database if not exists db_cloud default charset utf8mb4;
use db_cloud;
create table t_product (
    id bigint auto_increment primary key,
    product_name varchar(200) not null,
    price decimal(10,2) default 0,
    stock int default 0
);
insert into t_product(product_name, price, stock) values('iphone 15', 7999.00, 100);
insert into t_product(product_name, price, stock) values('macbook pro', 14999.00, 50);

1.9 验证

启动后访问
curl http://localhost:8080/api/test

druid 监控
浏览器打开 http://localhost:8080/druid/
账号: admin 密码: admin123

方案二:abstractroutingdatasource + druid(动态切换)

适用场景:运行时根据请求参数/注解动态切换数据源(多租户)

2.1 pom.xml

与方案一完全相同,不再重复。

2.2 application.yml

server:
  port: 8081
spring:
  datasource:
    type: com.alibaba.druid.pool.druiddatasource
    primary:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://localhost:3306/db_primary?useunicode=true&characterencoding=utf-8&usessl=false&servertimezone=asia/shanghai
      username: root
      password: root123456
      initial-size: 5
      min-idle: 5
      max-active: 30
      max-wait: 60000
      validation-query: select 1
      test-while-idle: true
      filters: stat,wall,slf4j
    secondary:
      driver-class-name: org.postgresql.driver
      url: jdbc:postgresql://localhost:5432/db_secondary?currentschema=public
      username: postgres
      password: pg123456
      initial-size: 3
      min-idle: 3
      max-active: 20
      max-wait: 60000
      validation-query: select 1
      test-while-idle: true
      filters: stat,wall,slf4j
    third:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://rm-bp1xxxxxxxxxxxx.mysql.rds.aliyuncs.com:3306/db_cloud?usessl=true&servertimezone=asia/shanghai
      username: cloud_user
      password: cloud@2024!
      initial-size: 5
      min-idle: 5
      max-active: 40
      max-wait: 30000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: true
      filters: stat,wall,slf4j
      keep-alive: true
logging:
  level:
    com.example: debug

2.3 启动类

@springbootapplication(exclude = {druiddatasourceautoconfigure.class})
public class dynamicdsapplication {
    public static void main(string[] args) {
        springapplication.run(dynamicdsapplication.class, args);
    }
}

2.4 数据源上下文持有者

package com.example.dynamic;

public class datasourcecontextholder {

    public static final string primary = "primary";
    public static final string secondary = "secondary";
    public static final string third = "third";

    private static final threadlocal<string> holder = new threadlocal<>();

    public static void set(string ds) {
        holder.set(ds);
    }

    public static string get() {
        return holder.get();
    }

    public static void clear() {
        holder.remove();
    }
}

2.5 动态路由数据源

package com.example.dynamic;

import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource;

public class dynamicdatasource extends abstractroutingdatasource {

    @override
    protected object determinecurrentlookupkey() {
        return datasourcecontextholder.get();
    }
}

2.6 数据源配置类

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import com.example.dynamic.datasourcecontextholder;
import com.example.dynamic.dynamicdatasource;
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
public class dynamicdatasourceconfig {

    @bean("primarydatasource")
    @configurationproperties(prefix = "spring.datasource.primary")
    public datasource primarydatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @bean("secondarydatasource")
    @configurationproperties(prefix = "spring.datasource.secondary")
    public datasource secondarydatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @bean("thirddatasource")
    @configurationproperties(prefix = "spring.datasource.third")
    public datasource thirddatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @primary
    @bean("dynamicdatasource")
    public datasource dynamicdatasource(
            @qualifier("primarydatasource") datasource primary,
            @qualifier("secondarydatasource") datasource secondary,
            @qualifier("thirddatasource") datasource third) {

        dynamicdatasource dynamicds = new dynamicdatasource();

        map<object, object> targetmap = new hashmap<>();
        targetmap.put(datasourcecontextholder.primary, primary);
        targetmap.put(datasourcecontextholder.secondary, secondary);
        targetmap.put(datasourcecontextholder.third, third);

        dynamicds.settargetdatasources(targetmap);
        dynamicds.setdefaulttargetdatasource(primary);  // 默认走 primary

        return dynamicds;
    }
}

2.7 mybatis 配置(只需一个 sqlsessionfactory)

package com.example.config;

import org.apache.ibatis.session.sqlsessionfactory;
import org.mybatis.spring.sqlsessionfactorybean;
import org.mybatis.spring.annotation.mapperscan;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.core.io.support.pathmatchingresourcepatternresolver;
import org.springframework.jdbc.datasource.datasourcetransactionmanager;
import org.springframework.transaction.platformtransactionmanager;

import javax.sql.datasource;

@configuration
@mapperscan(basepackages = "com.example.mapper")
public class mybatisconfig {

    @bean
    public sqlsessionfactory sqlsessionfactory(
            @qualifier("dynamicdatasource") datasource ds) throws exception {
        sqlsessionfactorybean bean = new sqlsessionfactorybean();
        bean.setdatasource(ds);
        bean.setmapperlocations(new pathmatchingresourcepatternresolver()
                .getresources("classpath:mapper/*/*.xml"));
        org.apache.ibatis.session.configuration cfg =
                new org.apache.ibatis.session.configuration();
        cfg.setmapunderscoretocamelcase(true);
        cfg.setlogimpl(org.apache.ibatis.logging.slf4j.slf4jimpl.class);
        bean.setconfiguration(cfg);
        return bean.getobject();
    }

    @bean
    public platformtransactionmanager transactionmanager(
            @qualifier("dynamicdatasource") datasource ds) {
        return new datasourcetransactionmanager(ds);
    }
}

2.8 自定义注解 @ds

package com.example.dynamic;

import java.lang.annotation.;

@target({elementtype.method, elementtype.type})
@retention(retentionpolicy.runtime)
@documented
public @interface ds {
    string value() default datasourcecontextholder.primary;
}

2.9 aop 切面

package com.example.dynamic;

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.springframework.core.annotation.order;
import org.springframework.stereotype.component;

import java.lang.reflect.method;

@aspect
@component
@order(-1)  // 必须在 @transactional 之前执行
public class datasourceaspect {

    @around("@annotation(com.example.dynamic.ds) || @within(com.example.dynamic.ds)")
    public object around(proceedingjoinpoint point) throws throwable {
        string dskey = determinedatasource(point);
        try {
            datasourcecontextholder.set(dskey);
            return point.proceed();
        } finally {
            datasourcecontextholder.clear();
        }
    }

    private string determinedatasource(proceedingjoinpoint point) {
        methodsignature signature = (methodsignature) point.getsignature();
        method method = signature.getmethod();

        // 优先取方法上的注解
        ds ds = method.getannotation(ds.class);
        if (ds != null) {
            return ds.value();
        }
        // 再取类上的注解
        ds = point.gettarget().getclass().getannotation(ds.class);
        if (ds != null) {
            return ds.value();
        }
        return datasourcecontextholder.primary;
    }
}

2.10 mapper(所有放同一个包)

package com.example.mapper;

import com.example.entity.user;
import java.util.list;

public interface usermapper {
    list<user> selectall();
}

package com.example.mapper;

import com.example.entity.order;
import java.util.list;

public interface ordermapper {
    list<order> selectall();
}

package com.example.mapper;

import com.example.entity.product;
import java.util.list;

public interface productmapper {
    list<product> selectall();
}

2.11 service 使用

package com.example.service;

import com.example.dynamic.ds;
import com.example.dynamic.datasourcecontextholder;
import com.example.entity.;
import com.example.mapper.;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;

import java.util.list;

@service
public class dynamicservice {

    @autowired
    private usermapper usermapper;
    @autowired
    private ordermapper ordermapper;
    @autowired
    private productmapper productmapper;

    // 方式1:注解切换
    @ds(datasourcecontextholder.primary)
    public list<user> getusers() {
        return usermapper.selectall();
    }

    @ds(datasourcecontextholder.secondary)
    public list<order> getorders() {
        return ordermapper.selectall();
    }

    @ds(datasourcecontextholder.third)
    public list<product> getproducts() {
        return productmapper.selectall();
    }

    // 方式2:手动切换(适合复杂逻辑)
    public object querybysource(string source) {
        try {
            datasourcecontextholder.set(source);
            if ("primary".equals(source)) return usermapper.selectall();
            if ("secondary".equals(source)) return ordermapper.selectall();
            if ("third".equals(source)) return productmapper.selectall();
            return "未知数据源";
        } finally {
            datasourcecontextholder.clear();
        }
    }
}

2.12 controller

package com.example.controller;

import com.example.service.dynamicservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.;
import java.util.;

@restcontroller
@requestmapping("/api")
public class dynamiccontroller {

    @autowired
    private dynamicservice service;

    @getmapping("/all")
    public map<string, object> all() {
        map<string, object> map = new linkedhashmap<>();
        map.put("primary_用户", service.getusers());
        map.put("secondary_订单", service.getorders());
        map.put("third_商品", service.getproducts());
        return map;
    }

    @getmapping("/switch")
    public object switchds(@requestparam string source) {
        return service.querybysource(source);
    }
}

2.13 验证

curl http://localhost:8081/api/all
curl http://localhost:8081/api/switch?source=primary
curl http://localhost:8081/api/switch?source=secondary
curl http://localhost:8081/api/switch?source=third

方案三:dynamic-datasource(苞米豆)+ druid

适用场景:快速集成,注解驱动,最省心

3.1 pom.xml

<?xml version="1.0" encoding="utf-8"?>
<project>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>3.2.5</version>
    </parent>
    <groupid>com.example</groupid>
    <artifactid>multi-ds-dynamic</artifactid>
    <version>1.0.0</version>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!-- mybatis-plus(dynamic-datasource 最佳搭档) -->
        <dependency>
            <groupid>com.baomidou</groupid>
            <artifactid>mybatis-plus-spring-boot3-starter</artifactid>
            <version>3.5.6</version>
        </dependency>
        <!-- 多数据源核心 -->
        <dependency>
            <groupid>com.baomidou</groupid>
            <artifactid>dynamic-datasource-spring-boot3-starter</artifactid>
            <version>4.3.0</version>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupid>com.alibaba</groupid>
            <artifactid>druid-spring-boot-3-starter</artifactid>
            <version>1.2.21</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupid>com.mysql</groupid>
            <artifactid>mysql-connector-j</artifactid>
            <version>8.3.0</version>
        </dependency>
        <!-- postgresql -->
        <dependency>
            <groupid>org.postgresql</groupid>
            <artifactid>postgresql</artifactid>
            <version>42.7.3</version>
        </dependency>
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

3.2 application.yml

server:
  port: 8082

spring:
  datasource:
    dynamic:
      # 默认数据源
      primary: master
      # 严格模式(未匹配到数据源直接报错)
      strict: false
      # 懒加载(按需初始化数据源)
      lazy: true

      # ========== 全局 druid 配置 ==========
      druid:
        initial-size: 5
        min-idle: 5
        max-active: 20
        max-wait: 60000
        time-between-eviction-runs-millis: 60000
        min-evictable-idle-time-millis: 300000
        validation-query: select 1
        test-while-idle: true
        test-on-borrow: false
        test-on-return: false
        pool-prepared-statements: true
        max-pool-prepared-statement-per-connection-size: 20
        filters: stat,wall,slf4j
        # 监控
        stat-view-servlet:
          enabled: true
          url-pattern: /druid/
          login-username: admin
          login-password: admin123
          allow: ""
        web-stat-filter:
          enabled: true
          url-pattern: /*
          exclusions: ".js,.gif,.jpg,.png,.css,.ico,/druid/*"
        filter:
          stat:
            enabled: true
            log-slow-sql: true
            slow-sql-millis: 2000
            merge-sql: true
          wall:
            enabled: true
            config:
              multi-statement-allow: true

      # ========== 数据源定义 ==========
      datasource:
        # 本地 mysql
        master:
          driver-class-name: com.mysql.cj.jdbc.driver
          url: jdbc:mysql://localhost:3306/db_primary?useunicode=true&characterencoding=utf-8&usessl=false&servertimezone=asia/shanghai
          username: root
          password: root123456
          druid:
            initial-size: 10
            max-active: 50

        # postgresql
        postgres:
          driver-class-name: org.postgresql.driver
          url: jdbc:postgresql://localhost:5432/db_secondary?currentschema=public
          username: postgres
          password: pg123456
          druid:
            initial-size: 3
            max-active: 15

        # 阿里云 rds mysql
        aliyun:
          driver-class-name: com.mysql.cj.jdbc.driver
          url: jdbc:mysql://rm-bp1xxxxxxxxxxxx.mysql.rds.aliyuncs.com:3306/db_cloud?usessl=true&servertimezone=asia/shanghai
          username: cloud_user
          password: cloud@2024!
          druid:
            initial-size: 10
            max-active: 50
            test-on-borrow: true
            keep-alive: true

mybatis-plus
mybatis-plus:
  mapper-locations: classpath:mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.stdoutimpl

logging:
  level:
    com.baomidou.dynamic: debug

3.3 启动类

package com.example;

import org.mybatis.spring.annotation.mapperscan;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;

@springbootapplication
@mapperscan("com.example.mapper")
public class dynamicdsapplication {
    public static void main(string[] args) {
        springapplication.run(dynamicdsapplication.class, args);
    }
}

3.4 entity(mybatis-plus 风格)

package com.example.entity;

import com.baomidou.mybatisplus.annotation.;
import lombok.data;
import java.time.localdatetime;

@data
@tablename("t_user")
public class user {
    @tableid(type = idtype.auto)
    private long id;
    private string username;
    private string email;
    @tablefield(fill = fieldfill.insert)
    private localdatetime createtime;
}

@data
@tablename("t_order")
public class order {
    @tableid(type = idtype.auto)
    private long id;
    private long userid;
    private string orderno;
    private java.math.bigdecimal amount;
    private integer status;
    private localdatetime createtime;
}

@data
@tablename("t_product")
public class product {
    @tableid(type = idtype.auto)
    private long id;
    private string productname;
    private java.math.bigdecimal price;
    private integer stock;
}

3.5 mapper

package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.basemapper;
import com.example.entity.user;

public interface usermapper extends basemapper<user> {
}

package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.basemapper;
import com.example.entity.order;

public interface ordermapper extends basemapper<order> {
}

package com.example.mapper;

import com.baomidou.mybatisplus.core.mapper.basemapper;
import com.example.entity.product;

public interface productmapper extends basemapper<product> {
}

3.6 service(核心:@ds 注解)

package com.example.service;

import com.baomidou.dynamic.datasource.annotation.ds;
import com.baomidou.mybatisplus.extension.service.impl.serviceimpl;
import com.example.entity.;
import com.example.mapper.;
import org.springframework.stereotype.service;

import java.util.list;

// ========== 用户服务 → master(本地mysql) ==========
@service
@ds("master")
public class userservice extends serviceimpl<usermapper, user> {

    public list<user> listall() {
        return this.list();
    }
}

// ========== 订单服务 → postgres ==========
@service
@ds("postgres")
public class orderservice extends serviceimpl<ordermapper, order> {

    public list<order> listall() {
        return this.list();
    }

    public list<order> listbyuserid(long userid) {
        return this.lambdaquery().eq(order::getuserid, userid).list();
    }
}

// ========== 商品服务 → aliyun ==========
@service
@ds("aliyun")
public class productservice extends serviceimpl<productmapper, product> {

    public list<product> listall() {
        return this.list();
    }
}

方法级别切换(更细粒度)

package com.example.service;

import com.baomidou.dynamic.datasource.annotation.ds;
import com.example.entity.user;
import com.example.mapper.usermapper;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import java.util.list;

@service
public class mixedservice {

    @autowired
    private usermapper usermapper;

    // 默认走 master
    public list<user> getusers() {
        return usermapper.selectlist(null);
    }

    // 方法级别切换到 postgres
    @ds("postgres")
    public list<user> getusersfrompostgres() {
        return usermapper.selectlist(null);
    }

    // 方法级别切换到 aliyun
    @ds("aliyun")
    public list<user> getusersfromaliyun() {
        return usermapper.selectlist(null);
    }
}

3.7 controller

package com.example.controller;

import com.example.service.;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.;
import java.util.;

@restcontroller
@requestmapping("/api")
public class testcontroller {

    @autowired
    private userservice userservice;
    @autowired
    private orderservice orderservice;
    @autowired
    private productservice productservice;

    @getmapping("/all")
    public map<string, object> all() {
        map<string, object> map = new linkedhashmap<>();
        map.put("master_本地mysql", userservice.listall());
        map.put("postgres_pg", orderservice.listall());
        map.put("aliyun_阿里云", productservice.listall());
        return map;
    }

    @postmapping("/user")
    public boolean adduser(@requestbody com.example.entity.user user) {
        return userservice.save(user);
    }

    @getmapping("/orders/{userid}")
    public object orders(@pathvariable long userid) {
        return orderservice.listbyuserid(userid);
    }
}

3.8 验证

curl http://localhost:8082/api/all
curl -x post http://localhost:8082/api/user
-h “content-type: application/json”
-d ‘{“username”:“王五”,“email”:“wangwu@test.com”}’

druid 监控
http://localhost:8082/druid/

方案四:jpa 多数据源 + druid

适用场景:使用 jpa/hibernate 的项目

4.1 pom.xml

<?xml version="1.0" encoding="utf-8"?>
<project>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>3.2.5</version>
    </parent>
    <groupid>com.example</groupid>
    <artifactid>multi-ds-jpa</artifactid>
    <version>1.0.0</version>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!-- jpa -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-data-jpa</artifactid>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupid>com.alibaba</groupid>
            <artifactid>druid-spring-boot-3-starter</artifactid>
            <version>1.2.21</version>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupid>com.mysql</groupid>
            <artifactid>mysql-connector-j</artifactid>
            <version>8.3.0</version>
        </dependency>
        <!-- postgresql -->
        <dependency>
            <groupid>org.postgresql</groupid>
            <artifactid>postgresql</artifactid>
            <version>42.7.3</version>
        </dependency>
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
    </dependencies>
</project>

4.2 application.yml

server:
  port: 8083
spring:
  datasource:
    type: com.alibaba.druid.pool.druiddatasource
    # 本地 mysql
    primary:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://localhost:3306/db_primary?usessl=false&servertimezone=asia/shanghai
      username: root
      password: root123456
      initial-size: 5
      min-idle: 5
      max-active: 30
      max-wait: 60000
      validation-query: select 1
      test-while-idle: true
      filters: stat,wall,slf4j
    # postgresql
    secondary:
      driver-class-name: org.postgresql.driver
      url: jdbc:postgresql://localhost:5432/db_secondary
      username: postgres
      password: pg123456
      initial-size: 3
      min-idle: 3
      max-active: 20
      max-wait: 60000
      validation-query: select 1
      test-while-idle: true
      filters: stat,wall,slf4j
    # 阿里云 rds
    third:
      driver-class-name: com.mysql.cj.jdbc.driver
      url: jdbc:mysql://rm-bp1xxxxxxxxxxxx.mysql.rds.aliyuncs.com:3306/db_cloud?usessl=true&servertimezone=asia/shanghai
      username: cloud_user
      password: cloud@2024!
      initial-size: 5
      min-idle: 5
      max-active: 40
      max-wait: 30000
      validation-query: select 1
      test-while-idle: true
      test-on-borrow: true
      filters: stat,wall,slf4j
      keep-alive: true
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true
logging:
  level:
    org.hibernate.sql: debug

4.3 启动类

@springbootapplication(exclude = {druiddatasourceautoconfigure.class})
public class jpamultidsapplication {
    public static void main(string[] args) {
        springapplication.run(jpamultidsapplication.class, args);
    }
}

4.4 jpa 数据源配置

primaryjpaconfig.java(本地 mysql)

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import jakarta.persistence.entitymanagerfactory;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.boot.orm.jpa.entitymanagerfactorybuilder;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.context.annotation.primary;
import org.springframework.data.jpa.repository.config.enablejparepositories;
import org.springframework.orm.jpa.jpatransactionmanager;
import org.springframework.orm.jpa.localcontainerentitymanagerfactorybean;
import org.springframework.transaction.platformtransactionmanager;
import org.springframework.transaction.annotation.enabletransactionmanagement;

import javax.sql.datasource;
import java.util.hashmap;
import java.util.map;

@configuration
@enabletransactionmanagement
@enablejparepositories(
    basepackages = "com.example.repository.primary",
    entitymanagerfactoryref = "primaryentitymanagerfactory",
    transactionmanagerref = "primarytransactionmanager"
)
public class primaryjpaconfig {

    @primary
    @bean("primarydatasource")
    @configurationproperties(prefix = "spring.datasource.primary")
    public datasource primarydatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @primary
    @bean("primaryentitymanagerfactory")
    public localcontainerentitymanagerfactorybean primaryentitymanagerfactory(
            entitymanagerfactorybuilder builder,
            @qualifier("primarydatasource") datasource ds) {

        map<string, object> properties = new hashmap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.mysqldialect");
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.show_sql", "true");
        properties.put("hibernate.format_sql", "true");

        return builder
                .datasource(ds)
                .packages("com.example.entity.primary")  // entity 扫描包
                .persistenceunit("primary")
                .properties(properties)
                .build();
    }

    @primary
    @bean("primarytransactionmanager")
    public platformtransactionmanager primarytransactionmanager(
            @qualifier("primaryentitymanagerfactory") entitymanagerfactory emf) {
        return new jpatransactionmanager(emf);
    }
}

secondaryjpaconfig.java(postgresql)

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import jakarta.persistence.entitymanagerfactory;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.boot.orm.jpa.entitymanagerfactorybuilder;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.jpa.repository.config.enablejparepositories;
import org.springframework.orm.jpa.jpatransactionmanager;
import org.springframework.orm.jpa.localcontainerentitymanagerfactorybean;
import org.springframework.transaction.platformtransactionmanager;
import org.springframework.transaction.annotation.enabletransactionmanagement;

import javax.sql.datasource;
import java.util.hashmap;
import java.util.map;

@configuration
@enabletransactionmanagement
@enablejparepositories(
    basepackages = "com.example.repository.secondary",
    entitymanagerfactoryref = "secondaryentitymanagerfactory",
    transactionmanagerref = "secondarytransactionmanager"
)
public class secondaryjpaconfig {

    @bean("secondarydatasource")
    @configurationproperties(prefix = "spring.datasource.secondary")
    public datasource secondarydatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @bean("secondaryentitymanagerfactory")
    public localcontainerentitymanagerfactorybean secondaryentitymanagerfactory(
            entitymanagerfactorybuilder builder,
            @qualifier("secondarydatasource") datasource ds) {

        map<string, object> properties = new hashmap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.postgresqldialect");
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.show_sql", "true");

        return builder
                .datasource(ds)
                .packages("com.example.entity.secondary")
                .persistenceunit("secondary")
                .properties(properties)
                .build();
    }

    @bean("secondarytransactionmanager")
    public platformtransactionmanager secondarytransactionmanager(
            @qualifier("secondaryentitymanagerfactory") entitymanagerfactory emf) {
        return new jpatransactionmanager(emf);
    }
}

thirdjpaconfig.java(阿里云 rds)

package com.example.config;

import com.alibaba.druid.spring.boot3.autoconfigure.druiddatasourcebuilder;
import jakarta.persistence.entitymanagerfactory;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.boot.orm.jpa.entitymanagerfactorybuilder;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.jpa.repository.config.enablejparepositories;
import org.springframework.orm.jpa.jpatransactionmanager;
import org.springframework.orm.jpa.localcontainerentitymanagerfactorybean;
import org.springframework.transaction.platformtransactionmanager;
import org.springframework.transaction.annotation.enabletransactionmanagement;

import javax.sql.datasource;
import java.util.hashmap;
import java.util.map;

@configuration
@enabletransactionmanagement
@enablejparepositories(
    basepackages = "com.example.repository.third",
    entitymanagerfactoryref = "thirdentitymanagerfactory",
    transactionmanagerref = "thirdtransactionmanager"
)
public class thirdjpaconfig {

    @bean("thirddatasource")
    @configurationproperties(prefix = "spring.datasource.third")
    public datasource thirddatasource() {
        return druiddatasourcebuilder.create().build();
    }

    @bean("thirdentitymanagerfactory")
    public localcontainerentitymanagerfactorybean thirdentitymanagerfactory(
            entitymanagerfactorybuilder builder,
            @qualifier("thirddatasource") datasource ds) {

        map<string, object> properties = new hashmap<>();
        properties.put("hibernate.dialect", "org.hibernate.dialect.mysqldialect");
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.show_sql", "true");

        return builder
                .datasource(ds)
                .packages("com.example.entity.third")
                .persistenceunit("third")
                .properties(properties)
                .build();
    }

    @bean("thirdtransactionmanager")
    public platformtransactionmanager thirdtransactionmanager(
            @qualifier("thirdentitymanagerfactory") entitymanagerfactory emf) {
        return new jpatransactionmanager(emf);
    }
}

4.5 entity(按包隔离)

com.example.entity.primary.user

package com.example.entity.primary;

import jakarta.persistence.;
import lombok.data;
import java.time.localdatetime;

@data
@entity
@table(name = "t_user")
public class user {
    @id
    @generatedvalue(strategy = generationtype.identity)
    private long id;

    @column(nullable = false, length = 50)
    private string username;

    @column(length = 100)
    private string email;

    @column(name = "create_time")
    private localdatetime createtime;

    @prepersist
    public void prepersist() {
        this.createtime = localdatetime.now();
    }
}

com.example.entity.secondary.order

package com.example.entity.secondary;

import jakarta.persistence.;
import lombok.data;
import java.math.bigdecimal;
import java.time.localdatetime;

@data
@entity
@table(name = "t_order")
public class order {
    @id
    @generatedvalue(strategy = generationtype.identity)
    private long id;

    @column(name = "user_id", nullable = false)
    private long userid;

    @column(name = "order_no", nullable = false, length = 64)
    private string orderno;

    @column(precision = 10, scale = 2)
    private bigdecimal amount;

    private integer status;

    @column(name = "create_time")
    private localdatetime createtime;

    @prepersist
    public void prepersist() {
        this.createtime = localdatetime.now();
    }
}

com.example.entity.third.product

package com.example.entity.third;

import jakarta.persistence.;
import lombok.data;
import java.math.bigdecimal;

@data
@entity
@table(name = "t_product")
public class product {
    @id
    @generatedvalue(strategy = generationtype.identity)
    private long id;

    @column(name = "product_name", nullable = false, length = 200)
    private string productname;

    @column(precision = 10, scale = 2)
    private bigdecimal price;

    private integer stock;
}

4.6 repository(按包隔离)

com.example.repository.primary.userrepository

package com.example.repository.primary;

import com.example.entity.primary.user;
import org.springframework.data.jpa.repository.jparepository;
import org.springframework.stereotype.repository;
import java.util.list;

@repository
public interface userrepository extends jparepository<user, long> {
    list<user> findbyusernamecontaining(string keyword);
}

com.example.repository.secondary.orderrepository

package com.example.repository.secondary;

import com.example.entity.secondary.order;
import org.springframework.data.jpa.repository.jparepository;
import org.springframework.stereotype.repository;
import java.util.list;

@repository
public interface orderrepository extends jparepository<order, long> {
    list<order> findbyuserid(long userid);
    list<order> findbystatus(integer status);
}

com.example.repository.third.productrepository

package com.example.repository.third;

import com.example.entity.third.product;
import org.springframework.data.jpa.repository.jparepository;
import org.springframework.data.jpa.repository.modifying;
import org.springframework.data.jpa.repository.query;
import org.springframework.data.repository.query.param;
import org.springframework.stereotype.repository;

@repository
public interface productrepository extends jparepository<product, long> {

    @modifying
    @query("update product p set p.stock = :stock where p.id = :id")
    int updatestock(@param("id") long id, @param("stock") integer stock);
}

4.7 service

package com.example.service;

import com.example.entity.primary.user;
import com.example.entity.secondary.order;
import com.example.entity.third.product;
import com.example.repository.primary.userrepository;
import com.example.repository.secondary.orderrepository;
import com.example.repository.third.productrepository;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;

import java.util.list;

@service
public class jpabusinessservice {

    @autowired
    private userrepository userrepository;
    @autowired
    private orderrepository orderrepository;
    @autowired
    private productrepository productrepository;

    // ===== 本地 mysql =====
    public list<user> getallusers() {
        return userrepository.findall();
    }

    @transactional(transactionmanager = "primarytransactionmanager")
    public user saveuser(user user) {
        return userrepository.save(user);
    }

    // ===== postgresql =====
    public list<order> getordersbyuserid(long userid) {
        return orderrepository.findbyuserid(userid);
    }

    @transactional(transactionmanager = "secondarytransactionmanager")
    public order saveorder(order order) {
        return orderrepository.save(order);
    }

    // ===== 阿里云 rds =====
    public list<product> getallproducts() {
        return productrepository.findall();
    }

    @transactional(transactionmanager = "thirdtransactionmanager")
    public void updatestock(long productid, integer stock) {
        productrepository.updatestock(productid, stock);
    }
}

4.8 controller

package com.example.controller;

import com.example.entity.primary.user;
import com.example.service.jpabusinessservice;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.;
import java.util.*;

@restcontroller
@requestmapping("/api")
public class jpatestcontroller {

    @autowired
    private jpabusinessservice service;

    @getmapping("/all")
    public map<string, object> all() {
        map<string, object> map = new linkedhashmap<>();
        map.put("primary_mysql用户", service.getallusers());
        map.put("secondary_pg订单", service.getordersbyuserid(1l));
        map.put("third_阿里云商品", service.getallproducts());
        return map;
    }

    @postmapping("/user")
    public user adduser(@requestbody user user) {
        return service.saveuser(user);
    }
}

4.9 验证

curl http://localhost:8083/api/all
curl -x post http://localhost:8083/api/user
-h “content-type: application/json”
-d ‘{“username”:“赵六”,“email”:“zhaoliu@test.com”}’

四方案对比总结

维度方案一:手动配置方案二:动态路由方案三:dynamic-datasource方案四:jpa
切换方式包路径隔离注解/手动 threadlocal@ds 注解包路径隔离
数据源数量固定,编译时确定可动态增减可动态增减固定
代码侵入中(需写aop)极低
事务管理每个数据源独立 tm单个 tm自动处理每个数据源独立 tm 
druid 监控手动注册自动自动(内置)手动注册
学习成本⭐⭐⭐⭐⭐⭐⭐⭐
推荐场景固定2~3个库多租户/saas通用(首选)✅jpa 技术栈

通用注意事项

1. 排除自动配置
   @springbootapplication(exclude = {druiddatasourceautoconfigure.class})
   多数据源时必须排除,否则 druid 会尝试自动创建单个数据源

2. 事务不能跨库
   一个 @transactional 只能管一个数据源
   跨库一致性 → seata / 消息最终一致性

3. 阿里云 rds 注意
   - 开启 ssl:usessl=true
   - 开启 test-on-borrow(网络抖动多)
   - 开启 keep-alive(防止连接被 rds 防火墙断开)
   - 白名单配置你的服务器 ip

4. postgresql 注意
   - validation-query 用 select 1
   - 注意 schema 配置:currentschema=public
   - 自增用 bigserial / identity

5. druid 监控安全
   - 生产环境限制 allow ip
   - 修改默认账号密码
   - 关闭 resetenable

以上就是springboot实现多数据源配置的四种方案的详细内容,更多关于springboot多数据源配置的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com