10.整合多数据源

SpringBoot项目如何整合多数据源

课程标题《代码实际落地手写多数据源插件》
课程内容:
1.什么是多数据源?
2.多数据源分包和自定义注解区别
3.如何之定义springboot starter
4.如何封装多数据源框架
5.封装多数据源框架架构设计原理


字典表

品牌:

小米品牌

华为品牌

三星品牌

支付类型

平安支付

支付宝

建设支付

微信支付

联合登录

QQ联合登录

微信联合登录

钉钉联合登录


管理系统

主管理系统-增删改查操作

订单子系统-----字典表数据

支付子系统-----字典表数据

会员子系统

mayikt-DataSource-spring-boot-starter

如何整合多个数据源?

spring.DataSource.url---

配置文件:

spring.DataSource.admin.url---连接到admin数据库

spring.DataSource.order.url---连接到订单数据库

程序中如何区分 定位具体的数据源?


1. 分包的形式

例如:

com.mayikt.db1----db1对应独立的数据源1

com.mayikt.db2----db2对应独立的数据源2

2. 基于注解的形式

@MayiktDataSource("db1") 对应独立的数据源1

@MayiktDataSource("db2") 对应独立的数据源2


步骤

1.定义多个不同的数据源

2.自定义注解MayiktDataSource("admin")

MayiktDataSource("admin")----admin数据库

MayiktDataSource("order")----order数据库

3.spring提供回调方法 直接告诉当前线程 spring使用那个数据源

“admin”

public class MultipleDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return “order”
    }
}

@ MayiktDataSource("order")

public void addOrder(){

}

1.执行aop 拦截在那个方式上有加上MayiktDataSource

2.走aop前置通知回调方法 获取到该 MayiktDataSource("order") value

3.value值就是为order 存放在当前线程 ThreadLocal

4.走spring回调方法 直接从当前线程ThreadLocal 获取order 告诉 spring说

当前连接order数据库。



创建两个不同的数据库

sys-order

sys-admin

sys-order
CREATE TABLE `sys_order` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3;
sys-admin
CREATE TABLE `mayikt_dictionary` (
  `id` int NOT NULL AUTO_INCREMENT,
  `dict_name` varchar(50) DEFAULT NULL,
  `dict_value` varchar(50) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `is_delete` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3;

CREATE TABLE `mayikt_dictionary_data` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `type_id` int DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `is_delete` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb3;


分包的形式整合多数据源

maven依赖


        
            com.mayikt
            mayikt-order-api
            1.0-SNAPSHOT
            compile
        

        
            org.springframework.boot
            spring-boot-starter-web
        
        
            log4j
            log4j
            1.2.17
        
        
            org.projectlombok
            lombok
        

        
            org.springframework.boot
            spring-boot-starter-aop
        
        
            org.apache.commons
            commons-lang3
        

        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.1
        
        
            mysql
            mysql-connector-java
        
        
            com.alibaba
            fastjson
            1.2.62
        
        
            org.projectlombok
            lombok
        


        
            com.baomidou
            mybatis-plus-generator
            3.4.1
        
        
            com.baomidou
            mybatis-plus-annotation
            3.4.1
        

    

定义配置文件

server:
  port: 8080 # 启动端口
spring:
  datasource:
    admin: # 数据源1
      jdbc-url: jdbc:mysql://localhost:3306/sys_admin?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
    order: # 数据源2
      jdbc-url: jdbc:mysql://localhost:3306/sys_order?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver

main包

com.mayikt.main

package com.mayikt.main.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

/**
* @since 2022-05-28
*/
@TableName("mayikt_dictionary")
@ApiModel(value = "MayiktDictionary对象", description = "")
public class MayiktDictionary implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    
    private String dictName;
    
    private String dictValue;
    
    private LocalDateTime createTime;
    
    private Integer isDelete;
    
    
    public Integer getId() {
        return id;
    }
    
    public void setId(Integer id) {
        this.id = id;
    }
    
    public String getDictName() {
        return dictName;
    }
    
    public void setDictName(String dictName) {
        this.dictName = dictName;
    }
    
    public String getDictValue() {
        return dictValue;
    }
    
    public void setDictValue(String dictValue) {
        this.dictValue = dictValue;
    }
    
    public LocalDateTime getCreateTime() {
        return createTime;
    }
    
    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }
    
    public Integer getIsDelete() {
        return isDelete;
    }
    
    public void setIsDelete(Integer isDelete) {
        this.isDelete = isDelete;
    }
    
    @Override
    public String toString() {
        return "MayiktDictionary{" +
            "id=" + id +
            ", dictName=" + dictName +
            ", dictValue=" + dictValue +
            ", createTime=" + createTime +
            ", isDelete=" + isDelete +
            "}";
    }
}

