Configuración multientorno y esquema de configuración multifuente de datos en Spring Boot

1. Configuración multientorno de SpringBoot

1.1 Prefacio

Para proyectos SpringBoot, puede haber información de configuración diferente (configurada en application.yml o application.properties) en diferentes entornos (como dev, test, prod, uat, etc.), como la variable swagger.enable, en dev y valores del entorno de prueba Es verdadero y el valor en el entorno de producción es falso.
En SpringBoot, hay dos formas de implementar archivos de configuración de varios entornos:
una es configurar directamente la información de configuración de varios entornos en un archivo de configuración (es decir, bloques de varios documentos, separados por —), que solo admite application.yml archivos ;
Uno es un archivo de configuración principal (application.yml o application.properties) y múltiples archivos de configuración de entorno (application-dev.yml, application-test.yml, application-prod.yml, application-uat.yml, etc.) .

1.2 Configuración única para lograr una configuración multientorno

Configurado en el archivo application.yml, la misma información de configuración en diferentes entornos se puede configurar en el bloque de documentos de nivel superior, y la misma información de configuración en diferentes entornos se puede configurar en diferentes bloques de documentos de entorno. La configuración de qué bloque de documentos se usa en diferentes entornos se puede especificar a través de la variable 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 Múltiples formatos de archivo de configuración

Este formulario es para escribir la configuración de un solo archivo en varios archivos, lo que se ve más claro y conciso (opinión personal).

Un punto a tener en cuenta aquí es que en Spring Boot 2.4 y superior, se dibuja una línea en el elemento de configuración spring.profiles.active, es decir, el elemento de configuración spring.profiles que admite múltiples entornos en Spring Boot .active ha sido obsoleto. Por lo tanto, aquí se utiliza el último método de configuración spring.config.activate.on-profile.
Hay dos motivaciones principales para los cambios a gran escala de Spring Boot: una es admitir la compatibilidad con Kubernetes y la otra es solucionar los problemas de procesamiento de archivos causados ​​por la clase ConfigFileApplicationListener. Como resultado, se han producido dos cambios importantes en la forma en que se cargan los archivos: los documentos se cargarán en el orden en que están definidos y los interruptores de activación de perfiles no se pueden configurar en entornos específicos.

Cree un archivo de configuración principal y archivos de subconfiguración en diferentes entornos, como se muestra en la siguiente figura:
inserte la descripción de la imagen aquí
Entre ellos, el archivo de configuración principal application.yml es el siguiente:

# 不同环境相同的配置信息可以配置在这个文件
server:
  port: 8080

# 激活指定使用哪个环境配置文件
spring:
  profiles:
    active: dev

Entorno de desarrollo: application-dev.yml

server:
  port: 8081
spring:
  config:
    activate:
      on-profile: dev

Entorno de prueba: application-test.yml

server:
  port: 8082
spring:
  config:
    activate:
      on-profile: test

entorno uat: aplicación-uat.yml

server:
  port: 8083
spring:
  config:
    activate:
      on-profile: uat

Entorno de producción: application-prod.yml

server:
  port: 8084
spring:
  config:
    activate:
      on-profile: prod

1.4 Cómo activar el archivo de configuración

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 Orden de carga de los archivos de configuración

El inicio de springBoot escaneará y leerá los archivos de configuración en las siguientes ubicaciones, y la prioridad es de mayor a menor:
-file:./config/, que es la carpeta de configuración en el proyecto actual (directorio src en el mismo nivel)
- file:./, es decir, en el proyecto actual
-classpath:./config/, es decir, la carpeta de configuración en la carpeta de recursos de recursos en el proyecto actual
-classpath:./, es decir, en la carpeta de recursos de recursos en el proyecto actual

2. Configuración de varias fuentes de datos de SpringBoot

2.1 ¿Qué es una fuente de datos?

Fuente de datos (Fuente de datos) Como su nombre lo indica, la fuente de datos es un dispositivo o medio original que proporciona algunos datos requeridos. Toda la información para establecer una conexión con la base de datos se almacena en la fuente de datos. Al igual que puede encontrar un archivo en el sistema de archivos especificando el nombre del archivo, puede encontrar la conexión de base de datos correspondiente proporcionando el nombre de origen de datos correcto. En pocas palabras, una fuente de datos es una base de datos o un servidor de base de datos utilizado por una aplicación de base de datos.
Múltiples fuentes de datos pueden entenderse como múltiples bases de datos, o incluso múltiples bases de datos de diferentes tipos, como MySql y Oracle. A medida que el proyecto se expande, a veces es necesario dividir la base de datos o introducir otra base de datos, y luego es necesario configurar varias fuentes de datos.

2.2 Preparación de datos

Es relativamente simple usar múltiples fuentes de datos en SpringBoot.Para la conveniencia de la demostración, creamos dos bases de datos en MySql: ds1 y ds2, y creamos una tabla de estudiantes en la base de datos ds1 y una tabla de maestros en la base de datos ds2. El script de la base de datos es el siguiente:

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 utiliza la integración de subpaquetes

2.3.1 Diagrama de estructura del proyecto

El diagrama de estructura del proyecto es el siguiente:
Por favor agregue una descripción de la imagen

2.3.2 Configuración de la conexión a la base de datos

Dado que hay múltiples fuentes de datos, la información de conexión de la base de datos puede ser diferente, por lo que la información de conexión de múltiples fuentes de datos debe configurarse en el archivo de configuración. Aquí tomamos el conjunto de conexiones de la base de datos de druida como ejemplo.

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 Reescribir la configuración de la fuente de datos de SpringBoot

