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);
        }
    }
}
相关推荐
火星数据-Tina2 小时前
巨省Token:OpenClaw安装部署并接入数眼智能特价模型全流程(包含Windows和Mac)
windows·macos
love530love12 小时前
Windows 11 源码编译 vLLM 0.16 完全指南(CUDA 12.6 / PyTorch 2.7.1+cu126)
人工智能·pytorch·windows·python·深度学习·comfyui·vllm
阿昭L13 小时前
Windows内核驱动开发时是否要设置版本控制宏
windows·驱动开发·内核
yuweiade14 小时前
新版 WSL2 2.0 设置 Windows 和 WSL 镜像网络教程
windows
FreeBuf_15 小时前
黑客涉嫌兜售Windows远程桌面服务0Day漏洞利用程序
windows·0day漏洞
要开心吖ZSH15 小时前
(三)OpenClaw 云端服务器控制本地 Windows 浏览器完整配置指南(SSH方式)
运维·服务器·windows·openclaw
知智前沿16 小时前
OpenClaw 本地部署详细教程(Windows+Mac 双系统)
windows·macos
劳埃德福杰17 小时前
Windows电脑安装双系统如何切换默认开机系统
运维·windows·电脑
MIXLLRED17 小时前
解决:Ubuntu系统引导修复操作步骤
linux·windows·ubuntu