转载请注明出处!
yml文件:这个是配置是别人写的,我只是把数据源改了,贴这个的原因是因为在数据源切换的是后token出问题了,原因就在这个配置里
spring:
datasource:
druid:
# 数据库 1
db1:
url: jdbc:sqlserver://localhost:1433;DatabaseName=db1
username: sa
password: 123
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
initial-size: 1
max-active: 20
min-idle: 1
# 数据库 2
db2:
url: jdbc:sqlserver://localhost:1433;DatabaseName=db2
username: sa
password: 123
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
initialSize: 1
minIdle: 1
maxActive: 20
mybatis:
type-aliases-package: com.demo.entity
mapper-locations: classpath:/com/demo/mapper/xml/*.xml
configuration:
callSettersOnNulls: true
map-underscore-to-camel-case: true
default-fetch-size: 100
default-statement-timeout: 3000
mybatis-plus:
# 如果是放在src/main/java目录下 classpath:/com/yourpackage/*/mapper/*Mapper.xml
# 如果是放在resource目录 classpath:/mapper/*Mapper.xml
mapper-locations: classpath:/com/demo/mapper/xml/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.demo.entity
global-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: 0
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: 2
#驼峰下划线转换
db-column-underline: true
#刷新mapper 调试神器
refresh-mapper: true
#数据库大写下划线转换
capital-mode: true
# Sequence序列接口实现类配置
# key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
#逻辑删除配置(下面3个配置)
logic-delete-value: 1
logic-not-delete-value: 0
sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
#自定义填充策略接口实现
# meta-object-handler: com.baomidou.springboot.MyMetaObjectHandler
configuration:
#配置返回数据库(column下划线命名&&返回java实体是驼峰命名),自动匹配无需as(没开启这个,SQL需要写as: select user_id as userId)
map-underscore-to-camel-case: true
cache-enabled: false
# JWT 认证配置
jwt:
header: Authorization
secret: MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY=
expiration: 604800 #token七天不过期
# expiration: 3600 #token一小时不过期
tokenHead: "Bearer "
exceptUrl: "/auth/**"
MybatisPlusConfig 配置:
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.plugins.PerformanceInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
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;
import java.util.Map;
@Configuration
@MapperScan(basePackages = { "com.demo.mapper" })
public class MybatisPlusConfig {
/*
* 分页插件,自动识别数据库类型
* 多租户,请参考官网【插件扩展】
*/
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor();
}
/***
* plus 的性能优化
* @return
*/
@Bean
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
/*<!-- SQL 执行性能分析,开发环境使用,线上不推荐。 maxTime 指的是 sql 最大执行时长 -->*/
//performanceInterceptor.setMaxTime(1000);
/*<!--SQL是否格式化 默认false-->*/
performanceInterceptor.setFormat(true);
return performanceInterceptor;
}
@Bean(name = "db1")
@ConfigurationProperties(prefix = "spring.datasource.druid.db1" )
public DataSource db1 () {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.druid.db2" )
public DataSource db2 () {
return DruidDataSourceBuilder.create().build();
}
/**
* 动态数据源配置
* @return
*/
@Bean
@Primary
public DataSource multipleDataSource (@Qualifier("db1") DataSource db1,
@Qualifier("db2") DataSource db2 ) {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map< Object, Object > targetDataSources = new HashMap<>();
targetDataSources.put(DBTypeEnum.db1.getValue(), db1 );
targetDataSources.put(DBTypeEnum.db2.getValue(), db2);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.setDefaultTargetDataSource(db1);
return dynamicDataSource;
}
/**下面的我注了,yml文件里面有配置,不注释掉会报错*/
/* @Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
MybatisSqlSessionFactoryBean sqlSessionFactory = new MybatisSqlSessionFactoryBean();
sqlSessionFactory.setDataSource(multipleDataSource(db1(),db2()));
MybatisConfiguration configuration = new MybatisConfiguration();
//configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
configuration.setJdbcTypeForNull(JdbcType.NULL);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setCacheEnabled(false);
sqlSessionFactory.setConfiguration(configuration);
sqlSessionFactory.setPlugins(new Interceptor[]{ //PerformanceInterceptor(),OptimisticLockerInterceptor()
paginationInterceptor() //添加分页功能
});
sqlSessionFactory.setGlobalConfig(globalConfiguration());
return sqlSessionFactory.getObject();
}
@Bean
public GlobalConfiguration globalConfiguration() {
GlobalConfiguration conf = new GlobalConfiguration(new LogicSqlInjector());
conf.setLogicDeleteValue("1");
conf.setLogicNotDeleteValue("0");
conf.setIdType(0);
*//*conf.setMetaObjectHandler(new MyMetaObjectHandler());*//*
conf.setDbColumnUnderline(true);
conf.setRefresh(true);
return conf;
}*/
}
DataSourceSwitchAspect 配置:动态改变数据源
import lombok.extern.slf4j.Slf4j;
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
@Aspect
@Order(-100) //这是为了保证AOP在事务注解之前生效,Order的值越小,优先级越高
@Slf4j
public class DataSourceSwitchAspect {
@Pointcut("execution(* com.demo.service.db1..*.*(..))")
private void db1Aspect() {
}
@Pointcut("execution(* com.demo.service.db2..*.*(..))")
private void db2Aspect() {
}
@Before( "db1Aspect()" )
public void db1() {
log.info("切换到db1 数据源...");
DbContextHolder.setDbType(DBTypeEnum.db1);
}
@Before("db2Aspect()" )
public void db2 () {
log.info("切换到db2 数据源...");
DbContextHolder.setDbType(DBTypeEnum.db2);
}
}
DbContextHolder 配置:
public class DbContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal<>();
/**
* 设置数据源
* @param dbTypeEnum
*/
public static void setDbType(DBTypeEnum dbTypeEnum) {
contextHolder.set(dbTypeEnum.getValue());
}
/**
* 取得当前数据源
* @return
*/
public static String getDbType() {
return (String) contextHolder.get();
}
/**
* 清除上下文数据
*/
public static void clearDbType() {
contextHolder.remove();
}
}
DBTypeEnum 配置:
public enum DBTypeEnum {
db1("db1"), db2("db2");
private String value;
DBTypeEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
DynamicDataSource 配置:
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 取得当前使用哪个数据源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}
}
启动类:一定要加 exclude={DataSourceAutoConfiguration.class}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@SpringBootApplication()
@EnableAutoConfiguration(exclude={
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
})
@ServletComponentScan
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这样就算配好了,两个数据源要对应的mapper,service要分开写,这样的例子百度多的很。大家可以借鉴,我也是百度然后根据项目改改。
接下要说的是我这个项目在切换数据源时数据库报 无效的‘sys_user’对象,很纳闷。
开始以为是数据源没有配好,百度了一番数据源的配置没有问题,后来同事说可能是token验证的问题,想了一下又觉得跟token没什么关系,token在Local Storage里面存的跟数据源切换没什么关系。
又开始找数据源的问题,改各种配置弄了一下午没什么结果。后来debug跟进去再结合代码发现baseServiceImpl每个方法执行前都会进行token验证。
问题就在这里,在数据源切换的时候用户验证报错了,打印了一下token,发现数据源切换时token不一样,切换数据源db2时token 前面多了一个“Bearer ”,就是前面yml里面最后的jwt 配置
tokenHead:“Bearer ”,而请求数据源db1前面并没有,导致token在获取用户名失败。问题找到了就自然有解决办法了。