提高工作效率的万能Java行列转换工具类

简介: 提高工作效率的万能Java行列转换工具类

1、说明

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

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

2、工具类代码

1. package com.lili.util;
2. 
3. 
4. import java.lang.reflect.Field;
5. import java.util.*;
6. 
7. /**
8.  * 行转列终极工具类,通用于查询单个列或者多个列的结果
9.  *
10.  * @author QiJingJing
11.  */
12. public class RowConvertColUtil {
13. private static Set<Object> headerSet;
14. private static Set<Object> fixedColumnSet;
15. 
16. private RowConvertColUtil() {
17.     }
18. 
19. public static class ConvertData {
20. private Set<Object> headerSet;
21. private Set<Object> fixedColumnSet;
22. private List<Map<String, Object>> dataList;
23. 
24. public ConvertData(List<Map<String, Object>> dataList, Set<Object> headerSet, Set<Object> fixedColumnSet) {
25. this.headerSet = headerSet;
26. this.fixedColumnSet = fixedColumnSet;
27. this.dataList = dataList;
28.         }
29. 
30. public Set<Object> getHeaderSet() {
31. return headerSet;
32.         }
33. 
34. public void setHeaderSet(Set<Object> headerSet) {
35. this.headerSet = headerSet;
36.         }
37. 
38. public Set<Object> getFixedColumnSet() {
39. return fixedColumnSet;
40.         }
41. 
42. public void setFixedColumnSet(Set<Object> fixedColumnSet) {
43. this.fixedColumnSet = fixedColumnSet;
44.         }
45. 
46. public List<Map<String, Object>> getDataList() {
47. return dataList;
48.         }
49. 
50. public void setDataList(List<Map<String, Object>> dataList) {
51. this.dataList = dataList;
52.         }
53.     }
54. 
55. /**
56.      * 行转列返回 ConvertData 我们想要展示的格式
57.      *
58.      * @param orignalList     要行转列的list
59.      * @param headerName      要行转列的字段
60.      * @param fixedColumn     固定需要查询列字段
61.      * @param valueFiedName   行转列字段对应值列的字段名
62.      * @param needHeader      是否返回表头
63.      * @param fixedColumnName 固定需要查询列字段的昵称
64.      * @param nullValue       空值填充
65.      **/
66. public static synchronized ConvertData doConvertReturnObj(List<?> orignalList, String headerName, String[] fixedColumn, String valueFiedName, boolean needHeader, String[] fixedColumnName, String nullValue) throws Exception {
67.         List<List<Object>> lists = doConvert(orignalList, headerName, fixedColumn, valueFiedName, needHeader, fixedColumnName, nullValue);
68. // 拿到每个列表需要的属性个数
69. int size = lists.get(0).size();
70. // 拿出总共的集合数量
71. int dataListNum = lists.size() - 1;
72. // 将固定字段和固定字段值做kv映射
73.         Map<String, String> columnMap = new HashMap<>(16);
74. for (int i = 0; i < fixedColumn.length; i++) {
75.             columnMap.put(fixedColumnName[i], fixedColumn[i]);
76.         }
77. // 对lists里面的数据做转换,转换成原本类的格式(一个属性对应一个值的形式)
78.         List<Map<String, Object>> maps = new ArrayList<>();
79. for (int i = 0; i < dataListNum; i++) {
80.             Map<String, Object> map = new LinkedHashMap<>(16);
81. for (int j = 0; j < size; j++) {
82. // 列的表头昵称
83. String columnName = String.valueOf(lists.get(0).get(j));
84. if (fixedColumn.length > j) {
85. // 根据昵称,拿到属性名,然后将下一个列表的对应值加进去
86.                     map.put(columnMap.get(columnName), lists.get(i + 1).get(j));
87.                 } else {
88.                     map.put(columnName, lists.get(i + 1).get(j));
89.                 }
90.             }
91.             maps.add(map);
92.         }
93. return new ConvertData(maps, headerSet, fixedColumnSet);
94.     }
95. 
96. /**
97.      * 列表行转列的最终结果
98.      *
99.      * @param orignalList     要行转列的list
100.      * @param headerName      要行转列的字段
101.      * @param fixedColumn     固定需要查询列字段
102.      * @param valueFiedName   行转列字段对应值列的字段名
103.      * @param needHeader      是否返回表头
104.      * @param fixedColumnName 固定需要查询列字段的昵称
105.      * @param nullValue       空值填充
106.      **/
107. public static synchronized List<List<Object>> doConvert(List<?> orignalList, String headerName, String[] fixedColumn, String valueFiedName, boolean needHeader, String[] fixedColumnName, String nullValue) throws Exception {
108. // 行转列的字段表头
109.         headerSet = new LinkedHashSet<>();
110. // 固定列的值的集合
111.         fixedColumnSet = new LinkedHashSet<>();
112. // 首行完整固定表头list
113.         List<List<Object>> resultList = new ArrayList<>();
114. // 获取headerSet和fixedColumnSet
115.         getHeaderfixedColumnSet(orignalList, headerName, fixedColumn);
116. if (needHeader) {
117.             List<Object> headerList = new ArrayList<>();
118. //填充进header
119.             headerList.addAll(Arrays.asList(fixedColumnName));
120.             headerList.addAll(headerSet);
121.             resultList.add(headerList);
122.         }
123. // 遍历固定列的值
124. for (Object fixedColumnItem : fixedColumnSet) {
125. // 每个固定列的值加入集合的前几个固定位置
126.             List<Object> colList = new ArrayList<>(Arrays.asList(fixedColumnItem.toString().split("\\|")));
127. // 遍历表头
128. for (Object headerItem : headerSet) {
129. boolean flag = true;
130. for (Object orignalObjectItem : orignalList) {
131. Field headerField = orignalObjectItem.getClass().getDeclaredField(headerName);
132.                     headerField.setAccessible(true);
133. // 如果表头一样
134. if (headerItem.equals(headerField.get(orignalObjectItem))) {
135. boolean flg = true;
136.                         Field fixedColumnField;
137. Field valueField = orignalObjectItem.getClass().getDeclaredField(valueFiedName);
138.                         valueField.setAccessible(true);
139. // 判断当前列是否于固定列的所有值都一样
140. for (int i = 0; i < fixedColumn.length; i++) {
141.                             fixedColumnField = orignalObjectItem.getClass().getDeclaredField(fixedColumn[i]);
142.                             fixedColumnField.setAccessible(true);
143. if (!fixedColumnItem.toString().split("\\|")[i].equals(fixedColumnField.get(orignalObjectItem).toString())) {
144.                                 flg = false;
145.                             }
146.                         }
147. if (flg) {
148. // 如果一样的话,则将需要行转列的表头加入进来
149.                             colList.add(valueField.get(orignalObjectItem));
150.                             flag = false;
151. break;
152.                         }
153.                     }
154.                 }
155. if (flag) {
156. // 反之,加入你自定义的代替值
157.                     colList.add(nullValue);
158.                 }
159.             }
160. // 加入集合
161.             resultList.add(colList);
162.         }
163. return resultList;
164.     }
165. 
166. private static void getHeaderfixedColumnSet(List<?> orignalList, String headerName, String[] fixedColumn) {
167. try {
168. for (Object item : orignalList) {
169. // 拿到list中每一列的行转列字段信息
170. Field headerField = item.getClass().getDeclaredField(headerName);
171.                 headerField.setAccessible(true);
172. // 将值作为表头加入headerSet
173.                 headerSet.add(headerField.get(item));
174. StringBuilder sBuffer = new StringBuilder();
175. int len = 1;
176. for (String name : fixedColumn) {
177. Field fixedColumnField = item.getClass().getDeclaredField(name);
178.                     fixedColumnField.setAccessible(true);
179. // 添加每个列表固定列的值
180.                     sBuffer.append(fixedColumnField.get(item));
181. if (len < fixedColumn.length) {
182. // 如果有多个固定列的话,则值用|隔开
183.                         sBuffer.append("|");
184.                     }
185.                     len++;
186.                 }
187. // 加入固定表头值集合
188.                 fixedColumnSet.add(sBuffer.toString());
189.             }
190.         } catch (NoSuchFieldException | IllegalAccessException e) {
191.             e.printStackTrace();
192.         }
193.     }
194. }

