Java行转列通用工具类适用于各种查询情况

news2025/7/19 12:07:00

1、说明

有时候工作中需要动态生成列,也就是不确定的列,那么在数据库层就不是那么好操作了,可以使用java工具类来实现。

本工具类是对市面上的工具类进行加工改造,可以通用于各种情况,更加灵活,下面我来演示一下

2、工具类代码

package com.lili.util;


import java.lang.reflect.Field;
import java.util.*;

/**
 * 行转列终极工具类,通用于查询单个列或者多个列的结果
 *
 * @author QiJingJing
 */
public class RowConvertColUtil {
    private static Set<Object> headerSet;
    private static Set<Object> fixedColumnSet;

    private RowConvertColUtil() {
    }

    public static class ConvertData {
        private Set<Object> headerSet;
        private Set<Object> fixedColumnSet;
        private List<Map<String, Object>> dataList;

        public ConvertData(List<Map<String, Object>> dataList, Set<Object> headerSet, Set<Object> fixedColumnSet) {
            this.headerSet = headerSet;
            this.fixedColumnSet = fixedColumnSet;
            this.dataList = dataList;
        }

        public Set<Object> getHeaderSet() {
            return headerSet;
        }

        public void setHeaderSet(Set<Object> headerSet) {
            this.headerSet = headerSet;
        }

        public Set<Object> getFixedColumnSet() {
            return fixedColumnSet;
        }

        public void setFixedColumnSet(Set<Object> fixedColumnSet) {
            this.fixedColumnSet = fixedColumnSet;
        }

        public List<Map<String, Object>> getDataList() {
            return dataList;
        }

        public void setDataList(List<Map<String, Object>> dataList) {
            this.dataList = dataList;
        }
    }

    /**
     * 行转列返回 ConvertData 我们想要展示的格式
     *
     * @param orignalList     要行转列的list
     * @param headerName      要行转列的字段
     * @param fixedColumn     固定需要查询列字段
     * @param valueFiedName   行转列字段对应值列的字段名
     * @param needHeader      是否返回表头
     * @param fixedColumnName 固定需要查询列字段的昵称
     * @param nullValue       空值填充
     **/
    public static synchronized ConvertData doConvertReturnObj(List<?> orignalList, String headerName, String[] fixedColumn, String valueFiedName, boolean needHeader, String[] fixedColumnName, String nullValue) throws Exception {
        List<List<Object>> lists = doConvert(orignalList, headerName, fixedColumn, valueFiedName, needHeader, fixedColumnName, nullValue);
        // 拿到每个列表需要的属性个数
        int size = lists.get(0).size();
        // 拿出总共的集合数量
        int dataListNum = lists.size() - 1;
        // 将固定字段和固定字段值做kv映射
        Map<String, String> columnMap = new HashMap<>(16);
        for (int i = 0; i < fixedColumn.length; i++) {
            columnMap.put(fixedColumnName[i], fixedColumn[i]);
        }
        // 对lists里面的数据做转换,转换成原本类的格式(一个属性对应一个值的形式)
        List<Map<String, Object>> maps = new ArrayList<>();
        for (int i = 0; i < dataListNum; i++) {
            Map<String, Object> map = new LinkedHashMap<>(16);
            for (int j = 0; j < size; j++) {
                // 列的表头昵称
                String columnName = String.valueOf(lists.get(0).get(j));
                if (fixedColumn.length > j) {
                    // 根据昵称,拿到属性名,然后将下一个列表的对应值加进去
                    map.put(columnMap.get(columnName), lists.get(i + 1).get(j));
                } else {
                    map.put(columnName, lists.get(i + 1).get(j));
                }
            }
            maps.add(map);
        }
        return new ConvertData(maps, headerSet, fixedColumnSet);
    }

