1.简介
当微服务需要连接多个数据源到时候,我们需要手动去创建相应的SqlSessionFactory、DataSourceTransactionManager、SqlSessionTemplate 对象并交给spring托管,以及创建相应的mapper,若想添加自定义的拦截器,我们可以在相应的SqlSessionFactory对象对拦截器进行赋值,下面给大家将一下如何配置多数据源以及添加自定义拦截器
2.代码
2.1 配置文件
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/test_service?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=true&autoReconnect=true
username: ***
password: ****
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
keepalive-time: 30000
max-lifetime: 14400000
mapper: **** //这个为自定义的mapper文件包路径
xml: mybatis/mapper/*.xml /*注意:xml文件一定是要在resources目录下*/
interceptor: aaaa,bbbb //这个为自定义的拦截器的名称(若拦截器在第三方jar里,此时名称需要写拦截器的完整包路径+类名,若拦截器在此项目中,只需要写ioc容器中的类名即可)
slave:
url: jdbc:mysql://localhost:3306/test_service?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=true&autoReconnect=true
username: ***
password: ****
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
keepalive-time: 30000
max-lifetime: 14400000
mapper: **** //这个为自定义的mapper文件包路径
xml: mybatis/mapper/*.xml /*注意:xml文件一定是要在resources目录下*/
interceptor: aaaa,bbbb //这个为自定义的拦截器的名称(若拦截器在第三方jar里,此时名称需要写拦截器的完整包路径+类名,若拦截器在此项目中,只需要写ioc容器中的类名即可)
2.2 自定义mapper
需要创建两个不同的mapper对象,例如:
注意:配置文件中的mapper字段只能填到TeacherMapper/StudentMapper文件的包路径,即com.*.*.mapper.one/com.*.*.mapper.two ,因为后续的@MapperScan注解扫描的是包路径
2.3 配置类
primary配置文件
package com.example.aopservice.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author zhl
* @date 2023/12/7 16:28
*/
@MapperScan(basePackages = "${spring.datasource.primary.mapper}",sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class DataSourcePrimaryConfig {
@Value("${spring.datasource.primary.interceptor:}")
private String interceptorName;//当这个字段不为空时,会给SqlSessionFactory添加自定义的拦截器
@Value("${spring.datasource.primary.xml}")
private String xmlPath; //xml路径
@Resource
private Map<String,Interceptor> map;//将所有Interceptor对象都注入,为后续根据类名取出相应拦截器并赋值到SqlSessionFactory
@Bean
@ConfigurationProperties(prefix = "spring.datasource.primary")
@Primary
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:"+xmlPath));
if(interceptorName!=null && !"".equals(interceptorName)){
List<String> collect = Stream.of(interceptorName.split(",")).collect(Collectors.toList());
Interceptor[] interceptors = collect.stream().map(name -> map.get(name)).toArray(Interceptor[]::new);
bean.setPlugins(interceptors);
}
return bean.getObject();
}
@Bean
@Primary
public DataSourceTransactionManager db1TransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
@Primary
public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
slave配置文件
package com.example.aopservice.config;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.Interceptor;
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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author zhl
* @date 2023/12/7 16:32
*/
@MapperScan(basePackages = "${spring.datasource.slave.mapper}",sqlSessionTemplateRef = "db2SqlSessionTemplate")
public class DataSourceSlaveConfig {
@Value("${spring.datasource.slave.interceptor:}")
private String interceptorName;//当这个字段不为空时,会给SqlSessionFactory添加自定义的拦截器
@Value("${spring.datasource.slave.xml}")
private String xmlPath;
@Resource
private Map<String,Interceptor> map;//将所有Interceptor对象都注入,为后续根据类名取出相应拦截器并赋值到SqlSessionFactory
@Bean
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:"+xmlPath));
if(interceptorName!=null && !"".equals(interceptorName)){
List<String> collect = Stream.of(interceptorName.split(",")).collect(Collectors.toList());
Interceptor[] interceptors = collect.stream().map(name -> map.get(name)).toArray(Interceptor[]::new);
bean.setPlugins(interceptors);
}
return bean.getObject();
}
@Bean
public DataSourceTransactionManager db2TransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public SqlSessionTemplate db2SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
注意:mapper对应的xml文件在一定要resources文件下的mybatis/mapper文件夹下面(后续也可以让使用者动态添加xml文件的路径,暂时没实现,后续有时间会补充一下) 已补充
2.4 动态添加自定义拦截器
上述代码大家可以看到,我们把所有的Interceptor对象都注入进每个config文件中了,每个config文件根据配置文件里面的interceptorName,可以动态添加我们想要的拦截器。
3.总结
上述为配置多数据源以及如何动态添加自定义拦截器,其实我是将这两个配置文件分装成了一个自定义jar,后续多数据源情况可以直接在pom文件引入jar就可以。
为什么称之为动态呢,是因为我在封装jar的时候在每个配置类没有添加@Component注解,而是通过在springboot启动类上添加了@Import注解创建的config对象,因为大多数情况都是单一数据源,而且我之前写过一篇文章springboot+mybatis拦截器+自定义注解实现数据脱敏-CSDN博客我在封装自定义jar的时候也把那两个拦截器封装进去了,那两个拦截器同样没有加@Component注解,即没有交给spring托管,都是通过@Import注解手动去引入的。
以上就是如何配置多数据源及动态添加自定义拦截器,有问题的小伙伴请留言