3、准备工作

Oracle查询方式如下:

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

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

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

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

结果如下:

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

mysql方式:

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

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

用Oracle查询结果如下

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

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

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

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

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

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

页面数据显示如下:

1. [
2.     {
3. "name": "张三",
4. "numberNo": "111",
5. "语文": 138.0,
6. "数学": 150.0,
7. "英语": 120.0,
8. "物理": null
9.     },
10.     {
11. "name": "李四",
12. "numberNo": "222",
13. "语文": 98.0,
14. "数学": 99.0,
15. "英语": 87.0,
16. "物理": null
17.     },
18.     {
19. "name": "王五",
20. "numberNo": "333",
21. "语文": 98.0,
22. "数学": 99.0,
23. "英语": 87.0,
24. "物理": 100.0
25.     },
26.     {
27. "name": "张三",
28. "numberNo": "444",
29. "语文": 76.0,
30. "数学": 67.0,
31. "英语": 98.0,
32. "物理": null
33.     }
34. ]


目录
相关文章
|
29天前
|
算法 搜索推荐 Java
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
这篇文章介绍了如何使用Java后端技术,结合Graphics2D和Echarts等工具,生成包含个性化信息和图表的海报,并提供了详细的代码实现和GitHub项目链接。
96 0
java 后端 使用 Graphics2D 制作海报,画echarts图,带工具类,各种细节:如头像切割成圆形,文字换行算法(完美实验success),解决画上文字、图片后不清晰问题
|
1月前
|
Java
Java 些许公共工具类
Java 些许公共工具类
14 1
|
3月前
|
缓存 前端开发 Java
【前端学java】java基础巩固复习巩固语法练习-工具类的封装(14)
【8月更文挑战第10天】java基础巩固,工具类的封装
26 1
【前端学java】java基础巩固复习巩固语法练习-工具类的封装(14)
|
3月前
|
Java
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
Java应用结构规范问题之在UnitConvertUtils工具类将千米转换为米的问题如何解决
|
3月前
|
存储 设计模式 安全
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
Java GenericObjectPool 对象池化技术--SpringBoot sftp 连接池工具类
50 0
|
4月前
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
61 1
|
4月前
|
安全 Java 开发者
Java中的并发工具类与线程安全实现
Java中的并发工具类与线程安全实现
|
5月前
|
设计模式 缓存 算法
编写高效的Java工具类:实用技巧与设计模式
编写高效的Java工具类:实用技巧与设计模式
|
4月前
|
设计模式 缓存 算法
编写高效的Java工具类:实用技巧与设计模式
编写高效的Java工具类:实用技巧与设计模式
|
4月前
|
并行计算 Java API
Java中的并发工具类详解
Java中的并发工具类详解
下一篇
无影云桌面