Skip to content

MyBatis-Plus

1. MyBatis-Plus 简介

1.1 什么是 MyBatis-Plus

MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

1.2 特性

  • 无侵入:只做增强不做改变,引入它不会对现有工程产生影响
  • 损耗小:启动即会自动注入基本 CURD,性能基本无损耗
  • 强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅通过少量配置即可实现单表大部分 CRUD 操作
  • 支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件
  • 支持主键自动生成:支持多达 4 种主键策略
  • 支持 ActiveRecord 模式:支持 ActiveRecord 形式调用
  • 支持自定义全局通用操作:支持全局通用方法注入
  • 内置代码生成器:采用代码或者 Maven 插件可快速生成代码
  • 内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作
  • 内置性能分析插件:可输出 SQL 语句以及其执行时间
  • 内置全局拦截插件:提供全表 delete、update 操作智能分析阻断

2. 快速开始

2.1 环境要求

  • JDK 8+
  • Maven 3.0+
  • MyBatis
  • Spring Boot 2.x/3.x(可选)

2.2 引入依赖

Maven:

xml
<dependencies>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.6</version>
    </dependency>

    <!-- 代码生成器 -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.5.6</version>
    </dependency>

    <!-- 模板引擎 -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.3</version>
    </dependency>
</dependencies>

Gradle:

gradle
dependencies {
    implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.6'
    implementation 'com.baomidou:mybatis-plus-generator:3.5.6'
    implementation 'org.apache.velocity:velocity-engine-core:2.3'
}

2.3 配置

application.yml:

yaml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root

mybatis-plus:
  configuration:
    # 日志实现
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 驼峰下划线转换
    map-underscore-to-camel-case: true
  global-config:
    db-config:
      # 主键类型
      id-type: auto
      # 逻辑删除字段名
      logic-delete-field: deleted
      # 逻辑删除值
      logic-delete-value: 1
      # 逻辑未删除值
      logic-not-delete-value: 0

2.4 启动类配置

java
@SpringBootApplication
@MapperScan("com.example.mapper")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. 核心功能

3.1 实体类注解

java
import com.baomidou.mybatisplus.annotation.*;

import java.time.LocalDateTime;

@Data
@TableName("user")  // 指定表名
public class User {

    @TableId(type = IdType.AUTO)  // 主键策略
    private Long id;

    private String name;

    private Integer age;

    private String email;

    @TableField(fill = FieldFill.INSERT)  // 字段填充策略
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @Version  // 乐观锁注解
    private Integer version;

    @TableLogic  // 逻辑删除注解
    private Integer deleted;

    // 排除非表字段的三种方式
    @TableField(exist = false)
    private String remark;

    // transient 关键字
    private transient String tempField;

    // static 字段
    private static String staticField;
}

主键策略 IdType:

描述
AUTO数据库 ID 自增
NONE无状态,该类型为未设置主键类型
INPUT插入前自行 set 主键值
ASSIGN_ID分配 ID(主键类型为 Number 或 String)
ASSIGN_UUID分配 UUID(主键类型为 String)

3.2 Mapper 接口

java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserMapper extends BaseMapper<User> {
    // 自定义方法
    List<User> selectAllByName(String name);

    // 注解方式
    @Select("SELECT * FROM user WHERE age > #{age}")
    List<User> selectByAge(@Param("age") Integer age);
}

3.3 Service 接口

java
import com.baomidou.mybatisplus.extension.service.IService;

public interface UserService extends IService<User> {
    // 自定义业务方法
    List<User> customMethod(String name);
}
java
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public List<User> customMethod(String name) {
        // 自定义实现
        return baseMapper.selectAllByName(name);
    }
}

4. CRUD 接口

4.1 Mapper CRUD

java
@Autowired
private UserMapper userMapper;

// 插入
User user = new User();
user.setName("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
int result = userMapper.insert(user);

// 根据ID删除
int result = userMapper.deleteById(1L);

// 根据条件删除
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三");
int result = userMapper.delete(wrapper);

// 根据ID更新
User user = new User();
user.setId(1L);
user.setName("李四");
int result = userMapper.updateById(user);

// 根据条件更新
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "张三").set("age", 25);
int result = userMapper.update(null, updateWrapper);

