跳到主要内容

Db 静态工具类

Db 是 MyBatis Plus 3.5.4+ 提供的静态工具类,它允许你在不注入 Mapper 或 Service 的情况下直接执行 CRUD 操作。这对于工具类、测试代码、定时任务等场景非常有用。

为什么需要 Db 类?

传统方式的局限性

在传统的 MyBatis Plus 使用方式中,我们需要注入 Mapper 或 Service 才能操作数据库:

@Service
public class OrderService {

@Autowired
private UserMapper userMapper; // 需要注入 Mapper

@Autowired
private UserService userService; // 或者注入 Service

public void processOrder(Long userId) {
// 必须通过注入的对象操作数据库
User user = userMapper.selectById(userId);
}
}

这种方式存在一些问题:

  1. 工具类无法使用:静态工具类、枚举类无法注入 Spring Bean
  2. 测试代码繁琐:单元测试需要配置 Spring 上下文
  3. 代码冗余:简单查询也要注入完整的 Mapper 或 Service

Db 类的解决方案

Db 类提供了一种简洁的替代方案:

import com.baomidou.mybatisplus.extension.toolkit.Db;

// 无需注入,直接调用静态方法
User user = Db.getById(1L, User.class);

基本用法

查询操作

import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import java.util.List;

public class DbExample {

/**
* 根据 ID 查询
*/
public void getById() {
// 查询单个实体
User user = Db.getById(1L, User.class);
System.out.println(user);
// 生成 SQL: SELECT * FROM t_user WHERE id = 1
}

/**
* 批量查询
*/
public void listByIds() {
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = Db.listByIds(ids, User.class);
users.forEach(System.out::println);
// 生成 SQL: SELECT * FROM t_user WHERE id IN (1, 2, 3)
}

/**
* 查询全部
*/
public void listAll() {
List<User> users = Db.list(User.class);
users.forEach(System.out::println);
}

/**
* 条件查询
*/
public void queryByCondition() {
// 使用 LambdaQueryWrapper
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1)
.gt(User::getAge, 18);

List<User> users = Db.list(wrapper, User.class);
users.forEach(System.out::println);
}

/**
* 查询总数
*/
public void count() {
long total = Db.count(User.class);
System.out.println("总记录数:" + total);

// 条件计数
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getStatus, 1);
long activeCount = Db.count(wrapper, User.class);
System.out.println("活跃用户数:" + activeCount);
}
}

Lambda 链式查询

Db 类提供了 lambdaQuery 方法,支持更优雅的链式查询:

import com.baomidou.mybatisplus.extension.toolkit.Db;

public class LambdaQueryExample {

/**
* 基本链式查询
*/
public void lambdaQuery() {
List<User> users = Db.lambdaQuery(User.class)
.eq(User::getStatus, 1)
.gt(User::getAge, 18)
.like(User::getName, "张")
.orderByDesc(User::getCreateTime)
.list(); // 返回列表

users.forEach(System.out::println);
}

/**
* 查询单条记录
*/
public void lambdaQueryOne() {
User user = Db.lambdaQuery(User.class)
.eq(User::getEmail, "[email protected]")
.one(); // 返回单条记录

System.out.println(user);
}

/**
* 查询记录数
*/
public void lambdaQueryCount() {
long count = Db.lambdaQuery(User.class)
.eq(User::getStatus, 1)
.count(); // 返回记录数

System.out.println("记录数:" + count);
}

/**
* 检查记录是否存在
*/
public void lambdaQueryExists() {
boolean exists = Db.lambdaQuery(User.class)
.eq(User::getEmail, "[email protected]")
.exists();

System.out.println("记录是否存在:" + exists);
}

/**
* 分页查询
*/
public void lambdaQueryPage() {
Page<User> page = new Page<>(1, 10);

IPage<User> result = Db.lambdaQuery(User.class)
.eq(User::getStatus, 1)
.page(page);

System.out.println("总记录数:" + result.getTotal());
result.getRecords().forEach(System.out::println);
}

/**
* 只查询特定字段
*/
public void lambdaQuerySelect() {
List<User> users = Db.lambdaQuery(User.class)
.select(User::getId, User::getName) // 只查询 id 和 name
.eq(User::getStatus, 1)
.list();

users.forEach(u -> System.out.println(u.getId() + ": " + u.getName()));
}
}

插入操作

import com.baomidou.mybatisplus.extension.toolkit.Db;