package com.mayikt.main.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.time.LocalDateTime;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

/**
 * @since 2022-05-28
 */
@TableName("mayikt_dictionary_data")
@ApiModel(value = "MayiktDictionaryData对象", description = "")
public class MayiktDictionaryData implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String name;

    private Integer typeId;

    private LocalDateTime createTime;

    private Integer isDelete;


    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getTypeId() {
        return typeId;
    }

    public void setTypeId(Integer typeId) {
        this.typeId = typeId;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public Integer getIsDelete() {
        return isDelete;
    }

    public void setIsDelete(Integer isDelete) {
        this.isDelete = isDelete;
    }

    @Override
    public String toString() {
        return "MayiktDictionaryData{" +
                "id=" + id +
                ", name=" + name +
                ", typeId=" + typeId +
                ", createTime=" + createTime +
                ", isDelete=" + isDelete +
                "}";
    }
}

package com.mayikt.main.mapper;

import com.mayikt.main.entity.MayiktDictionaryData;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * 

* Mapper 接口 *

* * @author mayikt * @since 2022-05-28 */ public interface MayiktDictionaryDataMapper extends BaseMapper { } package com.mayikt.main.mapper; import com.mayikt.main.entity.MayiktDictionary; import com.baomidou.mybatisplus.core.mapper.BaseMapper; /** *

* Mapper 接口 *

* * @author mayikt * @since 2022-05-28 */ public interface MayiktDictionaryMapper extends BaseMapper { }

order包

com.mayikt.order.mapper

package com.mayikt.order.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mayikt.order.entity.OrderEntity;

/**
 * @ClassName OrderMapper
 */
public interface OrderMapper extends BaseMapper {
}
package com.mayikt.order.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @ClassName OrderEntity
 */
@TableName("sys_order")
@Data
public class OrderEntity {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;

    public OrderEntity(String name) {
        this.name = name;
    }

    public OrderEntity() {

    }
}

配置类

package com.mayikt.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
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.boot.jdbc.DataSourceBuilder;
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 javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.mayikt.main.mapper", sqlSessionFactoryRef = "db1SqlSessionFactory")
public class DataSourceConfig1 {

    @Primary // 表示这个数据源是默认数据源, 这个注解必须要加,因为不加的话spring将分不清楚那个为主数据源(默认数据源)
    @Bean("db1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.admin") //读取application.yml中的配置参数映射成为一个对象
    public DataSource getDb1DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean("db1SqlSessionFactory")
    public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        // mapper的xml形式文件位置必须要配置,不然将报错:no statement (这种错误也可能是mapper的xml中,namespace与项目的路径不一致导致)
//        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/db1/*.xml"));
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*Mapper.xml"));
        return bean.getObject();
    }

    @Primary
    @Bean("db1SqlSessionTemplate")
    public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
package com.mayikt.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
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.boot.jdbc.DataSourceBuilder;
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 javax.sql.DataSource;

@Configuration
@MapperScan(basePackages = "com.mayikt.order.mapper", sqlSessionFactoryRef = "db2SqlSessionFactory")
public class DataSourceConfig2 {