// 根据ID查询
User user = userMapper.selectById(1L);

// 查询所有
List<User> users = userMapper.selectList(null);

// 查询一条
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三");
User user = userMapper.selectOne(wrapper);

4.2 Service CRUD

java
@Autowired
private UserService userService;

// 插入
boolean success = userService.save(user);

// 批量插入
boolean success = userService.saveBatch(userList);

// 根据ID删除
boolean success = userService.removeById(1L);

// 根据条件删除
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三");
boolean success = userService.remove(wrapper);

// 根据ID更新
boolean success = userService.updateById(user);

// 根据条件更新
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("name", "张三").set("age", 25);
boolean success = userService.update(updateWrapper);

// 根据ID查询
User user = userService.getById(1L);

// 查询所有
List<User> users = userService.list();

// 查询一条
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "张三");
User user = userService.getOne(wrapper);

// 分页查询
Page<User> page = new Page<>(1, 10);
Page<User> result = userService.page(page, wrapper);

5. 条件构造器

5.1 QueryWrapper

java
QueryWrapper<User> wrapper = new QueryWrapper<>();

// 等于
wrapper.eq("name", "张三");

// 不等于
wrapper.ne("name", "张三");

// 大于
wrapper.gt("age", 18);

// 大于等于
wrapper.ge("age", 18);

// 小于
wrapper.lt("age", 65);

// 小于等于
wrapper.le("age", 65);

// BETWEEN
wrapper.between("age", 18, 65);

// NOT BETWEEN
wrapper.notBetween("age", 18, 65);

// LIKE
wrapper.like("name", "张");

// NOT LIKE
wrapper.notLike("name", "张");

// LIKE LEFT
wrapper.likeLeft("name", "三");

// LIKE RIGHT
wrapper.likeRight("name", "张");

// IN
wrapper.in("age", Arrays.asList(18, 19, 20));

// NOT IN
wrapper.notIn("age", Arrays.asList(18, 19, 20));

// IS NULL
wrapper.isNull("email");

// IS NOT NULL
wrapper.isNotNull("email");

// 分组
wrapper.groupBy("age");

// 排序
wrapper.orderByAsc("age");
wrapper.orderByDesc("create_time");

// HAVING
wrapper.having("count(*) > 1");

// 嵌套条件
wrapper.and(w -> w.eq("name", "张三").or().eq("name", "李四"));
wrapper.or(w -> w.eq("age", 18).eq("age", 20));

// 选择字段
wrapper.select("id", "name", "age");

5.2 LambdaQueryWrapper

java
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

// 使用Lambda表达式,避免硬编码
wrapper.eq(User::getName, "张三")
       .gt(User::getAge, 18)
       .like(User::getEmail, "@example.com")
       .orderByAsc(User::getAge)
       .select(User::getId, User::getName, User::getAge);

5.3 UpdateWrapper

java
UpdateWrapper<User> wrapper = new UpdateWrapper<>();

// 设置更新字段
wrapper.set("name", "李四")
       .set("age", 25)
       .eq("id", 1L);

// Lambda方式
LambdaUpdateWrapper<User> lambdaWrapper = new LambdaUpdateWrapper<>();
lambdaWrapper.set(User::getName, "李四")
             .set(User::getAge, 25)
             .eq(User::getId, 1L);

6. 分页查询

6.1 配置分页插件

java
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));

        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return interceptor;
    }
}

6.2 分页查询使用

java
// 方式一:使用Page对象
Page<User> page = new Page<>(1, 10); // 当前页,每页大小
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18);
Page<User> result = userMapper.selectPage(page, wrapper);

// 获取分页信息
System.out.println("总记录数:" + result.getTotal());
System.out.println("总页数:" + result.getPages());
System.out.println("当前页:" + result.getCurrent());
System.out.println("每页大小:" + result.getSize());
List<User> records = result.getRecords();

