Mybatis-plus配置多数据源,连接多数据库方式
作者:Cz范特西
前言
工作的时候,遇到了需要将一个数据库的一些数据插入或更新到另一个数据库。
一开始使用
insert into TABLE (col1,col2) VALUES (val1,val2) ON DUPLICATE KEY update col1 = "val1";
(这句sql语句的意思是:将val1,val2值插入到TABLE表的col1和col2字段中,如果出现主键或唯一冲突,就进行更新,只将col1值更新为val1)进行数据的插入和更新。
但是每次都要对着这一条sql语句进行修改,十分麻烦,就想着能否同时连接两个数据库进行业务处理。
业务逻辑
使用Mybatis实现
首先,如果你的项目用的是Mybatis,那么以下配置可以实现配置多数据源,连接多数据库的作用。但是,如果你使用的是Mybatis-plus,本人建议使用Mybatis-plus实现更加简单易操作。
1、在yml配置文件中配置多数据库
例如:
spring: application: name: CONNECTION datasource: db1: driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://localhost:3306/test?serverTimezone=UTC username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource db2: driver-class-name: com.mysql.cj.jdbc.Driver jdbc-url: jdbc:mysql://11.11.11.11:3306/test?serverTimezone=UTC username: root password: 654321 type: com.alibaba.druid.pool.DruidDataSource
注意,将数据库配置中的url改为jdbc-url,否则无法配置多数据源。
2、创建不同的mapper,用于不同的数据库
3、编写数据源的配置类
例如:
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.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = "com.czf.connect.mapper.db1", sqlSessionTemplateRef = "db1SqlSessionTemplate") //此处的basePackages指向的是你存放数据库db1的mapper的包 public class DataSource1Config { @Bean(name = "db1DataSource") @ConfigurationProperties(prefix = "spring.datasource.db1")//指向yml配置文件中的数据库配置 @Primary //主库加这个注解,修改优先权,表示发现相同类型bean,优先使用该方法。 public DataSource dbDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "db1SqlSessionFactory") @Primary public SqlSessionFactory dbSqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*xml")); //这个的getResources指向的是你的mapper.xml文件,相当于在yml中配置的mapper-locations,此处配置了yml中就不用配置,或者说不会读取yml中的该配置。 return bean.getObject(); } @Bean(name = "db1TransactionManager") @Primary public DataSourceTransactionManager dbTransactionManager(@Qualifier("db1DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "db1SqlSessionTemplate") @Primary public SqlSessionTemplate dbSqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
数据库db2的配置类:
@Configuration @MapperScan(basePackages = "com.czf.connect.mapper.db2", sqlSessionTemplateRef = "db2SqlSessionTemplate") public class DataSource2Config { @Bean(name = "db2DataSource") @ConfigurationProperties(prefix = "spring.datasource.db2") public DataSource dbDataSource() { return DataSourceBuilder.create().build(); } @Bean(name = "db2SqlSessionFactory") public SqlSessionFactory dbSqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); bean.setDataSource(dataSource); bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*xml")); return bean.getObject(); } @Bean(name = "db2TransactionManager") public DataSourceTransactionManager dbTransactionManager(@Qualifier("db2DataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean(name = "db2SqlSessionTemplate") public SqlSessionTemplate dbSqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception { return new SqlSessionTemplate(sqlSessionFactory); } }
整体结构:
至此,需要修改或查询哪个数据库,只需要在对应的com///mapper/db包中创建对应的mapper类或者编写特定的sql语句即可。
使用Mybatis-plus实现
Mybatis-plus官网很清楚的告诉了我们如何配置多数据源。
1、引入dynamic-datasource-spring-boot-starter
<dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>${version}</version> </dependency>
2、配置数据源
spring: datasource: dynamic: primary: mysql1 #设置默认的数据源或者数据源组,默认值即为mysql1 strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: mysql1: url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 mysql2: url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver mysql2: url: ENC(xxxxx) # 内置加密,使用请查看详细文档 username: ENC(xxxxx) password: ENC(xxxxx) driver-class-name: com.mysql.jdbc.Driver #......省略
3、使用 @DS 切换数据源
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。
注解 | 结果 |
---|---|
没有@DS | 默认数据源 |
@DS(“dsName”) | dsName可以为组名也可以为具体某个库的名称 |
@Service @DS("mysql1") public class UserServiceImpl implements UserService { @Autowired private JdbcTemplate jdbcTemplate; public List selectAll() { return jdbcTemplate.queryForList("select * from user"); } @Override @DS("mysql2") public List selectByCondition() { return jdbcTemplate.queryForList("select * from user where age >10"); } }
在这里会有个小问题,假如我编写了两个方法,方法A使用的是 @DS(“mysql1”) ,功能是查询mysql1中的数据;方法B调用的是@DS(“mysql2”),是将数据插入到mysql2数据库中,那么我想在方法B中调用方法A,实现mysql1中查询的数据插入到mysql2中,能够成功吗?
答案是:不可以。
要想实现这个功能,我们可以使用多数据源的一个类,简单来说是一个队列,将需要使用到的数据源push进行,不用时再poll掉。就不用使用@DS注解了。
比如:
@RequestMapping("/Bmetohd") public int Bmethod(){ DynamicDataSourceContextHolder.push("mysql1"); List<User> users = Amethod(); DynamicDataSourceContextHolder.poll(); DynamicDataSourceContextHolder.push("mysql2"); int num = 0; for(User user: users){ int i = User2Mapper.insert(user); num += i; } DynamicDataSourceContextHolder.poll(); return num; }
重点:
DynamicDataSourceContextHolder.push("mysql1"); //业务代码 DynamicDataSourceContextHolder.poll();
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。