前言
在我们使用金蝶云苍穹进行实际开发的时候,会遇到在后端获取前端页面各个属性的时候,例如:字段标识、字段名、字段类型、字段长度等。
一、页面模型获取
- 采用缓存获取页面模型:
MainEntityType entityType = EntityMetadataCache.getDataEntityType(entityName);
- 采用工具类获取页面模型:
MainEntityType mainEntityType = MetadataServiceHelper.getDataEntityType(entityName);
二、使用步骤
1.采用缓存获取
代码如下(示例):
package rea5.cosmic.debug.bamp.mservicehelper.mservice;
import cfca.org.slf4j.Logger;
import cfca.org.slf4j.LoggerFactory;
import kd.bos.algo.DataSet;
import kd.bos.algo.Row;
import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
import kd.bos.dataentity.metadata.dynamicobject.DynamicPropertyCollection;
import kd.bos.db.DB;
import kd.bos.db.DBRoute;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.entity.EntityType;
import kd.bos.entity.MainEntityType;
import kd.bos.entity.property.TextProp;
import kd.bos.servicehelper.MetadataServiceHelper;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class CheckDBServiceHelper {
private static final Logger logger = LoggerFactory.getLogger(CheckDBServiceHelper.class);
public Map<String, Map<String, Object>> checkDB(String entityName) {
Map<String, Map<String, Object>> result = new HashMap<>();
try {
//MainEntityType mainEntityType = EntityMetadataCache.getDataEntityType(entityName);
//返回结果
Map<String, EntityType> allEntities = mainEntityType.getAllEntities();//单据或者基础资料的所有表信息(包含单据体)
for (Map.Entry<String, EntityType> allEntitiesInformation : allEntities.entrySet()) {
Map<String, Object> fieldMap = new HashMap<>();
String dbKeyRoute = "";//数据库路由
String tableName = "";//表名
String entryName = "";//实体标识
DynamicPropertyCollection fields = null;//属性
EntityType tableInformation = allEntitiesInformation.getValue();//单个表的信息
// 获取 DynamicObjectType 类的所有字段
Field[] dynamicObjectTypeFields = DynamicObjectType.class.getDeclaredFields();
Field[] entityTypeFields = EntityType.class.getDeclaredFields();
// 合并数组
Field[] combinedFields = Stream.concat(Arrays.stream(dynamicObjectTypeFields), Arrays.stream(entityTypeFields))
.toArray(Field[]::new);
for (Field field : combinedFields) {
field.setAccessible(true); // 设置字段可访问
try {
Object value = field.get(tableInformation);
String name = field.getName();
switch (name) {
case "dbRouteKey":
// 代码块
dbKeyRoute = value.toString();
break;
case "_tableName":
// 代码块
tableName = value.toString();
break;
case "_name":
// 代码块
entryName = value.toString();
break;
case "_properties":
// 代码块
if (value instanceof List) {
@SuppressWarnings("unchecked")
DynamicPropertyCollection castedFields = (DynamicPropertyCollection) value;
fields = castedFields;
}
break;
default:
// 默认代码块
break;
}
} catch (IllegalAccessException ex) {
logger.error("KN_无法访问字段: {}", ex.getMessage(), ex);
}
}
//执行SQL
if (!tableName.isEmpty()) {
String sql = "SELECT data_type,table_schema,table_name,column_name,character_maximum_length FROM information_schema.columns " +
"WHERE table_schema = 'public'AND data_type IN ('character varying', 'character') and table_name = ?";
DataSet rows = DB.queryDataSet(this.getClass().toString(), DBRoute.of(dbKeyRoute), sql, new String[]{tableName.toLowerCase()});
//数据库字段和长度的map集合
Map<String, Integer> DBFieldAndLength = new HashMap<>();
for (Row row : rows) {
DBFieldAndLength.put(row.getString("column_name"), row.getInteger("character_maximum_length"));
}
//实体标识和长度的map集合
Map<String, Integer> localFieldAndLength = new HashMap<>();
assert fields != null;
fields.forEach(item -> {
String alias = item.getAlias();
if (StringUtils.hasText(alias) && item instanceof TextProp) {
localFieldAndLength.put(alias, ((TextProp) item).getMaxLenth());
}
});
//有问题的字段集合
Map<String, Integer> problemFields = new HashMap<>();
//对比字段
localFieldAndLength.forEach((itemKey, itemValue) -> {
Integer DBValueData = DBFieldAndLength.get(itemKey);
if (DBValueData != null && DBValueData.compareTo(itemValue) != 0) {
problemFields.put(itemKey, itemValue);
}
});
//生成扩容sql
if (!problemFields.isEmpty()) {
StringBuilder expSql = new StringBuilder("ALTER TABLE ");
expSql.append(tableName);
//字段个数
List<String> sqlList = problemFields.keySet().stream()
.map(item -> " ALTER COLUMN " + item + " TYPE character varying(" + problemFields.get(item) + ")")
.collect(Collectors.toList());
expSql.append(String.join(",", sqlList));
doRes(fieldMap, expSql.toString(), String.format("存在以下字段:%s 实体与数据库长度不一致。", String.join(",", problemFields.keySet())), localFieldAndLength, tableName, dbKeyRoute);
} else {
doRes(fieldMap, " ", "实体与数据表健康。", localFieldAndLength, tableName, dbKeyRoute);
}
}
result.put(entryName, fieldMap);
}
} catch (Exception e1) {
logger.error("KN_异常: {}", e1.getMessage(), e1);
}
return result;
}
private static void doRes(Map<String, Object> fieldMap, String expSql, String message, Map<String, Integer> problemFields, String tableName, String dbKeyRoute) {
fieldMap.put("status", !StringUtils.hasText(expSql));
fieldMap.put("sql", expSql);
fieldMap.put("message", message);
fieldMap.put("table_name", tableName);
fieldMap.put("checkFields", problemFields.keySet());
fieldMap.put("dbKeyRoute", dbKeyRoute);
}
}
2.采用工具类获取
代码如下(示例):
package rea5.cosmic.debug.bamp.mservicehelper.mservice;
import cfca.org.slf4j.Logger;
import cfca.org.slf4j.LoggerFactory;
import kd.bos.algo.DataSet;
import kd.bos.algo.Row;
import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
import kd.bos.dataentity.metadata.dynamicobject.DynamicPropertyCollection;
import kd.bos.db.DB;
import kd.bos.db.DBRoute;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.entity.EntityType;
import kd.bos.entity.MainEntityType;
import kd.bos.entity.property.TextProp;
import kd.bos.servicehelper.MetadataServiceHelper;
import org.springframework.util.StringUtils;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class CheckDBServiceHelper {
private static final Logger logger = LoggerFactory.getLogger(CheckDBServiceHelper.class);
public Map<String, Map<String, Object>> checkDB(String entityName) {
Map<String, Map<String, Object>> result = new HashMap<>();
try {
//MainEntityType entityType = EntityMetadataCache.getDataEntityType(entityName);
MainEntityType mainEntityType = MetadataServiceHelper.getDataEntityType(entityName);
//返回结果
Map<String, EntityType> allEntities = mainEntityType.getAllEntities();//单据或者基础资料的所有表信息(包含单据体)
for (Map.Entry<String, EntityType> allEntitiesInformation : allEntities.entrySet()) {
Map<String, Object> fieldMap = new HashMap<>();
String dbKeyRoute = "";//数据库路由
String tableName = "";//表名
String entryName = "";//实体标识
DynamicPropertyCollection fields = null;//属性
EntityType tableInformation = allEntitiesInformation.getValue();//单个表的信息
// 获取 DynamicObjectType 类的所有字段
Field[] dynamicObjectTypeFields = DynamicObjectType.class.getDeclaredFields();
Field[] entityTypeFields = EntityType.class.getDeclaredFields();
// 合并数组
Field[] combinedFields = Stream.concat(Arrays.stream(dynamicObjectTypeFields), Arrays.stream(entityTypeFields))
.toArray(Field[]::new);
for (Field field : combinedFields) {
field.setAccessible(true); // 设置字段可访问
try {
Object value = field.get(tableInformation);
String name = field.getName();
switch (name) {
case "dbRouteKey":
// 代码块
dbKeyRoute = value.toString();
break;
case "_tableName":
// 代码块
tableName = value.toString();
break;
case "_name":
// 代码块
entryName = value.toString();
break;
case "_properties":
// 代码块
if (value instanceof List) {
@SuppressWarnings("unchecked")
DynamicPropertyCollection castedFields = (DynamicPropertyCollection) value;
fields = castedFields;
}
break;
default:
// 默认代码块
break;
}
} catch (IllegalAccessException ex) {
logger.error("KN_无法访问字段: {}", ex.getMessage(), ex);
}
}
//执行SQL
if (!tableName.isEmpty()) {
String sql = "SELECT data_type,table_schema,table_name,column_name,character_maximum_length FROM information_schema.columns " +
"WHERE table_schema = 'public'AND data_type IN ('character varying', 'character') and table_name = ?";
DataSet rows = DB.queryDataSet(this.getClass().toString(), DBRoute.of(dbKeyRoute), sql, new String[]{tableName.toLowerCase()});
//数据库字段和长度的map集合
Map<String, Integer> DBFieldAndLength = new HashMap<>();
for (Row row : rows) {
DBFieldAndLength.put(row.getString("column_name"), row.getInteger("character_maximum_length"));
}
//实体标识和长度的map集合
Map<String, Integer> localFieldAndLength = new HashMap<>();
assert fields != null;
fields.forEach(item -> {
String alias = item.getAlias();
if (StringUtils.hasText(alias) && item instanceof TextProp) {
localFieldAndLength.put(alias, ((TextProp) item).getMaxLenth());
}
});
//有问题的字段集合
Map<String, Integer> problemFields = new HashMap<>();
//对比字段
localFieldAndLength.forEach((itemKey, itemValue) -> {
Integer DBValueData = DBFieldAndLength.get(itemKey);
if (DBValueData != null && DBValueData.compareTo(itemValue) != 0) {
problemFields.put(itemKey, itemValue);
}
});
//生成扩容sql
if (!problemFields.isEmpty()) {
StringBuilder expSql = new StringBuilder("ALTER TABLE ");
expSql.append(tableName);
//字段个数
List<String> sqlList = problemFields.keySet().stream()
.map(item -> " ALTER COLUMN " + item + " TYPE character varying(" + problemFields.get(item) + ")")
.collect(Collectors.toList());
expSql.append(String.join(",", sqlList));
doRes(fieldMap, expSql.toString(), String.format("存在以下字段:%s 实体与数据库长度不一致。", String.join(",", problemFields.keySet())), localFieldAndLength, tableName, dbKeyRoute);
} else {
doRes(fieldMap, " ", "实体与数据表健康。", localFieldAndLength, tableName, dbKeyRoute);
}
}
result.put(entryName, fieldMap);
}
} catch (Exception e1) {
logger.error("KN_异常: {}", e1.getMessage(), e1);
}
return result;
}
private static void doRes(Map<String, Object> fieldMap, String expSql, String message, Map<String, Integer> problemFields, String tableName, String dbKeyRoute) {
fieldMap.put("status", !StringUtils.hasText(expSql));
fieldMap.put("sql", expSql);
fieldMap.put("message", message);
fieldMap.put("table_name", tableName);
fieldMap.put("checkFields", problemFields.keySet());
fieldMap.put("dbKeyRoute", dbKeyRoute);
}
}
总结
1、采用缓存在效率上有一定的提升,但是缓存采用的是懒加载,一旦页面数据未加载,将获取不到对应的数据。
2、工具类采用直接查询数据库的方式,所以性能上存在一些出入,但是此方案会更加稳定。