springboot+dynamic多数据源配置
dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器。
一、配置
- pom引入
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version
</dependency>
示例:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>2.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.18</version>
</dependency>
配置数据源
spring:
datasource:
# 可以先在这里配置好通用的配置,比如
druid:
initial-size: 10
max-active: 100
min-idle: 3
max-wait: 5000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 100
#dynamic开始多数据源配置
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
datasource:
master: # 数据源的名字:master
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
slave_1: # 数据源的名字:slave_1
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
slave_2:
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://xx.xx.xx.xx:3308/dynamic
#......省略
#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
主类修改
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
/**
*修改主类的SpringBootApplication注解
*mybatisplus配置动态数据源时,切记需要关闭自带的自动数据源配置
**/
@SpringBootApplication(exclude = DruidDataSourceAutoConfigure.class)
二、使用
使用@DS切换数据源。
@DS可以注解在方法上和类上,同时存在方法注解优先于类上注解。
免去了sql工厂的配置,直接使用@DS就可以注明是哪个数据源,超级方便,mybatis这种直接配置到Mapper层即可。
注解 | 结果 |
没有@DS | 默认数据源 |
@DS("dsName") | dsName可以为组名也可以为具体某个库的名称 |
@Service
@DS("slave_2")
public class UserServiceImpl implements UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
public List<Map<String, Object>> selectAll() {
return jdbcTemplate.queryForList("select * from user");
}
@Override
@DS("slave_1")
public List<Map<String, Object>> selectByCondition() {
return jdbcTemplate.queryForList("select * from user where age >10");
}
}
官方地址:https://mp.baomidou.com/guide/dynamic-datasource.html
三、 多数据源事务控制
使用@DSTransactional
示例:
public class OrderService {
private final OrderMapper orderMapper;
private final AccountService accountService;
private final ProductService productService;
@DSTransactional
public void placeOrder(PlaceOrderRequest request) {
log.info("=============ORDER START=================");
Long userId = request.getUserId();
Long productId = request.getProductId();
Integer amount = request.getAmount();
log.info("收到下单请求,用户:{}, 商品:{},数量:{}", userId, productId, amount);
log.info("当前 XID: {}", TransactionContext.getXID());
Order order = Order.builder()
.userId(userId)
.productId(productId)
.status(OrderStatus.INIT)
.amount(amount)
.build();
orderMapper.insert(order);
log.info("订单一阶段生成,等待扣库存付款中");
// 扣减库存并计算总价
Double totalPrice = productService.reduceStock(productId, amount);
// 扣减余额
accountService.reduceBalance(userId, totalPrice);
order.setStatus(OrderStatus.SUCCESS);
order.setTotalPrice(totalPrice);
orderMapper.updateById(order);
log.info("订单已成功下单");
log.info("=============ORDER END=================");
}
}
public class ProductService {
private final ProductMapper productMapper;
@DS("product")
public Double reduceStock(Long productId, Integer amount) {
log.info("=============PRODUCT START=================");
log.info("当前 XID: {}", TransactionContext.getXID());
// 检查库存
Product product = productMapper.selectById(productId);
Assert.notNull(product, "商品不存在");
Integer stock = product.getStock();
log.info("商品编号为 {} 的库存为{},订单商品数量为{}", productId, stock, amount);
if (stock < amount) {
log.warn("商品编号为{} 库存不足,当前库存:{}", productId, stock);
throw new RuntimeException("库存不足");
}
log.info("开始扣减商品编号为 {} 库存,单价商品价格为{}", productId, product.getPrice());
// 扣减库存
int currentStock = stock - amount;
product.setStock(currentStock);
productMapper.updateById(product);
double totalPrice = product.getPrice() * amount;
log.info("扣减商品编号为 {} 库存成功,扣减后库存为{}, {} 件商品总价为 {} ", productId, currentStock, amount, totalPrice);
log.info("=============PRODUCT END=================");
return totalPrice;
}
}