MyBatis-Plus 多字段排序详解

1. 基本多字段排序

QueryWrapper 方式

java

// 多个字段排序:按年份降序,按状态升序,按创建时间降序
QueryWrapper<CapitalInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("capital_year")   // 第一排序:年份降序
           .orderByAsc("capital_state")    // 第二排序:状态升序
           .orderByDesc("create_time");    // 第三排序:创建时间降序

List<CapitalInfo> list = capitalInfoMapper.selectList(queryWrapper);

LambdaQueryWrapper 方式

java

// Lambda方式多字段排序
LambdaQueryWrapper<CapitalInfo> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.orderByDesc(CapitalInfo::getCapitalYear)    // 年份降序
             .orderByAsc(CapitalInfo::getCapitalState)    // 状态升序
             .orderByDesc(CapitalInfo::getCreateTime);    // 创建时间降序

List<CapitalInfo> list = capitalInfoMapper.selectList(lambdaWrapper);

2. 高级排序方法

2.1 orderBy() 方法

java

// orderBy(boolean condition, boolean isAsc, R... columns)
// condition: 是否应用排序,isAsc: 是否升序,columns: 排序字段

QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();

// 动态条件排序
boolean needSort = true;
boolean isAsc = false; // 降序

wrapper.orderBy(needSort, isAsc, "capital_year", "create_time");
// 等价于:ORDER BY capital_year DESC, create_time DESC

// Lambda方式
LambdaQueryWrapper<CapitalInfo> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.orderBy(needSort, isAsc, 
    CapitalInfo::getCapitalYear, 
    CapitalInfo::getCreateTime);

2.2 orderByAsc() 和 orderByDesc() 单个字段

java

QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();

// 混合升降序
wrapper.orderByAsc("capital_state")      // 状态升序
       .orderByDesc("capital_year")      // 年份降序
       .orderByAsc("capital_type")       // 类型升序
       .orderByDesc("create_time");      // 创建时间降序

// 生成的SQL: ORDER BY capital_state ASC, capital_year DESC, 
//           capital_type ASC, create_time DESC

2.3 多字段统一升降序

java

// 多个字段统一升序
QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
wrapper.orderByAsc(Arrays.asList("capital_year", "capital_state", "create_time"));
// 等价于: ORDER BY capital_year ASC, capital_state ASC, create_time ASC

// 多个字段统一降序
wrapper.orderByDesc(Arrays.asList("capital_year", "capital_state", "create_time"));
// 等价于: ORDER BY capital_year DESC, capital_state DESC, create_time DESC

// Lambda方式
LambdaQueryWrapper<CapitalInfo> lambdaWrapper = new LambdaQueryWrapper<>();
lambdaWrapper.orderByAsc(Arrays.asList(
    CapitalInfo::getCapitalYear,
    CapitalInfo::getCapitalState,
    CapitalInfo::getCreateTime
));

3. 动态条件排序

3.1 根据参数动态排序

java

public List<CapitalInfo> queryWithDynamicSort(CapitalInfoQueryDTO queryDTO, 
                                              String sortField, 
                                              String sortOrder) {
    QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
    
    // 构建查询条件
    if (StringUtils.isNotBlank(queryDTO.getCapitalName())) {
        wrapper.like("capital_name", queryDTO.getCapitalName());
    }
    
    // 动态排序
    if (StringUtils.isNotBlank(sortField)) {
        if ("asc".equalsIgnoreCase(sortOrder)) {
            wrapper.orderByAsc(sortField);
        } else {
            wrapper.orderByDesc(sortField);
        }
    } else {
        // 默认排序
        wrapper.orderByDesc("create_time").orderByAsc("capital_state");
    }
    
    return capitalInfoMapper.selectList(wrapper);
}

3.2 多字段动态排序

java

public List<CapitalInfo> queryWithMultiSort(CapitalInfoQueryDTO queryDTO,
                                           List<SortItem> sortItems) {
    QueryWrapper<CapitalInfo> wrapper = buildQueryWrapper(queryDTO);
    
    // sortItems: [{"field":"capitalYear","order":"desc"},
    //             {"field":"capitalState","order":"asc"}]
    if (CollectionUtils.isNotEmpty(sortItems)) {
        for (SortItem item : sortItems) {
            if ("asc".equalsIgnoreCase(item.getOrder())) {
                wrapper.orderByAsc(item.getField());
            } else if ("desc".equalsIgnoreCase(item.getOrder())) {
                wrapper.orderByDesc(item.getField());
            }
        }
    }
    
    return capitalInfoMapper.selectList(wrapper);
}