    /**
     * 列表行转列的最终结果
     *
     * @param orignalList     要行转列的list
     * @param headerName      要行转列的字段
     * @param fixedColumn     固定需要查询列字段
     * @param valueFiedName   行转列字段对应值列的字段名
     * @param needHeader      是否返回表头
     * @param fixedColumnName 固定需要查询列字段的昵称
     * @param nullValue       空值填充
     **/
    public static synchronized List<List<Object>> doConvert(List<?> orignalList, String headerName, String[] fixedColumn, String valueFiedName, boolean needHeader, String[] fixedColumnName, String nullValue) throws Exception {
        // 行转列的字段表头
        headerSet = new LinkedHashSet<>();
        // 固定列的值的集合
        fixedColumnSet = new LinkedHashSet<>();
        // 首行完整固定表头list
        List<List<Object>> resultList = new ArrayList<>();
        // 获取headerSet和fixedColumnSet
        getHeaderfixedColumnSet(orignalList, headerName, fixedColumn);
        if (needHeader) {
            List<Object> headerList = new ArrayList<>();
            //填充进header
            headerList.addAll(Arrays.asList(fixedColumnName));
            headerList.addAll(headerSet);
            resultList.add(headerList);
        }
        // 遍历固定列的值
        for (Object fixedColumnItem : fixedColumnSet) {
            // 每个固定列的值加入集合的前几个固定位置
            List<Object> colList = new ArrayList<>(Arrays.asList(fixedColumnItem.toString().split("\\|")));
            // 遍历表头
            for (Object headerItem : headerSet) {
                boolean flag = true;
                for (Object orignalObjectItem : orignalList) {
                    Field headerField = orignalObjectItem.getClass().getDeclaredField(headerName);
                    headerField.setAccessible(true);
                    // 如果表头一样
                    if (headerItem.equals(headerField.get(orignalObjectItem))) {
                        boolean flg = true;
                        Field fixedColumnField;
                        Field valueField = orignalObjectItem.getClass().getDeclaredField(valueFiedName);
                        valueField.setAccessible(true);
                        // 判断当前列是否于固定列的所有值都一样
                        for (int i = 0; i < fixedColumn.length; i++) {
                            fixedColumnField = orignalObjectItem.getClass().getDeclaredField(fixedColumn[i]);
                            fixedColumnField.setAccessible(true);
                            if (!fixedColumnItem.toString().split("\\|")[i].equals(fixedColumnField.get(orignalObjectItem).toString())) {
                                flg = false;
                            }
                        }
                        if (flg) {
                            // 如果一样的话,则将需要行转列的表头加入进来
                            colList.add(valueField.get(orignalObjectItem));
                            flag = false;
                            break;
                        }
                    }
                }
                if (flag) {
                    // 反之,加入你自定义的代替值
                    colList.add(nullValue);
                }
            }
            // 加入集合
            resultList.add(colList);
        }
        return resultList;
    }