// 方式二:使用Service
Page<User> page = new Page<>(1, 10);
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.gt("age", 18);
Page<User> result = userService.page(page, wrapper);

// 不查询总记录数(性能优化)
Page<User> page = new Page<>(1, 10, false);

7. 高级功能

7.1 自动填充

java
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}

7.2 乐观锁

java
// 实体类中添加版本字段
public class User {
    @Version
    private Integer version;
}

// 使用乐观锁
User user = userMapper.selectById(1L);
user.setName("新名字");
user.setEmail("new@example.com");
// 此时version会自动+1
userMapper.updateById(user);

7.3 逻辑删除

java
// 实体类中添加逻辑删除字段
public class User {
    @TableLogic
    private Integer deleted;
}

// 删除操作会自动变为更新
userMapper.deleteById(1L);
// 实际执行:UPDATE user SET deleted=1 WHERE id=1 AND deleted=0

// 查询会自动过滤已删除数据
userMapper.selectList(null);
// 实际执行:SELECT * FROM user WHERE deleted=0

7.4 枚举处理

java
// 定义枚举
public enum GenderEnum {
    MALE(1, "男"),
    FEMALE(2, "女");

    private final Integer code;
    private final String desc;

    GenderEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    // getter...
}

// 实体类中使用枚举
public class User {
    @EnumValue  // 标记数据库存储的值
    private GenderEnum gender;
}

// 配置枚举处理器
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // ... 其他配置
        return interceptor;
    }

    @Bean
    public MybatisConfiguration mybatisConfiguration() {
        MybatisConfiguration configuration = new MybatisConfiguration();
        configuration.setDefaultEnumTypeHandler(MybatisEnumTypeHandler.class);
        return configuration;
    }
}

8. 代码生成器

8.1 快速代码生成

java
public class CodeGenerator {

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator generator = new AutoGenerator();

        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        globalConfig.setOutputDir(projectPath + "/src/main/java");
        globalConfig.setAuthor("Your Name");
        globalConfig.setOpen(false);
        globalConfig.setSwagger2(true); // 实体属性 Swagger2 注解
        generator.setGlobalConfig(globalConfig);

        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mybatis_plus");
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("root");
        generator.setDataSource(dataSourceConfig);

        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setModuleName("system");
        packageConfig.setParent("com.example");
        packageConfig.setEntity("entity");
        packageConfig.setMapper("mapper");
        packageConfig.setService("service");
        packageConfig.setServiceImpl("service.impl");
        packageConfig.setController("controller");
        generator.setPackageInfo(packageConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude("user", "role"); // 表名
        strategy.setControllerMappingHyphenStyle(true);
        strategy.setTablePrefix("t_"); // 表前缀
        generator.setStrategy(strategy);

        // 执行生成
        generator.execute();
    }
}

8.2 自定义模板

java
// 自定义模板配置
TemplateConfig templateConfig = new TemplateConfig();
templateConfig.setEntity("templates/entity.java");
templateConfig.setMapper("templates/mapper.java");
templateConfig.setService("templates/service.java");
templateConfig.setServiceImpl("templates/serviceImpl.java");
templateConfig.setController("templates/controller.java");
generator.setTemplate(templateConfig);

9. 多数据源配置

9.1 依赖引入

xml
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.6.1</version>
</dependency>

9.2 配置多数据源

yaml
spring:
  datasource:
    dynamic:
      primary: master # 设置默认的数据源或者数据源组
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/master_db
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver
        slave:
          url: jdbc:mysql://localhost:3306/slave_db
          username: root
          password: root
          driver-class-name: com.mysql.cj.jdbc.Driver

9.3 使用注解切换数据源

java
@Service
public class UserService {

    @DS("master") // 使用主数据源
    public void addUser(User user) {
        userMapper.insert(user);
    }

    @DS("slave") // 使用从数据源
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
}

10. 性能优化

10.1 SQL 性能分析插件

