大致是GraphQL的思路
分开配置接口数据结构和数据实体的元数据
支持列表查询,支持多层级的数据聚合
参数选叶子节点就行,后续可以把参数用JS实现一个选择树状结构的UI,生成出查询字符串来,或者按照字段分配权限给租户
异常处理的不太好,有待继续调试
不支持数据权限,只支持根据聚合根向下查,相当于跨端的JPA/hibernate多表查询,好处是依赖很简单,用项目现存的Entity就行。
// 接口
@RestController
@RequestMapping("/open")
public class OpenSearchController {
@Resource
private ApplicationContext applicationContext;
/**
* 车辆查询
*/
@GetMapping("/search/vehicle/{no}")
public String search(@PathVariable("no") String no, @RequestParam String propertys) throws InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException {
// 需要查询的接口定义(叶子节点)
List<OpenSearchDifination> propertyLeafList = Arrays.stream(propertys.split(","))
.map(x -> EnumUtil.fromString(OpenSearchDifination.class, x))
.collect(Collectors.toList());
// 需要查询的接口定义(所有路径节点)
Set<OpenSearchDifination> propertySet = new HashSet<>();
propertyLeafList.forEach(x -> {
do {
propertySet.add(x);
x = x.getParent();
} while (x != null);
});
// 查询
Map<String, Object> param = new HashMap<>(1);
param.put("no", no);
return JSONUtil.toJsonStr(search(OpenSearchDifination.VEHICLE, propertySet, param));
}
/**
* 递归查询并构建结果
*
* @param root 查询节点
* @param propertySet 需要查询的接口定义
* @param parentData 父节点数据(用于子节点的查询)
* @return 查询结果
*/
private Object search(OpenSearchDifination root, Set<OpenSearchDifination> propertySet, Map<String, Object> parentData) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
if (!propertySet.contains(root)) {
// 不属于请求参数声明的查询范围
return null;
}
if (root.getType().equals(OpenSearchDifinationType.ARRAY)) {
// 当前节点是数组【需要查询出结果集给到子节点】
// 取元数据
OpenSearchMetaData metaData = EnumUtil.getBy(OpenSearchMetaData::getDifination, root);
// 反射取到ORM层实例
ResolvableType entityType = ResolvableType.forClass(metaData.getEntity());
ResolvableType mapperType = ResolvableType.forClassWithGenerics(BaseMapper.class, entityType);
Object mapper = applicationContext.getBean(applicationContext.getBeanNamesForType(mapperType)[0]);
// 构建查询条件
Object selectParam = metaData.getEntity().newInstance();
// 构建查询条件:父节点关联字段值
Object relevanceParentProperty = parentData.get(metaData.getRelevanceParentProperty());
// 构建查询条件:赋值方法名
String setMethodName = "set" + metaData.getRelevanceChildProperty().substring(0, 1).toUpperCase() + metaData.getRelevanceChildProperty().substring(1);
// 构建查询条件:赋值(反射工具类,忽略参数)
ReflectUtil.getMethodByName(metaData.getEntity(), setMethodName).invoke(selectParam, relevanceParentProperty);
// 查询
List<?> result = (List<?>) ReflectUtil.getMethodByName(mapper.getClass(), "select").invoke(mapper, selectParam);
return result.stream().map(x -> {
OpenSearchDifination child = root.getChild().stream()
.filter(y -> y.getType().equals(OpenSearchDifinationType.OBJECT))
.findFirst().orElse(null);
try {
return search(child, propertySet, BeanUtil.beanToMap(x));
} catch (Exception e) {
e.printStackTrace();
return e.getMessage();
}
}).collect(Collectors.toList());
} else if (root.getType().equals(OpenSearchDifinationType.OBJECT)) {
// 当前节点是对象
List<OpenSearchDifination> childs = root.getChild();
Map<String, Object> result = new HashMap<>(childs.size());
childs.stream()
.filter(x -> propertySet.contains(root))
.forEach(x -> {
try {
// 元数据
OpenSearchMetaData metaData = EnumUtil.getBy(OpenSearchMetaData::getDifination, x);
if (x.getType().equals(OpenSearchDifinationType.OBJECT) && metaData != null) {
// 子节点是对象【需要查询出单条结果给到子节点】
// 反射取到ORM层实例
ResolvableType entityType = ResolvableType.forClass(metaData.getEntity());
ResolvableType mapperType = ResolvableType.forClassWithGenerics(BaseMapper.class, entityType);
Object mapper = applicationContext.getBean(applicationContext.getBeanNamesForType(mapperType)[0]);
// 构建查询条件
Object selectParam = metaData.getEntity().newInstance();
// 构建查询条件:父节点关联字段
Object relevanceParentProperty = parentData.get(metaData.getRelevanceParentProperty());
// 构建查询条件:赋值方法名
String setMethodName = "set" + metaData.getRelevanceChildProperty().substring(0, 1).toUpperCase() + metaData.getRelevanceChildProperty().substring(1);
ReflectUtil.getMethodByName(metaData.getEntity(), setMethodName).invoke(selectParam, relevanceParentProperty);
// 查询
Object searchResult = ReflectUtil.getMethodByName(mapper.getClass(), "selectOne").invoke(mapper, selectParam);
// 附带主键
result.put("id", ReflectUtil.getMethodByName(AutoIncrementKeyBaseDomain.class, "getId").invoke(searchResult));
// 递归
result.put(x.getName(), search(x, propertySet, BeanUtil.beanToMap(searchResult)));
} else {
// 附带主键
result.put("id", parentData.get("id"));
// 子节点不是对象【递归】
result.put(x.getName(), search(x, propertySet, parentData));
}
} catch (Exception e) {
e.printStackTrace();
result.put(x.getName(), e.getMessage());
}
});
return result;
} else {
// 当前节点是叶子节点
// 元数据
OpenSearchMetaData metaData = EnumUtil.getBy(OpenSearchMetaData::getDifination, root);
return parentData.get(metaData.getPropertyName());
}
}
}
// 接口数据结构定义枚举
@Getter
public enum OpenSearchDifination {
/**
* 车辆
*/
VEHICLE("vehicle", OpenSearchDifinationType.ARRAY, null),
/**
* 车辆-列表项
*/
VEHICLE_I(null, OpenSearchDifinationType.OBJECT, VEHICLE),
/**
* 车辆-列表项-车牌号
*/
VEHICLE_I_NO("no", OpenSearchDifinationType.STRING, VEHICLE_I),;
/**
* 对象名
*/
private final String name;
/**
* 对象类型
*/
private final OpenSearchDifinationType type;
/**
* 子元素
*/
private final OpenSearchDifination parent;
OpenSearchDifination(String name, OpenSearchDifinationType type, OpenSearchDifination parent) {
this.name = name;
this.type = type;
this.parent = parent;
}
public List<OpenSearchDifination> getChild() {
List<OpenSearchDifination> result = new ArrayList<>();
for (OpenSearchDifination x : OpenSearchDifination.values()) {
if (this.equals(x.getParent())) {
result.add(x);
}
}
return result;
}
}
// 数据类型
@Getter
public enum OpenSearchDifinationType {
/**
* 字符串
*/
STRING(true),
/**
* 整型
*/
INT(true),
/**
* 列表
*/
ARRAY(false),
/**
* 对象
*/
OBJECT(false);
/**
* 是否叶子节点
*/
private final boolean isLeaf;
OpenSearchDifinationType(boolean isLeaf) {
this.isLeaf = isLeaf;
}
}
// 元数据
@Getter
public enum OpenSearchMetaData {
/**
* 车辆
*/
VEHICLE(OpenSearchDifination.VEHICLE, null, DppVehicleInfo.class, "no", "no"),
/**
* 车辆-列表项-车牌号
*/
VEHICLE_I_NO(OpenSearchDifination.VEHICLE_I_NO, "no", null, null, null);
/**
* 定义
*/
private final OpenSearchDifination difination;
/**
* 属性名
*/
private final String propertyName;
/**
* OBJECT:实体类
*/
private final Class<? extends AutoIncrementKeyBaseDomain<Long>> entity;
/**
* OBJECT:关联关系
* 父节点属性(OpenSearchDifination接口定义中的name)
*/
private final String relevanceParentProperty;
/**
* OBJECT:关联关系
* 当前节点属性(Entity中的属性)
*/
private final String relevanceChildProperty;
OpenSearchMetaData(OpenSearchDifination difination, String propertyName, Class<? extends AutoIncrementKeyBaseDomain<Long>> entity, String relevanceParentProperty, String relevanceChildProperty) {
this.difination = difination;
this.entity = entity;
this.propertyName = propertyName;
this.relevanceParentProperty = relevanceParentProperty;
this.relevanceChildProperty = relevanceChildProperty;
}
}