    private static void getHeaderfixedColumnSet(List<?> orignalList, String headerName, String[] fixedColumn) {
        try {
            for (Object item : orignalList) {
                // 拿到list中每一列的行转列字段信息
                Field headerField = item.getClass().getDeclaredField(headerName);
                headerField.setAccessible(true);
                // 将值作为表头加入headerSet
                headerSet.add(headerField.get(item));
                StringBuilder sBuffer = new StringBuilder();
                int len = 1;
                for (String name : fixedColumn) {
                    Field fixedColumnField = item.getClass().getDeclaredField(name);
                    fixedColumnField.setAccessible(true);
                    // 添加每个列表固定列的值
                    sBuffer.append(fixedColumnField.get(item));
                    if (len < fixedColumn.length) {
                        // 如果有多个固定列的话,则值用|隔开
                        sBuffer.append("|");
                    }
                    len++;
                }
                // 加入固定表头值集合
                fixedColumnSet.add(sBuffer.toString());
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

3、准备工作

Oracle查询方式如下

 使用sql进行行转列并且查询所有字段,结果如下(我这里用的oracle)

Mysql查询方式如下(根据id分组)

 用java的方式(则需要加上id,name,age这几个固定表头即可):

List<Student> list = new ArrayList<>();
list.add(new Student("1","张三",20,"语文",138.0));
list.add(new Student("2","张三",20,"数学",150.0));
list.add(new Student("3","张三",20,"英语",120.0));
list.add(new Student("4","李四",19,"语文",98.0));
list.add(new Student("5","李四",19,"数学",99.0));
list.add(new Student("6","李四",19,"英语",87.0));
list.add(new Student("7","王五",18,"语文",98.0));
list.add(new Student("8","王五",18,"数学",99.0));
list.add(new Student("9","王五",18,"英语",87.0));
list.add(new Student("10","王五",18,"物理",100.0));
String[] fixedColumn = {"id","name","age"};
String[] fixedColumnName = {"学号","姓名","年龄"};
// 要行转列的List,要行转列的字段,固定列字段数组,行转列对应值列的字段,是否返回表头,固定列字段名称数组,定义空值补数
List<List<Object>> lists = RowConvertColUtil.doConvert(list, "subject", fixedColumn, "scope", true, fixedColumnName, null);
for (List<Object> objects : lists) {
    System.out.println(objects);
}

结果如下:

由于我们需要把相同姓名的人放在一组,所以我们不能查询这么多字段,根据名字分组,查询名字如下 oracle方式

 

mysql方式:

 

那么在我们的固定列就可只写个name即可,如下

 假如有重复名字的,我们为了确保到唯一,可以加上学号标识,即固定列可以跟随自己的需求添加或者修改。演示如下:添加学号字段用来区分

 用Oracle查询结果如下

用mysql查询结果如下(按照名字分组,名字一样按照学号分)

 java工具类结果如下(再加一个即可)

 具体固定列有多少个也就是根据什么分组,是要看本身业务需求,可以灵活变化。

这种数据格式返回给前端的话,显然还需要转换,由于是动态列的形式,这里返回的格式为List<Map<String,Object>> 格式

直接调用下面这个方法即可

return RowConvertColUtil.doConvertReturnObj(list, "subject", fixedColumn, "scope", true, fixedColumnName, null).getDataList();

页面数据显示如下:

[
    {
        "name": "张三",
        "numberNo": "111",
        "语文": 138.0,
        "数学": 150.0,
        "英语": 120.0,
        "物理": null
    },
    {
        "name": "李四",
        "numberNo": "222",
        "语文": 98.0,
        "数学": 99.0,
        "英语": 87.0,
        "物理": null
    },
    {
        "name": "王五",
        "numberNo": "333",
        "语文": 98.0,
        "数学": 99.0,
        "英语": 87.0,
        "物理": 100.0
    },
    {
        "name": "张三",
        "numberNo": "444",
        "语文": 76.0,
        "数学": 67.0,
        "英语": 98.0,
        "物理": null
    }
]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/369046.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GitHub

什么是 Github?GitHub是一个面向开源及私有软件项目的托管平台&#xff0c;因为只支持Git作为唯一的版本库格式进行托管&#xff0c;故名GitHub。一、常用词Watch&#xff1a;观察。如果watch了一个项目&#xff0c;之后这个项目有更新&#xff0c;你会在第一时间收到该项目更…

pytorch1.2.0+python3.6

一、说明 pytorch1.2.0python3.6CUDA10.0cudnn7.4.1.5 二、步骤 在conda中创建一个新的虚拟环境 查看一下自己的所有环境 激活虚拟环境 conda activate torch1.2.0 关于cuda和cudnn 1、查看自己电脑系统是10.2版本 http://链接&#xff1a;https://pan.baidu.com/s/1v5cN6…

Vivado_FIR滤波器输出位宽计算方法

计算方法 全精度输出宽度可以定义为输入数据宽度加上由滤波器系数导致的位增长数。 最坏情况下的位增长等于系数宽度加上所需非零乘法次数的以2为底的对数并四舍五入后的值。然而&#xff0c;这没有考虑实际系数值。计算公式如下。 BCWceil⁡[log⁡2N]BC_{W}\operatorname{cei…

滑台模组的应用有哪些?

在自动化生产中&#xff0c;我们常常会看到滑台模组的身影&#xff0c;那么&#xff0c;滑台模组究竟在自动化生产设备中起着怎样的作用呢&#xff1f; 简单点说&#xff0c;滑台模组由滑块、滚珠丝杆、导轨、主体等其它传动零件组成的自动化晋级单元&#xff0c;经过各单元的组…

储物流行业解决方案

行业分析 第三方物流仓储物流的日常管理控制活动主要包括进,出&#xff0c;存三个方面。在没有实现计算机化管理的商业企业中&#xff0c;大量的业务操作和管理活动由人工来完成。在管理层中&#xff0c;由于大量必要的信息不能及时被采集﹑加工和整理使用&#xff0c;造成了极…

想成为一名专业黑客,但不知道从哪里学起?我来教你。

成为一名黑客需要学什么&#xff1f; 想成为一名专业黑客&#xff0c;但不知道从哪里学起”很多人在后台问过这个问题&#xff0c;今天就为你介绍成为专业黑客必须学习的十个方面的知识&#xff0c;希望能为迷惘中的你指明方向。 想要成为网络hacker黑客&#xff1f;先来学习…

未来土地利用模拟FLUS模型

未来土地利用模拟&#xff08;FutureLand-Use Simulation, FLUS&#xff09;模型1 模型简介1.1 基于ANN 的适宜性概率计算1.2 基于自适应惯性机制的元胞自动机1.3 模拟精度评价参考流域 径流变化是 自然因素和 人为因素共同作用的结果&#xff0c;其中人为因素最为直接的方式就…

SpringMVC框架知识详解(入门版)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

【mysql 5.7】基础入门(一)

文章目录1.常用命令2.SQL语句3导入数据4.DQL4.1 查询一个字段4.2 查询多个字段4.3 查询所有字段4.4 列起别名4.5 列参与数学运算4.6 条件查询4.7 排序4.8 多个字段排序5.函数5.1 单行处理函数5.2 分组函数&#xff16;&#xff0e;分组查询1.常用命令 退出mysql exit;查看有哪…

Redis 集群搭建

前缀参考文章1&#xff1a;Centos7 安装并启动 Redis-6.2.6 前缀参考文章2&#xff1a;Redis 主从复制-服务器搭建【薪火相传/哨兵模式】 管道符查看所有redis进程&#xff1a;ps -ef|grep redis 杀死所有redis进程&#xff1a;killall redis-server 1. 首先修改 redis.conf 配…

Jmeter(GUI模式)详细教程

Jmeter&#xff08;GUI模式&#xff09;详细教程 目录&#xff1a;导读 一、安装Jmeter 二、Jmeter工作原理 三、Jmeter操作步骤 Jmeter界面 1、测试计划 2、线程组 3、HTTP请求 4、监听器 四、压力测试 写在最后 前些天&#xff0c;领导让我做接口的压力测试。What…

cocos2dx+lua学习笔记:UIScrollView的使用

前言 本篇在讲什么 本篇简单介绍Lua篇cocos2dx中UIScrollView的相关内容 仅介绍简单的应用&#xff0c;仅供参考 本篇适合什么 适合初学Cocos2dX的小白 适合想要在Cocos2dx-lua中使用UIScrollView的人 本篇需要什么 对Lua语法有简单认知 对Cocos2dx-Lua有简单认知 Co…

【JavaSE】复习(基础)

文章目录基础1.1. public class 和 class1.2. 字面量1.3. 变量的引出1.4. javadoc的使用1.5. 转义字符1.6. 逻辑运算1.7. 用户键盘输入1.8. switch1.9. for循环1.10.方法的调用1.11.break return1.12.方法重载&#xff08;overload&#xff09;1.13.成员变量中的实例变量1.14.方…

计算机网络你都懂了吗

文章目录一、计算机网络的定义简单定义通用定义二、计算机网络通信过程三、什么是网络协议&#xff08;Protocol&#xff09;四、网络协议组成及功能一、计算机网络的定义 简单定义 计算机网络是一些相互连接的、自治的计算机系统的集合。 通用定义 将处于不同位置并具有独…

ChatGPT?听说Biying把它下架了

ChatGPT被玩疯了&#xff0c;开始放飞自我 ChatGPT版微软必应上线不到10天…就被网友玩坏了 先说这个词&#xff0c;放飞自我&#xff0c;什么东西才会放飞自我&#xff1f; 人放飞自我&#xff0c;人&#xff1f;你确定是人&#xff1f; 所以让我们来把上面的句子改写一下。…

怎样激发读者好奇心?短视频营销之场景化

目录 激发读者好奇心&#xff1f;四个小技巧帮你搞定 1.省略法 2.欲言又止法: 3.问句法:就是用疑问的形式引起别人的好奇。 4.反差法 选择合适的主题。 利用场景化效果 使用滤镜。 如何提高用户的留存率。 1、设置一个有趣的话题。 2、用好道具。 3、多用竖屏。 什…

适合python游戏开发的库你知道几个?

python游戏开发的库 01 PyGame 官网&#xff1a; https://www.pygame.org/docs/ 概述&#xff1a; Pygame 是一组专为编写视频游戏而设计的 Python 模块。 它在优秀的 SDL 库之上添加了功能。这允许您使用 python 语言创建功能齐全的游戏和多媒体程序。Pygame具有高度的可…

即将报名,如何选择软考考试科目?

软考全称为计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试 是职业资格、专业技术资格考试&#xff0c;也是职称考试。 计算机软件资格考试设置了27个专业资格&#xff0c;涵盖5个专业领域&#xff0c; 3个级别层次&#xff08;初级、中级、高级&#xff09; …

ubuntu20.04安装conda

1)conda与miniconda 任何语言的包、依赖和环境管理---Python, R, Ruby, Lua, Scala, Java, JavaScript, C/ C, FORTRAN。Conda 是一个运行在 Windows、macOS 和 Linux 上的开源包管理系统和环境管理系统。Conda 可以快速安装、运行和更新包及其依赖项。Conda 可以轻松地在本地计…

关于高并发场景和进程线程协程的一些总结

1、IO复用和线程池哪个好&#xff1f;应用场景&#xff1f; IO复用就是一个线程处理多个客户端连接。如果自己实现的话&#xff0c;就是要不断轮询每个客户端连接&#xff0c;看看有没有事件发生&#xff08;数据到达&#xff09;&#xff0c;即使可以用非阻塞的read函数&…