一、核心概念

  1. 服务层扩展点
    • IService:MP 提供的服务层接口(含 17+ 内置方法)
    • ServiceImpl:服务层基础实现类
  2. 扩展类型
    • 自定义 CRUD 方法
    • 复杂业务逻辑封装
    • 跨表事务操作
    • 批量处理优化
  3. 核心注解
    • @Service:声明 Spring 服务
    • @DS:多数据源路由(需配合 dynamic-datasource)
    • @Transactional:事务控制

二、详细操作步骤

1. 基础服务接口定义
// 基础接口继承 IService
public interface UserService extends IService<User> {
    // 此处声明自定义方法
}
2. 服务实现类扩展
@Service
public class UserServiceImpl 
       extends ServiceImpl<UserMapper, User> // 继承MP基础实现
       implements UserService {               // 实现自定义接口

    // 示例1:带业务逻辑的查询
    @Override
    public List<User> findActiveUsers() {
        return lambdaQuery()
            .eq(User::getStatus, 1)
            .gt(User::getLoginCount, 0)
            .orderByDesc(User::getLastLoginTime)
            .list();
    }

    // 示例2:跨表更新(事务操作)
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateUserWithLog(User user, String operation) {
        // 更新用户
        boolean updateSuccess = updateById(user);
        
        // 插入日志
        UserLog log = new UserLog()
            .setUserId(user.getId())
            .setOperation(operation);
        return updateSuccess && userLogService.save(log);
    }

    // 示例3:批量处理扩展
    @Override
    public boolean batchUpdateStatus(List<Long> ids, int status) {
        if (CollectionUtils.isEmpty(ids)) return false;
        
        return update(new LambdaUpdateWrapper<User>()
            .set(User::getStatus, status)
            .in(User::getId, ids)
        );
    }
}
3. 复杂分页查询扩展
// 接口中定义
IPage<UserVO> pageUserWithDept(Page<User> page, UserQueryDTO query);

// 实现类
@Override
public IPage<UserVO> pageUserWithDept(Page<User> page, UserQueryDTO query) {
    return baseMapper.selectUserPage(
        page, 
        Wrappers.<User>lambdaQuery()
            .like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
            .eq(query.getDeptId() != null, User::getDeptId, query.getDeptId())
    );
}

