JdbcTemplate配置Druid多数据源的核心在于创建JdbcTemplate时候为其分配不同的数据源,然后在需要访问不同数据库的时候使用对应的JdbcTemplate即可。这里介绍在Spring Boot中基于Oracle和Mysql配置Druid多数据源。

引入依赖

先根据开启Spring Boot开启一个最简单的Spring Boot应用,然后引入如下依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-jdbc</artifactId>
  4. </dependency>
  5. <!-- oracle驱动 -->
  6. <dependency>
  7. <groupId>com.oracle.database.jdbc</groupId>
  8. <artifactId>ojdbc8</artifactId>
  9. <version>21.1.0.0</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.oracle.database.jdbc</groupId>
  13. <artifactId>ucp</artifactId>
  14. <version>21.1.0.0</version>
  15. </dependency>
  16. <!-- mysql驱动 -->
  17. <dependency>
  18. <groupId>mysql</groupId>
  19. <artifactId>mysql-connector-java</artifactId>
  20. </dependency>
  21. <!-- druid数据源驱动 -->
  22. <dependency>
  23. <groupId>com.alibaba</groupId>
  24. <artifactId>druid-spring-boot-starter</artifactId>
  25. <version>1.1.6</version>
  26. </dependency>

初始化数据库

Spring Boot 2.5.0 版本更新:

  1. # Spring Boot 2.5.0 init schema & data
  2. # 执行初始化脚本的用户名称
  3. spring.sql.init.username=root
  4. # 执行初始化脚本的用户密码
  5. spring.sql.init.password=
  6. # 初始化的schema脚本位置
  7. spring.sql.init.schema-locations=classpath*:schema-all.sql

