1. SpringBootマルチ環境構成
1.1 序文
SpringBoot プロジェクトの場合、異なる環境 (dev、test、prod、uat など) ごとに異なる構成情報 (application.yml または application.properties で構成) が存在する場合があります。たとえば、dev および swagger.enable 変数などです。テスト環境の値は true で、本番環境の値は false です。
SpringBoot では、マルチ環境構成ファイルを実装する方法が 2 つあります。1
つは、application.yml のみをサポートする 1 つの構成ファイル (つまり、— で区切られたマルチドキュメント ブロック)で複数の環境の構成情報を直接構成する方法です。 files ;
1 つはメイン構成ファイル (application.yml または application.properties) と複数の環境構成ファイル(application-dev.yml、application-test.yml、application-prod.yml、application-uat.yml など) です。 。
1.2 単一構成でマルチ環境構成を実現
application.yml ファイルで構成すると、異なる環境の同じ構成情報を最上位のドキュメント ブロックで構成でき、異なる環境の異なる構成情報を異なる環境のドキュメント ブロックで構成できます。さまざまな環境でどのドキュメント ブロックが使用されるかの構成は、 spring.profiles.active 変数を通じて指定できます。
server:
port: 8080
spring:
profiles:
active: dev # 激活指定的环境
---
# 开发环境
server:
port: 8081
spring:
profiles: dev
swagger:
enable: true
---
# 测试环境
server:
port: 8082
spring:
profiles: test
swagger:
enable: true
# 验收环境
server:
port: 8083
spring:
profiles: uat
swagger:
enable: false
# 生产环境
server:
port: 8084
spring:
profiles: prod
swagger:
enable: false
1.3 複数の設定ファイル形式
この形式は、1つのファイルの構成を複数のファイルに記述する形式であり、より明確かつ簡潔に見えます(個人的な意見)。
ここで注意すべき点は、Spring Boot 2.4 以降では、 spring.profiles.active 設定項目に線が引かれていることです。つまり、Spring Boot .active で複数の環境をサポートする設定項目 spring.profiles が、廃止されました。したがって、ここでは最新の設定メソッド spring.config.activate.on-profile が使用されます。
Spring Boot の大規模な変更の主な動機は 2 つあり、1 つは Kubernetes の互換性をサポートすること、もう 1 つは ConfigFileApplicationListener クラスによって引き起こされるファイル処理の問題を修正することです。その結果、ファイルのロード方法に 2 つの大きな変更が発生しました。1 つはドキュメントが定義された順序でロードされること、もう 1 つは特定の環境ではプロファイルのアクティブ化スイッチを構成できないことです。
以下の図に示すように、メイン構成ファイルとサブ構成ファイルを異なる環境に作成します。
このうち、メイン構成ファイル application.yml は次のとおりです。
# 不同环境相同的配置信息可以配置在这个文件
server:
port: 8080
# 激活指定使用哪个环境配置文件
spring:
profiles:
active: dev
開発環境: application-dev.yml
server:
port: 8081
spring:
config:
activate:
on-profile: dev
テスト環境: application-test.yml
server:
port: 8082
spring:
config:
activate:
on-profile: test
UAT 環境: application-uat.yml
server:
port: 8083
spring:
config:
activate:
on-profile: uat
本番環境: application-prod.yml
server:
port: 8084
spring:
config:
activate:
on-profile: prod
1.4 設定ファイルの有効化方法
1.在主配置文件中(application.yml或application.properties)指定变2.spring.profiles.active的值,例如spring.profiles.active=dev
命令行指定:java -jar springboot-demo.jar --spring.profiles.active=dev
3.虚拟机参数指定:-Dspring.profiles.active=dev
1.5 設定ファイルのロード順序
springBoot の起動は、次の場所にある構成ファイルをスキャンして読み取ります。優先順位は高から低の順です:
-file:./config/、これは現在のプロジェクトの下の config フォルダー (同じレベルの src ディレクトリ)
- file:./、つまり、現在のプロジェクトの下
-classpath:./config/、つまり、現在のプロジェクトのリソース リソース フォルダーの下の config フォルダー
-classpath:./、つまり、リソース リソース フォルダーの下現在のプロジェクト
2. SpringBootの複数データソース構成
2.1 データソースとは何ですか?
データ ソース (データ ソース) 名前が示すように、データ ソースは、必要なデータを提供するデバイスまたはオリジナルのメディアです。データベース接続を確立するためのすべての情報はデータ ソースに保存されます。ファイル名を指定してファイル システム内でファイルを検索できるのと同じように、正しいデータ ソース名を指定すると、対応するデータベース接続を検索できます。簡単に言えば、データ ソースは、データベース アプリケーションによって使用されるデータベースまたはデータベース サーバーです。
複数のデータ ソースは、複数のデータベース、または MySql や Oracle などの異なるタイプの複数のデータベースとして理解できます。プロジェクトが拡大するにつれて、データベースを分割したり、別のデータベースを導入したりする必要が生じ、複数のデータ ソースを構成する必要が生じることがあります。
2.2 データの準備
SpringBoot で複数のデータ ソースを使用するのは比較的簡単です。デモンストレーションの便宜上、MySql に 2 つのデータベース (ds1 と ds2) を作成し、ds1 データベースに Student テーブルを作成し、ds2 データベースに Teacher テーブルを作成します。データベース スクリプトは次のとおりです。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` varchar(16) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
`class` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES ('123456', 'zhangsan', '北京');
INSERT INTO `student` VALUES ('123457', 'lisi', '上海');
SET FOREIGN_KEY_CHECKS = 1;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for teacher
-- ----------------------------
DROP TABLE IF EXISTS `teacher`;
CREATE TABLE `teacher` (
`id` varchar(16) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
`class` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of teacher
-- ----------------------------
INSERT INTO `teacher` VALUES ('0000001', 'wangwu', '上海');
SET FOREIGN_KEY_CHECKS = 1;
2.3 springboot+mybatis はサブパッケージ統合を使用します
2.3.1 プロジェクト構造図
プロジェクトの構造図は次のとおりです。
2.3.2 データベース接続設定
データソースが複数あるため、データベース接続情報が異なる可能性があるため、複数のデータソースの接続情報を設定ファイルに設定する必要があります。ここでは、druid データベース接続プールを例に挙げます。
spring:
datasource:
ds1: #数据源1,默认数据源
url: jdbc:mysql://localhost:3306/ds1?serverTimezone=GMT&useSSL=false&useUnicode=true&characterEncoding=utf8
username: root
password: root
typ: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 2
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
ds2: #数据源2
url: jdbc:mysql://localhost:3306/ds2?serverTimezone=GMT&useSSL=false&useUnicode=true&characterEncoding=utf8
username: root
password: root
typ: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
filters: stat
maxActive: 2
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
mybatis:
mapper-locations: classpath:mapper/*.xml
2.3.3 SpringBootのデータソース構成を書き換える
単一のデータ ソースとの違いは、dataSource、sqlSessionFactory、sqlSessionTemplate、transactionManager がすべて個別に構成されていることです。さらに、データ ソース 1 とデータ ソース 2 の間には 2 つの主な違いがあります:
1. @MapperScan のパケット スキャン パスが異なります。データ ソース 1 は com.demo.multipledatasource.dao.ds1 パスの下のマッパーのみをスキャンします。データ ソース 2 は、com.demo.multipledatasource.dao.ds2 パスの下の Mapper のみをスキャンするため、前に作成したときに StudentMapper と TeacherMapper を分離する必要があります。さらに、@MapperScan が構成クラスで構成されているため、@MapperScan アノテーションがスタートアップ クラス
2 に存在してはなりません。データ ソース 1 にはもう 1 つ @Primary アノテーションがあり、これにより Spring に使用するデフォルトのデータ ソースが通知されます。データ ソース プロジェクトには不可欠なものがたくさんあります。
- データソースの構成1
package com.demo.multipledatasource.config;
import com.alibaba.druid.pool.DruidDataSource;
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.beans.factory.annotation.Value;
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 javax.sql.DataSource;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.config.Datasource1Configuration
* @Author: Mr.Wang
* @Create: 2022/10/31 22:20
* @Version 1.0
* @Description:
*/
@Configuration
@MapperScan(basePackages = {
"com.demo.multipledatasource.dao.ds1"}, sqlSessionFactoryRef = "sqlSessionFactory1")
public class Datasource1Configuration {
@Value("${mybatis.mapper-locations}")
private String mapperLocation;
@Value("${spring.datasource.ds1.url}")
private String jdbcUrl;
@Value("${spring.datasource.ds1.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.ds1.username}")
private String username;
@Value("${spring.datasource.ds1.password}")
private String password;
@Value("${spring.datasource.ds1.initialSize}")
private int initialSize;
@Value("${spring.datasource.ds1.minIdle}")
private int minIdle;
@Value("${spring.datasource.ds1.maxActive}")
private int maxActive;
@Bean(name = "dataSource1")
@Primary
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(jdbcUrl);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
return dataSource;
}
@Bean("sqlSessionFactory1")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource1") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(mapperLocation));
return sqlSessionFactoryBean.getObject();
}
@Bean("sqlSessionTemplate1")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory1") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean("transactionManager1")
public DataSourceTransactionManager transactionManager(@Qualifier("dataSource1")DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
- データソースの構成2
package com.demo.multipledatasource.config;
import com.alibaba.druid.pool.DruidDataSource;
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.beans.factory.annotation.Value;
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 javax.sql.DataSource;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.config.Datasource2Configuration
* @Author: Mr.Wang
* @Create: 2022/10/31 22:26
* @Version 1.0
* @Description:
*/
@Configuration
@MapperScan(basePackages = {
"com.demo.multipledatasource.dao.ds2"}, sqlSessionFactoryRef = "sqlSessionFactory2")
public class Datasource2Configuration {
@Value("${mybatis.mapper-locations}")
private String mapperLocation;
@Value("${spring.datasource.ds2.url}")
private String jdbcUrl;
@Value("${spring.datasource.ds2.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.ds2.username}")
private String username;
@Value("${spring.datasource.ds2.password}")
private String password;
@Value("${spring.datasource.ds2.initialSize}")
private int initialSize;
@Value("${spring.datasource.ds2.minIdle}")
private int minIdle;
@Value("${spring.datasource.ds2.maxActive}")
private int maxActive;
@Bean(name = "dataSource2")
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(jdbcUrl);
dataSource.setDriverClassName(driverClassName);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setInitialSize(initialSize);
dataSource.setMinIdle(minIdle);
dataSource.setMaxActive(maxActive);
return dataSource;
}
@Bean("sqlSessionFactory2")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource2") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources(mapperLocation));
return sqlSessionFactoryBean.getObject();
}
@Bean("sqlSessionTemplate2")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory2") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean("transactionManager2")
public DataSourceTransactionManager transactionManager(@Qualifier("dataSource2") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
2.4 3 層のコードを作成する
対応するコントローラー層とサービス層のコードを記述し、すべての生徒と教師の情報をクエリし、テストのためにブラウザーを直接開きます。もちろん、swagger、postman、apifox などもテストに使用できます。対応するコードは次
のとおりです。
package com.demo.multipledatasource.entity;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.entity.Student
* @Author: Mr.Wang
* @Create: 2022/10/31 22:30
* @Version 1.0
* @Description:
*/
public class Student {
private String id;
private String name;
private String address;
public Student() {
}
public Student(String id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
教師
package com.demo.multipledatasource.entity;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.entity.Teacher
* @Author: Mr.Wang
* @Create: 2022/10/31 22:32
* @Version 1.0
* @Description:
*/
public class Teacher {
private String id;
private String name;
private String address;
public Teacher() {
}
public Teacher(String id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Teacher{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
StudentController
package com.demo.multipledatasource.controller;
import com.demo.multipledatasource.entity.Student;
import com.demo.multipledatasource.service.StudentService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.controller.StudentController
* @Author: Mr.Wang
* @Create: 2022/10/31 22:29
* @Version 1.0
* @Description:
*/
@RestController
@RequestMapping("student")
public class StudentController {
@Resource
private StudentService studentService;
@GetMapping("selectAllStudent")
public List<Student> selectAllStudent(){
return studentService.selectAllStudent();
}
}
教師コントローラー
package com.demo.multipledatasource.controller;
import com.demo.multipledatasource.entity.Student;
import com.demo.multipledatasource.entity.Teacher;
import com.demo.multipledatasource.service.TeacherService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.controller.TeacherController
* @Author: Mr.Wang
* @Create: 2022/10/31 22:29
* @Version 1.0
* @Description:
*/
@RestController
@RequestMapping("teacher")
public class TeacherController {
@Resource
private TeacherService teacherService;
@GetMapping("selectAllTeacher")
public List<Teacher> selectAllTeacher(){
return teacherService.selectAllTeacher();
}
}
学生サービス
package com.demo.multipledatasource.service;
import com.demo.multipledatasource.entity.Student;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.service.StudentService
* @Author: Mr.Wang
* @Create: 2022/10/31 22:34
* @Version 1.0
* @Description:
*/
public interface StudentService {
/**
* 查询所有学生
* @return
*/
List<Student> selectAllStudent();
}
教師サービス
package com.demo.multipledatasource.service;
import com.demo.multipledatasource.entity.Teacher;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.service.TeacherService
* @Author: Mr.Wang
* @Create: 2022/10/31 22:44
* @Version 1.0
* @Description:
*/
public interface TeacherService {
/**
* 查询所有教师
* @return
*/
List<Teacher> selectAllTeacher();
}
StudnetServiceImpl
package com.demo.multipledatasource.service.impl;
import com.demo.multipledatasource.dao.ds1.StudentDao;
import com.demo.multipledatasource.entity.Student;
import com.demo.multipledatasource.service.StudentService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.service.impl.StudnetServiceImpl
* @Author: Mr.Wang
* @Create: 2022/10/31 22:35
* @Version 1.0
* @Description:
*/
@Service
public class StudnetServiceImpl implements StudentService {
@Resource
private StudentDao studentDao;
@Override
public List<Student> selectAllStudent() {
return studentDao.selectAllStudent();
}
}
TeacherServiceImpl
package com.demo.multipledatasource.service.impl;
import com.demo.multipledatasource.dao.ds2.TeacherDao;
import com.demo.multipledatasource.entity.Teacher;
import com.demo.multipledatasource.service.TeacherService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.service.impl.TeacherServiceImpl
* @Author: Mr.Wang
* @Create: 2022/10/31 22:44
* @Version 1.0
* @Description:
*/
@Service
public class TeacherServiceImpl implements TeacherService {
@Resource
private TeacherDao teacherDao;
@Override
public List<Teacher> selectAllTeacher() {
return teacherDao.selectAllTeacher();
}
}
学生ダオ
package com.demo.multipledatasource.dao.ds1;
import com.demo.multipledatasource.entity.Student;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.dao.ds1.StudentDao
* @Author: Mr.Wang
* @Create: 2022/10/31 22:36
* @Version 1.0
* @Description:
*/
public interface StudentDao {
/**
* 查询所有学生
* @return
*/
List<Student> selectAllStudent();
}
ダオ先生
package com.demo.multipledatasource.dao.ds2;
import com.demo.multipledatasource.entity.Teacher;
import java.util.List;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multipledatasource.dao.ds2.TeacherDao
* @Author: Mr.Wang
* @Create: 2022/10/31 22:42
* @Version 1.0
* @Description:
*/
public interface TeacherDao {
/**
* 查询所有教师
* @return
*/
List<Teacher> selectAllTeacher();
}
StudentDao.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.demo.multipledatasource.dao.ds1.StudentDao">
<resultMap type="com.demo.multipledatasource.entity.Student" id="StudentMap">
<result property="id" column="id" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="address" column="address" jdbcType="VARCHAR"/>
</resultMap>
<!--查询指定行数据-->
<select id="selectAllStudent" resultMap="StudentMap">
select
id, name, address
from student
</select>
</mapper>
TeacherDao.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.demo.multipledatasource.dao.ds2.TeacherDao">
<resultMap type="com.demo.multipledatasource.entity.Teacher" id="TeacherMap">
<result property="id" column="id" jdbcType="VARCHAR"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="address" column="address" jdbcType="VARCHAR"/>
</resultMap>
<!--查询指定行数据-->
<select id="selectAllTeacher" resultMap="TeacherMap">
select
id, name, address
from teacher
</select>
</mapper>
2.5 Mybatisの下請け統合テスト
それぞれアドレスを入力してください:
http://localhost:8083/student/selectAllStudent
http://localhost:8083/Teacher/selectAllTeacher
にアクセスすると、すべての訪問が成功し、MyBatis が対応するデータ ソースに自動的に切り替わることが示されます。。
2.6 統合を実現するカスタム アノテーションの方法
以上でMyBatisを中心にデータソースの自動切り替えを改善しましたが、プロジェクト内でMyBatisを使用していない場合はどうすればよいでしょうか?
カスタムアノテーションを利用して複数のデータソースを動的に切り替える方法を紹介します。SpringBoot には AbstractRoutingDataSource 抽象クラスがあり、その抽象メソッド決定 CurrentLookupKey() を実装してデータ ソースを指定できます。そして、AOPを通じてカスタムアノテーション処理クラスを記述し、SQL文の実行前にカスタムアノテーションに設定されたデータソースに切り替えることで、データソースの自動切り替えを実現します。
2.6.1 2つのデータベース接続情報を設定する
構成情報は 2.3 と同じなので、ここではコードを繰り返しません。
2.6.2 データソースストレージクラスの作成
DataSource はスレッドに関連付けられているため、DataSource を格納し、determineCurrentLookupKey() でこのクラスを通じてデータ ソースを取得するにはスレッドセーフ クラスが必要です。
AbstractRoutingDataSource クラスでは、DataSource がキーと値のペアの形式で保存され、ThreadLocal を使用してキーを保存することで、複数のデータ ソースの自動切り替えを実現できます。
package com.demo.multidatasource.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.util.DataSourceContextHolder
* @Author: Mr.Wang
* @Create: 2022/11/1 9:26
* @Version 1.0
* @Description:
*/
public class DataSourceContextHolder {
private static Logger logger = LoggerFactory.getLogger(DataSourceContextHolder.class);
// 使用ThreadLocal线程安全的使用变量副本
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<String>();
/**
* 设置数据源
* */
public static void setDataSource(String dataSource) {
logger.info("切换到数据源:{}", dataSource);
CONTEXT_HOLDER.set(dataSource);
}
/**
* 获取数据源
* */
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
/**
* 清空数据源
* */
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
2.6.3 データソース列挙クラスの作成
package com.demo.multidatasource.util;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.util.DataSourceEnum
* @Author: Mr.Wang
* @Create: 2022/11/1 9:29
* @Version 1.0
* @Description:
*/
public enum DataSourceEnum {
PRIMARY,
DATASOURCE1
}
2.6.4 動的データソースクラスの作成
DynamicDataSource クラスは AbstractRoutingDataSource クラスを継承し、determineCurrentLookupKey メソッドを書き換えてデータ ソースを指定します。
package com.demo.multidatasource.config;
import com.demo.multidatasource.util.DataSourceContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.config.DynamicDataSource
* @Author: Mr.Wang
* @Create: 2022/11/1 9:30
* @Version 1.0
* @Description:
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
2.6.5 データソース構成クラスの作成
package com.demo.multidatasource.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.demo.multidatasource.util.DataSourceEnum;
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;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.config.DynamicDataSourceConfiguration
* @Author: Mr.Wang
* @Create: 2022/11/1 9:33
* @Version 1.0
* @Description:
*/
@Configuration
public class DynamicDataSourceConfiguration {
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.ds1")
public DataSource primaryDataSource(){
return new DruidDataSource();
}
@Bean(name = "dataSource1")
@ConfigurationProperties(prefix = "spring.datasource.ds2")
public DataSource dataSource1(){
return new DruidDataSource();
}
@Bean("dynamicDataSource")
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//配置默认数据源
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
//配置多数据源
HashMap<Object, Object> dataSourceMap = new HashMap();
dataSourceMap.put(DataSourceEnum.PRIMARY.name(),primaryDataSource());
dataSourceMap.put(DataSourceEnum.DATASOURCE1.name(),dataSource1());
dynamicDataSource.setTargetDataSources(dataSourceMap);
dynamicDataSource.afterPropertiesSet();
return dynamicDataSource;
}
}
2.6.6 カスタム注釈の作成
package com.demo.multidatasource.annotation;
import com.demo.multidatasource.util.DataSourceEnum;
import java.lang.annotation.*;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.annotation.DataSource
* @Author: Mr.Wang
* @Create: 2022/11/1 9:52
* @Version 1.0
* @Description:
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceAnnotation {
DataSourceEnum value() default DataSourceEnum.PRIMARY;
}
2.6.7 AOP アスペクトクラスの作成
AOP を通じて SQL ステートメントを実行する前にインターセプトし、カスタム アノテーションで指定されたデータ ソースに切り替えます。注意点としては、カスタムデータソースアノテーションと@Transactionアノテーションが同じメソッドの場合、つまりデータソースを切り替える前にデータソースを取得してしまうと、@Transactionが先に実行されてしまうため、カスタムアノテーションが無効になってしまうことです。したがって、@Order (@Order 値が小さいほど早く実行されます) を使用して、AOP が @Transactional より前に実行されるようにする必要があります。
package com.demo.multidatasource.aop;
import com.demo.multidatasource.annotation.DataSourceAnnotation;
import com.demo.multidatasource.util.DataSourceContextHolder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.aop.DataSourceAspect
* @Author: Mr.Wang
* @Create: 2022/11/1 9:53
* @Version 1.0
* @Description:
*/
@Aspect
@Component
@Order(-1)
public class DataSourceAspect {
@Pointcut("@annotation(com.demo.multidatasource.annotation.DataSourceAnnotation)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object dataSourceArround(ProceedingJoinPoint proceed) throws Throwable {
MethodSignature methodSignature = (MethodSignature) proceed.getSignature();
Method method = methodSignature.getMethod();
System.out.println("method是"+method);
DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);
if(dataSourceAnnotation != null) {
System.out.println("设置的是"+dataSourceAnnotation.value().name());
DataSourceContextHolder.setDataSource(dataSourceAnnotation.value().name());
}
try {
return proceed.proceed();
} finally {
// 方法执行后销毁数据源
DataSourceContextHolder.clearDataSource();
}
}
}
2.6.8 3 層のコードを記述する
dao 層実装クラスは JdbcTemplate クエリを使用します。強迫性障害の患者はリストに挙げられます
package com.demo.multidatasource.dao.ds1.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import com.demo.multidatasource.dao.ds1.StudentDao;
import com.demo.multidatasource.entity.Student;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.dao.ds1.impl.StudentDaoImpl
* @Author: Mr.Wang
* @Create: 2022/11/1 10:10
* @Version 1.0
* @Description:
*/
@Repository
public class StudentDaoImpl implements StudentDao {
@Resource
JdbcTemplate jdbcTemplate;
@Override
public List<Student> selectAllStudent() {
String sql="select * from student";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
ArrayList<Student> studentList = new ArrayList<>();
for (Map<String, Object> map : list) {
Student student = Convert.convert(new TypeReference<Student>() {
},map);
studentList.add(student);
}
return studentList;
}
}
package com.demo.multidatasource.dao.ds2.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.TypeReference;
import com.demo.multidatasource.dao.ds2.TeacherDao;
import com.demo.multidatasource.entity.Teacher;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Program: IntelliJ IDEA
* @ClassName: com.demo.multidatasource.dao.ds2.impl.TeacherDaoImpl
* @Author: Mr.Wang
* @Create: 2022/11/1 10:19
* @Version 1.0
* @Description:
*/
@Repository
public class TeacherDaoImpl implements TeacherDao {
@Resource
JdbcTemplate jdbcTemplate;
@Override
public List<Teacher> selectAllTeacher() {
String sql="select * from teacher";
List<Map<String, Object>> list = jdbcTemplate.queryForList(sql);
ArrayList<Teacher> teacherList = new ArrayList<>();
for (Map<String, Object> map : list) {
Teacher teacher = Convert.convert(new TypeReference<Teacher>() {
},map);
teacherList.add(teacher);
}
return teacherList;
}
}
コードの残りの部分は、mybatis 下請けメソッドの統合コードと同じであるため、繰り返しません。
2.6.9 DataSource 自動構成クラスの削除
スタートアップ クラスの @SpringBootApplication アノテーションで DataSource 自動構成クラスを削除します。そうしないと、カスタム DataSource を使用する代わりにデフォルトで自動的に構成され、起動時に循環依存関係エラーが発生します。
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
2.7 カスタムアノテーションメソッドのテスト
それぞれアドレスを入力します:
http://localhost:8003/student/selectAllStudent
http://localhost:8003/Teacher/selectAllTeacher
にアクセスすると、すべての訪問が成功し、カスタム アノテーション メソッドが複数のデータ ソースの切り替えを実現していることを示します。