一、通用service常用方法
新增:
default boolean save(T entity):新增记录
boolean saveBatch(Collection<T> entityList):批量插入
saveBatch(Collection<T> entityList, int batchSize):一次性批量插入batchSize条记录
删除:
boolean removeById(Serializable id):根据id删除
boolean removeByMap(Map<String, Object> columnMap):根据条件删除
boolean remove(Wrapper<T> queryWrapper):使用Wrapper封装条件删除
boolean removeByIds(Collection<? extends Serializable> idList):删除一批
修改:
boolean updateById(T entity):修改
boolean update(Wrapper<T> updateWrapper):根据Wrapper修改
boolean update(T entity, Wrapper<T> updateWrapper):使用Wrapper查询出结果,修改为entity
boolean updateBatchById(Collection<T> entityList):批量修改
updateBatchById(Collection<T> entityList, int batchSize):一次性批量修改batchSize条记录
boolean saveOrUpdate(T entity):如果id存在则修改,如果id不存在则新增
查询 :
T getById(Serializable id):根据id查询
List<T> listByIds(Collection<? extends Serializable> idList):根据一批id查询多条记录
List<T> listByMap(Map<String, Object> columnMap):根据条件查询多条记录
T getOne(Wrapper<T> queryWrapper):根据Wrapper查询一条记录,如果查询到多条则抛出异常
T getOne(Wrapper<T> queryWrapper, boolean throwEx):根据Wrapper查询一条记录,通过throwEx决定是否抛出异常
int count():查询总记录数
int count(Wrapper<T> queryWrapper):根据条件查询总记录数
分页:
<E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper):带条件分页查询,当前页数据为T类型
<E extends IPage<T>> E page(E page):无条件分页
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper):带条件分页查询,当前页数据为HashMap类型
List<Map<String, Object>> listMaps():无条件分页
测试代码:
java
package com.dfbz;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.dfbz.entity.User;
import com.dfbz.service.IUserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.List;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class Demo07_Service查询 {
@Autowired
private IUserService userService;
/**
* 新增
*
* @throws Exception
*/
@Test
public void test1() throws Exception {
User user = new User(null, "xiaohui", "0", 20);
userService.save(user);
}
/**
* 如果id存在则修改,不存在则新增
*
* @throws Exception
*/
@Test
public void test2() throws Exception {
User user = new User(1L, "xiaohui", "0", 20);
userService.saveOrUpdate(user);
}
/**
* 根据id删除
*
* @throws Exception
*/
@Test
public void test3() throws Exception {
userService.removeById(1L);
}
/**
* 根据id修改
*
* @throws Exception
*/
@Test
public void test4() throws Exception {
User user = new User(1L, "xiaolan", "1", 18);
userService.updateById(user);
}
/**
* 根据id查询
*
* @throws Exception
*/
@Test
public void test5() throws Exception {
User user = userService.getById(1L);
System.out.println(user);
}
/**
* 查询列表
*
* @throws Exception
*/
@Test
public void test6() throws Exception {
QueryWrapper<User> wrapper = Wrappers.query();
wrapper.in("id", "1", "2");
// 查询所有
// List<User> userList = userService.list();
// 通过wrapper查询
List<User> userList = userService.list(wrapper);
for (User user : userList) {
System.out.println(user);
}
}
/**
* 查询总记录数
*
* @throws Exception
*/
@Test
public void test7() throws Exception {
QueryWrapper<User> wrapper = Wrappers.query();
wrapper.like("name", "a");
// 查询总记录数
// int count = userService.count();
// 根据条件查询总记录数
int count = userService.count(wrapper);
System.out.println(count);
}
/**
* 分页查询(当前页类型为指定类型)
*
* @throws Exception
*/
@Test
public void test8() throws Exception {
Page<User> page = new Page<>(1, 3);
userService.page(page);
// 当前页数据
List<User> pageData = page.getRecords();
for (User user : pageData) {
System.out.println(user);
}
System.out.println("------------");
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
/**
* 分页查询(当前页结果为HashMap类型)
*
* @throws Exception
*/
@Test
public void test9() throws Exception {
Page page = new Page<>(1, 3);
userService.pageMaps(page);
// 当前页数据
List<HashMap<String, Object>> pageData = page.getRecords();
for (HashMap userMap : pageData) {
System.out.println(userMap);
}
System.out.println("------------");
System.out.println("当前页:" + page.getCurrent());
System.out.println("每页显示的条数:" + page.getSize());
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("是否有上一页:" + page.hasPrevious());
System.out.println("是否有下一页:" + page.hasNext());
}
/**
* 链式查询
*
* @throws Exception
*/
@Test
public void test10() throws Exception {
QueryChainWrapper<User> chainWrapper = userService.query();
// SQL: SELECT id,name,age FROM user WHERE (id IN (?,?,?) AND name LIKE ?)
List<User> userList = chainWrapper.select("id", "name", "age")
.in("id", "1", "2", "3")
.like("name", "a")
.list();
for (User user : userList) {
System.out.println(user);
}
}
/**
* 链式修改
*
* @throws Exception
*/
@Test
public void test11() throws Exception {
UpdateChainWrapper<User> chainWrapper = userService.update();
// SQL: UPDATE user SET age=? WHERE (id IN (?,?) OR sex = ?)
chainWrapper.in("id","1","2")
.or()
.eq("sex","0")
.set("age",20).
update();
}
}
二、常用注解
1. @TableName:表名映射
- 解决「实体类名 ≠ 数据库表名」的问题;
- 可通过
global-config.db-config.table-prefix配置全局表前缀(如t_),替代单个实体的@TableName; - 优先级:全局配置 < 实体类
@TableName(局部覆盖全局)。
java
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
// 场景1:表名=t_user,实体名=User,全局配置了table-prefix: t_ → 无需加@TableName
// 场景2:若表名=user(无前缀),需手动指定:@TableName("user")
@Data
@TableName("t_user") // 显式指定表名(全局配置生效时可省略)
public class User {
private Long id;
private String userName;
private Integer age;
private String email;
}
2. @TableId:主键映射
- 标记实体类中的主键字段,必加(否则 MyBatis-Plus 无法识别主键);
value:指定数据库主键字段名(实体属性名 ≠ 主键字段名时用);type:主键生成策略(常用:AUTO自增、ASSIGN_ID雪花算法、INPUT手动输入);- 可通过
global-config.db-config.id-type配置全局主键策略,简化代码。
java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class User {
// 场景1:主键字段名=id,实体属性名=id → 仅配置生成策略
@TableId(type = IdType.AUTO) // 数据库自增(需保证表id字段自增)
// @TableId(type = IdType.ASSIGN_ID) // 雪花算法(默认,无需数据库自增)
// 场景2:主键字段名=user_id,实体属性名=id → 指定value
// @TableId(value = "user_id", type = IdType.AUTO)
private Long id;
private String userName;
private Integer age;
private String email;
}
3. @TableField:字段映射
- 解决「实体属性名 ≠ 数据库字段名」「属性非数据库字段」的问题;
- 常用属性:
value:指定数据库字段名(如实体name→ 表user_name);exist:是否为数据库字段(false表示仅业务属性,不参与 CRUD);select:是否参与查询(false表示查询时不返回该字段);
- MyBatis-Plus 自动支持「驼峰命名 ↔ 下划线命名」转换(如
userName↔user_name),无需手动配置。
java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
// 场景1:实体属性名=name → 表字段名=user_name(驼峰转下划线生效,可省略@TableField)
@TableField("user_name") // 显式指定,增强可读性(可选)
private String name;
private Integer age;
private String email;
// 场景2:属性仅用于业务逻辑,不入库 → exist=false
@TableField(exist = false)
private String tempData; // 查询/插入/更新时,该字段会被忽略
// 场景3:查询时不返回该字段 → select=false
@TableField(select = false)
private String password; // 执行SELECT时,不会查询password字段
}
4. @TableLogic:逻辑删除
- 实现「软删除」:删除操作转为更新逻辑删除字段(而非物理删除);
- 查询操作自动拼接
WHERE 逻辑删除字段=未删除值,过滤已删除数据; - 全局配置:可通过
global-config.db-config配置默认值,无需单个实体加注解;logic-delete-field:逻辑删除字段名(如isDeleted);logic-not-delete-value:未删除值(默认 0);logic-delete-value:已删除值(默认 1)。
Step1:实体类添加注解
java
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
// 逻辑删除字段:0=未删,1=已删
@TableLogic // 全局配置生效时,仅需加注解,无需指定value
// 自定义值:@TableLogic(value = "0", delval = "1")
private Integer isDeleted;
}
Step2:测试逻辑删除 & 查询
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.List;
@SpringBootTest
public class TableLogicTest {
@Autowired
private UserMapper userMapper; // 需先定义UserMapper extends BaseMapper<User>
// 测试逻辑删除:DELETE → UPDATE
@Test
public void testDelete() {
int result = userMapper.deleteById(1L);
System.out.println("受影响行数:" + result);
// 实际执行SQL:UPDATE t_user SET is_deleted=1 WHERE id=1 AND is_deleted=0
}
// 测试查询:自动过滤已删除数据
@Test
public void testSelect() {
List<User> list = userMapper.selectList(new LambdaQueryWrapper<User>());
list.forEach(System.out::println);
// 实际执行SQL:SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0
}
}
三、乐观锁 & 悲观锁
| 锁类型 | 核心思想 | 类比场景 | 核心特点 |
|---|---|---|---|
| 悲观锁 | 认为每次操作数据都会有并发冲突,先加锁独占资源,操作完释放锁 | 卫生间先反锁门,用完再解锁 | 独占、阻塞、安全性高、并发效率低 |
| 乐观锁 | 认为每次操作数据大概率无冲突,不加锁,仅在提交修改时校验数据是否被修改过 | 卫生间先不锁门,进门发现有人就退出 | 无锁、非阻塞、并发效率高、需处理冲突 |
实体类(Product.java)
java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
@TableName("t_product")
public class Product {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer stock;
@Version // 乐观锁版本号字段(仅乐观锁需要)
private Integer version;
}
Mapper 接口(ProductMapper.java)
java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductMapper extends BaseMapper<Product> {
// 悲观锁专用:查询商品并加行锁
Product selectProductByIdForUpdate(@Param("id") Long id);
}
悲观锁实现(MySQL 行锁)
核心原理
通过 SELECT ... FOR UPDATE 给数据加行锁,其他线程查询该数据时会阻塞,直到锁释放。
步骤 1:编写 Mapper XML(ProductMapper.xml)
java
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.ProductMapper">
<!-- 悲观锁查询:FOR UPDATE 加行锁 -->
<select id="selectProductByIdForUpdate" resultType="com.example.demo.entity.Product">
SELECT id, name, stock, version
FROM t_product
WHERE id = #{id} FOR UPDATE;
</select>
</mapper>
步骤 2:Service 层实现(加事务保证锁生效)
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
/**
* 悲观锁扣减库存
* @Transactional:必须加,事务内加锁才生效
*/
@Transactional
public boolean deductStockWithPessimisticLock(Long productId) {
// 1. 查询商品并加行锁(其他线程查该商品会阻塞)
Product product = productMapper.selectProductByIdForUpdate(productId);
if (product == null || product.getStock() <= 0) {
return false; // 库存不足
}
// 2. 扣减库存
product.setStock(product.getStock() - 1);
productMapper.updateById(product);
return true;
}
}
步骤 3:测试悲观锁(模拟并发)
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class PessimisticLockTest {
@Autowired
private ProductService productService;
@Test
public void testPessimisticLock() {
// 模拟两个线程同时扣减库存
new Thread(() -> productService.deductStockWithPessimisticLock(1L)).start();
new Thread(() -> productService.deductStockWithPessimisticLock(1L)).start();
// 等待线程执行完成
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 查询最终库存(应为98)
Product product = productMapper.selectById(1L);
System.out.println("悲观锁最终库存:" + product.getStock());
}
}
关键验证
- 控制台 SQL:
SELECT ... FOR UPDATE加锁,更新后库存正确扣减; - 并发时第二个线程会阻塞,直到第一个线程事务提交释放锁,无超卖问题。
乐观锁实现
核心原理
修改数据时校验版本号:UPDATE ... WHERE version=当前值,成功则版本号 + 1;失败则说明数据被修改,需重试。
步骤 1:配置乐观锁插件
java
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
步骤 2:Service 层实现(带重试机制)
java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private ProductMapper productMapper;
/**
* 乐观锁扣减库存(带重试)
* @param productId 商品ID
* @return 是否成功
*/
public boolean deductStockWithOptimisticLock(Long productId) {
int retryCount = 3; // 最多重试3次,避免无限重试
while (retryCount > 0) {
// 1. 查询当前商品(不加锁)
Product product = productMapper.selectOne(
new LambdaQueryWrapper<Product>().eq(Product::getId, productId)
);
if (product == null || product.getStock() <= 0) {
return false; // 库存不足
}
// 2. 扣减库存
product.setStock(product.getStock() - 1);
// 3. 更新数据(MyBatis-Plus自动拼接version条件)
int affectedRows = productMapper.updateById(product);
if (affectedRows > 0) {
return true; // 更新成功,版本号匹配
}
// 4. 更新失败(版本号不匹配),重试
retryCount--;
System.out.println("库存被修改,重试次数剩余:" + retryCount);
}
return false; // 重试多次仍失败
}
}
步骤 3:测试乐观锁(模拟并发)
java
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class OptimisticLockTest {
@Autowired
private ProductService productService;
@Test
public void testOptimisticLock() {
// 模拟两个线程同时扣减库存
new Thread(() -> productService.deductStockWithOptimisticLock(1L)).start();
new Thread(() -> productService.deductStockWithOptimisticLock(1L)).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 查询最终库存(应为98)
Product product = productMapper.selectById(1L);
System.out.println("乐观锁最终库存:" + product.getStock());
}
}
关键验证
- 控制台 SQL:
UPDATE t_product SET stock=99, version=2 WHERE id=1 AND version=1; - 并发时若版本号不匹配,会触发重试,最终库存正确,无超卖。
1. 选悲观锁的场景
- 数据冲突率极高(如秒杀、库存扣减、金融交易);
- 业务不允许并发重试,要求数据强一致性;
- 操作耗时短(避免长时间占锁导致性能问题)。
2. 选乐观锁的场景
- 数据冲突率低(如普通用户信息修改、订单状态更新);
- 追求高并发、低延迟,不希望线程阻塞;
- 可接受少量重试(重试次数建议 3-5 次)。
总结
- 悲观锁是「先锁后操作」,靠数据库行锁实现,阻塞式保证一致性,适合高冲突场景;
- 乐观锁是「先操作后校验」,靠版本号实现,非阻塞式提升并发,适合低冲突场景;
- MyBatis-Plus 对乐观锁有成熟封装,只需加
@Version注解 + 配置插件即可使用; - 实战中优先选乐观锁,仅在冲突率极高时用悲观锁。
四、多数据源的配置
1、多数据源是什么?
多数据源是指一个项目中同时连接多个不同的数据库 (或同一个数据库的不同实例),并能根据业务需求动态切换数据源的技术方案。
- 本质:突破单数据源的限制,解决「数据隔离、读写分离、分库分表、多租户」等问题;
- 通俗类比:你有两个钱包(数据源 A、B),买零食时用钱包 A,交房租时用钱包 B,程序能自动选对应的钱包。
常见的多数据源类型
| 类型 | 场景举例 |
|---|---|
| 读写分离 | 主库(master)写数据,从库(slave)读数据 |
| 分库分表 | 订单表按年份拆分到 db_2024、db_2025 库 |
| 多业务库隔离 | 用户库(user_db)、订单库(order_db) |
| 多租户分库 | 租户 A 用 tenant_a_db,租户 B 用 tenant_b_db |
1)引入多数据源依赖:
XML
<!--MyBatisPlus多数据源依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
-
2)准备两个数据库
sqlDROP database IF EXISTS test1; create database test1; use test1; DROP TABLE IF EXISTS user; CREATE TABLE `user` ( `id` bigint(20) NOT NULL COMMENT '主键ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `sex` char(1) DEFAULT NULL COMMENT '性别 0:男 1:女', `age` int(11) DEFAULT NULL COMMENT '年龄', `birthday` date DEFAULT NULL COMMENT '生日', PRIMARY KEY (`id`) ); INSERT INTO `user` VALUES (1, 'Jone', '1', 27, '2001-10-04'); INSERT INTO `user` VALUES (2, 'Jack', '0', 20, '1999-10-04'); INSERT INTO `user` VALUES (3, 'Tom', '1', 28, '1996-08-12'); -- ----------------------------------------------- DROP database IF EXISTS test2; create database test2; use test2; DROP TABLE IF EXISTS user; CREATE TABLE `user` ( `id` bigint(20) NOT NULL COMMENT '主键ID', `name` varchar(30) DEFAULT NULL COMMENT '姓名', `sex` char(1) DEFAULT NULL COMMENT '性别 0:男 1:女', `age` int(11) DEFAULT NULL COMMENT '年龄', `birthday` date DEFAULT NULL COMMENT '生日', PRIMARY KEY (`id`) ); INSERT INTO `user` VALUES (4, 'Sandy', '1', 21, '2001-10-04'); INSERT INTO `user` VALUES (5, 'Billie', '0', 24, '1992-09-07'); INSERT INTO `user` VALUES (6, 'Jackson', '0', 18, '1996-08-12'); -
3)多数据源配置:
java
spring:
datasource:
dynamic:
# 没有匹配的数据源时默认匹配的数据源
primary: master
# 当没有匹配的数据源是是否采用默认的数据源,
# true: 严格匹配,如果没有匹配到数据源则抛出异常
# false(默认值): 如果没有匹配到数据源则使用默认的数据源
strict: false
datasource:
# 数据源的名称(名字任意)
master:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test1?serverTimezone=GMT%2b8
username: root
password: admin
# 数据源的名称(名字任意)
slave:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2b8
username: root
password: admin
#配置日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
- 4)启动类:
java
package com.dfbz;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@SpringBootApplication
@MapperScan("com.dfbz.mapper")
public class MyBatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(MyBatisPlusApplication.class, args);
}
}
- 5)实体类:
java
package com.dfbz.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("user")
public class User extends Model<User> {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String sex;
private Integer age;
}
- 6)编写两个Mapper:
java
package com.dfbz.mapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dfbz.entity.User;
import org.springframework.stereotype.Repository;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@DS("master") // 指定数据源的名称(若指定的数据源不存在则使用默认的)
@Repository
public interface UserMapperMaster extends BaseMapper<User> {
}
java
package com.dfbz.mapper;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.dfbz.entity.User;
import org.springframework.stereotype.Repository;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@DS("slave") // 指定数据源的名称(若指定的数据源不存在则使用默认的)
@Repository
public interface UserMapperSlave extends BaseMapper<User> {
}
- 7)测试类:
java
package com.dfbz;
import com.dfbz.entity.User;
import com.dfbz.mapper.UserMapperMaster;
import com.dfbz.mapper.UserMapperSlave;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
/**
* @author lscl
* @version 1.0
* @intro:
*/
@SpringBootTest(classes = MyBatisPlusApplication.class)
@RunWith(SpringRunner.class)
public class Demo01_测试多数据源 {
@Autowired
private UserMapperMaster userMapperMaster;
@Autowired
private UserMapperSlave userMapperSlave;
@Test
public void test1() {
// 使用的是master数据源
List<User> userList = userMapperMaster.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
@Test
public void test2() {
// 使用slave数据源
List<User> userList = userMapperSlave.selectList(null);
for (User user : userList) {
System.out.println(user);
}
}
}