springboot
整合mybatis
多数据源,之前写过springboot
整合 tk mybatis
多数据源https://blog.csdn.net/qq_37362891/article/details/80457778,今天实现mybatis-plus的多数据源问题,上一篇写到了mybatis多租户问题的处理,有时候多个租户需要共同的配置,这个场景就要使用一个项目配置多个数据源了。
思路:
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml # 你的mapper sql文件位置
type-aliases-package: fast.cloud.nacos.tenant.entity # mybatis 扫遍实体类的位置
configuration:
map-underscore-to-camel-case: true # 驼峰配置
# 打印sql配置
logging:
level:
fast.cloud.nacos.tenant.mapper: debug
spring:
datasource:
druid:
my-cat:
url: jdbc:mysql://localhost:8066/t1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: batman
driver-class-name: com.mysql.jdbc.Driver
tenant-manager:
url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
aop:
auto: true
proxy-target-class: false
server:
servlet:
context-path: /tenant/
创建MybatisDataSourceConfig
,配置多个数据源,代码如下:
package fast.cloud.nacos.tenant.dynamic.mybatis;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import fast.cloud.nacos.tenant.enums.DS;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
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 org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@AutoConfigureBefore(value = {DataSourceAutoConfiguration.class, MybatisAutoConfiguration.class, DruidDataSourceAutoConfigure.class})
@MapperScan(basePackages = {"fast.cloud.nacos.tenant.mapper"})
public class MybatisDataSourceConfig {
@Bean(DS.MY_CAT)
@ConfigurationProperties(prefix = "spring.datasource.druid.my-cat")
public DataSource myCatDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(DS.TENANT_MANAGE)
@ConfigurationProperties(prefix = "spring.datasource.druid.tenant-manager")
public DataSource tenantManager() {
return DruidDataSourceBuilder.create().build();
}
@Bean("dynamicDataSource")
@Primary
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put(DS.MY_CAT, myCatDataSource());
dataSourceMap.put(DS.TENANT_MANAGE, tenantManager());
// 将 master 数据源作为默认指定的数据源
dynamicDataSource.setDefaultDataSource(myCatDataSource());
// 将 master 和 slave 数据源作为指定的数据源
dynamicDataSource.setDataSources(dataSourceMap);
return dynamicDataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
// 配置事务管理, 使用事务时在方法头部添加@Transactional注解即可
return new DataSourceTransactionManager(dynamicDataSource());
}
}
3、 定义数据源的种类
public interface DS {
String MY_CAT = "myCatDataSource";
String TENANT_MANAGE = "tenantManager";
}
4、动态数据源决策
package fast.cloud.nacos.tenant.dynamic.mybatis;
import fast.cloud.nacos.tenant.enums.DS;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 myCatDataSource 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
return DS.MY_CAT;
}
};
/**
* 数据源的 key集合,用于切换时判断数据源是否存在
*/
public static List<Object> dataSourceKeys = new ArrayList<>();
/**
* 切换数据源
*
* @param key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
/**
* 获取数据源
*
* @return
*/
public static String getDataSourceKey() {
return contextHolder.get();
}
/**
* 重置数据源
*/
public static void clearDataSourceKey() {
contextHolder.remove();
}
/**
* 判断是否包含数据源
*
* @param key 数据源key
* @return
*/
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
/**
* 添加数据源keys
*
* @param keys
* @return
*/
public static boolean addDataSourceKeys(Collection<?> keys) {
return dataSourceKeys.addAll(keys);
}
}
package fast.cloud.nacos.tenant.dynamic.mybatis;
import fast.cloud.nacos.tenant.enums.DS;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 myCatDataSource 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
return DS.MY_CAT;
}
};
/**
* 数据源的 key集合,用于切换时判断数据源是否存在
*/
public static List<Object> dataSourceKeys = new ArrayList<>();
/**
* 切换数据源
*
* @param key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
}
/**
* 获取数据源
*
* @return
*/
public static String getDataSourceKey() {
return contextHolder.get();
}
/**
* 重置数据源
*/
public static void clearDataSourceKey() {
contextHolder.remove();
}
/**
* 判断是否包含数据源
*
* @param key 数据源key
* @return
*/
public static boolean containDataSourceKey(String key) {
return dataSourceKeys.contains(key);
}
/**
* 添加数据源keys
*
* @param keys
* @return
*/
public static boolean addDataSourceKeys(Collection<?> keys) {
return dataSourceKeys.addAll(keys);
}
}
@Order设置的足够小是为了让他先执行
package fast.cloud.nacos.tenant.dynamic.mybatis;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect
@Order(-1)
@Slf4j
@Component
public class DynamicDataSourceAspect {
/**
* 切换数据源
* 获取类上面的bean,逻辑是如果类上面有,方法上面也有,默认用方法上面的进行覆盖
*
* @param point
* @param dataSource
*/
@Before("@within(dataSource)")
public void switchDataSource(JoinPoint point, DataSource dataSource) {
MethodSignature sign = (MethodSignature) point.getSignature();
Method method = sign.getMethod();
//获取方法上的注解
DataSource targetAnnotation;
targetAnnotation = method.getAnnotation(DataSource.class);
if (targetAnnotation == null) {
targetAnnotation = point.getTarget().getClass().getAnnotation(DataSource.class);
if (targetAnnotation == null) {
for (Class<?> cls : point.getClass().getInterfaces()) {
targetAnnotation = cls.getAnnotation(DataSource.class);
if (targetAnnotation != null) {
break;
}
}
}
}
if (targetAnnotation != null) {
dataSource = targetAnnotation;
}
if (!DynamicDataSourceContextHolder.containDataSourceKey(dataSource.value())) {
log.debug("DataSource [{}] doesn't exist, use default DataSource [{}] " + dataSource.value());
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value());
log.debug("Switch DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]");
}
}
/**
* 重置数据源
*
* @param point
* @param dataSource
*/
@After("@within(dataSource)")
public void restoreDataSource(JoinPoint point, DataSource dataSource) {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
log.debug("Restore DataSource to [" + DynamicDataSourceContextHolder.getDataSourceKey()
+ "] in Method [" + point.getSignature() + "]");
}
}
定义注解 DataSource
package fast.cloud.nacos.tenant.dynamic.mybatis;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 数据源key值
*
* @return
*/
String value();
}
package fast.cloud.nacos.tenant.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import fast.cloud.nacos.tenant.dynamic.mybatis.DataSource;
import fast.cloud.nacos.tenant.entity.DemoEntity;
import fast.cloud.nacos.tenant.enums.DS;
import java.util.List;
//@Mapper
@DataSource(DS.MY_CAT)
public interface DemoMapper extends BaseMapper<DemoEntity> {
List<DemoEntity> selectDemoPage(Page<DemoEntity> page);
}
package fast.cloud.nacos.tenant.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import fast.cloud.nacos.tenant.dynamic.mybatis.DataSource;
import fast.cloud.nacos.tenant.entity.TenantDemoEntity;
import fast.cloud.nacos.tenant.enums.DS;
import java.util.List;
//@Mapper
@DataSource(DS.TENANT_MANAGE)
public interface TenantDemoMapper {
@DataSource(DS.TENANT_MANAGE)
List<TenantDemoEntity> selectTenantDemoPage(Page<TenantDemoEntity> page);
}
package fast.cloud.nacos.tenant.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import fast.cloud.nacos.tenant.entity.DemoEntity;
import fast.cloud.nacos.tenant.entity.TenantDemoEntity;
import fast.cloud.nacos.tenant.mapper.DemoMapper;
import fast.cloud.nacos.tenant.mapper.TenantDemoMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RequestMapping("test-tenant")
@RestController
public class DemoController {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private DemoMapper demoMapper;
@Autowired
private TenantDemoMapper tenantDemoMapper;
@RequestMapping("mysql-query")
public List<DemoEntity> mysqlQuery() {
return demoMapper.selectList(new QueryWrapper<>());
}
@RequestMapping("mysql-tenant")
public List<TenantDemoEntity> mysqlQueryTenant() {
Page<TenantDemoEntity> page = new Page<>();
return tenantDemoMapper.selectTenantDemoPage(page);
}
}
二、访问t1租户的数据库
三、访问t2租户的数据库