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);
}
}
这种方式存在一些问题:
- 工具类无法使用:静态工具类、枚举类无法注入 Spring Bean
- 测试代码繁琐:单元测试需要配置 Spring 上下文
- 代码冗余:简单查询也要注入完整的 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/Service | Db 静态类 |
|---|---|---|
| 适用场景 | 常规业务代码 | 工具类、枚举、测试 |
| 事务支持 | 完整支持 | 需要外层控制 |
| 性能 | 更优(单例) | 略有开销 |
| 代码风格 | 面向对象 | 静态方法调用 |
| 可测试性 | 更容易 Mock | 相对困难 |
小结
本章我们学习了:
- Db 类的作用:解决工具类、枚举类无法注入 Spring Bean 的问题
- 基本 CRUD:
save、getById、list、updateById、removeById等方法 - Lambda 链式操作:
lambdaQuery和lambdaUpdate提供更优雅的 API - 实战场景:静态工具类、枚举、定时任务、单元测试
Db 类是 MyBatis Plus 提供的便捷工具,在特定场景下可以简化代码。但在常规业务代码中,仍然推荐使用注入 Mapper 或 Service 的方式,以便更好地管理事务和进行单元测试。