除了上面用到的配置属性之外,还有一些其他的配置,下面详细讲解一下作用。

  • spring.sql.init.enabled:是否启动初始化的开关,默认是true。如果不想执行初始化脚本,设置为false即可。通过-D的命令行参数会更容易控制。
  • spring.sql.init.usernamespring.sql.init.password:配置执行初始化脚本的用户名与密码。这个非常有必要,因为安全管理要求,通常给业务应用分配的用户对一些建表删表等命令没有权限。这样就可以与datasource中的用户分开管理。
  • pring.sql.init.schema-locations:配置与schema变更相关的sql脚本,可配置多个(默认用;分割)
  • spring.sql.init.data-locations:用来配置与数据相关的sql脚本,可配置多个(默认用;分割)
  • spring.sql.init.encoding:配置脚本文件的编码
  • spring.sql.init.separator:配置多个sql文件的分隔符,默认是;
  • spring.sql.init.continue-on-error:如果执行脚本过程中碰到错误是否继续,默认是false`;

    Spring Boot can automatically create the schema (DDL scripts) of your JDBC DataSource or R2DBC ConnectionFactory and initialize it (DML scripts). It loads SQL from the standard root classpath locations: schema.sql and data.sql, respectively. In addition, Spring Boot processes the schema-${platform}.sql and data-${platform}.sql files (if present), where platform is the value of spring.sql.init.platform. This allows you to switch to database-specific scripts if necessary. For example, you might choose to set it to the vendor name of the database (hsqldb, h2, oracle, mysql, postgresql, and so on). By default, SQL database initialization is only performed when using an embedded in-memory database. To always initialize an SQL database, irrespective of its type, set spring.sql.init.mode to always. Similarly, to disable initialization, set spring.sql.init.mode to never. By default, Spring Boot enables the fail-fast feature of its script-based database initializer. This means that, if the scripts cause exceptions, the application fails to start. You can tune that behavior by setting spring.sql.init.continue-on-error.

准备数据

  1. DROP TABLE if exists STUDENT;
  2. CREATE TABLE STUDENT (
  3. SNO VARCHAR2(3) NOT NULL primary key,
  4. SNAME VARCHAR2(9) NOT NULL ,
  5. SSEX CHAR(2) NOT NULL ,
  6. database VARCHAR2(10) NULL
  7. );
  8. INSERT INTO STUDENT VALUES ('001', 'KangKang', 'M ', 'h2');
  9. INSERT INTO STUDENT VALUES ('002', 'Mike', 'M ', 'h2');
  10. INSERT INTO STUDENT VALUES ('003', 'Jane', 'F ', 'h2');
  11. INSERT INTO STUDENT VALUES ('004', 'Maria', 'F ', 'h2');

多数据源配置

接着在Spring Boot配置文件application.yml中配置多数据源:

  1. server:
  2. context-path: /web
  3. spring:
  4. datasource:
  5. druid:
  6. # 数据库访问配置, 使用druid数据源
  7. # 数据源1 mysql
  8. mysql:
  9. type: com.alibaba.druid.pool.DruidDataSource
  10. driver-class-name: com.mysql.jdbc.Driver
  11. url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&rewriteBatchedStatements=true&autoReconnect=true&failOverReadOnly=false&zeroDateTimeBehavior=convertToNull
  12. username: root
  13. password: 123456
  14. # 数据源2 oracle
  15. oracle:
  16. type: com.alibaba.druid.pool.DruidDataSource
  17. driver-class-name: oracle.jdbc.driver.OracleDriver
  18. url: jdbc:oracle:thin:@localhost:1521/xe #jdbc:oracle:thin:@ip:port/instance
  19. username: scott
  20. password: tiger
  21. # 连接池配置
  22. initial-size: 5
  23. min-idle: 5
  24. max-active: 20
  25. # 连接等待超时时间
  26. max-wait: 30000
  27. # 配置检测可以关闭的空闲连接间隔时间
  28. time-between-eviction-runs-millis: 60000
  29. # 配置连接在池中的最小生存时间
  30. min-evictable-idle-time-millis: 300000
  31. validation-query: select '1' from dual
  32. test-while-idle: true
  33. test-on-borrow: false
  34. test-on-return: false
  35. # 打开PSCache,并且指定每个连接上PSCache的大小
  36. pool-prepared-statements: true
  37. max-open-prepared-statements: 20
  38. max-pool-prepared-statement-per-connection-size: 20
  39. # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙
  40. filters: stat,wall
  41. # Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔
  42. aop-patterns: com.springboot.servie.*
  43. # WebStatFilter配置
  44. web-stat-filter:
  45. enabled: true
  46. # 添加过滤规则
  47. url-pattern: /*
  48. # 忽略过滤的格式
  49. exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
  50. # StatViewServlet配置
  51. stat-view-servlet:
  52. enabled: true
  53. # 访问路径为/druid时,跳转到StatViewServlet
  54. url-pattern: /druid/*
  55. # 是否能够重置数据
  56. reset-enable: false
  57. # 需要账号密码才能访问控制台
  58. login-username: druid
  59. login-password: druid123
  60. # IP白名单
  61. # allow: 127.0.0.1
  62. # IP黑名单(共同存在时,deny优先于allow)
  63. # deny: 192.168.1.218
  64. # 配置StatFilter
  65. filter:
  66. stat:
  67. log-slow-sql: true

MySQL+H2:

  1. server:
  2. servlet:
  3. application-display-name: ProjectTemplate-Web
  4. context-path: /web
  5. spring:
  6. h2:
  7. console:
  8. path: /h2-console
  9. enabled: true
  10. settings:
  11. web-allow-others: true
  12. datasource:
  13. druid:
  14. # 数据库访问配置, 使用druid数据源
  15. # 数据源1 mysql
  16. mysql:
  17. # 驱动配置信息
  18. db-type: com.alibaba.druid.pool.DruidDataSource
  19. #mysql 配置
  20. driver-class-name: com.mysql.cj.jdbc.Driver
  21. url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false
  22. username: test
  23. password: 12345678
  24. # 数据源2 H2
  25. h2:
  26. db-type: com.alibaba.druid.pool.DruidDataSource #使用阿里druid连接池
  27. url: jdbc:h2:~/test #test是我创建的数据库名
  28. username: sa
  29. password:
  30. driver-class-name: org.h2.Driver
  31. # schema: classpath:schema-h2.sql #程序运行时,使用schema.sql来创建数据库中的表
  32. # data: classpath:data-h2.sql #程序运行时,使用data.sql来创建初始数据
  33. # 连接池配置
  34. initial-size: 5
  35. min-idle: 5
  36. max-active: 20
  37. # 连接等待超时时间
  38. max-wait: 30000
  39. # 配置检测可以关闭的空闲连接间隔时间
  40. time-between-eviction-runs-millis: 60000
  41. # 配置连接在池中的最小生存时间
  42. min-evictable-idle-time-millis: 300000
  43. test-while-idle: true
  44. test-on-borrow: false
  45. test-on-return: false
  46. # 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,
  47. # testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
  48. validation-query: select 1 from dual
  49. # 是否缓存preparedStatement,也就是PSCache 官方建议MySQL下建议关闭 个人建议如果想用SQL防火墙 建议打开
  50. # 打开PSCache,并且指定每个连接上PSCache的大小
  51. pool-prepared-statements: true
  52. max-open-prepared-statements: 20
  53. max-pool-prepared-statement-per-connection-size: 20
  54. # 配置监控统计拦截的filters, 去掉后监控界面sql无法统计, 'wall'用于防火墙
  55. filters: stat,wall
  56. # Spring监控AOP切入点,如x.y.z.service.*,配置多个英文逗号分隔
  57. aop-patterns: com.springboot.service.*
  58. # WebStatFilter配置
  59. web-stat-filter:
  60. enabled: true
  61. # 添加过滤规则
  62. url-pattern: /*
  63. # 忽略过滤的格式
  64. exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
  65. # StatViewServlet配置
  66. stat-view-servlet:
  67. enabled: true
  68. # 访问路径为/druid时,跳转到StatViewServlet
  69. url-pattern: /druid/*
  70. # 是否能够重置数据
  71. reset-enable: false
  72. # 需要账号密码才能访问控制台
  73. login-username: druid
  74. login-password: druid123
  75. # IP白名单
  76. allow:
  77. # IP黑名单(共同存在时,deny优先于allow)
  78. # deny: 192.168.1.218
  79. # 配置StatFilter
  80. filter:
  81. stat:
  82. log-slow-sql: true
  83. sql:
  84. init:
  85. mode: embedded
  86. platform: h2
  87. # classpath 等价于 main/java + main/resources + 第三方jar包的根目录
  88. schema-locations: classpath:schema-h2.sql #程序运行时,使用schema.sql来创建数据库中的表
  89. data-locations: classpath:data-h2.sql #程序运行时,使用data.sql来创建初始数据

然后根据配置,可以知道 H2的console目录在[http://localhost:8080/web/h2-console/](http://localhost:8080/web/h2-console/)

Screen Shot 2021-09-23 at 9.38.57 AM.png

然后创建一个多数据源配置类,根据application.yml分别配置一个Mysql和Oracle的数据源,并且将这两个数据源注入到两个不同的JdbcTemplate中:

  1. @Configuration
  2. public class DataSourceConfig {
  3. @Primary
  4. @Bean(name = "mysqldatasource")
  5. @ConfigurationProperties("spring.datasource.druid.mysql")
  6. public DataSource dataSourceOne(){
  7. return DruidDataSourceBuilder.create().build();
  8. }
  9. @Bean(name = "oracledatasource")
  10. @ConfigurationProperties("spring.datasource.druid.oracle")
  11. public DataSource dataSourceTwo(){
  12. return DruidDataSourceBuilder.create().build();
  13. }
  14. @Bean(name = "mysqlJdbcTemplate")
  15. public JdbcTemplate primaryJdbcTemplate(
  16. @Qualifier("mysqldatasource") DataSource dataSource) {
  17. return new JdbcTemplate(dataSource);
  18. }
  19. @Bean(name = "oracleJdbcTemplate")
  20. public JdbcTemplate secondaryJdbcTemplate(
  21. @Qualifier("oracledatasource") DataSource dataSource) {
  22. return new JdbcTemplate(dataSource);
  23. }
  24. }

上述代码根据application.yml创建了mysqldatasource和oracledatasource数据源,其中mysqldatasource用@Primary标注为主数据源,接着根据这两个数据源创建了mysqlJdbcTemplate和oracleJdbcTemplate。

@Primary标志这个Bean如果在多个同类Bean候选时,该Bean优先被考虑。多数据源配置的时候,必须要有一个主数据源,用@Primary标志该Bean。

数据源创建完毕,接下来开始进行测试代码编写。

测试

首先往Mysql和Oracle中创建测试表,并插入一些测试数据:

Mysql:

  1. DROP TABLE IF EXISTS `student`;
  2. CREATE TABLE `student` (
  3. `sno` varchar(3) NOT NULL,
  4. `sname` varchar(9) NOT NULL,
  5. `ssex` char(2) NOT NULL,
  6. `database` varchar(10) DEFAULT NULL
  7. ) DEFAULT CHARSET=utf8;
  8. INSERT INTO `student` VALUES ('001', '康康', 'M', 'mysql');
  9. INSERT INTO `student` VALUES ('002', '麦克', 'M', 'mysql');

Oracle:

  1. DROP TABLE "SCOTT"."STUDENT";
  2. CREATE TABLE "SCOTT"."STUDENT" (
  3. "SNO" VARCHAR2(3 BYTE) NOT NULL ,
  4. "SNAME" VARCHAR2(9 BYTE) NOT NULL ,
  5. "SSEX" CHAR(2 BYTE) NOT NULL ,
  6. "database" VARCHAR2(10 BYTE) NULL
  7. );
  8. INSERT INTO "SCOTT"."STUDENT" VALUES ('001', 'KangKang', 'M ', 'oracle');
  9. INSERT INTO "SCOTT"."STUDENT" VALUES ('002', 'Mike', 'M ', 'oracle');
  10. INSERT INTO "SCOTT"."STUDENT" VALUES ('003', 'Jane', 'F ', 'oracle');
  11. INSERT INTO "SCOTT"."STUDENT" VALUES ('004', 'Maria', 'F ', 'oracle');

接着创建两个Dao及其实现类,分别用于从Mysql和Oracle中获取数据:

MysqlStudentDao接口:

  1. public interface MysqlStudentDao {
  2. List<Map<String, Object>> getAllStudents();
  3. }

MysqlStudentDao实现;

  1. @Repository
  2. public class MysqlStudentDaoImp implements MysqlStudentDao{
  3. @Autowired
  4. @Qualifier("mysqlJdbcTemplate")
  5. private JdbcTemplate jdbcTemplate;
  6. @Override
  7. public List<Map<String, Object>> getAllStudents() {
  8. return this.jdbcTemplate.queryForList("select * from student");
  9. }
  10. }

可看到,在MysqlStudentDaoImp中注入的是mysqlJdbcTemplate。

OracleStudentDao接口:

  1. public interface OracleStudentDao {
  2. List<Map<String, Object>> getAllStudents();
  3. }

OracleStudentDao实现:

  1. @Repository
  2. public class OracleStudentDaoImp implements OracleStudentDao{
  3. @Autowired
  4. @Qualifier("oracleJdbcTemplate")
  5. private JdbcTemplate jdbcTemplate;
  6. @Override
  7. public List<Map<String, Object>> getAllStudents() {
  8. return this.jdbcTemplate.queryForList("select * from student");
  9. }
  10. }

在OracleStudentDaoImp中注入的是oracleJdbcTemplate。

随后编写Service层:

StudentService接口:

  1. public interface StudentService {
  2. List<Map<String, Object>> getAllStudentsFromOralce();
  3. List<Map<String, Object>> getAllStudentsFromMysql();
  4. }

StudentService实现:

  1. @Service("studentService")
  2. public class StudentServiceImp implements StudentService{
  3. @Autowired
  4. private OracleStudentDao oracleStudentDao;
  5. @Autowired
  6. private MysqlStudentDao mysqlStudentDao;
  7. @Override
  8. public List<Map<String, Object>> getAllStudentsFromOralce() {
  9. return this.oracleStudentDao.getAllStudents();
  10. }
  11. @Override
  12. public List<Map<String, Object>> getAllStudentsFromMysql() {
  13. return this.mysqlStudentDao.getAllStudents();
  14. }
  15. }

最后编写一个Controller:

  1. @RestController
  2. public class StudentController {
  3. @Autowired
  4. private StudentService studentService;
  5. @RequestMapping("querystudentsfromoracle")
  6. public List<Map<String, Object>> queryStudentsFromOracle(){
  7. return this.studentService.getAllStudentsFromOralce();
  8. }
  9. @RequestMapping("querystudentsfrommysql")
  10. public List<Map<String, Object>> queryStudentsFromMysql(){
  11. return this.studentService.getAllStudentsFromMysql();
  12. }
  13. }

最终项目目录如下图所示:

Spring Boot JdbcTemplate配置Druid多数据源 - 图2

启动项目,访问:http://localhost:8080/web/querystudentsfrommysql

Spring Boot JdbcTemplate配置Druid多数据源 - 图3

http://localhost:8080/web/querystudentsfromoracle

Spring Boot JdbcTemplate配置Druid多数据源 - 图4