同步到1.7版本

This commit is contained in:
Jerry
2021-06-06 22:53:13 +08:00
parent ecfa3349bf
commit 562d4bc726
349 changed files with 7056 additions and 7458 deletions

View File

@@ -48,7 +48,7 @@ public class DataSourceResolveAspect {
resolver = ApplicationContextHolder.getBean(resolverClass);
resolverMap.put(resolverClass, resolver);
}
int type = resolver.resolve(dsr.arg());
int type = resolver.resolve(dsr.arg(), point.getArgs());
// 通过判断 DataSource 中的值来判断当前方法应用哪个数据源
DataSourceContextHolder.setDataSourceType(type);
log.debug("set datasource is " + type);

View File

@@ -560,16 +560,31 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
return mapper().getCountByCondition(this.tableName, whereClause);
}
/**
* 集成所有与主表实体对象相关的关联数据列表。包括本地和远程服务的一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
* @param relationParam 实体对象数据组装的参数构建器。
*/
@Override
public void buildRelationForDataList(List<M> resultList, MyRelationParam relationParam) {
this.buildRelationForDataList(resultList, relationParam, null);
}
/**
* 集成所有与主表实体对象相关的关联数据列表。包括一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
* @param relationParam 实体对象数据组装的参数构建器。
* @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
* @param relationParam 实体对象数据组装的参数构建器。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
@Override
public void buildRelationForDataList(List<M> resultList, MyRelationParam relationParam) {
public void buildRelationForDataList(
List<M> resultList, MyRelationParam relationParam, Set<String> ignoreFields) {
if (relationParam == null || CollectionUtils.isEmpty(resultList)) {
return;
}
@@ -579,24 +594,24 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
boolean buildOneToOne = relationParam.isBuildOneToOne() || relationParam.isBuildOneToOneWithDict();
// 这里集成一对一关联。
if (buildOneToOne) {
this.buildOneToOneForDataList(resultList, relationParam.isBuildOneToOneWithDict());
this.buildOneToOneForDataList(resultList, relationParam.isBuildOneToOneWithDict(), ignoreFields);
}
// 集成一对多关联
if (relationParam.isBuildOneToMany()) {
this.buildOneToManyForDataList(resultList);
this.buildOneToManyForDataList(resultList, ignoreFields);
}
// 这里集成字典关联
if (relationParam.isBuildDict()) {
// 构建常量字典关联关系
this.buildConstDictForDataList(resultList);
this.buildDictForDataList(resultList, buildOneToOne);
this.buildConstDictForDataList(resultList, ignoreFields);
this.buildDictForDataList(resultList, buildOneToOne, ignoreFields);
}
// 组装本地聚合计算关联数据
if (relationParam.isBuildRelationAggregation()) {
// 处理多对多场景下,根据主表的结果,进行从表聚合数据的计算。
this.buildManyToManyAggregationForDataList(resultList, buildAggregationAdditionalWhereCriteria());
this.buildManyToManyAggregationForDataList(resultList, buildAggregationAdditionalWhereCriteria(), ignoreFields);
// 处理多一多场景下,根据主表的结果,进行从表聚合数据的计算。
this.buildOneToManyAggregationForDataList(resultList, buildAggregationAdditionalWhereCriteria());
this.buildOneToManyAggregationForDataList(resultList, buildAggregationAdditionalWhereCriteria(), ignoreFields);
}
} finally {
GlobalThreadLocal.setDataFilter(dataFilterValue);
@@ -621,6 +636,29 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*/
@Override
public void buildRelationForDataList(List<M> resultList, MyRelationParam relationParam, int batchSize) {
this.buildRelationForDataList(resultList, relationParam, batchSize, null);
}
/**
* 该函数主要用于对查询结果的批量导出。不同于支持分页的列表查询,批量导出没有分页机制,
* 因此在导出数据量较大的情况下很容易给数据库的内存、CPU和IO带来较大的压力。而通过
* 我们的分批处理可以极大的规避该问题的出现几率。调整batchSize的大小也可以有效的
* 改善运行效率。
* 我们目前的处理机制是,先从主表取出所有符合条件的主表数据,这样可以避免分批处理时,
* 后面几批数据因为skip过多而带来的效率问题。因为是单表过滤不会给数据库带来过大的压力。
* 之后再在主表结果集数据上进行分批级联处理。
* 集成所有与主表实体对象相关的关联数据列表。包括一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
* @param relationParam 实体对象数据组装的参数构建器。
* @param batchSize 每批集成的记录数量。小于等于时将不做分批处理。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
@Override
public void buildRelationForDataList(
List<M> resultList, MyRelationParam relationParam, int batchSize, Set<String> ignoreFields) {
if (CollectionUtils.isEmpty(resultList)) {
return;
}
@@ -633,23 +671,38 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
int toIndex = Math.min(batchSize, totalCount);
while (toIndex > fromIndex) {
List<M> subResultList = resultList.subList(fromIndex, toIndex);
this.buildRelationForDataList(subResultList, relationParam);
this.buildRelationForDataList(subResultList, relationParam, ignoreFields);
fromIndex = toIndex;
toIndex = Math.min(batchSize + fromIndex, totalCount);
}
}
/**
* 集成所有与主表实体对象相关的关联数据对象。包括本地和远程服务的一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param dataObject 主表实体对象。数据集成将直接作用于该对象。
* @param relationParam 实体对象数据组装的参数构建器。
* @param <T> 实体对象类型。
*/
@Override
public <T extends M> void buildRelationForData(T dataObject, MyRelationParam relationParam) {
this.buildRelationForData(dataObject, relationParam, null);
}
/**
* 集成所有与主表实体对象相关的关联数据对象。包括一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param dataObject 主表实体对象。数据集成将直接作用于该对象。
* @param relationParam 实体对象数据组装的参数构建器。
* @param <T> 实体对象类型
* @param dataObject 主表实体对象。数据集成将直接作用于该对象。
* @param relationParam 实体对象数据组装的参数构建器。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装
* @param <T> 实体对象类型。
*/
@Override
public <T extends M> void buildRelationForData(T dataObject, MyRelationParam relationParam) {
public <T extends M> void buildRelationForData(T dataObject, MyRelationParam relationParam, Set<String> ignoreFields) {
if (dataObject == null || relationParam == null) {
return;
}
@@ -658,27 +711,27 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
// 集成本地一对一和字段级别的数据关联。
boolean buildOneToOne = relationParam.isBuildOneToOne() || relationParam.isBuildOneToOneWithDict();
if (buildOneToOne) {
this.buildOneToOneForData(dataObject, relationParam.isBuildOneToOneWithDict());
this.buildOneToOneForData(dataObject, relationParam.isBuildOneToOneWithDict(), ignoreFields);
}
// 集成一对多关联
if (relationParam.isBuildOneToMany()) {
this.buildOneToManyForData(dataObject);
this.buildOneToManyForData(dataObject, ignoreFields);
}
if (relationParam.isBuildDict()) {
// 构建常量字典关联关系
this.buildConstDictForData(dataObject);
this.buildConstDictForData(dataObject, ignoreFields);
// 构建本地数据字典关联关系。
this.buildDictForData(dataObject, buildOneToOne);
this.buildDictForData(dataObject, buildOneToOne, ignoreFields);
}
// 组装本地聚合计算关联数据
if (relationParam.isBuildRelationAggregation()) {
// 开始处理多对多场景。
buildManyToManyAggregationForData(dataObject, buildAggregationAdditionalWhereCriteria());
buildManyToManyAggregationForData(dataObject, buildAggregationAdditionalWhereCriteria(), ignoreFields);
// 构建一对多场景
buildOneToManyAggregationForData(dataObject, buildAggregationAdditionalWhereCriteria());
buildOneToManyAggregationForData(dataObject, buildAggregationAdditionalWhereCriteria(), ignoreFields);
}
if (relationParam.isBuildRelationManyToMany()) {
this.buildRelationManyToMany(dataObject);
this.buildRelationManyToMany(dataObject, ignoreFields);
}
} finally {
GlobalThreadLocal.setDataFilter(dataFilterValue);
@@ -688,13 +741,17 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 集成主表和多对多中间表之间的关联关系。
*
* @param dataObject 关联后的主表数据对象。
* @param dataObject 关联后的主表数据对象。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private <T extends M> void buildRelationManyToMany(T dataObject) {
private <T extends M> void buildRelationManyToMany(T dataObject, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationManyToManyStructList)) {
return;
}
for (RelationStruct relationStruct : this.relationManyToManyStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
Example e = new Example(relationStruct.relationManyToMany.relationModelClass());
e.createCriteria().andEqualTo(relationStruct.masterIdField.getName(), masterIdValue);
@@ -706,13 +763,17 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 为实体对象参数列表数据集成本地静态字典关联数据。
*
* @param resultList 主表数据列表。
* @param resultList 主表数据列表。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildConstDictForDataList(List<M> resultList) {
private void buildConstDictForDataList(List<M> resultList, Set<String> ignoreFields) {
if (CollectionUtils.isEmpty(this.relationConstDictStructList) || CollectionUtils.isEmpty(resultList)) {
return;
}
for (RelationStruct relationStruct : this.relationConstDictStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
for (M dataObject : resultList) {
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (id != null) {
@@ -731,13 +792,17 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 为参数实体对象数据集成本地静态字典关联数据。
*
* @param dataObject 实体对象。
* @param dataObject 实体对象。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private <T extends M> void buildConstDictForData(T dataObject) {
private <T extends M> void buildConstDictForData(T dataObject, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationConstDictStructList)) {
return;
}
for (RelationStruct relationStruct : this.relationConstDictStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (id != null) {
String name = relationStruct.dictMap.get(id);
@@ -757,12 +822,16 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
* @param resultList 实体对象数据列表。
* @param hasBuiltOneToOne 性能优化参数。如果该值为true同时注解参数RelationDict.equalOneToOneRelationField
* 不为空,则直接从已经完成一对一数据关联的从表对象中获取数据,减少一次数据库交互。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildDictForDataList(List<M> resultList, boolean hasBuiltOneToOne) {
private void buildDictForDataList(List<M> resultList, boolean hasBuiltOneToOne, Set<String> ignoreFields) {
if (CollectionUtils.isEmpty(this.relationDictStructList) || CollectionUtils.isEmpty(resultList)) {
return;
}
for (RelationStruct relationStruct : this.relationDictStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
List<Object> relationList = null;
if (hasBuiltOneToOne && relationStruct.equalOneToOneRelationField != null) {
relationList = resultList.stream()
@@ -790,12 +859,16 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
* @param dataObject 实体对象。
* @param hasBuiltOneToOne 性能优化参数。如果该值为true同时注解参数RelationDict.equalOneToOneRelationField
* 不为空,则直接从已经完成一对一数据关联的从表对象中获取数据,减少一次数据库交互。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private <T extends M> void buildDictForData(T dataObject, boolean hasBuiltOneToOne) {
private <T extends M> void buildDictForData(T dataObject, boolean hasBuiltOneToOne, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationDictStructList)) {
return;
}
for (RelationStruct relationStruct : this.relationDictStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Object relationObject = null;
if (hasBuiltOneToOne && relationStruct.equalOneToOneRelationField != null) {
relationObject = ReflectUtil.getFieldValue(dataObject, relationStruct.equalOneToOneRelationField);
@@ -813,14 +886,18 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 为实体对象参数列表数据集成本地一对一关联数据。
*
* @param resultList 实体对象数据列表。
* @param withDict 关联从表数据后,是否把从表的字典数据也一起关联了。
* @param resultList 实体对象数据列表。
* @param withDict 关联从表数据后,是否把从表的字典数据也一起关联了。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildOneToOneForDataList(List<M> resultList, boolean withDict) {
private void buildOneToOneForDataList(List<M> resultList, boolean withDict, Set<String> ignoreFields) {
if (CollectionUtils.isEmpty(this.relationOneToOneStructList) || CollectionUtils.isEmpty(resultList)) {
return;
}
for (RelationStruct relationStruct : this.relationOneToOneStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Set<Object> masterIdSet = resultList.stream()
.map(obj -> ReflectUtil.getFieldValue(obj, relationStruct.masterIdField))
.filter(Objects::nonNull)
@@ -839,9 +916,9 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
BaseService<Object, Object> proxyTarget =
(BaseService<Object, Object>) AopTargetUtil.getTarget(relationService);
// 关联本地字典。
proxyTarget.buildDictForDataList(relationList, false);
proxyTarget.buildDictForDataList(relationList, false, ignoreFields);
// 关联常量字典
proxyTarget.buildConstDictForDataList(relationList);
proxyTarget.buildConstDictForDataList(relationList, ignoreFields);
}
}
}
@@ -850,14 +927,18 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 为实体对象数据集成本地一对一关联数据。
*
* @param dataObject 实体对象。
* @param withDict 关联从表数据后,是否把从表的字典数据也一起关联了。
* @param dataObject 实体对象。
* @param withDict 关联从表数据后,是否把从表的字典数据也一起关联了。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildOneToOneForData(M dataObject, boolean withDict) {
private void buildOneToOneForData(M dataObject, boolean withDict, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationOneToOneStructList)) {
return;
}
for (RelationStruct relationStruct : this.relationOneToOneStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (id != null) {
BaseService<Object, Object> relationService = relationStruct.service;
@@ -869,9 +950,9 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
BaseService<Object, Object> proxyTarget =
(BaseService<Object, Object>) AopTargetUtil.getTarget(relationService);
// 关联本地字典
proxyTarget.buildDictForData(relationObject, false);
proxyTarget.buildDictForData(relationObject, false, ignoreFields);
// 关联常量字典
proxyTarget.buildConstDictForData(relationObject);
proxyTarget.buildConstDictForData(relationObject, ignoreFields);
}
}
}
@@ -880,13 +961,17 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 为实体对象参数列表数据集成本地一对多关联数据。
*
* @param resultList 实体对象数据列表。
* @param resultList 实体对象数据列表。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildOneToManyForDataList(List<M> resultList) {
private void buildOneToManyForDataList(List<M> resultList, Set<String> ignoreFields) {
if (CollectionUtils.isEmpty(this.relationOneToManyStructList) || CollectionUtils.isEmpty(resultList)) {
return;
}
for (RelationStruct relationStruct : this.relationOneToManyStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Set<Object> masterIdSet = resultList.stream()
.map(obj -> ReflectUtil.getFieldValue(obj, relationStruct.masterIdField))
.filter(Objects::nonNull)
@@ -905,13 +990,17 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
/**
* 为实体对象数据集成本地一对多关联数据。
*
* @param dataObject 实体对象。
* @param dataObject 实体对象。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildOneToManyForData(M dataObject) {
private void buildOneToManyForData(M dataObject, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationOneToManyStructList)) {
return;
}
for (RelationStruct relationStruct : this.relationOneToManyStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Object id = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (id != null) {
BaseService<Object, Object> relationService = relationStruct.service;
@@ -929,9 +1018,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*
* @param resultList 实体对象数据列表。
* @param criteriaListMap 过滤参数。key为主表字段名称value是过滤条件列表。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildManyToManyAggregationForDataList(
List<M> resultList, Map<String, List<MyWhereCriteria>> criteriaListMap) {
List<M> resultList, Map<String, List<MyWhereCriteria>> criteriaListMap, Set<String> ignoreFields) {
if (CollectionUtils.isEmpty(this.relationManyToManyAggrStructList) || CollectionUtils.isEmpty(resultList)) {
return;
}
@@ -939,6 +1029,9 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
criteriaListMap = new HashMap<>(this.relationManyToManyAggrStructList.size());
}
for (RelationStruct relationStruct : this.relationManyToManyAggrStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Set<Object> masterIdSet = resultList.stream()
.map(obj -> ReflectUtil.getFieldValue(obj, relationStruct.masterIdField))
.filter(Objects::nonNull)
@@ -1000,9 +1093,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*
* @param dataObject 实体对象。
* @param criteriaListMap 过滤参数。key为主表字段名称value是过滤条件列表。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private <T extends M> void buildManyToManyAggregationForData(
T dataObject, Map<String, List<MyWhereCriteria>> criteriaListMap) {
T dataObject, Map<String, List<MyWhereCriteria>> criteriaListMap, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationManyToManyAggrStructList)) {
return;
}
@@ -1010,28 +1104,30 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
criteriaListMap = new HashMap<>(relationManyToManyAggrStructList.size());
}
for (RelationStruct relationStruct : this.relationManyToManyAggrStructList) {
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (masterIdValue == null) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
BasicAggregationRelationInfo basicRelationInfo =
this.parseBasicAggregationRelationInfo(relationStruct, criteriaListMap);
// 组装过滤条件
String whereClause = this.makeManyToManyWhereClause(
relationStruct, masterIdValue, basicRelationInfo, criteriaListMap);
StringBuilder tableNames = new StringBuilder(64);
tableNames.append(basicRelationInfo.relationTable);
if (!basicRelationInfo.onlySelectRelationTable) {
tableNames.append(", ").append(basicRelationInfo.slaveTable);
}
List<Map<String, Object>> aggregationMapList =
mapper().getGroupedListByCondition(tableNames.toString(),
basicRelationInfo.selectList, whereClause, basicRelationInfo.groupBy);
// 将查询后的结果回填到主表数据中。
if (CollectionUtils.isNotEmpty(aggregationMapList)) {
Object value = aggregationMapList.get(0).get(AGGREGATED_VALUE);
if (value != null) {
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, value);
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (masterIdValue != null) {
BasicAggregationRelationInfo basicRelationInfo =
this.parseBasicAggregationRelationInfo(relationStruct, criteriaListMap);
// 组装过滤条件
String whereClause = this.makeManyToManyWhereClause(
relationStruct, masterIdValue, basicRelationInfo, criteriaListMap);
StringBuilder tableNames = new StringBuilder(64);
tableNames.append(basicRelationInfo.relationTable);
if (!basicRelationInfo.onlySelectRelationTable) {
tableNames.append(", ").append(basicRelationInfo.slaveTable);
}
List<Map<String, Object>> aggregationMapList =
mapper().getGroupedListByCondition(tableNames.toString(),
basicRelationInfo.selectList, whereClause, basicRelationInfo.groupBy);
// 将查询后的结果回填到主表数据中。
if (CollectionUtils.isNotEmpty(aggregationMapList)) {
Object value = aggregationMapList.get(0).get(AGGREGATED_VALUE);
if (value != null) {
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, value);
}
}
}
}
@@ -1042,9 +1138,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*
* @param resultList 实体对象数据列表。
* @param criteriaListMap 过滤参数。key为主表字段名称value是过滤条件列表。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private void buildOneToManyAggregationForDataList(
List<M> resultList, Map<String, List<MyWhereCriteria>> criteriaListMap) {
List<M> resultList, Map<String, List<MyWhereCriteria>> criteriaListMap, Set<String> ignoreFields) {
// 处理多一多场景下,根据主表的结果,进行从表聚合数据的计算。
if (CollectionUtils.isEmpty(this.relationOneToManyAggrStructList) || CollectionUtils.isEmpty(resultList)) {
return;
@@ -1053,6 +1150,9 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
criteriaListMap = new HashMap<>(relationOneToManyAggrStructList.size());
}
for (RelationStruct relationStruct : this.relationOneToManyAggrStructList) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
Set<Object> masterIdSet = resultList.stream()
.map(obj -> ReflectUtil.getFieldValue(obj, relationStruct.masterIdField))
.filter(Objects::nonNull)
@@ -1098,9 +1198,10 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
*
* @param dataObject 实体对象。
* @param criteriaListMap 过滤参数。key为主表字段名称value是过滤条件列表。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
private <T extends M> void buildOneToManyAggregationForData(
T dataObject, Map<String, List<MyWhereCriteria>> criteriaListMap) {
T dataObject, Map<String, List<MyWhereCriteria>> criteriaListMap, Set<String> ignoreFields) {
if (dataObject == null || CollectionUtils.isEmpty(this.relationOneToManyAggrStructList)) {
return;
}
@@ -1108,29 +1209,31 @@ public abstract class BaseService<M, K> implements IBaseService<M, K> {
criteriaListMap = new HashMap<>(relationOneToManyAggrStructList.size());
}
for (RelationStruct relationStruct : this.relationOneToManyAggrStructList) {
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (masterIdValue == null) {
if (ignoreFields != null && ignoreFields.contains(relationStruct.relationField.getName())) {
continue;
}
RelationOneToManyAggregation relation = relationStruct.relationOneToManyAggregation;
String slaveTable = MyModelUtil.mapToTableName(relation.slaveModelClass());
String slaveColumnName =
MyModelUtil.mapToColumnName(relation.slaveIdField(), relation.slaveModelClass());
Tuple2<String, String> selectAndGroupByTuple = makeSelectListAndGroupByClause(
slaveTable, slaveColumnName, relation.slaveModelClass(),
slaveTable, relation.aggregationField(), relation.aggregationType());
String selectList = selectAndGroupByTuple.getFirst();
String groupBy = selectAndGroupByTuple.getSecond();
String whereClause = this.makeOneToManyWhereClause(
relationStruct, masterIdValue, slaveColumnName, criteriaListMap);
// 获取分组聚合计算结果
List<Map<String, Object>> aggregationMapList =
mapper().getGroupedListByCondition(slaveTable, selectList, whereClause, groupBy);
// 将计算结果回填到主表关联字段
if (CollectionUtils.isNotEmpty(aggregationMapList)) {
Object value = aggregationMapList.get(0).get(AGGREGATED_VALUE);
if (value != null) {
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, value);
Object masterIdValue = ReflectUtil.getFieldValue(dataObject, relationStruct.masterIdField);
if (masterIdValue != null) {
RelationOneToManyAggregation relation = relationStruct.relationOneToManyAggregation;
String slaveTable = MyModelUtil.mapToTableName(relation.slaveModelClass());
String slaveColumnName =
MyModelUtil.mapToColumnName(relation.slaveIdField(), relation.slaveModelClass());
Tuple2<String, String> selectAndGroupByTuple = makeSelectListAndGroupByClause(
slaveTable, slaveColumnName, relation.slaveModelClass(),
slaveTable, relation.aggregationField(), relation.aggregationType());
String selectList = selectAndGroupByTuple.getFirst();
String groupBy = selectAndGroupByTuple.getSecond();
String whereClause = this.makeOneToManyWhereClause(
relationStruct, masterIdValue, slaveColumnName, criteriaListMap);
// 获取分组聚合计算结果
List<Map<String, Object>> aggregationMapList =
mapper().getGroupedListByCondition(slaveTable, selectList, whereClause, groupBy);
// 将计算结果回填到主表关联字段
if (CollectionUtils.isNotEmpty(aggregationMapList)) {
Object value = aggregationMapList.get(0).get(AGGREGATED_VALUE);
if (value != null) {
ReflectUtil.setFieldValue(dataObject, relationStruct.relationField, value);
}
}
}
}

View File

@@ -199,6 +199,17 @@ public interface IBaseService<M, K> {
*/
void buildRelationForDataList(List<M> resultList, MyRelationParam relationParam);
/**
* 集成所有与主表实体对象相关的关联数据列表。包括本地和远程服务的一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
* @param relationParam 实体对象数据组装的参数构建器。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
void buildRelationForDataList(List<M> resultList, MyRelationParam relationParam, Set<String> ignoreFields);
/**
* 该函数主要用于对查询结果的批量导出。不同于支持分页的列表查询,批量导出没有分页机制,
* 因此在导出数据量较大的情况下很容易给数据库的内存、CPU和IO带来较大的压力。而通过
@@ -217,6 +228,26 @@ public interface IBaseService<M, K> {
*/
void buildRelationForDataList(List<M> resultList, MyRelationParam relationParam, int batchSize);
/**
* 该函数主要用于对查询结果的批量导出。不同于支持分页的列表查询,批量导出没有分页机制,
* 因此在导出数据量较大的情况下很容易给数据库的内存、CPU和IO带来较大的压力。而通过
* 我们的分批处理可以极大的规避该问题的出现几率。调整batchSize的大小也可以有效的
* 改善运行效率。
* 我们目前的处理机制是,先从主表取出所有符合条件的主表数据,这样可以避免分批处理时,
* 后面几批数据因为skip过多而带来的效率问题。因为是单表过滤不会给数据库带来过大的压力。
* 之后再在主表结果集数据上进行分批级联处理。
* 集成所有与主表实体对象相关的关联数据列表。包括一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param resultList 主表实体对象列表。数据集成将直接作用于该对象列表。
* @param relationParam 实体对象数据组装的参数构建器。
* @param batchSize 每批集成的记录数量。小于等于时将不做分批处理。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
*/
void buildRelationForDataList(
List<M> resultList, MyRelationParam relationParam, int batchSize, Set<String> ignoreFields);
/**
* 集成所有与主表实体对象相关的关联数据对象。包括一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
@@ -228,6 +259,18 @@ public interface IBaseService<M, K> {
*/
<T extends M> void buildRelationForData(T dataObject, MyRelationParam relationParam);
/**
* 集成所有与主表实体对象相关的关联数据对象。包括本地和远程服务的一对一、字典、一对多和多对多聚合运算等。
* 也可以根据实际需求,单独调用该函数所包含的各个数据集成函数。
* NOTE: 该方法内执行的SQL将禁用数据权限过滤。
*
* @param dataObject 主表实体对象。数据集成将直接作用于该对象。
* @param relationParam 实体对象数据组装的参数构建器。
* @param ignoreFields 该集合中的字段,即便包含注解也不会在当前调用中进行数据组装。
* @param <T> 实体对象类型。
*/
<T extends M> void buildRelationForData(T dataObject, MyRelationParam relationParam, Set<String> ignoreFields);
/**
* 仅仅在spring boot 启动后的监听器事件中调用缓存所有service的关联关系加速后续的数据绑定效率。
*/

View File

@@ -71,7 +71,6 @@ public class MyRelationParam {
.buildDict(true)
.buildOneToOneWithDict(true)
.buildRelationAggregation(true)
.buildOneToMany(true)
.build();
}

View File

@@ -11,8 +11,9 @@ public interface DataSourceResolver {
/**
* 动态解析方法。实现类可以根据当前的请求,或者上下文环境进行动态解析。
*
* @param arg 可选的入参。MyDataSourceResolver注解中的arg参数。
* @param arg 可选的入参。MyDataSourceResolver注解中的arg参数。
* @param methodArgs 被织入方法的所有参数。
* @return 返回用于多数据源切换的类型值。DataSourceResolveAspect 切面方法会根据该返回值和配置信息,进行多数据源切换。
*/
int resolve(String arg);
int resolve(String arg, Object[] methodArgs);
}

View File

@@ -1,214 +0,0 @@
package com.orange.demo.common.core.util;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.sax.Excel07SaxReader;
import com.orange.demo.common.core.constant.ApplicationConstant;
import com.orange.demo.common.core.exception.MyRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;
/**
* 导入工具类目前支持xlsx和csv两种类型。
*
* @author Jerry
* @date 2020-09-24
*/
@Slf4j
public class ImportUtil {
private static final String IMPORT_EXCEPTION_ERROR = "Failed to call ImportUtil.doImport";
private static final String UNSUPPORT_FILE_EXT_ERROR = "不支持的导入文件类型!";
/**
* 同步导入方式。
*
* @param filename 导入文件名。
* @return 导入数据列表。
*/
public static List<Map<String, Object>> doImport(String filename) {
if (ApplicationConstant.XLSX_EXT.equals(FilenameUtils.getExtension(filename))) {
try (ExcelReader reader = ExcelUtil.getReader(filename)) {
return reader.readAll();
}
} else if (ApplicationConstant.CSV_EXT.equals(FilenameUtils.getExtension(filename))) {
List<Map<String, Object>> resultList = new LinkedList<>();
try (FileReader reader = new FileReader(filename)) {
CSVParser parser = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(reader);
Map<String, Integer> headerMap = parser.getHeaderMap();
for (CSVRecord record : parser) {
Map<String, Object> rowMap = new LinkedHashMap<>();
for (final Map.Entry<String, Integer> header : headerMap.entrySet()) {
int col = header.getValue();
if (col < record.size()) {
rowMap.put(header.getKey(), record.get(col));
}
}
resultList.add(rowMap);
}
} catch (Exception e) {
log.error(IMPORT_EXCEPTION_ERROR, e);
}
return resultList;
}
throw new MyRuntimeException(UNSUPPORT_FILE_EXT_ERROR);
}
/**
* 异步导入方式即SAX导入方式。
*
* @param filename 导入文件名。
* @param importer 异步导入处理接口。
* @throws IOException 文件处理异常。
*/
public static <T> void doImport(String filename, BaseImporter<T> importer) throws IOException {
if (ApplicationConstant.XLSX_EXT.equals(FilenameUtils.getExtension(filename))) {
Excel07SaxReader reader = new MyExcel07SaxReader<>(importer);
try (InputStream in = new FileInputStream(filename)) {
reader.read(in, 0);
}
} else if (ApplicationConstant.CSV_EXT.equals(FilenameUtils.getExtension(filename))) {
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
int rowIndex = 0;
do {
String rowData = reader.readLine();
if (StringUtils.isBlank(rowData)) {
importer.doImport(-1, null);
break;
}
String[] dataArray = StringUtils.split(rowData, ",");
importer.doImport(rowIndex++, Arrays.asList(dataArray));
} while (!importer.doInterrupt());
} catch (Exception e) {
log.error(IMPORT_EXCEPTION_ERROR, e);
}
}
throw new MyRuntimeException(UNSUPPORT_FILE_EXT_ERROR);
}
/**
* 异步导入抽象类。
*
* @param <T> 导入数据对象类型。
*/
public abstract static class BaseImporter<T> {
private final Class<T> beanType;
private final List<T> batchRowList = new LinkedList<>();
private final int batchSize;
private final Map<String, String> headerColumnMap;
private Field[] fieldArray = null;
public BaseImporter(int batchSize, Class<T> beanType, Map<String, String> headerColumnMap) {
if (batchSize <= 0) {
batchSize = 100;
}
this.batchSize = batchSize;
this.beanType = beanType;
this.headerColumnMap = headerColumnMap;
}
/**
* 导入操作执行函数。
*
* @param rowIndex 当前行号。
* @param row 当前行数据列表对象。
*/
public void doImport(long rowIndex, List<Object> row) {
if (row == null) {
doProcess(batchRowList);
doFinish();
batchRowList.clear();
return;
}
if (rowIndex <= 0) {
fieldArray = new Field[row.size()];
List<String> headerList = row.stream().map(Object::toString).collect(Collectors.toList());
List<String> columnList = new ArrayList<>(row.size());
for (String headerName : headerList) {
String columnName = headerColumnMap.get(headerName);
if (columnName != null) {
columnList.add(columnName);
}
}
columnList.stream()
.map(columnName -> ReflectUtil.getField(beanType, columnName))
.collect(Collectors.toList())
.toArray(fieldArray);
return;
}
T data;
try {
data = beanType.newInstance();
for (int i = 0; i < row.size(); i++) {
Object value = row.get(i);
Field field = fieldArray[i];
if (field != null) {
ReflectUtil.setFieldValue(data, field, value);
}
}
batchRowList.add(data);
} catch (Exception e) {
log.error(IMPORT_EXCEPTION_ERROR, e);
}
if (rowIndex % batchSize == 0) {
doProcess(batchRowList);
batchRowList.clear();
}
}
/**
* 数据处理进行中回调模板函数。
*
* @param batchRowList 一批数据行。
*/
public abstract void doProcess(List<T> batchRowList);
/**
* 数据处理完毕回调模板函数。
*/
public abstract void doFinish();
/**
* 数据处理终端标记模板函数。
* @return 是否中断。true则中断后面的处理。
*/
public abstract boolean doInterrupt();
}
static class MyExcel07SaxReader<T> extends Excel07SaxReader {
private final BaseImporter<T> importer;
MyExcel07SaxReader(BaseImporter<T> importer) {
super((sheetIndex, rowIndex, rowList) -> importer.doImport(rowIndex, rowList));
this.importer = importer;
}
@Override
public void endElement(String uri, String localName, String qName) {
super.endElement(uri, localName, qName);
if (importer.doInterrupt()) {
throw new MyRuntimeException();
}
}
@Override
public void endDocument() {
importer.doImport(-1, null);
}
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/
private ImportUtil() {
}
}

View File

@@ -239,7 +239,8 @@ public class MyModelUtil {
continue;
}
Field dictMapField = ReflectUtil.getField(r.constantDictClass(), "DICT_MAP");
Map<Object, String> dictMap = (Map<Object, String>) ReflectUtil.getFieldValue(thisClazz, dictMapField);
Map<Object, String> dictMap =
(Map<Object, String>) ReflectUtil.getFieldValue(r.constantDictClass(), dictMapField);
Object id = ReflectUtil.getFieldValue(thisModel, r.masterIdField());
if (id != null) {
String name = dictMap.get(id);
@@ -253,6 +254,48 @@ public class MyModelUtil {
}
}
/**
* 主Model类型中遍历所有包含RelationConstDict注解的字段并将关联的静态字典中的数据
* 填充到thisModelList集合元素对象的被注解字段中。
*
* @param thisClazz 主对象的Class对象。
* @param thisModelList 主对象列表。
* @param <T> 主表对象类型。
*/
@SuppressWarnings("unchecked")
public static <T> void makeConstDictRelation(Class<T> thisClazz, List<T> thisModelList) {
if (CollectionUtils.isEmpty(thisModelList)) {
return;
}
Field[] fields = ReflectUtil.getFields(thisClazz);
for (Field field : fields) {
// 这里不做任何空值判断,从而让配置错误在调试期间即可抛出
Field thisTargetField = ReflectUtil.getField(thisClazz, field.getName());
RelationConstDict r = thisTargetField.getAnnotation(RelationConstDict.class);
if (r == null) {
continue;
}
Field dictMapField = ReflectUtil.getField(r.constantDictClass(), "DICT_MAP");
Map<Object, String> dictMap =
(Map<Object, String>) ReflectUtil.getFieldValue(r.constantDictClass(), dictMapField);
for (T thisModel : thisModelList) {
if (thisModel == null) {
continue;
}
Object id = ReflectUtil.getFieldValue(thisModel, r.masterIdField());
if (id != null) {
String name = dictMap.get(id);
if (name != null) {
Map<String, Object> m = new HashMap<>(2);
m.put("id", id);
m.put("name", name);
ReflectUtil.setFieldValue(thisModel, thisTargetField, m);
}
}
}
}
}
/**
* 在主Model类型中根据thisRelationField字段的RelationDict注解参数将被关联对象thatModel中的数据
* 关联到thisModel对象的thisRelationField字段中。

View File

@@ -33,7 +33,7 @@ public class RedisKeyUtil {
* @param sessionId 会话Id。
* @return 会话存储于Redis中的键值。
*/
public static String makeSessionIdKeyForRedis(String sessionId) {
public static String makeSessionIdKey(String sessionId) {
return "SESSIONID__" + sessionId;
}
@@ -43,7 +43,7 @@ public class RedisKeyUtil {
* @param sessionId 会话Id。
* @return 会话关联的权限数据存储于Redis中的键值。
*/
public static String makeSessionPermIdKeyForRedis(String sessionId) {
public static String makeSessionPermIdKey(String sessionId) {
return "PERM__" + sessionId;
}
@@ -53,10 +53,20 @@ public class RedisKeyUtil {
* @param sessionId 会话Id。
* @return 会话关联的数据权限数据存储于Redis中的键值。
*/
public static String makeSessionDataPermIdKeyForRedis(String sessionId) {
public static String makeSessionDataPermIdKey(String sessionId) {
return "DATA_PERM__" + sessionId;
}
/**
* 计算在线表对象缓存在Redis中的键值。
*
* @param tableId 在线表主键Id。
* @return 会话关联的数据权限数据存储于Redis中的键值。
*/
public static String makeOnlineTableKey(Long tableId) {
return "ONLINE_TABLE_" + tableId;
}
/**
* 私有构造函数,明确标识该常量类的作用。
*/