La diferencia aquí con la fuente de datos única es que dataSource, sqlSessionFactory, sqlSessionTemplate y transactionManager se configuran por separado. Además, existen dos diferencias principales entre la fuente de datos 1 y la fuente de datos 2:
1. La ruta de escaneo de paquetes en @MapperScan es diferente. La fuente de datos 1 solo escanea Mapper bajo la ruta com.demo.multipledatasource.dao.ds1, y la fuente de datos 2 solo escanea Scan the Mapper en la ruta com.demo.multipledatasource.dao.ds2, por lo que debemos separar StudentMapper y TeacherMapper cuando lo creamos anteriormente. Además, dado que @MapperScan se configuró en la clase de configuración, la anotación @MapperScan no debe existir en la clase de inicio
2. Hay una anotación @Primary más en la fuente de datos 1, que le dice a Spring la fuente de datos predeterminada que usamos, y hay muchos Indispensables en los proyectos de fuentes de datos.

  • Configuración de la fuente de datos 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);
    }
}

  • Configuración de la fuente de datos 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 Escribir tres capas de código

Escriba los códigos de capa de controlador y servicio correspondientes, consulte toda la información del alumno y del profesor y abra el navegador directamente para realizar pruebas. Por supuesto, swagger, postman, apifox, etc. también se pueden usar para realizar pruebas.
El código correspondiente es el siguiente:
Estudiante

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 + '\'' +
                '}';
    }
}

Maestro

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();
    }
}

ProfesorControlador

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();
    }
}

EstudianteServicio

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();
}

MaestroServicio

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();
    }
}

MaestroServicioImpl

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();
    }
}

EstudianteDao

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();
}

MaestraDao

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>

ProfesorDao.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 Prueba de integración de subcontratos de Mybatis

Ingrese la dirección respectivamente:
http://localhost:8083/student/selectAllStudent
http://localhost:8083/teacher/selectAllTeacher
para visitar, todas las visitas son exitosas, lo que indica que MyBatis cambia automáticamente a la fuente de datos correspondiente para nosotros. .
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

2.6 Formas de anotación personalizada para realizar la integración

Arriba, mejoramos el cambio automático de fuentes de datos principalmente apoyándonos en MyBatis.¿Qué debo hacer si MyBatis no se usa en el proyecto?

Aquí hay un método basado en anotaciones personalizadas para realizar el cambio dinámico de múltiples fuentes de datos. Hay una clase abstracta AbstractRoutingDataSource en SpringBoot, podemos implementar su método abstracto determineCurrentLookupKey() para especificar la fuente de datos. Y escriba una clase de procesamiento de anotaciones personalizadas a través de AOP, antes de que se ejecute la instrucción SQL, cambie a la fuente de datos establecida en la anotación personalizada para realizar el cambio automático de la fuente de datos.

2.6.1 Configurar información de conexión de dos bases de datos

La información de configuración es la misma que en 2.3 y el código no se repetirá aquí.

2.6.2 Crear una clase de almacenamiento de origen de datos

DataSource está vinculado a subprocesos, por lo tanto, necesitamos una clase segura para subprocesos para almacenar DataSource y obtener la fuente de datos a través de esta clase en determineCurrentLookupKey().
En la clase AbstractRoutingDataSource, DataSource se guarda en forma de pares clave-valor, y ThreadLocal se puede usar para guardar la clave, a fin de realizar el cambio automático de múltiples fuentes de datos.

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 Crear una clase de enumeración de fuente de datos

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 Crear una clase de fuente de datos dinámica

La clase DynamicDataSource hereda la clase AbstractRoutingDataSource y vuelve a escribir el método determineCurrentLookupKey para especificar el origen de datos.

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 Crear una clase de configuración de origen de datos

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 Crear anotaciones personalizadas

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 Crear clase de aspecto AOP

Intercepte antes de ejecutar la instrucción sql a través de AOP y cambie a la fuente de datos especificada por la anotación personalizada. Una cosa a tener en cuenta es que @Transaction se ejecutará primero cuando la anotación de la fuente de datos personalizada y la anotación de @Transaction sean el mismo método, es decir, la fuente de datos se adquiere antes de que se cambie la fuente de datos, por lo que la anotación personalizada dejará de ser válida. , por lo que debe usar @Order (@Order Cuanto menor sea el valor, más temprana será la ejecución), para asegurarse de que el AOP se ejecute antes que @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 Escribir tres capas de código

La clase de implementación de la capa dao usa la consulta JdbcTemplate. Los pacientes con trastorno obsesivo-compulsivo enumerarán转为了List<实体>。

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;
    }
}


El resto del código es el mismo que el código de integración del método de subcontratación de mybatis y no se repetirá.

2.6.9 Eliminar la clase de configuración automática DataSource

Elimine la clase de configuración automática DataSource en la anotación @SpringBootApplication de la clase de inicio; de lo contrario, se configurará automáticamente de forma predeterminada en lugar de usar nuestro DataSource personalizado, y habrá un error de dependencia circular al inicio.

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

2.7 Prueba del método de anotación personalizado

Ingrese la dirección respectivamente:
http://localhost:8003/student/selectAllStudent
http://localhost:8003/teacher/selectAllTeacher
para visitar, todas las visitas son exitosas, lo que indica que el método de anotación personalizado realiza el cambio de múltiples fuentes de datos.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_44834205/article/details/127635253
Recomendado
Clasificación