MybatisPlus【数据源配置(单、多数据源)】
文章目录
前言
MybatisPlus多数据源配置主要解决的是多数据库连接和切换的问题。在一些大型应用中,由于数据量的增长或者业务模块的增多,可能需要访问多个数据库。这时,就需要配置多个数据源。
一、单数据源配置
使用方法
坐标依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
application.yml配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
通用配置
配置类
@Configuration
@MapperScan(basePackages = {"com.xx.**.mapper"})
public class MybatisPlusConfig {
public MybatisPlusInterceptor mybatisPlusInterceptor(){
//新的分页插件配置方法(Mybatis Plus 3.4.0版本及其之后的版本)
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
二、多数据源配置
坐标依赖【新增】:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
application.yml配置
spring:
datasource:
db1:
url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
db2:
url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
1.单配置类
使用方法【配合注解@DS(“db1”)】
@DS(“db1”)是一个MyBatis的注解,用于动态数据源切换。在MyBatis中,我们可以通过在mapper.xml文件或者Java接口上使用@DS注解来指定数据源。
实体类
@Data
@TableName("tb_user")
public class User {
@TableId(type = IdType.AUTO)
private Integer id;
@TableField("name")
private String name;
}
Service
@Service
@DS("db1")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("db1_1")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
2.多配置类
使用方法【根据不同Mapper路径配置不同数据源】
多配置类【根据自己需要使用的插件进行配置、不需要的可以删除】
@Configuration
@MapperScan(basePackages = "com.xx.xx.mapper.db1", sqlSessionFactoryRef = "db1SqlSessionFactory")
public class db1DataSourceConfig {
// 将这个对象放入Spring容器中
@Bean(name = "db1DataSource")
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource getDateSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db1SqlSessionFactory")
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource datasource)
throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
//configuration配置bean
//MybatisConfiguration configuration = new MybatisConfiguration();
//configuration.setMapUnderscoreToCamelCase(true);
//configuration.setCacheEnabled(false);
// 配置打印sql语句s
//configuration.setLogImpl(StdOutImpl.class);
// 添加自定义SQL注入
//bean.setConfiguration(configuration);
//插件对象
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
//动态表名
//DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
//可以传多个表名参数,指定哪些表使用MonthTableNameHandler处理表名称
//dynamicTableNameInnerInterceptor.setTableNameHandler(new MonthTableNameHandler("t_table_name"));
//以拦截器的方式处理表名称
//可以传递多个拦截器,即:可以传递多个表名处理器TableNameHandler
//mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
//分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
bean.setDataSource(datasource);
// 设置mybatis的xml所在位置
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/db1/*.xml"));
bean.setPlugins(mybatisPlusInterceptor);
return bean.getObject();
}
@Bean("db1SqlSessionTemplate")
public SqlSessionTemplate db1SqlSessionTemplate(
@Qualifier("db1SqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
@Bean("db1TransactionManager")
public PlatformTransactionManager db1TransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Configuration
@MapperScan(basePackages = "com.xx.xx.mapper.db2", sqlSessionFactoryRef = "db2SqlSessionFactory")
public class db2DataSourceConfig {
// 将这个对象放入Spring容器中
@Bean(name = "db2DataSource")
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource getDateSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean(name = "db2SqlSessionFactory")
public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource datasource)
throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
// 设置mybatis的xml所在位置
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/db2/*.xml"));
bean.setPlugins(mybatisPlusInterceptor);
return bean.getObject();
}
@Bean("db2SqlSessionTemplate")
public SqlSessionTemplate db2SqlSessionTemplate(
@Qualifier("db2SqlSessionFactory") SqlSessionFactory sessionFactory) {
return new SqlSessionTemplate(sessionFactory);
}
@Bean("db2TransactionManager")
public PlatformTransactionManager db2TransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
数据源为db1
package com.xx.xx.mapper.db1;
/**
Mapper 接口
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
启动类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DynamicApplication{
public static void main(String[] args) {
SpringApplication.run(DynamicApplication.class, args);
}
}
3.AOP动态切换数据源
使用方法【自定义注解方式】
动态数据源切换类
@NoArgsConstructor
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> DB_CONTEXT_HOLDER = new ThreadLocal<>();
/**
* 取得当前使用的数据源
* @return 当前使用的数据源
*/
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
/**
* 设置数据源
* @param dataSource 数据源
*/
public static void setDataSource(String dataSource) {
DB_CONTEXT_HOLDER.set(dataSource);
}
/**
* 获取当前数据源
* @return 数据源
*/
public static String getDataSource() {
return DB_CONTEXT_HOLDER.get();
}
/**
* 清除上下文
*/
public static void clearDataSource() {
DB_CONTEXT_HOLDER.remove();
}
/**
* 设置默认数据源,和可切换的数据源Map
* @param defaultTargetDataSource 默认数据源
* @param targetDataSources 可切换的数据源Map
*/
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
}
多数据源配置类
@Component
@Configuration
public class DataSourceConfig {
/**
* 创建数据源db1
* @return 数据源db1
*/
@Bean(name = "db1")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.db1")
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
/**
* 创建数据源db2
* @return 数据源db2
*/
@Bean(name = "db2")
@ConfigurationProperties(prefix = "spring.datasource.dynamic.datasource.db2")
public DataSource db2DataSource() {
return DataSourceBuilder.create().build();
}
/**
* 数据源配置
* @param db1数据源db1
* @param db2数据源db2
* @return 动态数据源切换对象。
* @Description @Primary赋予该类型bean更高的优先级,使至少有一个bean有资格作为autowire的候选者。
*/
@Bean
@Primary
public DataSource dataSource(@Qualifier("db1") DataSource db1,
@Qualifier("db2") DataSource db2) {
Map<Object, Object> dsMap = new HashMap<>(2);
dsMap.put("db1", db1);
dsMap.put("db2", db2);
return new DynamicDataSource(db1, dsMap);
}
}
启动类
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@Import({DataSourceConfig.class})
public class DynamicApplication{
public static void main(String[] args) {
SpringApplication.run(DynamicApplication.class, args);
}
}
创建自定义注解【用来指定使用哪个数据源】
/**
* @Description 在方法上使用,用于指定使用哪个数据源
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}
创建AOP【根据注解的参数切换数据源】
/**
* @Description 多数据源切换AOP,@Order(-100)是为了保证AOP在事务注解之前生效,Order的值越小,优先级越高
*/
@Aspect
@Component
@Order(-100)
@Slf4j
public class DataSourceAspect {
@Pointcut("execution(* com.xx.xx.service..*.*(..))")
private void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取当前指定的数据源
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
DataSource dataSource = method.getAnnotation(DataSource.class);
if (Objects.isNull(dataSource)) {
// 使用默认数据源
DynamicDataSource.setDataSource("db1");
log.info("默认数据源启动");
} else {
// 匹配到的话,设置到动态数据源上下文中
DynamicDataSource.setDataSource(dataSource.name());
log.info("匹配到数据源:{}", dataSource.name());
}
try {
// 执行目标方法,返回值即为当前方法的返回值
return joinPoint.proceed();
} finally {
// 方法执行完毕之后,销毁当前数据源信息,进行垃圾回收
DynamicDataSource.clearDataSource();
log.info("清空数据源");
}
}
}
Service
@Service
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
@DataSource(name = "db1")
public List selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DataSource(name = "db2")
public List selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
总结
1、三种数据源配置的方法可根据实际需要选择
2、使用 @DS 注解切换数据源时,使用springboot数据源的自动配置,需要将DataSourceConfig配置注释掉
3、使用 @DataSource 自定义注解时,排除springboot数据源的自动配置,引入DataSourceConfig配置