课程标题《代码实际落地手写多数据源插件》
课程内容:
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;
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
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 {
}
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";
}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();
Mappackage 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 条评论) “” |