java
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

    // SQL分析插件
    interceptor.addInnerInterceptor(new IllegalSQLInnerInterceptor());

    return interceptor;
}

10.2 分页优化

java
// 使用不查询总记录数的分页(大数据量时)
Page<User> page = new Page<>(1, 10, false);

// 自定义分页SQL(复杂查询时)
@Select("SELECT * FROM user ${ew.customSqlSegment}")
List<User> selectPageCustom(@Param(Constants.WRAPPER) Wrapper<User> wrapper, Page<User> page);

10.3 查询优化

java
// 只查询需要的字段
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("id", "name", "email");

// 使用Lambda避免字段名硬编码
LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.select(User::getId, User::getName, User::getEmail);

11. 常见问题

11.1 字段映射问题

java
// 解决数据库字段名与实体类属性名不一致
public class User {
    @TableField("user_name")  // 数据库字段名
    private String name;

    // 忽略非表字段
    @TableField(exist = false)
    private String tempField;
}

11.2 主键策略问题

java
public class User {
    // 雪花算法ID
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;

    // 数据库自增
    @TableId(type = IdType.AUTO)
    private Long id;
}

11.3 批量操作性能

java
// 批量插入
List<User> userList = new ArrayList<>();
// ... 添加数据
boolean success = userService.saveBatch(userList, 1000); // 每批1000条

// 批量更新
boolean success = userService.updateBatchById(userList, 1000);

12. 最佳实践

12.1 项目结构

src/main/java/com/example/
├── entity/          # 实体类
├── mapper/          # Mapper接口
├── service/         # Service接口
│   └── impl/       # Service实现
├── controller/      # 控制器
└── config/         # 配置类

12.2 统一返回结果

java
@Data
public class Result<T> {
    private Integer code;
    private String message;
    private T data;

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>();
        result.setCode(200);
        result.setMessage("success");
        result.setData(data);
        return result;
    }
}

12.3 全局异常处理

java
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(Exception.class)
    public Result<String> handleException(Exception e) {
        return Result.error(500, e.getMessage());
    }
}

13. 扩展功能

13.1 自定义注入方法

java
public interface MyBaseMapper<T> extends BaseMapper<T> {

    int alwaysDeleteSome();

    int deleteAll();
}

public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
        methodList.add(new DeleteAll());
        methodList.add(new AlwaysDeleteSome());
        return methodList;
    }
}

13.2 多租户支持

java
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

    // 多租户插件
    TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
    tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {
        @Override
        public Expression getTenantId() {
            // 从上下文中获取租户ID
            return new LongValue(1L);
        }

        @Override
        public String getTenantIdColumn() {
            return "tenant_id";
        }

        @Override
        public boolean ignoreTable(String tableName) {
            // 忽略不需要租户隔离的表
            return "tenant".equals(tableName);
        }
    });
    interceptor.addInnerInterceptor(tenantInterceptor);

    return interceptor;
}

Db 静态工具类

1. 静态工具类 Db 介绍

MyBatis Plus 提供了 Db 静态工具类,其中包含与 IService 中方法签名基本一致的静态方法,可以帮助我们实现 CRUD 功能,同时避免 Service 之间的循环依赖问题。

主要优势:

  • 避免循环依赖:无需注入 Service,直接通过静态方法操作数据库
  • 使用便捷:提供 Lambda 查询和更新方式,代码简洁
  • 功能丰富:包含常用的 CRUD 操作方法

2. 基本使用

2.1 根据 id 查询用户

java
@Test
public void testQueryById() {
    User user = Db.getById(1L, User.class);
    System.out.println(user);
}

2.2 条件查询:名字包含"o"且余额 ≥1000 的用户

java
@Test
public void testQueryByName() {
    List<User> userList = Db.lambdaQuery(User.class)
            .like(User::getUsername, "o")
            .ge(User::getBalance, 1000)
            .list();
    userList.forEach(System.out::println);
}

2.3 条件更新:更新用户名为"Rose"的余额为 2000

java
@Test
public void testUpdate() {
    Db.lambdaUpdate(User.class)
            .set(User::getBalance, 2000)
            .eq(User::getUsername, "Rose")
            .update();
}