// 排序项DTO
@Data
class SortItem {
    private String field;  // 排序字段
    private String order;  // asc/desc
}

4. 复杂排序场景

4.1 条件排序

java

public List<CapitalInfo> queryWithConditionalSort(CapitalInfoQueryDTO queryDTO) {
    LambdaQueryWrapper<CapitalInfo> wrapper = new LambdaQueryWrapper<>();
    
    // 根据资金状态决定排序规则
    if (queryDTO.getCapitalState() != null && queryDTO.getCapitalState() == 1) {
        // 状态为1时:按年份降序,创建时间升序
        wrapper.orderByDesc(CapitalInfo::getCapitalYear)
               .orderByAsc(CapitalInfo::getCreateTime);
    } else {
        // 其他状态:按类型升序,年份降序
        wrapper.orderByAsc(CapitalInfo::getCapitalType)
               .orderByDesc(CapitalInfo::getCapitalYear);
    }
    
    return capitalInfoMapper.selectList(wrapper);
}

4.2 自定义排序函数

java

// 使用 orderBySql 实现自定义排序
public List<CapitalInfo> queryWithCustomSort() {
    QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
    
    // 使用SQL函数排序
    wrapper.orderByAsc("FIELD(capital_state, 2, 1, 3, 0)");  // 自定义顺序
    wrapper.orderByDesc("LENGTH(capital_name)");             // 按名称长度排序
    
    // 按条件排序:状态为1的排前面
    wrapper.orderByDesc("CASE WHEN capital_state = 1 THEN 1 ELSE 0 END");
    
    return capitalInfoMapper.selectList(wrapper);
}

4.3 分组后排序

java

public List<CapitalInfo> queryWithGroupByAndSort() {
    QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
    
    // 分组统计并排序
    wrapper.select("capital_type", "capital_source", "COUNT(*) as count")
           .groupBy("capital_type", "capital_source")
           .orderByDesc("count")          // 按数量降序
           .orderByAsc("capital_type");   // 按类型升序
    
    return capitalInfoMapper.selectList(wrapper);
}

5. 分页查询中的排序

5.1 分页排序

java

public IPage<CapitalInfo> queryPageWithSort(CapitalInfoQueryDTO queryDTO, 
                                           Integer pageNum, 
                                           Integer pageSize) {
    Page<CapitalInfo> page = new Page<>(pageNum, pageSize);
    LambdaQueryWrapper<CapitalInfo> wrapper = new LambdaQueryWrapper<>();
    
    // 构建查询条件
    if (StringUtils.isNotBlank(queryDTO.getCapitalName())) {
        wrapper.like(CapitalInfo::getCapitalName, queryDTO.getCapitalName());
    }
    
    // 排序条件
    wrapper.orderByDesc(CapitalInfo::getCapitalYear)
           .orderByAsc(CapitalInfo::getCapitalState)
           .orderByDesc(CapitalInfo::getCreateTime);
    
    return capitalInfoMapper.selectPage(page, wrapper);
}

5.2 分页参数包含排序

java

@Data
public class PageQueryDTO {
    private Integer pageNum = 1;
    private Integer pageSize = 10;
    private List<SortItem> sortItems;  // 排序字段列表
}

public IPage<CapitalInfo> queryPage(PageQueryDTO pageQuery, 
                                   CapitalInfoQueryDTO queryDTO) {
    Page<CapitalInfo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
    LambdaQueryWrapper<CapitalInfo> wrapper = new LambdaQueryWrapper<>();
    
    // 构建查询条件
    buildQueryWrapper(wrapper, queryDTO);
    
    // 应用排序
    applySort(wrapper, pageQuery.getSortItems());
    
    return capitalInfoMapper.selectPage(page, wrapper);
}