public class DbInsertExample {

/**
* 插入单条记录
*/
public void save() {
User user = new User();
user.setName("张三");
user.setAge(25);
user.setEmail("[email protected]");

boolean success = Db.save(user);
System.out.println("插入结果:" + success);
// 插入成功后,主键会自动回填到 user 对象
System.out.println("生成的 ID:" + user.getId());
}

/**
* 批量插入
*/
public void saveBatch() {
List<User> users = new ArrayList<>();
for (int i = 0; i < 100; i++) {
User user = new User();
user.setName("用户" + i);
user.setAge(20 + i % 30);
users.add(user);
}

boolean success = Db.saveBatch(users);
System.out.println("批量插入结果:" + success);
}

/**
* 指定批次大小的批量插入
*/
public void saveBatchWithSize() {
List<User> users = new ArrayList<>();
// ... 准备数据

// 每批次 50 条
boolean success = Db.saveBatch(users, 50);
System.out.println("批量插入结果:" + success);
}

/**
* 存在则更新,不存在则插入
*/
public void saveOrUpdate() {
User user = new User();
user.setId(1L); // 设置 ID
user.setName("新名字");

// 如果 ID 存在则更新,不存在则插入
boolean success = Db.saveOrUpdate(user);
System.out.println("保存或更新结果:" + success);
}
}

更新操作

import com.baomidou.mybatisplus.extension.toolkit.Db;

public class DbUpdateExample {

/**
* 根据 ID 更新
*/
public void updateById() {
User user = new User();
user.setId(1L);
user.setName("新名字");
user.setAge(26);

boolean success = Db.updateById(user);
System.out.println("更新结果:" + success);
}

/**
* 批量更新
*/
public void updateBatchById() {
List<User> users = new ArrayList<>();

User user1 = new User();
user1.setId(1L);
user1.setName("名字1");
users.add(user1);

User user2 = new User();
user2.setId(2L);
user2.setName("名字2");
users.add(user2);

boolean success = Db.updateBatchById(users);
System.out.println("批量更新结果:" + success);
}
}

Lambda 链式更新

import com.baomidou.mybatisplus.extension.toolkit.Db;

public class LambdaUpdateExample {

/**
* 基本链式更新
*/
public void lambdaUpdate() {
boolean success = Db.lambdaUpdate(User.class)
.eq(User::getId, 1L)
.set(User::getName, "新名字")
.set(User::getAge, 26)
.update(); // 执行更新

System.out.println("更新结果:" + success);
// 生成 SQL: UPDATE t_user SET name = '新名字', age = 26 WHERE id = 1
}

/**
* 条件更新
*/
public void lambdaUpdateByCondition() {
boolean success = Db.lambdaUpdate(User.class)
.eq(User::getStatus, 0) // 条件:状态为 0
.set(User::getStatus, 1) // 设置状态为 1
.update();

System.out.println("更新结果:" + success);
// 生成 SQL: UPDATE t_user SET status = 1 WHERE status = 0
}

/**
* 更新为 NULL
*/
public void updateToNull() {
boolean success = Db.lambdaUpdate(User.class)
.eq(User::getId, 1L)
.set(User::getEmail, null) // 设置为 NULL
.update();

System.out.println("更新结果:" + success);
}

/**
* 使用 SQL 片段更新
*/
public void updateWithSql() {
boolean success = Db.lambdaUpdate(User.class)
.eq(User::getId, 1L)
.setSql("age = age + 1") // 自定义 SQL 片段
.update();

System.out.println("更新结果:" + success);
// 生成 SQL: UPDATE t_user SET age = age + 1 WHERE id = 1
}

/**
* 删除操作(链式)
*/
public void lambdaRemove() {
boolean success = Db.lambdaUpdate(User.class)
.eq(User::getStatus, -1) // 条件:状态为 -1(无效数据)
.remove(); // 执行删除

System.out.println("删除结果:" + success);
}
}

删除操作

import com.baomidou.mybatisplus.extension.toolkit.Db;

public class DbDeleteExample {

/**
* 根据 ID 删除
*/
public void removeById() {
boolean success = Db.removeById(1L, User.class);
System.out.println("删除结果:" + success);
}

/**
* 批量删除
*/
public void removeByIds() {
List<Long> ids = Arrays.asList(1L, 2L, 3L);
boolean success = Db.removeByIds(ids, User.class);
System.out.println("批量删除结果:" + success);
}

/**
* 条件删除
*/
public void removeByCondition() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.lt(User::getAge, 18); // 删除年龄小于 18 的用户

boolean success = Db.remove(wrapper, User.class);
System.out.println("条件删除结果:" + success);
}
}

实战场景

场景一:静态工具类中使用

import com.baomidou.mybatisplus.extension.toolkit.Db;