3. 实战案例:查询用户及收货地址

3.1 需求描述

改造根据 id 查询用户的接口,在查询用户的同时返回用户的收货地址列表。

3.2 实现步骤

步骤 1:导入 AddressVO

AddressVO.java 复制到 com.itheima.mp.domain.vo 包下:

java
package com.itheima.mp.domain.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "收货地址VO")
public class AddressVO {
    @ApiModelProperty("地址id")
    private Long id;

    @ApiModelProperty("用户id")
    private Long userId;

    @ApiModelProperty("省")
    private String province;

    @ApiModelProperty("市")
    private String city;

    @ApiModelProperty("区/县")
    private String town;

    @ApiModelProperty("详细地址")
    private String street;

    @ApiModelProperty("联系人")
    private String contact;

    @ApiModelProperty("手机号")
    private String mobile;

    @ApiModelProperty("是否是默认地址")
    private Boolean isDefault;
}
步骤 2:改造 UserVO

UserVO 中添加地址列表属性:

java
package com.itheima.mp.domain.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

@Data
@ApiModel(description = "用户VO")
public class UserVO {
    @ApiModelProperty("用户id")
    private Long id;

    @ApiModelProperty("用户名")
    private String username;

    @ApiModelProperty("详细信息")
    private String info;

    @ApiModelProperty("使用状态(1正常 2冻结)")
    private Integer status;

    @ApiModelProperty("账户余额")
    private Integer balance;

    @ApiModelProperty("收货地址列表")
    private List<AddressVO> addresses;
}
步骤 3:修改 UserController
java
@RestController
@RequestMapping("/users")
@Api(tags = "用户管理接口")
@RequiredArgsConstructor
public class UserController {

    private final IUserService userService;

    /**
     * 根据用户id查询用户
     * @param id 用户id
     */
    @ApiOperation("根据用户id查询用户")
    @GetMapping("/{id}")
    public UserVO queryById(@PathVariable("id") Long id) {
        return userService.queryUserAndAddressById(id);
    }
}
步骤 4:新增 IUserService 接口方法
java
public interface IUserService extends IService<User> {

    /**
     * 根据用户id查询用户和地址
     * @param id 用户id
     * @return userVO
     */
    UserVO queryUserAndAddressById(Long id);
}
步骤 5:实现 UserServiceImpl

使用 Db 工具类查询收货地址,避免循环依赖:

java
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Override
    public UserVO queryUserAndAddressById(Long id) {
        // 查询用户
        User user = getById(id);
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);

        // 使用Db工具类查询用户的收货地址列表,避免循环依赖
        List<Address> addressList = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id)
                .list();
        userVO.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));

        return userVO;
    }
}

4. 扩展练习:批量查询用户及地址

需求:改造根据用户 id 批量查询用户并返回用户的收货地址列表

java
@Override
public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {
    // 1. 查询用户列表
    List<User> userList = this.listByIds(ids);
    List<UserVO> userVOS = BeanUtil.copyToList(userList, UserVO.class);

    // 2. 使用Db工具类查询地址列表,并按用户id分组
    List<Address> addressList = Db.lambdaQuery(Address.class)
            .in(Address::getUserId, ids)
            .list();
    Map<Long, List<AddressVO>> addressVoMap = BeanUtil.copyToList(addressList, AddressVO.class)
            .stream().collect(Collectors.groupingBy(AddressVO::getUserId));

    // 3. 将地址列表设置到对应的用户VO中
    for (UserVO userVO : userVOS) {
        userVO.setAddresses(addressVoMap.get(userVO.getId()));
    }
    return userVOS;
}

5. 注意事项

  1. Mapper 要求:使用 Db 工具类时,必须有对应实体类的 Mapper
  2. 事务管理Db 操作默认在事务中执行
  3. 性能考虑:对于复杂查询,建议使用自定义 Mapper 方法
  4. 代码简洁性:简单的单表操作使用 Db,复杂业务逻辑使用 Service