文章目录
-
- 前言
- 业务逻辑
- 使用Mybatis实现
- 使用Mybatis-plus实现
前言
工作的时候,遇到了需要将一个数据库的一些数据插入或更新到另一个数据库。一开始使用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 (baomidou.com)
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();