// Mapper中实现
@Select("SELECT u.*, d.name AS dept_name FROM user u LEFT JOIN dept d ON u.dept_id = d.id ${ew.customSqlSegment}")
IPage<UserVO> selectUserPage(IPage<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
4. 多数据源方法扩展
// 指定数据源操作
@DS("slave") // 从库查询
@Override
public User getFromReplica(Long id) {
    return getById(id);
}

@DS("master") // 主库写入
@Transactional
@Override
public boolean criticalUpdate(User user) {
    return updateById(user);
}
5. 流式查询扩展
@Override
public void processLargeData(Consumer<User> processor) {
    try (Cursor<User> cursor = baseMapper.selectCursor(
        Wrappers.<User>lambdaQuery().gt(User::getId, 10000)
    )) {
        cursor.forEach(processor);
    }
}

三、常见错误与解决

  1. 事务失效

    • 错误:非 public 方法添加 @Transactional
    • 解决:确保方法为 public 且类被 Spring 代理
  2. NPE异常

    • 错误:未判空直接调用 baseMapper 方法
    • 解决:添加空值检查
      public User safeGetById(Long id) {
          return id != null ? getById(id) : null;
      }
      
  3. 批量操作性能差

    • 错误:循环中单条更新
    • 解决:使用 MP 批量方法 + 批处理配置
      mybatis-plus:
        global-config:
          db-config:
            batch-size: 1000
      
  4. 多数据源切换失败

    • 错误:同类内方法调用导致注解失效
    • 解决:通过 AopContext.currentProxy() 调用

四、注意事项

  1. 方法命名规范

    • 查询:findXxx/getXxx/listXxx
    • 更新:updateXxx/modifyXxx
    • 删除:removeXxx/deleteXxx
    • 插入:saveXxx/createXxx
  2. 事务边界控制

    • 只读方法:@Transactional(readOnly = true)
    • 写操作:明确指定回滚异常
    • 避免大事务:拆分业务逻辑
  3. 异常处理原则

    • 服务层捕获所有持久层异常
    • 转换为业务异常向上抛出
      try {
          return baseMapper.selectById(id);
      } catch (Exception e) {
          throw new BusinessException("用户查询失败", e);
      }
      

五、使用技巧

  1. 链式条件封装

    public LambdaQueryWrapper<User> buildQuery(UserQuery query) {
        return new LambdaQueryWrapper<User>()
            .like(StringUtils.isNotBlank(query.getName()), User::getName, query.getName())
            .between(query.getStartTime() != null && query.getEndTime() != null, 
                    User::getCreateTime, query.getStartTime(), query.getEndTime())
            .eq(query.getStatus() != null, User::getStatus, query.getStatus());
    }
    
  2. 通用结果包装

    public <T> Result<T> successResult(T data) {
        return Result.success(data);
    }
    
    public Result<Void> failResult(String msg) {
        return Result.fail(msg);
    }
    
  3. 多租户自动注入

    @Override
    public boolean save(User entity) {
        if (entity.getTenantId() == null) {
            entity.setTenantId(TenantContext.getCurrentId());
        }
        return super.save(entity);
    }
    
  4. 方法级权限控制

    @PreAuthorize("hasRole('ADMIN')")
    @Override
    public boolean deleteUser(Long id) {
        return removeById(id);
    }
    

六、最佳实践与性能优化

  1. 批量操作优化

    @Transactional
    public boolean batchInsert(List<User> users) {
        // 分批次插入(每批1000条)
        return saveBatch(users, 1000);
    }
    
    // JDBC批处理模式
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new BatchInsertInnerInterceptor());
        return interceptor;
    }
    
  2. 二级缓存应用

    @Cacheable(value = "userCache", key = "#id")
    @Override
    public User getByIdWithCache(Long id) {
        return getById(id);
    }
    
    @CacheEvict(value = "userCache", key = "#user.id")
    @Override
    public boolean updateUser(User user) {
        return updateById(user);
    }
    
  3. 读写分离配置

    @DS("slave")
    @Override
    public List<User> readOnlyQuery() {
        return list();
    }
    
    @DS("master")
    @Transactional
    @Override
    public boolean writeOperation() {
        // ...
    }
    
  4. 分页性能优化

    public Page<User> optimizePageQuery(PageQuery query) {
        Page<User> page = new Page<>(query.getPage(), query.getSize());
        page.setSearchCount(false); // 不执行COUNT查询
    
        List<User> records = baseMapper.selectPageWithoutCount(page, buildQuery(query));
        return page.setRecords(records);
    }
    
    // Mapper中使用覆盖索引
    @Select("SELECT id,name FROM user ${ew.customSqlSegment}")
    List<User> selectPageWithoutCount(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
    
  5. 异步处理

    @Async("taskExecutor")
    @Override
    public CompletableFuture<List<User>> asyncProcess() {
        return CompletableFuture.completedFuture(findActiveUsers());
    }
    

**七、复杂场景综合示例

场景:用户积分批量更新
@Transactional(rollbackFor = Exception.class)
public boolean batchUpdatePoints(List<PointUpdateDTO> updates) {
    // 1. 校验数据
    if (CollectionUtils.isEmpty(updates)) return false;
    
    // 2. 分组处理(每500条一批)
    Map<Boolean, List<PointUpdateDTO>> groups = updates.stream()
        .collect(Collectors.partitioningBy(dto -> dto.getDelta() > 0));
    
    // 3. 增加积分
    processPointGroup(groups.get(true), true);
    
    // 4. 扣除积分
    processPointGroup(groups.get(false), false);
    
    // 5. 记录日志
    return pointLogService.saveBatch(updates.stream()
        .map(dto -> new PointLog(dto.getUserId(), dto.getDelta()))
        .collect(Collectors.toList()));
}

private void processPointGroup(List<PointUpdateDTO> list, boolean isAdd) {
    if (CollectionUtils.isEmpty(list)) return;
    
    List<Long> userIds = list.stream()
        .map(PointUpdateDTO::getUserId)
        .collect(Collectors.toList());
    
    // 批量更新语句
    String operation = isAdd ? "+" : "-";
    String sql = String.format(
        "UPDATE user SET points = points %s {delta} WHERE id = {id} AND points %s {delta} >= 0",
        operation, isAdd ? ">=" : ""
    );
    
    // 执行批量更新
    executeBatch(userIds, (sqlSession, id) -> {
        PointUpdateDTO dto = findDtoById(list, id);
        sqlSession.update(sql, Map.of("id", id, "delta", Math.abs(dto.getDelta())));
    });
}

八、调试与监控

  1. SQL 执行监控

    public List<User> monitoredQuery() {
        StopWatch watch = new StopWatch();
        watch.start("query");
        List<User> users = findActiveUsers();
        watch.stop();
    
        if (watch.getTotalTimeMillis() > 500) {
            log.warn("慢查询: {}", watch.prettyPrint());
        }
        return users;
    }
    
  2. Arthas 诊断

    # 监控方法调用
    watch com.example.service.UserService * '{params, returnObj}' -x 3
    
  3. 性能分析插件

    @Bean
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor interceptor = new PerformanceInterceptor();
        interceptor.setMaxTime(200); // 阈值(ms)
        interceptor.setFormat(true); // 格式化SQL
        return interceptor;
    }
    

关键总结

  1. 扩展原则

    • 基础 CRUD 用内置方法
    • 业务逻辑封装在服务层
    • 复杂操作拆分为原子方法
  2. 性能铁律

    • 批量操作配置批处理大小
    • 分页查询禁用不必要 COUNT
    • 大结果集使用流式处理
  3. 事务准则

    • 只读操作用 readOnly=true
    • 写操作明确回滚规则
    • 事务内避免远程调用
  4. 扩展模式

    graph LR
      A[Controller] --> B[Service接口]
      B --> C[自定义方法]
      C --> D[ServiceImpl实现]
      D --> E[基础CRUD]
      D --> F[Mapper扩展]
      D --> G[其他Service]
    

通过合理扩展服务层方法,可实现:

  • 业务逻辑高内聚
  • 代码复用最大化
  • 性能瓶颈可优化
  • 系统架构更清晰