引入mybatis-plus依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
引入mysql依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version>
</dependency>
<!-- mybatis-plus 多数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
springboot启动类。配置@MapperScan注解,用于扫描Mapper文件位置:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@MapperScan("com.wjbgn.user.mapper")
@SpringBootApplication(@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}))
public class RobNecessitiesUserApplication {
public static void main(String[] args) {
SpringApplication.run(RobNecessitiesUserApplication.class, args);
}
}
数据源配置,此处配置一主一从的环境,当前我只有一台,所以此处配置一样的:
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/rob_necessities?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
username: root
password: 123456
slave_1:
url: jdbc:mysql://127.0.0.1:3306/rob_necessities?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone =Asia/Shanghai
username: root
password: 123456
补充:这里面因为默认使用的是HikariCP数据源,目前也推荐使用这个,相比于druid有更高的性能,但是不能忽略下面的配置,否则服务会不断抛出异常,原因是数据库的连接时常和连接池的配置没有做好。
spring:
datasource:
dynamic:
hikari:
max-lifetime: 1800000
connection-timeout: 5000
idle-timeout: 3600000
max-pool-size: 12
min-idle: 4
connection-test-query: /**ping*/
配置文件当中配置了主从的方式,其实mybatis-plus还支持更多的方式:
1、多主多从
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master_1:
master_2:
slave_1:
slave_2:
slave_3:
2、多种数据库
spring:
datasource:
dynamic:
primary: mysql #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
mysql:
oracle:
postgresql:
h2:
sqlserver:
3、混合配置
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
datasource:
master_1:
slave_1:
slave_2:
oracle_1:
oracle_2:
可以注解在方法上或类上,同时存在就近原则 【方法上注解】 优先于 【类上注解】:
@DS("slave_1")
public class UserServiceImpl extends ServiceImpl<UserMapper, UserDO> implements IUserService {
@DS("salve_1")
@Override
public List<UserDO> getList() {
return this.getList();
}
@DS("master")
@Override
public int saveUser(UserDO userDO) {
boolean save = this.save(userDO);
if (save){
return 1;
}else{
return 0;
}
}
}
当注解添加到类上,意味着此类里的方法都使用此数据源;
当注解添加到方法上时,意味着此方法上使用的数据源优先级高于其他一切配置;
1、错误方法如下(伪代码):
@Slf4j
@Service("serviceA")
public class ServiceImpl implements Service{
/**
* 实现方法A
* @param
* @return
*/
@Override
@DS("database1")
public void functionA() {
...
functionB(); //调用B方法且切换为B方法的所需要的数据源
}
/**
* 实现方法B
* @param
* @return
*/
@Override
@DS("database2")
public void function B() {
}
}
如上图所示,A方法调用B方法时,B方法的@DS注解不生效,将完全被A方法的@DS("database1)所覆盖。
2、加入上@Transactiona注解方式:
@Transactional 是声明式事务管理 编程中使用的注解,实质是使用了 JDBC 的事务来进行事务控制的,基于Spring 的动态代理的机制
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
即可让Spring事务生效。
@Slf4j
@Service("serviceA")
public class ServiceImpl implements Service{
/**
* 实现方法A
* @param
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
@DS("database1")
public void functionA() {
...
functionB(); //调用B方法且切换为B方法的所需要的数据源
}
/**
* 实现方法B
* @param
* @return
*/
@Override
@DS("database2")
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void function B() {
}
}
但是,重点来了!!!!方法B仍然未生效!依然被A方法的@DS("database1)所覆盖。
错误的根源在于:上面的方法只适用于在同一个类中不同方法不同数据源的使用,不能使用事务,否则数据源不会切换。
正确的方式:创建一个新类,调用不同类中的方法
注意仍需要加上@Transactional来让事务生效,如下图(伪代码)所示:
@Slf4j
@Service("serviceA")
public class ServiceImplA implements ServiceA{
@Resource
ServiceImplB serviceImplB ;
/**
* 实现方法A
* @param
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
@DS("database1")
public void functionA() {
...
ServiceImplB.functionB(); //调用B类的B方法
}
}
@Slf4j
@Service("serviceB")
public class ServiceImplB implements ServiceB{
/**
* 实现方法B
* @param
* @return
*/
@Override
@DS("database2")
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public void function B() {
}
}
最终B方法成功获取database2的数据源