springboot 配置动态切换多数据源
近来由于公司的一个项目需要用到多数据源,在其中遇到了一些问题,特在此记录。关于mybatis的配置,请自行百度。
目录
配置多数据源
配置连接数据库信息
在yml中或者自定义一个配置文件properties,填入以下信息
//数据源1,jurisdiction用于区别不同数据源
spring.datasource.jurisdiction.driverClassName=
spring.datasource.jurisdiction.url=
spring.datasource.jurisdiction.username=
spring.datasource.jurisdiction.password=
//数据源2,base用于区别不同数据源
spring.datasource.base.driverClassName=
spring.datasource.base.url=
spring.datasource.base.username=
spring.datasource.base.password=
定义存储数据源key值的类
新建一个DataSourceContextHolder.java的类,代码如下:
@Slf4j
public class DataSourceContextHolder {
//线程安全,存储数据源的key值
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
//设置当前的数据源
public static void setDatasource(String dbType){
log.info("切换到 [ {} ] 数据源",dbType);
contextHolder.set(dbType);
}
//获取当前数据源
public static String getDatasource(){
return contextHolder.get();
}
//清除当前数据源
public static void clearDatasource(){
contextHolder.remove();
}
}
定义数据源的常量类
新建一个DataSourceType.java的枚举类,添加需要用到的数据源的key值。
定于数据源注解
用于注解方法或注解,切换数据源,设置默认数据源的key,代码如下:
@Target({ElementType.ANNOTATION_TYPE,ElementType.METHOD})
public @interface DataSource {
//DataSourceType为自定义的数据源常量类
//JURISDICTION为我的系统的主数据源的key
DataSourceType value() default DataSourceType.JURISDICTION;
}
定义数据源注解的切面类
新建一个DataSourceHandle.java类,用于获取@DataSource注解的数据源信息,切换数据源,代码如下:
/**
* 需要将该类注册为组件,注册到spring中,让spring管理该类
*/
@Component
@Aspect
public class DataSourceHandle{
//定义切面,指被@DataSource注解的方法被拦截
@Pointcut("@annotation(com.example.demo.aspect.DataSource)")
public void pointcut(){ }
//该方法在进入被@DataSource注解的方法前执行,切换数据源
@Before("pointcut()")
public void changeDataSource(JoinPoint point){
Class clazz = point.getTarget().getClass();
String methodName = point.getSignature().getName();
Class[] classes = ((MethodSignature) point.getSignature()).getParameterTypes();
try {
Method method = clazz.getMethod(methodName,classes);
if (method.isAnnotationPresent(DataSource.class)){
DataSource dataSource = method.getAnnotation(DataSource.class);
DataSourceType dataSourceType = dataSource.value();
DataSourceContextHolder.setDatasource(dataSourceType.getValue());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
//该方法在进入被@DataSource注解的方法前执行,清除当前数据源
@After("pointcut()")
public void clearDatasource(){
DataSourceContextHolder.clearDatasource();
}
}
定义数据源配置类
新建一个DruidConfiguration.java的类,添加需要使用的数据源,及动态数据源。代码如下:
@Configuration
@PropertySource(value = "classpath:jdbc.properties")//自定义的数据库连接配置文件
public class DruidConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(DruidConfiguration.class);
//数据源1,该数据源连接配置的前缀为spring.datasource.jurisdiction
@Bean("jurisdictionDatasource")
@ConfigurationProperties(prefix = "spring.datasource.jurisdiction")
public DataSource jurDataSource() {
return new DruidDataSource();
}
//数据源2,该数据源连接配置的前缀为spring.datasource.base
@Bean("beanDatasource")
@ConfigurationProperties(prefix = "spring.datasource.base")
public DataSource baseDatasource(){
return new DruidDataSource();
}
//动态数据源
@Bean("dynamicDataSource")
@Primary//表示优先使用该bean
public DataSource dynamicDataSource(){
DynamicDataSource dynamicDataSource = new DynamicDataSource();
//配置动态数据源的主数据源
dynamicDataSource.setDefaultTargetDataSource(jurDataSource());
//配置动态数据源中使用到的数据源
Map<Object,Object> dataSources = new HashMap<>(2);
dataSources.put(DataSourceType.JURISDICTION.getValue(),jurDataSource());
dataSources.put(DataSourceType.BASE.getValue(),baseDatasource());
dynamicDataSource.setTargetDataSources(dataSources);
return dynamicDataSource;
}
//配置动态使用事务
@Bean
public PlatformTransactionManager platformTransactionManager(){
return new DataSourceTransactionManager(dynamicDataSource());
}
}
yml文件中mybatis的配置
在application.yml配置mybatis需要的.xml文件和entity包的位置,如下:
mybatis:
mapper-locations:
- classpath*: #.xml的位置
type-aliases-package: #
configuration:
map-underscore-to-camel-case: true # 下划线转驼峰
关闭springboot自动的数据源配置
由于springboot会自动添加数据源的配置,需要关闭该功能,不然不能加载自定义的多数据源配置,可在启动类中添加下面代码:
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
部分需要的依赖包
<!--数据库需要的依赖包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!--aop需要的依赖包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--lombok插件需要的依赖包,简化setter和getter等代码-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
到此为止,动态多数据源配置完成。
多数据源的使用
只需在需要切换数据源的方法上添加@DataSource(需要切换的数据源)即可,若果时使用主数据源,不用添加注解。
遇到的问题
dao包中的方法找不到对应的xml中的SQL语句
可能问题1:dao包中的方法与xml文件中的id对应不上
解决办法:认真对照dao文件中的方法与xml文件中id是否有单词拼写错误等
可能问题2:xml文件没有被打包
原因:springboot默认java包下是java文件,xml放在resource包中,由于xml文件与dao文件放在一起,在java包内,导致xml不能被打包。
解决办法1:把xml文件放到resource包下,修改application.yml中的xml文件的路径。
解决办法2:在pom.xml文件中加入以下代码:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/webapp</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
自定义数据源注解与@Transaction同时使用时不生效
可能问题:自定义数据源注解与@Transaction注解同一个方法,会先执行@Transaction注解,即获取数据源在切换数据源之前,所以会导致自定义注解失效。
解决方法:当需要使用事务及切换数据源注解时,要将自定义注解在被@Transaction注解方法的调用方
如:在service中使用@Transaction,需要在controller中使用切换数据源注解。