增强 Service 层
BaseService 是对 MyBatis-Plus 原生 IService 的增强封装,提供了容错模式(Ignore)和严格校验模式(Validate)两套 CRUD 方法,以及悲观锁、关联查询等高级功能。
核心特性
- ✅ 双模式方法:Ignore(容错)和 Validate(严格校验)
- ✅ 悲观锁支持:
getAndLock系列方法支持for update - ✅ 分页增强:自动按
updateTime降序排序 - ✅ 关联查询:
parseRelObject简化一对一、一对多查询 - ✅ 检查接口:集成
CheckProvider提供业务检查能力
快速开始
1. 定义 Service 接口
java
import host.springboot.framework.mybatisplus.service.BaseService;
public interface UserService extends BaseService<User> {
// 无需定义任何方法,继承 BaseService 即可
}2. 定义 Service 实现类
java
import host.springboot.framework.mybatisplus.service.BaseServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User>
implements UserService {
// 无需实现任何方法
}3. 使用示例
java
@RestController
@RequestMapping("/user")
public class UserController {
private final UserService userService;
// 容错模式 - 参数为 null 不抛异常
@GetMapping("/{id}")
public SingleVO<User> getById(@PathVariable Long id) {
User user = userService.getByIdIgnore(id); // 不存在返回 null
return R.okSingle(user);
}
// 严格校验模式 - 参数为 null 抛异常
@GetMapping("/validate/{id}")
public SingleVO<User> getByIdValidate(@PathVariable Long id) {
User user = userService.getByIdValidate(id); // 不存在抛 MybatisServiceException
return R.okSingle(user);
}
}方法体系
BaseService 提供了 3 大类方法:
| 类别 | 方法前缀/后缀 | 特点 | 适用场景 |
|---|---|---|---|
| Ignore 模式 | xxxIgnore | 容错,null 不抛异常 | 展示类查询、可选数据 |
| Validate 模式 | xxxValidate | 严格,null 抛异常 | 核心业务逻辑 |
| QueryLock 模式 | getAndLockXxx | 悲观锁查询 | 并发更新场景 |
Ignore 系列方法(容错模式)
存在性检查
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
isExistsByIdIgnore(id) | Serializable | boolean | id 为 null 返回 false |
isExistsByIdIgnore(ids) | Collection | boolean | ids 为 null/empty 返回 false |
countByIdIgnore(id) | Serializable | long | id 为 null 返回 -1 |
countByIdIgnore(ids) | Collection | long | ids 为 null/empty 返回 -1 |
使用示例:
java
// 检查用户是否存在(容错)
if (userService.isExistsByIdIgnore(userId)) {
// 用户存在,执行业务逻辑
}
// 统计用户数量(容错)
long count = userService.countByIdIgnore(userId); // 不存在返回 -1查询方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getByIdIgnore(id) | Serializable | T | 不存在返回 null |
getByIdIgnoreOpt(id) | Serializable | Optional<T> | 返回 Optional |
listByIdIgnore(ids) | Collection | List<T> | 不存在返回空集合 |
listIgnore(wrapper) | Wrapper | List<T> | 不存在返回空集合 |
pageIgnore(pageQuery) | PageQuery | Pageable<List<T>> | 自动按 updateTime 降序 |
使用示例:
java
// 根据 ID 查询(容错)
User user = userService.getByIdIgnore(1L); // 不存在返回 null
if (user != null) {
// 处理用户信息
}
// 使用 Optional(推荐)
userService.getByIdIgnoreOpt(1L).ifPresent(user -> {
// 处理用户信息
});
// 批量查询(容错)
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userService.listByIdIgnore(ids); // 部分不存在也不抛异常
// 分页查询(自动按 updateTime 降序)
PageQuery pageQuery = new PageQuery();
pageQuery.setPageNo(1L);
pageQuery.setPageSize(10L);
Pageable<List<User>> page = userService.pageIgnore(pageQuery);保存方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
saveIgnore(entity) | T | Boolean | entity 为 null 返回 null |
saveIgnore(entities) | Collection<T> | boolean | entities 为 null/empty 返回 false |
saveIgnore(entities, batchSize) | Collection<T>, int | boolean | 批量保存,指定批次大小 |
使用示例:
java
// 保存单个实体(容错)
User user = new User();
user.setUsername("zhangsan");
user.setEmail("zhangsan@example.com");
Boolean result = userService.saveIgnore(user); // null 参数返回 null
// 批量保存(容错)
List<User> users = buildUsers();
boolean success = userService.saveIgnore(users, 100); // 每批保存 100 条更新方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
updateByIdIgnore(entity) | T | boolean | entity 为 null 返回 false |
updateByIdIgnore(entities) | Collection<T> | boolean | entities 为 null/empty 返回 false |
updateByIdIgnore(entities, batchSize) | Collection<T>, int | boolean | 批量更新,指定批次大小 |
使用示例:
java
// 更新单个实体(容错)
User user = userService.getByIdIgnore(1L);
if (user != null) {
user.setEmail("new_email@example.com");
userService.updateByIdIgnore(user);
}
// 批量更新(容错)
List<User> users = userService.listByIdIgnore(ids);
users.forEach(u -> u.setStatus(1));
userService.updateByIdIgnore(users, 100);删除方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
removeByIdIgnore(id) | Serializable | boolean | id 为 null 返回 false |
removeByIdIgnore(ids) | Collection | boolean | ids 为 null/empty 返回 false |
使用示例:
java
// 删除单个实体(容错)
boolean success = userService.removeByIdIgnore(1L);
// 批量删除(容错)
List<Long> ids = Arrays.asList(1L, 2L, 3L);
userService.removeByIdIgnore(ids);Validate 系列方法(严格校验模式)
存在性检查
| 方法 | 参数 | 返回值 | 异常情况 |
|---|---|---|---|
isExistsByIdValidate(id) | Serializable | boolean | id 为 null 抛异常 |
isExistsByIdValidate(ids) | Collection | boolean | ids 为 null/empty 抛异常 |
countByIdValidate(id) | Serializable | long | id 为 null 抛异常 |
countByIdValidate(ids) | Collection | long | ids 为 null/empty 抛异常 |
使用示例:
java
// 检查用户是否存在(严格校验)
try {
boolean exists = userService.isExistsByIdValidate(userId);
if (exists) {
// 用户存在
}
} catch (MybatisServiceException e) {
// userId 为 null
logger.error("用户ID不能为空", e);
}
// 统计用户数量(严格校验)
long count = userService.countByIdValidate(userId); // null 直接抛异常查询方法
| 方法 | 参数 | 返回值 | 异常情况 |
|---|---|---|---|
getByIdValidate(id) | Serializable | T | id 为 null 或不存在抛异常 |
listByIdValidate(ids) | Collection | List<T> | ids 为 null/empty 或数据不完整抛异常 |
listValidate(wrapper) | Wrapper | List<T> | 查询结果为空抛异常 |
pageValidate(pageQuery) | PageQuery | Pageable<List<T>> | pageQuery 为 null 抛异常 |
使用示例:
java
// 根据 ID 查询(严格校验)
User user = userService.getByIdValidate(userId); // 不存在直接抛异常
user.setEmail("new_email@example.com");
userService.updateByIdValidate(user);
// 批量查询(严格校验)
List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userService.listByIdValidate(ids); // 必须全部存在,否则抛异常
// 分页查询(严格校验)
Pageable<List<User>> page = userService.pageValidate(pageQuery); // null 抛异常保存方法
| 方法 | 参数 | 返回值 | 异常情况 |
|---|---|---|---|
saveValidate(entity) | T | boolean | entity 为 null 抛异常 |
saveValidate(entities) | Collection<T> | boolean | entities 为 null/empty 抛异常 |
saveValidate(entities, batchSize) | Collection<T>, int | boolean | entities 为 null/empty 抛异常 |
使用示例:
java
// 保存单个实体(严格校验)
User user = buildUser();
userService.saveValidate(user); // null 直接抛异常
// 批量保存(严格校验)
List<User> users = buildUsers();
userService.saveValidate(users, 100); // 空集合直接抛异常更新方法
| 方法 | 参数 | 返回值 | 异常情况 |
|---|---|---|---|
updateByIdValidate(entity) | T | boolean | entity 为 null 或 ID 不存在抛异常 |
updateByIdValidate(entities) | Collection<T> | boolean | entities 为 null/empty 或 ID 不存在抛异常 |
使用示例:
java
// 更新单个实体(严格校验)
User user = userService.getByIdValidate(userId);
user.setEmail("new_email@example.com");
userService.updateByIdValidate(user); // ID 不存在直接抛异常
// 批量更新(严格校验)
List<User> users = userService.listByIdValidate(ids);
users.forEach(u -> u.setStatus(1));
userService.updateByIdValidate(users); // ID 不完整直接抛异常删除方法
| 方法 | 参数 | 返回值 | 异常情况 |
|---|---|---|---|
removeByIdValidate(id) | Serializable | boolean | id 为 null 或不存在抛异常 |
removeByIdValidate(ids) | Collection | boolean | ids 为 null/empty 或不存在抛异常 |
使用示例:
java
// 删除单个实体(严格校验)
userService.removeByIdValidate(userId); // 不存在直接抛异常
// 批量删除(严格校验)
userService.removeByIdValidate(ids); // 任一 ID 不存在直接抛异常QueryLock 系列方法(悲观锁)
使用 for update 实现悲观锁,适用于并发更新场景。
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
getAndLockIgnore(id) | Serializable | T | 容错模式 |
getAndLockValidate(id) | Serializable | T | 严格校验模式 |
getAndLockIgnore(ids) | Collection | Collection<T> | 批量容错模式 |
getAndLockValidate(ids) | Collection | Collection<T> | 批量严格校验模式 |
使用场景
场景1:库存扣减
java
@Transactional(rollbackFor = Exception.class)
public void deductStock(Long productId, Integer quantity) {
// 锁定商品记录(for update)
Product product = productService.getAndLockValidate(productId);
// 检查库存
if (product.getStock() < quantity) {
throw new BusinessException("库存不足");
}
// 扣减库存
product.setStock(product.getStock() - quantity);
productService.updateByIdValidate(product);
}场景2:余额扣除
java
@Transactional(rollbackFor = Exception.class)
public void deductBalance(Long userId, BigDecimal amount) {
// 锁定用户记录
User user = userService.getAndLockValidate(userId);
// 检查余额
if (user.getBalance().compareTo(amount) < 0) {
throw new BusinessException("余额不足");
}
// 扣除余额
user.setBalance(user.getBalance().subtract(amount));
userService.updateByIdValidate(user);
}生成的 SQL:
sql
SELECT * FROM sys_user WHERE id = 1 FOR UPDATE;注意
- 必须在事务中使用,否则锁立即释放
- 会阻塞其他事务对相同数据的
for update查询 - 慎用,可能导致性能问题
工具方法
clearEntityDefaultParameterAndGet
清除实体类的默认参数值,用于数据复制场景。
java
// 清除 createTime 和 updateTime
User newUser = BaseService.clearEntityDefaultParameterAndGet(oldUser);
// 自定义清除字段
User newUser = BaseService.clearEntityDefaultParameterAndGet(
oldUser,
true, // 清除 ID
true, // 清除 createTime
true // 清除 updateTime
);toUnderlineCase
驼峰转下划线。
java
String columnName = BaseService.toUnderlineCase("userName"); // "user_name"Ignore vs Validate 对比
| 对比项 | Ignore 模式 | Validate 模式 |
|---|---|---|
| 参数为 null | 返回默认值(null/false/-1) | 抛出 MybatisServiceException |
| 数据不存在 | 返回默认值 | 抛出 MybatisServiceException |
| 性能 | 略优(少一次校验) | 略差(多一次校验) |
| 适用场景 | 展示类查询、可选数据 | 核心业务逻辑 |
| 代码风格 | 需要 null 检查 | 无需 null 检查 |
使用建议
✅ 使用 Ignore 的场景:
java
// 1. 前端展示(数据可选)
User user = userService.getByIdIgnore(userId);
if (user != null) {
// 展示用户信息
}
// 2. 批量查询(部分数据不存在)
List<User> users = userService.listByIdIgnore(ids); // 容忍部分不存在
// 3. 数据统计(可选条件)
long count = userService.countByIdIgnore(userId);✅ 使用 Validate 的场景:
java
// 1. 核心业务逻辑(数据必须存在)
User user = userService.getByIdValidate(userId);
user.setBalance(user.getBalance().subtract(amount));
userService.updateByIdValidate(user);
// 2. 数据完整性要求高
List<User> users = userService.listByIdValidate(ids); // 必须全部存在
// 3. 关联数据校验
Order order = orderService.getByIdValidate(orderId);
Product product = productService.getByIdValidate(order.getProductId());异常处理
Validate 系列方法会抛出 MybatisServiceException,建议使用全局异常处理器统一处理:
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MybatisServiceException.class)
public BaseVO handleMybatisServiceException(MybatisServiceException e) {
log.error("数据库操作异常", e);
return R.base(e.getErrorCode(), e.getErrorMessage(), e.getUserTip());
}
}最佳实践
✅ 推荐做法
Service 接口继承 BaseService
javapublic interface UserService extends BaseService<User> { } // ✅根据场景选择模式
java// 展示类查询使用 Ignore User user = userService.getByIdIgnore(userId); // ✅ // 核心业务使用 Validate User user = userService.getByIdValidate(userId); // ✅悲观锁必须在事务中
java@Transactional // ✅ public void deductStock(Long productId, Integer quantity) { Product product = productService.getAndLockValidate(productId); // ... }使用 Optional 优雅处理 null
javauserService.getByIdIgnoreOpt(userId).ifPresent(user -> { // ✅ // 处理用户 });
❌ 不推荐做法
❌ 不继承 BaseService
javapublic interface UserService extends IService<User> { } // ❌❌ 滥用 Validate 模式
java// 前端展示不应该用 Validate User user = userService.getByIdValidate(userId); // ❌ 可能抛异常影响体验❌ 悲观锁不在事务中
javapublic void deductStock(Long productId, Integer quantity) { // ❌ 没有 @Transactional Product product = productService.getAndLockValidate(productId); // 锁立即释放,无效 }❌ 混用 Ignore 和 Validate
javaUser user = userService.getByIdIgnore(userId); // ❌ if (user != null) { userService.updateByIdValidate(user); // ❌ 风格不统一 }
常见问题
Q: Ignore 和 Validate 如何选择?
A: 简单判断标准:
- 数据可选 → 用 Ignore
- 数据必须存在 → 用 Validate
举例:
java
// 展示用户头像(可选)
userService.getByIdIgnore(userId); // ✅ 不存在就不显示
// 扣除用户余额(必须存在)
userService.getByIdValidate(userId); // ✅ 不存在就是异常Q: 为什么 Validate 方法要抛异常?
A: 设计理念:让异常在调用处立即暴露,避免 null 传播导致的 NPE。
对比:
java
// Ignore - 需要 null 检查
User user = userService.getByIdIgnore(userId);
if (user == null) {
throw new BusinessException("用户不存在");
}
user.setEmail(newEmail);
// Validate - 无需 null 检查
User user = userService.getByIdValidate(userId); // 不存在直接抛异常
user.setEmail(newEmail); // 确保 user 不为 nullQ: getAndLock 什么时候用?
A: 适用场景:
- 库存扣减:防止超卖
- 余额扣除:防止透支
- 乐观锁失败后:降级为悲观锁
不适用场景:
- 高并发读多写少(用乐观锁)
- 不需要事务一致性
Q: pageIgnore 为什么自动按 updateTime 排序?
A: 设计理念:大部分列表查询都希望看到最新修改的数据。
自定义排序:
java
// 使用 IPage 自定义排序
Page<User> page = new Page<>(1, 10);
page.addOrder(OrderItem.asc("username")); // 按用户名升序
Pageable<List<User>> result = userService.pageIgnore(page);Q: BaseService 和 IService 的关系?
A: BaseService 继承 IService,所以:
- MyBatis-Plus 原生方法都可以用
- 额外提供了 Ignore/Validate/QueryLock 等增强方法
java
userService.save(user); // ✅ IService 原生方法
userService.saveIgnore(user); // ✅ BaseService 增强方法
userService.getById(1L); // ✅ IService 原生方法
userService.getByIdIgnore(1L); // ✅ BaseService 增强方法