目录
前言
多数据源是一种常见的技术需求,它允许应用程序连接和操作多个不同的数据库或数据源。
通过自定义注解实现不同数据源直接的切换,不使用注解时默认使用主库数据源,在需要使用指定数据源的在调用Mapper的方法上加上自定义注解即可;
1、创建数据库(表)
分别在mysql中创建两个库:system
、log
,并且创建对应的表(根据项目实际情况的定),如下:
2、创建自定义注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 数据源名称
* @return
*/
String name();
}
3、配置yaml
spring:
application:
name: quartz
datasource:
# 数据源一
system:
driverClassName: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3307/system?useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: lhzlx
# 数据源二
log:
driverClassName: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3307/log?useUnicode=true&useSSL=false&zeroDateTimeBehavior=convertToNull&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: lhzlx
# hikari连接池
type: com.zaxxer.hikari.HikariDataSource
hikari:
#最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值
maximum-pool-size: 10
#最小空闲连接,默认值 10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
minimum-idle: 2
#连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒
connection-timeout: 60000
#空闲连接超时时间,默认值600000ms(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;
#不等于0且小于10秒,会被重置为10秒。
#只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放(自动释放过期链接)
idle-timeout: 600000
#连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
max-lifetime: 640000
#连接测试查询
connection-test-query: SELECT 1
#mapper 别名扫描
mybatis:
mapper-locations: classpath*:mappers/*.xml
type-aliases-package: com.lhz.demo.model.entity
#数据库类型
configuration.database-id: mysql
#自动驼峰转换
configuration.map-underscore-to-camel-case: true
注意: 在使用hikari
作为连接池时,如果配置多数据源需要将url
修改为jdbc-url
,使用druid
作为连接池则不需要修改;否则会出现jdbcUrl is required with driverClassName
错误;
4、DataSourceHolder
public class DataSourceHolder {
/**
* 使用ThreadLocal储存上下游的数据源名称
*/
private static final ThreadLocal<String> sourceName = new ThreadLocal<>();
/**
* 得到当前的数据库连接
* @return
*/
public static String getDataSource() {
return sourceName.get();
}
/**
* 设置当前的数据库连接
* @param name
*/
public static void setDataSource(String name) {
sourceName.set(name);
}
/**
* 清除当前的数据库连接
*/
public static void clearDataSource() {
sourceName.remove();
}
}
4、创建RoutingDataSource
public class RoutingDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSource();
}
}
5、数据源名称枚举定义
public enum DataSourceEnum {
/**
* 数据源名称,对应yaml中的数据源名称
*/
SYSTEM("system","系统默认数据源"),
LOG("log","日志数据源");
DataSourceEnum(String name, String desc) {
this.name = name;
this.desc = desc;
}
/**
* 数据源名称
*/
private final String name;
/**
* 描述
*/
private final String desc;
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
}
6、创建DataSourceAspect
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.lhz.demo.annotation.DataSource)")
public void dataSource() {}
@Before("dataSource()")
public void before(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method method = methodSignature.getMethod();
try {
// 添加默认数据源
String dataSource = DataSourceEnum.SYSTEM.getName();
boolean present = method.isAnnotationPresent(DataSource.class);
if(present){
DataSource annotation = method.getAnnotation(DataSource.class);
dataSource = annotation.name();
}
// 此处添加线程对应的数据源到上下文
// 在AbstractRoutingDataSource子类中拿到数据源, 加载后进行配置
DataSourceHolder.setDataSource(dataSource);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 清除数据源, 方法执行完成后, 清除数据源
*/
@After("dataSource()")
public void after(JoinPoint joinPoint) {
DataSourceHolder.clearDataSource();
}
}
7、数据源配置
@Configuration
public class DataSourceConfig {
/**
* yml中配置的数据源名称
* @return
*/
@Bean(name = "system")
@ConfigurationProperties(prefix = "spring.datasource.system")
public DataSource system() {
//database1数据源
return DataSourceBuilder.create().build();
}
@Bean(name = "log")
@ConfigurationProperties(prefix = "spring.datasource.log")
public DataSource log() {
//database2数据源
return DataSourceBuilder.create().build();
}
/**
* 指定主数据源
* @return
*/
@Bean(name="routingDataSource")
@Primary
public DataSource dataSource() {
RoutingDataSource routingDataSource = new RoutingDataSource();
// 设置默认数据源
routingDataSource.setDefaultTargetDataSource(system());
// 将数据源写入ThreadLocal
Map<Object, Object> dataSourceMap = new HashMap<>(4);
dataSourceMap.put(DataSourceEnum.SYSTEM.getName(), system());
dataSourceMap.put(DataSourceEnum.LOG.getName(), log());
routingDataSource.setTargetDataSources(dataSourceMap);
return routingDataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
}
8、Mapper接口
在com.lhz.demo.mapper
目录下创建LogMapper
与SysMapper
LogMapper:
@Mapper
public interface LogMapper {
List<TbLog> selectAll();
}
SysMapper:
@Mapper
public interface SysMapper {
List<TbSystem> selectAll();
}
9、Mappeing(XML)
在resources/mappers
目录下创建LogMapper.xml
与SystemMapper.xml
文件
LogMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lhz.demo.mapper.LogMapper">
<select id="selectAll" resultType="com.lhz.demo.model.entity.TbLog">
select * from tb_log
</select>
</mapper>
SystemMapper.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lhz.demo.mapper.SysMapper">
<select id="selectAll" resultType="com.lhz.demo.model.entity.TbSystem">
select * from tb_system
</select>
</mapper>
10、Service
@Service
public class TestService {
@Resource
private SysMapper sysMapper;
@Resource
private LogMapper logMapper;
/**
* 不指定数据源,模式使用system数据源
* @return
*/
public Object sys() {
return sysMapper.selectAll();
}
/**
* 指定数据源查询,该注解也可以放在Mapper接口上,表示整个Mapper接口都使用指定数据源
* @return
*/
@DataSource(name = "log")
public Object log() {
return logMapper.selectAll();
}
}
11、Controller
@RestController
@RequestMapping("/test")
@Slf4j
public class TestController {
@Resource
private TestService testService;
/**
* 查询system库
* @return
*/
@GetMapping("/sys")
public Object sys() {
return testService.sys();
}
/**
* 查询log库
* @return
*/
@GetMapping("/log")
public Object log() {
return testService.log();
}
}
12、配置启动类
Application
启动类需要屏蔽DataSource
自动配置,修改如下:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication .class, args);
}
}
13、目录结构
14、测试
启动项目分别访问`http://localhost:9090/test/sys`与`http://localhost:9090/test/log`均有数据输出,则表示配置成功;