MyBatisPlus重点内容

一、通用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 自动支持「驼峰命名 ↔ 下划线命名」转换(如 userNameuser_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 次)。

总结

  1. 悲观锁是「先锁后操作」,靠数据库行锁实现,阻塞式保证一致性,适合高冲突场景;
  2. 乐观锁是「先操作后校验」,靠版本号实现,非阻塞式提升并发,适合低冲突场景;
  3. MyBatis-Plus 对乐观锁有成熟封装,只需加@Version注解 + 配置插件即可使用;
  4. 实战中优先选乐观锁,仅在冲突率极高时用悲观锁。

四、多数据源的配置

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)准备两个数据库

    sql 复制代码
    DROP 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);
        }
    }
}
相关推荐
ι:2 小时前
NUC12 Pro 安装 Ubuntu 24.04 LTS 详细教程
linux·windows·ubuntu·nuc
YCOSA20252 小时前
微软宣布将于10月7日举行Windows与Surface发布会,聚焦“Windows、NVIDIA RTX Spark、Surface,以及更广泛PC生态”
windows
Source.Liu3 小时前
【A11】 labelprinter(Tauri 版)新建步骤
windows·rust
Punchline2784 小时前
解决 Windows 环境变量 Path 设置时提示“字符太长”的完整指南
windows·环境变量·path
chushiyunen5 小时前
claude code笔记(二)、claude桌面端(内网不好用)
windows
d_benhua5 小时前
以太网测试步骤
windows
冯一川5 小时前
DeepSeek在Windows系统上部署
windows·python
解道Jdon7 小时前
JDK 27发布:紧凑对象头、G1默认、量子安全TLS
ide·windows·git·svn·eclipse·github·visual studio
fengjianhua12259 小时前
免费PDF转换工具哪个转换效果最好?磨针PDF转换准确率评测
windows
TimberWill21 小时前
windows终端分屏设置
windows