private void applySort(LambdaQueryWrapper<CapitalInfo> wrapper, List<SortItem> sortItems) {
    if (CollectionUtils.isEmpty(sortItems)) {
        // 默认排序
        wrapper.orderByDesc(CapitalInfo::getCreateTime);
        return;
    }
    
    for (SortItem item : sortItems) {
        switch (item.getField()) {
            case "capitalYear":
                applyOrder(wrapper, CapitalInfo::getCapitalYear, item.getOrder());
                break;
            case "capitalState":
                applyOrder(wrapper, CapitalInfo::getCapitalState, item.getOrder());
                break;
            case "createTime":
                applyOrder(wrapper, CapitalInfo::getCreateTime, item.getOrder());
                break;
            // 其他字段...
        }
    }
}

private <T> void applyOrder(LambdaQueryWrapper<CapitalInfo> wrapper, 
                           SFunction<CapitalInfo, T> column, 
                           String order) {
    if ("asc".equalsIgnoreCase(order)) {
        wrapper.orderByAsc(column);
    } else {
        wrapper.orderByDesc(column);
    }
}

6. 性能优化建议

6.1 排序索引优化

java

// 为常用排序字段添加索引
public List<CapitalInfo> queryOptimized() {
    QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
    
    // 优先使用有索引的字段排序
    wrapper.orderByAsc("id")  // 主键索引,排序效率高
           .orderByDesc("capital_year_indexed")  // 有索引的字段
           .orderByAsc("capital_state");
    
    return capitalInfoMapper.selectList(wrapper);
}

6.2 限制排序数据量

java

public List<CapitalInfo> queryWithLimit() {
    QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
    
    // 先筛选再排序,减少排序数据量
    wrapper.eq("capital_state", 1)
           .orderByDesc("create_time")
           .last("LIMIT 1000");  // 限制结果集大小
    
    return capitalInfoMapper.selectList(wrapper);
}

6.3 避免文件排序

java

public List<CapitalInfo> queryAvoidFileSort() {
    QueryWrapper<CapitalInfo> wrapper = new QueryWrapper<>();
    
    // 确保where条件中的字段和order by字段有复合索引
    wrapper.eq("capital_year", 2024)
           .eq("capital_type", "A")
           .orderByAsc("capital_state")  // 复合索引(capital_year, capital_type, capital_state)
           .orderByDesc("create_time");
    
    return capitalInfoMapper.selectList(wrapper);
}

7. 最佳实践总结

java

@Service
public class CapitalInfoService {
    
    public List<CapitalInfo> queryWithBestPractice(CapitalInfoQueryDTO queryDTO) {
        // 推荐使用Lambda方式
        LambdaQueryWrapper<CapitalInfo> wrapper = new LambdaQueryWrapper<>();
        
        // 1. 先构建查询条件
        buildQueryConditions(wrapper, queryDTO);
        
        // 2. 应用排序规则(清晰明了)
        applySortRules(wrapper, queryDTO);
        
        // 3. 如果需要分页
        // Page<CapitalInfo> page = new Page<>(pageNum, pageSize);
        // return capitalInfoMapper.selectPage(page, wrapper);
        
        return capitalInfoMapper.selectList(wrapper);
    }
    
    private void buildQueryConditions(LambdaQueryWrapper<CapitalInfo> wrapper, 
                                     CapitalInfoQueryDTO queryDTO) {
        wrapper.eq(queryDTO.getCapitalState() != null, 
                  CapitalInfo::getCapitalState, queryDTO.getCapitalState())
               .like(StringUtils.isNotBlank(queryDTO.getCapitalName()),
                     CapitalInfo::getCapitalName, queryDTO.getCapitalName());
    }
    
    private void applySortRules(LambdaQueryWrapper<CapitalInfo> wrapper,
                               CapitalInfoQueryDTO queryDTO) {
        // 明确的排序优先级
        wrapper.orderByDesc(CapitalInfo::getCapitalYear)    // 第一优先级:年份
               .orderByAsc(CapitalInfo::getCapitalState)    // 第二优先级:状态
               .orderByDesc(CapitalInfo::getCreateTime)     // 第三优先级:创建时间
               .orderByAsc(CapitalInfo::getId);             // 最后:ID保证稳定性
    }
}

关键点总结:

  1. 优先使用 LambdaQueryWrapper,类型安全且重构友好

  2. 排序字段应遵循业务逻辑优先级

  3. 考虑添加数据库索引优化排序性能

  4. 对于大表查询,先筛选再排序

  5. 复杂的排序逻辑可以封装为独立的方法

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