Mybatis plus之LambdaQueryWrapper使用sum函数求和,你会用吗

本文共 6,345 字,预计阅读时间 21 分钟

 


MyBatis-Plus 实现按字段求和查询的通用方案

在使用 MyBatis-Plus 进行开发时,经常会遇到需要对查询数据按某个字段求和的需求,而 MyBatis-Plus 本身并不直接支持 SUM 函数。通常,开发者可能会选择在 XML 文件中编写 SQL 来解决这个问题,但本文将提供一种更便捷的方式,通过封装通用方法,直接在 Java 代码中调用实现求和查询。

环境版本说明

  • MyBatis-Plus:3.4.0
  • Spring Boot:2.7.17
  • MySQL:8.0

实现步骤

1. 定义实体类

首先,我们需要定义一个实体类来表示查询结果。以下是 OutputInvoice 实体类的代码:
复制代码
package com.zxh.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.math.BigDecimal;

// 实体类,用于表示查询结果
@Data
@TableName("output_invoice")
public class OutputInvoice {
    //开票金额
    private BigDecimal amount;
    //税额
    private BigDecimal tax;
    //发票状态,1'已开票',2'已作废'
    private String status;
    //发票号码
    private String invoiceNo;
    //发票代码
    private String invoiceCode;
}
复制代码

2. 创建数据库表并插入示例数据

接下来,我们需要创建对应的数据库表,并插入一些示例数据。以下是 SQL 代码:
复制代码
-- 创建 output_invoice 表
CREATE TABLE output_invoice (
    -- 开票金额,使用 DECIMAL 类型存储精确的数值,10 为总位数,2 为小数位数
    amount DECIMAL(10, 2) COMMENT '开票金额',
    -- 税额,使用 DECIMAL 类型存储精确的数值,10 为总位数,2 为小数位数
    tax DECIMAL(10, 2) COMMENT '税额',
    -- 发票状态,1 表示已开票,2 表示已作废
    status VARCHAR(2) COMMENT '发票状态,1已开票,2已作废',
    -- 发票号码,用于唯一标识一张发票
    invoiceNo VARCHAR(20) COMMENT '发票号码',
    -- 发票代码,用于标识发票的类别等信息
    invoiceCode VARCHAR(20) COMMENT '发票代码'
) COMMENT '销项发票信息表';

-- 插入示例数据
INSERT INTO output_invoice (amount, tax, status, invoiceNo, invoiceCode)
VALUES 
(1000.00, 130.00, '1', '12345678', '00000001'),
(2000.50, 260.07, '1', '87654321', '00000002'),
(500.20, 65.03, '2', '98765432', '00000003');
复制代码

3. 封装通用查询工具类

为了实现通用的求和查询功能,我们创建一个 QueryUtils 工具类,该类封装了 selectSumBySQL 方法,用于执行求和查询。以下是工具类的代码:
复制代码
package com.zxh.util;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;

import static cn.hutool.core.collection.CollectionUtil.isNotEmpty;

/**
 * 查询工具类,提供通用的求和查询方法
 */
@Slf4j
public class QueryUtils {