/**
* 用户工具类
* 无法注入 Spring Bean,使用 Db 类操作数据库
*/
public class UserUtils {

/**
* 检查邮箱是否已存在
*/
public static boolean isEmailExists(String email) {
return Db.lambdaQuery(User.class)
.eq(User::getEmail, email)
.exists();
}

/**
* 根据邮箱获取用户
*/
public static User getByEmail(String email) {
return Db.lambdaQuery(User.class)
.eq(User::getEmail, email)
.one();
}

/**
* 获取活跃用户数量
*/
public static long getActiveUserCount() {
return Db.lambdaQuery(User.class)
.eq(User::getStatus, 1)
.count();
}
}

场景二:枚举类中使用

import com.baomidou.mybatisplus.extension.toolkit.Db;

/**
* 用户状态枚举
* 需要从数据库查询状态对应的配置
*/
@Getter
@AllArgsConstructor
public enum UserStatus {

ACTIVE(1, "活跃"),
INACTIVE(0, "禁用"),
PENDING(2, "待审核");

private final Integer code;
private final String desc;

/**
* 获取该状态的用户列表
*/
public List<User> getUsers() {
return Db.lambdaQuery(User.class)
.eq(User::getStatus, this.code)
.list();
}

/**
* 获取该状态的用户数量
*/
public long getUserCount() {
return Db.lambdaQuery(User.class)
.eq(User::getStatus, this.code)
.count();
}
}

场景三:定时任务中使用

import com.baomidou.mybatisplus.extension.toolkit.Db;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;

/**
* 数据清理定时任务
*/
@Component
public class DataCleanupTask {

/**
* 每天凌晨 2 点清理过期数据
*/
@Scheduled(cron = "0 0 2 * * ?")
public void cleanExpiredData() {
LocalDateTime expireTime = LocalDateTime.now().minusDays(30);

// 删除 30 天前的临时数据
boolean success = Db.lambdaUpdate(TempData.class)
.lt(TempData::getCreateTime, expireTime)
.remove();

System.out.println("清理过期数据:" + (success ? "成功" : "失败"));
}

/**
* 每小时更新在线用户状态
*/
@Scheduled(fixedRate = 3600000)
public void updateOnlineStatus() {
LocalDateTime offlineTime = LocalDateTime.now().minusMinutes(30);

// 将超过 30 分钟未活跃的用户设为离线
Db.lambdaUpdate(User.class)
.eq(User::getOnline, true)
.lt(User::getLastActiveTime, offlineTime)
.set(User::getOnline, false)
.update();
}
}

场景四:简单单元测试

import com.baomidou.mybatisplus.extension.toolkit.Db;
import org.junit.jupiter.api.Test;

/**
* 无需 Spring 上下文的简单测试
*/
class UserQueryTest {

@Test
void testQueryUser() {
// 直接使用 Db 类,无需启动 Spring 容器
User user = Db.getById(1L, User.class);

assertNotNull(user);
assertEquals(1L, user.getId());
}

@Test
void testCountUsers() {
long count = Db.count(User.class);

assertTrue(count >= 0);
}
}

注意事项

1. 事务控制

Db 类本身不管理事务,事务需要在调用方法的外层控制:

@Service
public class OrderService {

@Transactional // 事务注解在 Service 层方法上
public void createOrder(OrderDTO dto) {
// 在事务中使用 Db 类
User user = Db.getById(dto.getUserId(), User.class);

// 这些操作会在同一个事务中
Db.lambdaUpdate(User.class)
.eq(User::getId, dto.getUserId())
.setSql("balance = balance - " + dto.getAmount())
.update();

// 如果发生异常,所有操作都会回滚
}
}

2. 性能考虑

在高频调用的场景中,Db 类每次都会获取 Mapper,有一定的性能开销。对于性能敏感的场景,建议直接注入 Mapper 或 Service。

3. 与注入方式的对比

对比项注入 Mapper/ServiceDb 静态类
适用场景常规业务代码工具类、枚举、测试
事务支持完整支持需要外层控制
性能更优(单例)略有开销
代码风格面向对象静态方法调用
可测试性更容易 Mock相对困难

小结

本章我们学习了:

  1. Db 类的作用:解决工具类、枚举类无法注入 Spring Bean 的问题
  2. 基本 CRUDsavegetByIdlistupdateByIdremoveById 等方法
  3. Lambda 链式操作lambdaQuerylambdaUpdate 提供更优雅的 API
  4. 实战场景:静态工具类、枚举、定时任务、单元测试

Db 类是 MyBatis Plus 提供的便捷工具,在特定场景下可以简化代码。但在常规业务代码中,仍然推荐使用注入 Mapper 或 Service 的方式,以便更好地管理事务和进行单元测试。

参考资源