    @Bean("db2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource getDb1DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean("db2SqlSessionFactory")
    public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*Mapper.xml"));
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean("db2SqlSessionTemplate")
    public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

自定义注解形式封装

aop切换动态数据源

自定义注解形式实现动态数据源底层原理是如何实现的呢?

Spring 提供 回调接口

Mapper层----回调(接口 返回 指定的数据源即可)

1. 指定当前有哪些数据源(多数据源)存放map集合中

Key=数据源的名称===db1 ----db1数据源bean对象---默认

Key=数据源的名称===db2 ----db2数据源bean对象

User1Mapper 接口------Mapper

走回调:

etermineCurrentLookupKey() ---指定访问数据源 key

的名称db2 查询 db2数据源

@MayiktDataSource(“db2”)

getBy2UserList();{

User2Mapper.getBy2UserList

}

Aop机制

MayiktDataSourceAop 拦截到 getBy2UserList();

是有加上数据源注解

直接设置当前线程数据源key db2---

Thearlocal db2---

1.Aop 前置通知

2.目标方法User2Mapper.getBy2UserList

3.etermineCurrentLookupKey() --获取当前线程(Thearlocal

当前线程设定数据源key 返回。

自定义注解

package com.mayikt.ext;

import java.lang.annotation.*;

/**
 * 自定义注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MayiktDataSource {
    String value() default "db1";
}

设置当前线程绑定的db数据源

package com.mayikt.ext;

/**
  * @ClassName DataSourceContextHolder
 */
public class DataSourceContextHolder {

    private static final ThreadLocal contextHolder = new InheritableThreadLocal<>();

    /**
     *  设置数据源
     * @param db
     */
    public static void setDataSource(String db){
        contextHolder.set(db);
    }

    /**
     * 取得当前数据源
     * @return
     */
    public static String getDataSource(){
        return contextHolder.get();
    }

    /**
     * 清除上下文数据
     */
    public static void clear(){
        contextHolder.remove();
    }
}

设置当前线绑定数据源

Spring 提供了AbstractRoutingDataSource 根据用户定义的规则选择当前的数据源

package com.mayikt.ext;

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

public class MultipleDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

多数据源配置

package com.mayikt.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.mayikt.ext.MultipleDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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
@MapperScan(value = {"com.mayikt.order.mapper", "com.mayikt.main.mapper"})
public class MyBatiesPlusConfiguration {


    @Bean("db1DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.admin") //读取application.yml中的配置参数映射成为一个对象
    public DataSource getDb1DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean("db2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.order")
    public DataSource getDb2DataSource() {
        return DataSourceBuilder.create().build();
    }


    /**
     * 动态数据源配置
     *
     * @return
     */
    @Bean
    @Primary
    public DataSource multipleDataSource(@Qualifier("db1DataSource") DataSource db1DataSource,
                                         @Qualifier("db2DataSource") DataSource db2DataSource
    ) {
        MultipleDataSource multipleDataSource = new MultipleDataSource();
        Map targetDataSources = new HashMap<>();
        targetDataSources.put("db1", db1DataSource);
        targetDataSources.put("db2", db2DataSource);
        //添加数据源
        multipleDataSource.setTargetDataSources(targetDataSources);
        //设置默认数据源
        multipleDataSource.setDefaultTargetDataSource(db1DataSource);
        return multipleDataSource;
    }

    @Bean("sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
        sqlSessionFactory.setDataSource(multipleDataSource(getDb1DataSource(), getDb2DataSource()));
        return sqlSessionFactory.getObject();
    }

}

Aop设置当前线程数据源

package com.mayikt.ext;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Slf4j
@Aspect
@Order(-1)
public class DataSourceAspect {


    @Pointcut("@within(com.mayikt.ext.MayiktDataSource) || @annotation(com.mayikt.ext.MayiktDataSource)")
    public void pointCut() {

    }

    @Before("pointCut() && @annotation(dataSource)")
    public void doBefore(MayiktDataSource dataSource) {
        log.info("===========选择数据源=========>>> " + dataSource.value());
        DataSourceContextHolder.setDataSource(dataSource.value());
    }

    @After("pointCut()")
    public void doAfter() {
        DataSourceContextHolder.clear();
    }
}

代码测试

package com.mayikt.manage.main;

import com.mayikt.ext.MayiktDataSource;
import com.mayikt.main.entity.MayiktDictionaryData;
import com.mayikt.main.mapper.MayiktDictionaryDataMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @ClassName MainManage
 */
@Component
public class DictionaryDataManage {
    @Autowired
    private MayiktDictionaryDataMapper mayiktDictionaryDataMapper;

    @MayiktDataSource("db1")
    public MayiktDictionaryData selectById(Integer id) {
        MayiktDictionaryData mayiktDictionaryData = mayiktDictionaryDataMapper.selectById(id);
        return mayiktDictionaryData;
    }
}

package com.mayikt.manage.order;

import com.mayikt.ext.MayiktDataSource;
import com.mayikt.order.entity.OrderEntity;
import com.mayikt.order.mapper.OrderMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * @ClassName OrderManage
 */
@Component
public class OrderManage {
    @Autowired
    private OrderMapper orderMapper;

    @MayiktDataSource("db2")
    public int insertOrder(OrderEntity orderEntity) {
        int result = orderMapper.insert(orderEntity);// 调用order 数据库
        return result;
    }
}

code.rar

发表评论
留言与评论(共有 0 条评论) “”
   
验证码:

相关文章

推荐文章