    /**
     * 执行按字段求和的查询
     * @param lambdaQueryWrapper Lambda 查询条件包装器
     * @param sumSQL 求和的 SQL 语句
     * @param tClass 实体类的 Class 对象
     * @param queryExecutor 查询执行器,用于执行查询操作
     * @param <T> 实体类的泛型类型
     * @return 查询结果的实体类对象
     */
    public <T> T selectSumBySQL(LambdaQueryWrapper<T> lambdaQueryWrapper, String sumSQL, Class<T> tClass, Function<QueryWrapper<T>, List<Map<String, Object>>> queryExecutor) {
        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
        // 使用 sum 求和
        queryWrapper.select(sumSQL);

        // 获取 LambdaQueryWrapper 的 SQL 片段和参数映射
        String sqlSegment = lambdaQueryWrapper.getCustomSqlSegment();
        Map<String, Object> paramNameValuePairs = lambdaQueryWrapper.getParamNameValuePairs();

        log.info("执行 sum 查询 lambdaQueryWrapper SQL: {}", sqlSegment);
        log.info("执行 sum 查询 lambdaQueryWrapper 参数: {}", paramNameValuePairs);

        // 拼接 SQL 片段
        if (sqlSegment != null && !sqlSegment.isEmpty()) {
            if (sqlSegment.trim().toLowerCase().startsWith("where")) {
                sqlSegment = sqlSegment.substring(5).trim();
            }
            queryWrapper.last("WHERE " + sqlSegment);
        }

        // 直接使用 LambdaQueryWrapper 的参数映射
        queryWrapper.getParamNameValuePairs().putAll(paramNameValuePairs);

        // 打印最终的 QueryWrapper 信息
        log.info("执行 sum 查询 QueryWrapper SQL: {}", queryWrapper.getCustomSqlSegment());
        log.info("执行 sum 查询 QueryWrapper 参数: {}", queryWrapper.getParamNameValuePairs());

        // 使用执行器进行查询
        List<Map<String, Object>> mapList = queryExecutor.apply(queryWrapper);
        if (isNotEmpty(mapList)) {
            Map<String, Object> sumMap = mapList.get(0);
            if (sumMap != null) {
                try {
                    // 创建实体类的实例
                    T result = tClass.getDeclaredConstructor().newInstance();
                    // 遍历实体类的字段
                    for (Field field : tClass.getDeclaredFields()) {
                        field.setAccessible(true);
                        String fieldName = field.getName();
                        Object value = sumMap.get(fieldName);
                        if (value != null) {
                            if (field.getType() == BigDecimal.class) {
                                value = new BigDecimal(value.toString());
                            }
                            // 将查询结果设置到实体类的对应字段中
                            field.set(result, value);
                        }
                    }
                    return result;
                } catch (Exception e) {
                    log.error("查询求和方法异常:", e);
                    throw new RuntimeException(e);
                }
            }
        }
        return null;
    }
}
复制代码

 

4.调用测试

最后,我们编写一个测试方法来验证 QueryUtils 工具类的功能。以下是测试代码:
复制代码
package com.zxh;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zxh.dao.OutputInvoiceMapper;
import com.zxh.entity.OutputInvoice;
import com.zxh.util.QueryUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;


@SpringBootTest
@Slf4j
class SpringbootDemoApplicationTests {

    @Resource
    private OutputInvoiceMapper outputInvoiceMapper;

    @Test
    public void test() {
        QueryUtils queryUtils = new QueryUtils();
        LambdaQueryWrapper<OutputInvoice> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OutputInvoice::getStatus, "1");
        //汇总的sql
        String sumSQL = "SUM(amount) as amount,SUM(tax) as tax";
        // 调用 selectSumBySQL 方法
        OutputInvoice sumResult = queryUtils.selectSumBySQL(
                lambdaQueryWrapper,
                sumSQL,
                OutputInvoice.class,
                outputInvoiceMapper::selectMaps);
        // 验证结果
        if (sumResult != null) {
            System.out.println("查询结果: " + sumResult);
        } else {
            System.out.println("未查询到结果");
        }

    }


}
复制代码

打印结果

代码解释

由于 LambdaQueryWrapper 本身不支持 SUM 函数,我们借助 QueryWrapper 传入自定义的求和 SQL 语句。将两者结合的原因是,LambdaQueryWrapper 在构建查询条件时具有优势,只需指定对象的字段名即可。通过这种方式,我们可以在不使用 XML 文件的情况下,直接在 Java 代码中实现按字段求和的查询功能。
通过以上步骤,你可以方便地在 MyBatis-Plus 项目中实现按字段求和的查询需求,提高开发效率。

 

posted @   钟小嘿  阅读(273)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2023-02-22 Springboot整合shiro
点击右上角即可分享
微信分享提示