Java行转列通用工具类适用于各种查询情况
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
}
]
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战