diff --git a/README.md b/README.md index c03e9c8ec8fb42569f041c147e164d4a8434e953..ff6e4c63557ebefa5e4d324581f5e0600cdc0613 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ https://search.maven.org/artifact/com.gitee.qdbp/qdbp-jdbc-spring com.gitee.qdbp qdbp-jdbc-spring - 4.1.5 + 4.1.8 ``` 如果不是spring-web项目,可以单独使用qdbp-jdbc-core
@@ -61,6 +61,14 @@ https://search.maven.org/artifact/com.gitee.qdbp/qdbp-jdbc-core com.gitee.qdbp qdbp-jdbc-core - 4.1.5 + 4.1.8 + +``` +如果是jdk8以上版本,存在LocalDateTime的转换问题,需要引入qdbp-jdbc-jdk8 +```xml + + com.gitee.qdbp + qdbp-jdbc-jdk8 + 4.1.8 ``` diff --git a/jdbc-core/pom.xml b/jdbc-core/pom.xml index a1b138fb7ec441cd0b15eeb539808d8713925692..5c3b20cf14503ba0f6692198f70c1f1372004a91 100644 --- a/jdbc-core/pom.xml +++ b/jdbc-core/pom.xml @@ -9,7 +9,7 @@ qdbp-jdbc-core - 4.1.5 + 4.1.8 jar ${project.artifactId} https://gitee.com/qdbp/qdbp-jdbc/ @@ -25,12 +25,12 @@ com.gitee.qdbp qdbp-able - 5.4.5 + 5.4.6 com.gitee.qdbp qdbp-staticize - 3.5.3 + 3.5.5 com.gitee.qdbp diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityFillType.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityFillType.java index 649c68272ae9f721fb06dd9bd33af65b67e431c1..e95b3d2392d81c9455aecc6ce50561b10e470664 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityFillType.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityFillType.java @@ -15,5 +15,6 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) public @interface EntityFillType { + /** 规则名称 **/ String[] value(); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueBasic.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueBasic.java new file mode 100644 index 0000000000000000000000000000000000000000..05d7998b875a4a7ab71602f04f35796f18a3726e --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueBasic.java @@ -0,0 +1,201 @@ +package com.gitee.qdbp.filling.biz; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.beans.KeyString; +import com.gitee.qdbp.able.beans.KeyValue; +import com.gitee.qdbp.filling.api.EntityDataTypedFillService; +import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.NamingTools; +import com.gitee.qdbp.tools.utils.StringTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 实体数据填充单一类型处理基础实现类 + * + * @author zhaohuihua + * @version 20210113 + */ +public abstract class BaseEntityDataFillFieldValueBasic implements EntityDataTypedFillService { + + protected boolean skipDataFill(Map data) { + return false; + } + + protected boolean skipSourceValue(Serializable sourceValue) { + return false; + } + + // originalData: key=sourceValue + // valueMaps: key=sourceValue, value=targetValue + protected void fillValueToOriginalData(Map> originalData, Map valueMaps) { + // # 将targetValue填充至originalData + for (Map.Entry> entry : originalData.entrySet()) { + Serializable sourceValue = entry.getKey(); + if (valueMaps.containsKey(sourceValue)) { + Object targetValue = valueMaps.get(sourceValue); + List fillors = entry.getValue(); + for (DataFillor fillor : fillors) { + fillor.fillValues(targetValue); + } + } + } + } + + /** + * options多个字段以逗号分隔, 默认的填充字段为源字段去掉Id/Code后缀, 加上Name
+ * -- createUser,updateUser
+ * -- 等于createUser-createUserName,updateUser-updateUserName
+ * 也可以自定义转换关系
+ * -- creatorId-createUser,updatorId-updateUser
+ * @param options 转换规则 + * @return 解析后的规则, key=fieldName, value=targetName + */ + protected List parseOptions(String options) { + List list = new ArrayList<>(); + if (options == null || options.trim().length() == 0) { + return list; + } + String[] array = StringTools.split(options, ','); + for (String option : array) { + option = option.trim(); + int index = option.indexOf('-'); + if (index < 0) { + list.add(new KeyString(option, convertToTargetName(option))); + } else if (index > 0) { + String source = option.substring(0, index).trim(); + String target = option.substring(index + 1).trim(); + list.add(new KeyString(source, target)); + // } else { + // 横框开头? 忽略! + } + } + return list; + } + + protected Map> collectFillInfos(Iterable> list, + List options) { + Map> sourceMaps = new HashMap<>(); + for (Map map : list) { + if (skipDataFill(map)) { + continue; + } + DataFillor fillor = collectDataFillor(map, options); + if (fillor.getFillFields().isEmpty()) { + continue; + } + // key=targetName, value=sourceValue + for (KeyValue item : fillor.getFillFields()) { + if (sourceMaps.containsKey(item.getValue())) { + sourceMaps.get(item.getValue()).add(fillor); + } else { + List fillors = ConvertTools.toList(fillor); + sourceMaps.put(item.getValue(), fillors); + } + } + } + return sourceMaps; + } + + protected DataFillor collectDataFillor(Map data, List options) { + DataFillor fillor = new DataFillor(data); + for (KeyString item : options) { + String sourceName = item.getKey(); + String targetName = item.getValue(); + // # 先按入参指定的字段名, 判断是否存在字段值 + Object sourceValue = data.get(sourceName); + if (VerifyTools.isNotBlank(sourceValue) && data.get(targetName) == null) { + if (sourceValue instanceof Serializable) { + Serializable value = (Serializable) sourceValue; + if (!skipSourceValue(value)) { + fillor.addFillField(targetName, value); + } + } + continue; + } + // # 如果不存在, 转换为驼峰命名或下划线命名再次判断 + String convertedSourceName; + String convertedTargetName; + if (isCamelNaming(sourceName)) { + // 入参是驼峰命名, 转换为下划线命名 + convertedSourceName = NamingTools.toUnderlineString(sourceName).toUpperCase(); + convertedTargetName = NamingTools.toUnderlineString(targetName).toUpperCase(); + } else { + // 入参是下划线命名, 转换为驼峰命名 + convertedSourceName = NamingTools.toCamelString(sourceName); + convertedTargetName = NamingTools.toCamelString(targetName); + } + Object convertedSourceValue = data.get(convertedSourceName); + if (VerifyTools.isNotBlank(convertedSourceValue) && data.get(convertedTargetName) == null) { + if (convertedSourceValue instanceof Serializable) { + Serializable value = (Serializable) convertedSourceValue; + if (!skipSourceValue(value)) { + fillor.addFillField(convertedTargetName, value); + } + } + continue; + } + } + return fillor; + } + + protected String convertToTargetName(String source) { + if (isCamelNaming(source)) { + String target = source; + if (target.endsWith("Id")) { + target = StringTools.removeSuffix(target, "Id"); + } else if (target.endsWith("Code")) { + target = StringTools.removeSuffix(target, "Code"); + } + return target + "Name"; + } else { + String target = source; + if (target.endsWith("_ID")) { + target = StringTools.removeSuffix(target, "_ID"); + } else if (target.endsWith("_CODE")) { + target = StringTools.removeSuffix(target, "_CODE"); + } + return target + "_NAME"; + } + } + + protected boolean isCamelNaming(String source) { + char[] chars = source.toCharArray(); + for (int i = 0, z = chars.length; i < z; i++) { + char c = source.charAt(i); + if (c >= 'a' && c <= 'z') { + return true; // 存在小写字母, 即判定为驼峰命名 + } + } + return false; + } + + protected static class DataFillor { + + // key=targetName, value=sourceValue + private final List> fillFields; + private final Map originalData; + + public DataFillor(Map originalData) { + this.originalData = originalData; + this.fillFields = new ArrayList<>(); + } + + protected void addFillField(String targetName, Serializable sourceValue) { + this.fillFields.add(new KeyValue<>(targetName, sourceValue)); + } + + public List> getFillFields() { + return this.fillFields; + } + + public void fillValues(Object targetValue) { + for (KeyValue item : fillFields) { + this.originalData.put(item.getKey(), targetValue); + } + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueService.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueService.java index b81b6aeac93c6d3818772e9649bce20369ed1df3..226f0a508f02a99462e02fbe6bf508e94dd559ad 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueService.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueService.java @@ -3,17 +3,9 @@ package com.gitee.qdbp.filling.biz; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import com.gitee.qdbp.able.beans.KeyString; -import com.gitee.qdbp.able.beans.KeyValue; -import com.gitee.qdbp.filling.api.EntityDataTypedFillService; -import com.gitee.qdbp.tools.utils.ConvertTools; -import com.gitee.qdbp.tools.utils.NamingTools; -import com.gitee.qdbp.tools.utils.StringTools; -import com.gitee.qdbp.tools.utils.VerifyTools; /** * 实体数据填充单一类型处理基础实现类
@@ -26,7 +18,10 @@ import com.gitee.qdbp.tools.utils.VerifyTools; * @author zhaohuihua * @version 20210113 */ -public abstract class BaseEntityDataFillFieldValueService implements EntityDataTypedFillService { +public abstract class BaseEntityDataFillFieldValueService extends BaseEntityDataFillFieldValueBasic { + + /** 查找目标值 (key=sourceValue, value=targetValue) **/ + protected abstract Map findTargetValueMaps(Collection sourceValues); @Override public void fillFieldValue(Iterable> list, String option) { @@ -40,176 +35,12 @@ public abstract class BaseEntityDataFillFieldValueService implements EntityDataT } // # 根据sourceValue查询targetValue List sourceValues = new ArrayList<>(sourceMaps.keySet()); // 复制副本以防修改 + // key=sourceValue, value=targetValue Map targetMaps = findTargetValueMaps(sourceValues); if (targetMaps == null || targetMaps.isEmpty()) { return; } // # 将targetValue填充至originalData - for (Entry> entry : sourceMaps.entrySet()) { - Serializable sourceValue = entry.getKey(); - if (targetMaps.containsKey(sourceValue)) { - Object targetValue = targetMaps.get(sourceValue); - List fillors = entry.getValue(); - for (DataFillor fillor : fillors) { - fillor.fillValues(targetValue); - } - } - } - } - - /** 查找目标值 (key=sourceValue, value=targetValue) **/ - protected abstract Map findTargetValueMaps(Collection sourceValues); - - protected boolean skipDataFill(Map data) { - return false; - } - - protected boolean skipSourceValue(Serializable sourceValue) { - return false; - } - - protected List parseOptions(String options) { - List list = new ArrayList<>(); - if (options == null || options.trim().length() == 0) { - return list; - } - String[] array = StringTools.split(options, ','); - for (String option : array) { - option = option.trim(); - int index = option.indexOf('-'); - if (index < 0) { - list.add(new KeyString(option, convertToTargetName(option))); - } else if (index > 0) { - String source = option.substring(0, index).trim(); - String target = option.substring(index + 1).trim(); - list.add(new KeyString(source, target)); - // } else { - // 横框开头? 忽略! - } - } - return list; - } - - protected Map> collectFillInfos(Iterable> list, - List options) { - Map> sourceMaps = new HashMap<>(); - for (Map map : list) { - if (skipDataFill(map)) { - continue; - } - DataFillor fillor = collectDataFillor(map, options); - if (fillor.getFillFields().isEmpty()) { - continue; - } - // key=targetName, value=sourceValue - for (KeyValue item : fillor.getFillFields()) { - if (sourceMaps.containsKey(item.getValue())) { - sourceMaps.get(item.getValue()).add(fillor); - } else { - List fillors = ConvertTools.toList(fillor); - sourceMaps.put(item.getValue(), fillors); - } - } - } - return sourceMaps; - } - - protected DataFillor collectDataFillor(Map data, List options) { - DataFillor fillor = new DataFillor(data); - for (KeyString item : options) { - String sourceName = item.getKey(); - String targetName = item.getValue(); - // # 先按入参指定的字段名, 判断是否存在字段值 - Object sourceValue = data.get(sourceName); - if (VerifyTools.isNotBlank(sourceValue) && data.get(targetName) == null) { - if (sourceValue instanceof Serializable) { - Serializable value = (Serializable) sourceValue; - if (!skipSourceValue(value)) { - fillor.addFillField(targetName, value); - } - } - continue; - } - // # 如果不存在, 转换为驼峰命名或下划线命名再次判断 - String convertedSourceName; - String convertedTargetName; - if (isCamelNaming(sourceName)) { - // 入参是驼峰命名, 转换为下划线命名 - convertedSourceName = NamingTools.toUnderlineString(sourceName).toUpperCase(); - convertedTargetName = NamingTools.toUnderlineString(targetName).toUpperCase(); - } else { - // 入参是下划线命名, 转换为驼峰命名 - convertedSourceName = NamingTools.toCamelString(sourceName); - convertedTargetName = NamingTools.toCamelString(targetName); - } - Object convertedSourceValue = data.get(convertedSourceName); - if (VerifyTools.isNotBlank(convertedSourceValue) && data.get(convertedTargetName) == null) { - if (convertedSourceValue instanceof Serializable) { - Serializable value = (Serializable) convertedSourceValue; - if (!skipSourceValue(value)) { - fillor.addFillField(convertedTargetName, value); - } - } - continue; - } - } - return fillor; - } - - protected String convertToTargetName(String source) { - if (isCamelNaming(source)) { - String target = source; - if (target.endsWith("Id")) { - target = StringTools.removeSuffix(target, "Id"); - } else if (target.endsWith("Code")) { - target = StringTools.removeSuffix(target, "Code"); - } - return target + "Name"; - } else { - String target = source; - if (target.endsWith("_ID")) { - target = StringTools.removeSuffix(target, "_ID"); - } else if (target.endsWith("_CODE")) { - target = StringTools.removeSuffix(target, "_CODE"); - } - return target + "_NAME"; - } - } - - protected boolean isCamelNaming(String source) { - char[] chars = source.toCharArray(); - for (int i = 0, z = chars.length; i < z; i++) { - char c = source.charAt(i); - if (c >= 'a' && c <= 'z') { - return true; // 存在小写字母, 即判定为驼峰命名 - } - } - return false; - } - - protected static class DataFillor { - - // key=targetName, value=sourceValue - private final List> fillFields; - private final Map originalData; - - public DataFillor(Map originalData) { - this.originalData = originalData; - this.fillFields = new ArrayList<>(); - } - - protected void addFillField(String targetName, Serializable sourceValue) { - this.fillFields.add(new KeyValue<>(targetName, sourceValue)); - } - - public List> getFillFields() { - return this.fillFields; - } - - public void fillValues(Object targetValue) { - for (KeyValue item : fillFields) { - this.originalData.put(item.getKey(), targetValue); - } - } + fillValueToOriginalData(sourceMaps, targetMaps); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillGroupValueService.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillGroupValueService.java new file mode 100644 index 0000000000000000000000000000000000000000..73a2dcd7f5a7497c1080f956b60507eccbd7f71f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillGroupValueService.java @@ -0,0 +1,79 @@ +package com.gitee.qdbp.filling.biz; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.beans.KeyString; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 实体数据分组填充单一类型处理基础实现类
+ * options多个字段以逗号分隔, 默认的填充字段为源字段去掉Id/Code后缀, 加上Name
+ * -- createUser,updateUser
+ * -- 等于createUser-createUserName,updateUser-updateUserName
+ * 也可以自定义转换关系
+ * -- creatorId-createUser,updatorId-updateUser
+ * + * @author zhaohuihua + * @version 20210113 + */ +public abstract class BaseEntityDataFillGroupValueService extends BaseEntityDataFillFieldValueBasic { + + /** 获取分组类型 **/ + protected T getTargetGroupType(Map data) { + return null; + } + + /** 查找目标值 (key=sourceValue, value=targetValue) **/ + protected abstract Map findTargetValueMaps(T groupType, Collection sourceValues); + + @Override + public void fillFieldValue(Iterable> list, String option) { + List options = parseOptions(option); + // 分组 + Map>> groups = new HashMap<>(); + List> defaultGroups = new ArrayList<>(); + for (Map item : list) { + T groupType = getTargetGroupType(item); + if (VerifyTools.isBlank(groupType)) { + defaultGroups.add(item); + } else { + if (groups.containsKey(groupType)) { + groups.get(groupType).add(item); + } else { + List> items = new ArrayList<>(); + items.add(item); + groups.put(groupType, items); + } + } + } + for (Map.Entry>> entry : groups.entrySet()) { + fillFieldValue(entry.getKey(), entry.getValue(), options); + } + if (!defaultGroups.isEmpty()) { + fillFieldValue(null, defaultGroups, options); + } + } + + protected void fillFieldValue(T groupType, Iterable> list, List options) { + // # 收集需要填充的信息 + // key=sourceValue + Map> sourceMaps = collectFillInfos(list, options); + if (sourceMaps == null || sourceMaps.isEmpty()) { + return; + } + // # 根据sourceValue查询targetValue + List sourceValues = new ArrayList<>(sourceMaps.keySet()); // 复制副本以防修改 + // key=sourceValue, value=targetValue + Map targetMaps = findTargetValueMaps(groupType, sourceValues); + if (targetMaps == null || targetMaps.isEmpty()) { + return; + } + // # 将targetValue填充至originalData + fillValueToOriginalData(sourceMaps, targetMaps); + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/CrudDao.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/CrudDao.java index 12211996b2aa93446353fc63156111575c589390..40231e9c7b0fba8a9259bf64884f73dfdb3d209d 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/CrudDao.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/CrudDao.java @@ -471,7 +471,7 @@ public interface CrudDao { DbWhere searchWhere, Orderings orderings); /** - * 递归查询所有父节点
+ * 递归查询所有父节点 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
@@ -484,7 +484,7 @@ public interface CrudDao { * @param filterWhere 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
* @param searchWhere 结果搜索条件 (如用户输入的查询条件)
* 这些条件如果放在filterWhere中将无法生成完整的树
- * @param orderings 排序条件
+ * @param orderings 排序条件 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 不需要排序时应传入Orderings.NONE
* 由字符串构造排序条件: Orderings.of("name asc, createTime desc");
* @return 父节点列表 @@ -496,7 +496,7 @@ public interface CrudDao { DbWhere searchWhere, Orderings orderings); /** - * 递归查询所有父节点
+ * 递归查询所有父节点 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
@@ -509,6 +509,9 @@ public interface CrudDao { * @param filterWhere 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
* @param searchWhere 结果搜索条件 (如用户输入的查询条件)
* 这些条件如果放在filterWhere中将无法生成完整的树
+ * @param orderings 排序条件 (如果未指定排序条件, 将会从顶级节点开始依次排列)
+ * 不需要排序时应传入Orderings.NONE
+ * 由字符串构造排序条件: Orderings.of("name asc, createTime desc");
* @return 父节点列表 * @see ParseTools#parseBeanToDbWhere(Object) * @see ParseTools#parseParamsToDbWhere(Map, Class, boolean) @@ -517,7 +520,7 @@ public interface CrudDao { DbWhere searchWhere, Orderings orderings); /** - * 递归查询所有父节点编号
+ * 递归查询所有父节点编号 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
@@ -530,7 +533,7 @@ public interface CrudDao { * @param filterWhere 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
* @param searchWhere 结果搜索条件 (如用户输入的查询条件)
* 这些条件如果放在filterWhere中将无法生成完整的树
- * @param orderings 排序条件
+ * @param orderings 排序条件 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 不需要排序时应传入Orderings.NONE
* 由字符串构造排序条件: Orderings.of("name asc, createTime desc");
* @return 父节点编号 @@ -542,7 +545,7 @@ public interface CrudDao { DbWhere searchWhere, Orderings orderings); /** - * 递归查询所有父节点编号
+ * 递归查询所有父节点编号 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
@@ -555,7 +558,7 @@ public interface CrudDao { * @param filterWhere 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
* @param searchWhere 结果搜索条件 (如用户输入的查询条件)
* 这些条件如果放在filterWhere中将无法生成完整的树
- * @param orderings 排序条件
+ * @param orderings 排序条件 (如果未指定排序条件, 将会从顶级节点开始依次排列)
* 不需要排序时应传入Orderings.NONE
* 由字符串构造排序条件: Orderings.of("name asc, createTime desc");
* @return 父节点编号 diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/CrudDaoImpl.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/CrudDaoImpl.java index 5dd90efe29ae5a86e6f92341ffe4831006e7a105..2642f7f34d4b4e1a51593bf13a13c57c394c391c 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/CrudDaoImpl.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/CrudDaoImpl.java @@ -3,6 +3,7 @@ package com.gitee.qdbp.jdbc.biz; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ import com.gitee.qdbp.jdbc.sql.fragment.CrudFragmentHelper; import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; import com.gitee.qdbp.jdbc.utils.ParseTools; import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.MapTools; import com.gitee.qdbp.tools.utils.VerifyTools; /** @@ -125,20 +127,26 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { @Override public List listChildren(String startCode, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { - DbWhere filter = checkWhere(filterWhere); - DbWhere search = checkWhere(searchWhere); - // 数据状态填充在filterWhere中 - entityFieldFillExecutor.fillQueryWhereDataState(filter, getMajorTableAlias()); - // 数据权限填充在searchWhere中 - entityFieldFillExecutor.fillQueryWhereParams(search, getMajorTableAlias()); + VerifyTools.requireNotBlank(startCode, "startCode"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); - List startCodes = ConvertTools.toList(startCode); - return doListChildren(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); + List startCodes = Collections.singletonList(startCode); + return doListChildren(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); } @Override public List listChildren(List startCodes, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { + VerifyTools.requireNotBlank(startCodes, "startCodes"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); + + return doListChildren(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + private List doListChildren(List startCodes, String codeField, String parentField, DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings) { DbWhere filter = checkWhere(filterWhere); DbWhere search = checkWhere(searchWhere); // 数据状态填充在filterWhere中 @@ -152,21 +160,26 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { @Override public List listChildrenCodes(String startCode, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { - DbWhere filter = checkWhere(filterWhere); - DbWhere search = checkWhere(searchWhere); - // 数据状态填充在filterWhere中 - entityFieldFillExecutor.fillQueryWhereDataState(filter, getMajorTableAlias()); - // 数据权限填充在searchWhere中 - entityFieldFillExecutor.fillQueryWhereParams(search, getMajorTableAlias()); + VerifyTools.requireNotBlank(startCode, "startCode"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); - List startCodes = ConvertTools.toList(startCode); - IncludeFields fields = new IncludeFields(codeField); - return doListChildren(startCodes, codeField, parentField, fields, filter, search, orderings, String.class); + List startCodes = Collections.singletonList(startCode); + return doListChildrenCodes(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); } @Override public List listChildrenCodes(List startCodes, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { + VerifyTools.requireNotBlank(startCodes, "startCodes"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); + + return doListChildrenCodes(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + private List doListChildrenCodes(List startCodes, String codeField, String parentField, + DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { DbWhere filter = checkWhere(filterWhere); DbWhere search = checkWhere(searchWhere); // 数据状态填充在filterWhere中 @@ -181,20 +194,26 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { @Override public List listParents(String startCode, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { - DbWhere filter = checkWhere(filterWhere); - DbWhere search = checkWhere(searchWhere); - // 数据状态填充在filterWhere中 - entityFieldFillExecutor.fillQueryWhereDataState(filter, getMajorTableAlias()); - // 数据权限填充在searchWhere中 - entityFieldFillExecutor.fillQueryWhereParams(search, getMajorTableAlias()); + VerifyTools.requireNotBlank(startCode, "startCode"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); - List startCodes = ConvertTools.toList(startCode); - return doListParents(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); + List startCodes = Collections.singletonList(startCode); + return doListParents(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); } @Override public List listParents(List startCodes, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { + VerifyTools.requireNotBlank(startCodes, "startCodes"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); + + return doListParents(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + private List doListParents(List startCodes, String codeField, String parentField, DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings) { DbWhere filter = checkWhere(filterWhere); DbWhere search = checkWhere(searchWhere); // 数据状态填充在filterWhere中 @@ -202,27 +221,52 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { // 数据权限填充在searchWhere中 entityFieldFillExecutor.fillQueryWhereParams(search, getMajorTableAlias()); - return doListParents(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); + if (orderings != null && !orderings.isEmpty()) { + return doListParents(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); + } else { + // 如果startCode只有一个, 且未指定排序字段, 则结果会保证顺序从顶级节点开始依次排列 + Map params = generateRecursiveParams(startCodes, codeField, parentField, Fields.ALL, + filterWhere, searchWhere, orderings); + + String sqlId = "GlobalRecursive:recursiveListParentsQuery"; + List> list = sqlDao.listForMaps(sqlId, params); + + List> sorted = MapTools.sortTreeNodes(list, codeField, parentField); + + List results = new ArrayList<>(); + MapToBeanConverter converter = sqlBoot.plugins().getMapToBeanConverter(); + for (Map item : sorted) { + T entity = converter.convert(item, beanClass); + results.add(entity); + } + return results; + } } @Override public List listParentCodes(String startCode, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { - DbWhere filter = checkWhere(filterWhere); - DbWhere search = checkWhere(searchWhere); - // 数据状态填充在filterWhere中 - entityFieldFillExecutor.fillQueryWhereDataState(filter, getMajorTableAlias()); - // 数据权限填充在searchWhere中 - entityFieldFillExecutor.fillQueryWhereParams(search, getMajorTableAlias()); + VerifyTools.requireNotBlank(startCode, "startCode"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); - List startCodes = ConvertTools.toList(startCode); - IncludeFields fields = new IncludeFields(codeField); - return doListParents(startCodes, codeField, parentField, fields, filter, search, orderings, String.class); + List startCodes = Collections.singletonList(startCode); + return doListParentCodes(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); } @Override public List listParentCodes(List startCodes, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { + VerifyTools.requireNotBlank(startCodes, "startCodes"); + VerifyTools.requireNotBlank(codeField, "codeField"); + VerifyTools.requireNotBlank(parentField, "parentField"); + + return doListParentCodes(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + private List doListParentCodes(List startCodes, String codeField, String parentField, + DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings) { DbWhere filter = checkWhere(filterWhere); DbWhere search = checkWhere(searchWhere); // 数据状态填充在filterWhere中 @@ -230,8 +274,28 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { // 数据权限填充在searchWhere中 entityFieldFillExecutor.fillQueryWhereParams(search, getMajorTableAlias()); - IncludeFields fields = new IncludeFields(codeField); - return doListParents(startCodes, codeField, parentField, fields, filter, search, orderings, String.class); + if (orderings != null && !orderings.isEmpty()) { + IncludeFields fields = new IncludeFields(codeField); + return doListParents(startCodes, codeField, parentField, fields, filter, search, orderings, + String.class); + } else { + IncludeFields fields = new IncludeFields(codeField, parentField); + Map params = generateRecursiveParams(startCodes, codeField, parentField, fields, + filterWhere, searchWhere, orderings); + + String sqlId = "GlobalRecursive:recursiveListParentsQuery"; + List> list = sqlDao.listForMaps(sqlId, params); + + List> sorted = MapTools.sortTreeNodes(list, codeField, parentField); + List results = new ArrayList<>(); + for (Map item : sorted) { + Object codeValue = item.get(codeField); + if (codeValue != null) { + results.add(codeValue.toString()); + } + } + return results; + } } // ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField}={parentField} @@ -241,18 +305,27 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { protected List doListChildren(List startCodes, String codeField, String parentField, Fields fields, DbWhere filter, DbWhere search, Orderings orderings, Class resultType) throws ServiceException { return doListRecursive("Children", startCodes, codeField, parentField, fields, filter, search, orderings, - resultType); + resultType); } protected List doListParents(List startCodes, String codeField, String parentField, Fields fields, DbWhere filter, DbWhere search, Orderings orderings, Class resultType) throws ServiceException { return doListRecursive("Parents", startCodes, codeField, parentField, fields, filter, search, orderings, - resultType); + resultType); } protected List doListRecursive(String type, List startCodes, String codeField, String parentField, Fields selectFields, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings, Class resultType) throws ServiceException { + Map params = generateRecursiveParams(startCodes, codeField, parentField, selectFields, + filterWhere, searchWhere, orderings); + + String sqlId = "GlobalRecursive:recursiveList" + type + "Query"; + return sqlDao.listForObjects(sqlId, params, resultType); + } + + private Map generateRecursiveParams(List startCodes, String codeField, String parentField, + Fields selectFields, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { CrudFragmentHelper sqlHelper = sqlHelper().fragment(); String codeColumn = sqlHelper.getColumnName(FieldScene.CONDITION, codeField); String parentColumn = sqlHelper.getColumnName(FieldScene.CONDITION, parentField); @@ -273,9 +346,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { if (orderings != null && !orderings.isEmpty()) { params.put("orderBy", sqlHelper.buildOrderBySql(orderings, false)); } - - String sqlId = "GlobalRecursive:recursiveList" + type + "Query"; - return sqlDao.listForObjects(sqlId, params, resultType); + return params; } protected Map copmareMapDifference(Map original, Map current) { @@ -338,7 +409,8 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { } @Override - public String insert(Map entity, ValidStrategy validStrategy, boolean fillCreateParams) throws ServiceException { + public String insert(Map entity, ValidStrategy validStrategy, boolean fillCreateParams) + throws ServiceException { VerifyTools.requireNonNull(entity, "entity"); return executeInsert(entity, validStrategy, fillCreateParams); } @@ -685,8 +757,8 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { // 生成主键限制条件 DbWhere where = new DbWhere().on(pk.getFieldName()).equals(pkValue).end(); // 与批量处理的逻辑保持一致, 不限制数据状态 - // entityFieldFillExecutor.fillUpdateWhereDataState(where); - // entityFieldFillExecutor.fillUpdateWhereParams(where); + // entityFieldFillExecutor fillUpdateWhereDataState(where); + // entityFieldFillExecutor fillUpdateWhereParams(where); return this.doUpdate(ud, where, validStrategy, false); } else if (batchSize <= 0 || entities.size() <= batchSize) { return doBatchUpdates(entities, validStrategy, fillUpdateParams); @@ -706,10 +778,10 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { /** * 执行批量更新 - * + * * @param entities 实体对象列表(只能是entity或map或IdEntity列表, 其他参数将会报错) * @param validStrategy 校验处理策略
- * 注意: 由于@Column的length/precision/scale有默认值, 一旦启用, 则所有字段都需要覆盖设置! + * 注意: 由于@Column的length/precision/scale有默认值, 一旦启用, 则所有字段都需要覆盖设置! * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) * @return 受影响行数 * @throws ServiceException 操作失败 @@ -908,7 +980,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { * 执行实体业务数据转换及创建参数填充
* 作为以下两个方法的入口, 参数只支持map和T两种:
* fillEntityCreateParams(Map, boolean)/convertEntityAndFillCreateParams(T, boolean)
- * + * * @param object 实体对象, 只支持map和T两种 * @param fillCreateParams 是否自动填充创建参数 * @return 实体类转换后的map和自动填充的主键ID @@ -957,7 +1029,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { * 1. 查找主键ID, 如果有主键字段且字段值为空, 就自动生成ID
* 2. 填充创建参数中的数据状态
* 3. 如果fillCreateParams=true, 则填充其他创建参数 - * + * * @param entity 实体对象 * @param fillCreateParams 是否自动填充创建参数 * @return 填充或获取的主键ID @@ -990,7 +1062,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { * 执行完fillEntityCreateParams之后比对字段差异, 将差异部分设置回入参entity中
* 比对差异是为了提升性能, 因为对entity操作需要用到反射, 而比对差异只是对map操作, 快很多
* 例如一个类有20个字段, fillEntityCreateParams只修改了2个, 比对差异可以避免对其他18个字段的反射操作
- * + * * @param entity 实体对象 * @param fillCreateParams 是否自动填充创建参数 * @return 实体类转换后的map和自动填充的主键ID @@ -1016,9 +1088,9 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { /** * 执行实体业务数据转换及更新参数填充 - * + * * @param object 实体对象, 只支持map/T/PkEntity, 其他类型将会报错
- * 如果object是map, map中不能有where, 如果有,调这个方法之前要移除, 否则将会报错 + * 如果object是map, map中不能有where, 如果有,调这个方法之前要移除, 否则将会报错 * @param fillUpdateParams 是否自动填充更新参数 * @return 转换后的update和where */ @@ -1093,7 +1165,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { /** * 执行实体业务数据更新参数填充 - * + * * @param entity 实体对象 * @param fillUpdateParams 是否自动填充创建参数 */ @@ -1107,7 +1179,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { /** * 执行实体业务数据更新参数填充 - * + * * @param ud 待更新的内容 * @param fillUpdateParams 是否自动填充创建参数 */ @@ -1126,7 +1198,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { * 执行完fillEntityUpdateParams之后比对字段差异, 将差异部分设置回入参entity中
* 比对差异是为了提升性能, 因为对entity操作需要用到反射, 而比对差异只是对map操作, 快很多
* 例如一个类有20个字段, fillEntityUpdateParams只修改了2个, 比对差异可以避免对其他18个字段的反射操作
- * + * * @param entity 实体对象 * @param fillUpdateParams 是否自动填充更新参数 * @return 实体类转换后的map diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/exception/DbErrorCode.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/exception/DbErrorCode.java index e1acb50959e00b49bf36519b3e09bf0156dc9826..77fca6da4049e11f705a0b8d5023fb8111ae2ae3 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/exception/DbErrorCode.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/exception/DbErrorCode.java @@ -13,6 +13,8 @@ public enum DbErrorCode implements IResultMessage { /** 字段值超长 **/ DB_COLUMN_VALUE_TOO_LONG, + /** 字段值错误 **/ + DB_COLUMN_VALUE_ERROR, /** 主键字段未找到 **/ DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, /** 主键值不能为空 **/ @@ -37,6 +39,8 @@ public enum DbErrorCode implements IResultMessage { DB_DATA_SOURCE_INIT_ERROR, /** SQL片断不存在 **/ DB_SQL_FRAGMENT_NOT_FOUND, + /** SQL片断存在冲突 **/ + DB_SQL_FRAGMENT_CONFLICT, /** SQL片断解析失败 **/ DB_SQL_FRAGMENT_PARSE_ERROR, /** SQL片断输出失败 **/ diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginInitTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginInitTools.java index 5ecfa1c5ab54d61038030018f425c1d7bba6ce9e..80b2be9546b9627260a6c8e69f05e016f897a991 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginInitTools.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginInitTools.java @@ -36,6 +36,9 @@ import com.gitee.qdbp.jdbc.support.convert.NumberToBooleanConverter; import com.gitee.qdbp.jdbc.support.convert.StringToDateConverter; import com.gitee.qdbp.jdbc.support.enums.AllEnumConverterRegister; import com.gitee.qdbp.tools.utils.Config; +import com.gitee.qdbp.tools.utils.ReflectTools; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.support.DefaultConversionService; @@ -48,6 +51,8 @@ import org.springframework.core.convert.support.DefaultConversionService; */ public class DbPluginInitTools { + private static final Logger log = LoggerFactory.getLogger(DbPluginInitTools.class); + public static void initDefaultConverter(ConversionService conversionService) { if (conversionService instanceof ConverterRegistry) { ConverterRegistry registry = (ConverterRegistry) conversionService; @@ -60,6 +65,16 @@ public class DbPluginInitTools { } // 注册其他枚举值转换处理类: oracle的BigDecimal转Enum等 AllEnumConverterRegister.registerEnumConverterFactory(registry); + + try { + // 注册日期转换处理类 + // DateConverter里面都是jdk8的转换处理类, qdbc默认支持jdk7, 因此使用反射调用 + Class dateConverter = Class.forName("com.gitee.qdbp.jdbc.support.convert.DateConverter"); + ReflectTools.invokeMethod(dateConverter, "registerConverters", conversionService); + } catch (ClassNotFoundException ignore) { + } catch (Exception e) { + log.warn("Failed to invoke method of DateConverter.registerConverters()", e); + } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseSqlFileScanner.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseSqlFileScanner.java index c966a7281ac68a1ce7a33b949e6e3c65cc315797..60b836c2bf80fe16646dddd0606c374c5690b235 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseSqlFileScanner.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseSqlFileScanner.java @@ -3,18 +3,15 @@ package com.gitee.qdbp.jdbc.plugins.impl; import java.io.IOException; import java.net.URL; import java.util.ArrayList; -import java.util.Collections; import java.util.Date; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitee.qdbp.jdbc.model.SqlFile; import com.gitee.qdbp.jdbc.plugins.SqlFileScanner; import com.gitee.qdbp.tools.files.PathTools; import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.DateTools; import com.gitee.qdbp.tools.utils.StringTools; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -42,7 +39,7 @@ public abstract class BaseSqlFileScanner implements SqlFileScanner { /** * 扫描SQL文件 - * + * * @param pattern 文件路径扫描规则 * @return SQL文件URL路径 * @throws IOException IO异常 @@ -55,31 +52,47 @@ public abstract class BaseSqlFileScanner implements SqlFileScanner { String[] patterns = StringTools.split(pattern, ','); Date startTime = new Date(); Long minTime = lastUpdateTime == null ? null : lastUpdateTime.getTime(); + // 扫描文件 List sqlFiles = scanSqlFiles(minTime, patterns); - // 按相对路径分组, 同一路径存在多个文件时, 优先取classpath下的, 然后再取jar包中的 - Map> maps = groupSqlFiles(sqlFiles); - List results = new ArrayList<>(); - List conflicts = new ArrayList<>(); - for (Entry> entry : maps.entrySet()) { - SqlFile first = entry.getValue().get(0); - results.add(first); // 排序好之后, 取第1个 - if (log.isTraceEnabled() && entry.getValue().size() > 1) { // 构造Trace日志 - conflicts.add(entry.getKey()); - conflicts.add('\t' + first.getAbsolutePath()); - for (int i = 1, z = entry.getValue().size(); i < z; i++) { - conflicts.add('\t' + entry.getValue().get(i).getAbsolutePath() + " (ignored)"); + // 输出日志 + if (lastUpdateTime == null) { + if (sqlFiles.isEmpty()) { + if (log.isWarnEnabled()) { + log.warn("Failed to scan sql templates, no files were found.\n\tpatterns is {}", pattern); } + } else if (log.isTraceEnabled()) { + String duration = ConvertTools.toDuration(startTime, true); + String msg = "Success to scan sql templates, elapsed time {}, total of {} files were found.\n\t{}"; + log.trace(msg, duration, sqlFiles.size(), collectSqlFilePaths(sqlFiles)); + } else if (log.isInfoEnabled()) { + String duration = ConvertTools.toDuration(startTime, true); + String msg = "Success to scan sql templates, elapsed time {}, total of {} files were found."; + log.info(msg, duration, sqlFiles.size()); + } + } else { + if (log.isTraceEnabled() && !sqlFiles.isEmpty()) { + String duration = ConvertTools.toDuration(startTime, true); + String updateTime = DateTools.toNormativeString(lastUpdateTime); + String msg = "Success to scan sql templates, elapsed time {}, " + + "total of {} files with update time > {} were found.\n\t{}"; + log.trace(msg, duration, sqlFiles.size(), updateTime, collectSqlFilePaths(sqlFiles)); + } else if (log.isInfoEnabled()) { + String duration = ConvertTools.toDuration(startTime, true); + String updateTime = DateTools.toNormativeString(lastUpdateTime); + String msg = "Success to scan sql templates, elapsed time {}, " + + "total of {} files with update time > {} were found."; + log.info(msg, duration, sqlFiles.size(), updateTime); } } - if (log.isTraceEnabled() && !conflicts.isEmpty()) { - String duration = ConvertTools.toDuration(startTime, true); - String msg = "Success to scan sql templates, elapsed time {}, total of {} files were found.\n{}:\n{}"; - log.trace(msg, duration, results.size(), "Conflict details", ConvertTools.joinToString(conflicts, '\n')); - } else if (log.isInfoEnabled()) { - String msg = "Success to scan sql templates, elapsed time {}, total of {} files were found."; - log.info(msg, ConvertTools.toDuration(startTime, true), results.size()); + return sqlFiles; + } + + protected String collectSqlFilePaths(List sqlFiles) { + List filePaths = new ArrayList<>(); + for (SqlFile sqlFile : sqlFiles) { + filePaths.add(PathTools.getClasspathRelativePath(sqlFile.getAbsolutePath())); } - return results; + return ConvertTools.joinToString(filePaths); } protected List scanSqlFiles(Long minTime, String[] patterns) throws IOException { @@ -114,45 +127,37 @@ public abstract class BaseSqlFileScanner implements SqlFileScanner { return sqlFile; } - protected LinkedHashMap> groupSqlFiles(List sqlFiles) { - // 按相对路径分组 - LinkedHashMap> maps = new LinkedHashMap<>(); - for (SqlFile item : sqlFiles) { - String relativePath = item.getRelativePath(); - if (maps.containsKey(relativePath)) { - maps.get(relativePath).add(item); - } else { - List list = ConvertTools.toList(item); - maps.put(relativePath, list); + /** 获取SQL相对路径 **/ + protected String getSqlRelativePath(String pattern, URL url) { + String urlPath = url.toString(); + // jar:file:/E:/repository/com/gitee/qdbp/xxx-1.0.0.jar!/settings/sqls/xxx.xml + // file:/D:/qdbp/qdbp-jdbc/jdbc-core/target/classes/settings/sqls/xxx.xml + String lowerCase = urlPath.toLowerCase(); + String jarFlag = ".jar!/"; + int jarIndex = lowerCase.indexOf(jarFlag); + if (jarIndex > 0) { + int folderIndex = lowerCase.lastIndexOf('/', jarIndex); + if (folderIndex >= 0) { + // xxx-1.0.0.jar!/settings/sqls/xxx.xml + return urlPath.substring(folderIndex + 1); } } - // 同一路径存在多个文件时, 优先取classpath下的, 然后再取jar包中的 - for (Entry> entry : maps.entrySet()) { - if (entry.getValue().size() > 1) { - sortSqlFiles(entry.getKey(), entry.getValue()); - } + int classesIndex = lowerCase.indexOf("/classes/"); + if (classesIndex > 0) { + // settings/sqls/xxx.xml + return urlPath.substring(classesIndex + "/classes/".length()); } - return maps; - } - - protected void sortSqlFiles(String relativePath, List sqlFiles) { - Collections.sort(sqlFiles); - } - - /** 获取SQL相对路径 **/ - protected String getSqlRelativePath(String pattern, URL url) { - String folder = null; + // 既不是jar路径也不是classes路径 + String folder = pattern; int starIndex = pattern.indexOf('*'); if (starIndex > 0) { folder = pattern.substring(0, starIndex); } - String urlPath = url.toString(); - int folderIndex = folder == null ? -1 : urlPath.indexOf(folder); + int folderIndex = urlPath.indexOf(folder); if (folderIndex > 0) { return StringTools.removeLeft(urlPath.substring(folderIndex), '/'); - } else { - return PathTools.getClasspathRelativePath(urlPath); } + return urlPath; } /** 文件路径扫描规则, 多个以逗号分隔 **/ diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/RandomNumberEntityDataStateFillStrategy.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/RandomNumberEntityDataStateFillStrategy.java index 9ec2a5cfeaa51369dec6d30b732872b9067e9d96..d06fa3ea09d40343202fc494e9497c0035254200 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/RandomNumberEntityDataStateFillStrategy.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/RandomNumberEntityDataStateFillStrategy.java @@ -153,7 +153,7 @@ public class RandomNumberEntityDataStateFillStrategy field.setFieldValue(0); } else if (greaterThen.matchers(operator)) { // 大于DataState.DELETED, 查询比最大值还大的, 应该查不到记录 - long maxDeletedFlag = (long) Math.pow(10, logicalDeleteRandoms) - 1; + long maxDeletedFlag = (long) Math.pow(10D, logicalDeleteRandoms) - 1; field.setOperateType(">"); field.setFieldValue(maxDeletedFlag); // } else { diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TableRowToBeanMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TableRowToBeanMapper.java index d6e8f210a0a147c5bf631e50e3bcb8045c12fd3b..e0653ba30a8dfc82f755ed576502e7fb09dc28a8 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TableRowToBeanMapper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TableRowToBeanMapper.java @@ -6,12 +6,15 @@ import java.sql.SQLException; import java.util.LinkedHashMap; import java.util.Map; import org.springframework.jdbc.support.JdbcUtils; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; import com.gitee.qdbp.jdbc.model.AllFieldColumn; import com.gitee.qdbp.jdbc.model.FieldColumns; import com.gitee.qdbp.jdbc.model.FieldScene; import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; import com.gitee.qdbp.jdbc.plugins.DbPluginHelper; import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser; +import com.gitee.qdbp.tools.utils.StringTools; /** * 单表查询结果集行数据到JavaBean的转换处理类
@@ -54,15 +57,33 @@ public class TableRowToBeanMapper implements RowToBeanMapper, DbPluginHelp ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); Map mapOfColumnValues = new LinkedHashMap<>(columnCount); - for (int i = 1; i <= columnCount; i++) { - String columnName = getColumnName(JdbcUtils.lookupColumnName(rsmd, i)); - // 根据列名查找字段信息 - SimpleFieldColumn field = fieldColumns.findByColumnAlias(columnName); - if (field != null) { - String fieldName = field.getFieldName(); - Class fieldType = field.getJavaType(); - Object value = JdbcUtils.getResultSetValue(rs, i, fieldType); - mapOfColumnValues.put(fieldName, value); + + Integer lastIndex = null; + SimpleFieldColumn lastField = null; + try { + for (int i = 1; i <= columnCount; i++) { + lastIndex = null; + lastField = null; + String columnName = getColumnName(JdbcUtils.lookupColumnName(rsmd, i)); + // 根据列名查找字段信息 + SimpleFieldColumn field = fieldColumns.findByColumnAlias(columnName); + if (field != null) { + lastIndex = i; + lastField = field; + fillColumnValue(rs, i, field, mapOfColumnValues); + } + } + } catch (Exception e) { + // try catch 放在循环外面, 提高性能 + if (lastField == null) { + throw e; + } else { + Object lastValue = JdbcUtils.getResultSetValue(rs, lastIndex); + String details = String.format("%s.%s, FieldType:%s, ColumnName:%s, ColumnType:%s, ColumnValue:%s", + mappedClass.getSimpleName(), lastField.getFieldName(), + lastField.getJavaType().getSimpleName(), lastField.getColumnName(), + lastValue.getClass().getSimpleName(), StringTools.ellipsis(lastValue.toString(), 100)); + throw new ServiceException(DbErrorCode.DB_COLUMN_VALUE_ERROR, details, e); } } return mapOfColumnValues; @@ -79,6 +100,18 @@ public class TableRowToBeanMapper implements RowToBeanMapper, DbPluginHelp return columnName; } + protected void fillColumnValue(ResultSet rs, int index, SimpleFieldColumn field, Map maps) + throws SQLException { + String fieldName = field.getFieldName(); + Class fieldType = field.getJavaType(); + Object value = getColumnValue(rs, index, fieldType); + maps.put(fieldName, value); + } + + protected Object getColumnValue(ResultSet rs, int index, Class fieldType) throws SQLException { + return JdbcUtils.getResultSetValue(rs, index, fieldType); + } + /** * TableRowToBeanMapper的工厂类 * diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TablesRowToProperyMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TablesRowToProperyMapper.java index f2303d33fe79e6562dc5c280cb783e5237ce7634..0afaf2df0624397345f4ed9969042b80b8adb871 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TablesRowToProperyMapper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/TablesRowToProperyMapper.java @@ -102,22 +102,27 @@ public class TablesRowToProperyMapper implements RowToBeanMapper, DbPlugin continue; // 如果没有指定resultField, 说明不需要保存结果 } - // 根据字段类型获取字段值 - String fieldName = field.getFieldName(); - Class fieldType = field.getJavaType(); - Object value = JdbcUtils.getResultSetValue(rs, i, fieldType); - // 将字段名和字段值根据resultField填充至对应的子对象中 // 如果resultField=this直接填充到主容器, 否则填充到指定的子容器 - if (resultField.equals("this")) { - result.put(fieldName, value); - } else { - subs.get(resultField).put(fieldName, value); - } + Map maps = resultField.equals("this") ? result : subs.get(resultField); + fillColumnValue(rs, i, field, maps); } return result; } + protected void fillColumnValue(ResultSet rs, int index, TablesFieldColumn field, Map maps) + throws SQLException { + // 根据字段类型获取字段值 + String fieldName = field.getFieldName(); + Class fieldType = field.getJavaType(); + Object value = getColumnValue(rs, index, fieldType); + maps.put(fieldName, value); + } + + protected Object getColumnValue(ResultSet rs, int index, Class fieldType) throws SQLException { + return JdbcUtils.getResultSetValue(rs, index, fieldType); + } + /** * TablesRowToProperyMapper的工厂类 * diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentContainer.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentContainer.java index 6af39b45b00583cda319e46d32928ea14d4b0f84..bbe8c689780de82fb0e20afbf9095ff38ea174b4 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentContainer.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentContainer.java @@ -2,8 +2,10 @@ package com.gitee.qdbp.jdbc.sql.parse; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; @@ -22,8 +24,10 @@ import com.gitee.qdbp.staticize.common.IMetaData; import com.gitee.qdbp.staticize.exception.TagException; import com.gitee.qdbp.staticize.publish.TemplateTools; import com.gitee.qdbp.staticize.tags.base.Taglib; +import com.gitee.qdbp.tools.compare.CompareTools; import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.StringTools; +import com.gitee.qdbp.tools.utils.VerifyTools; import com.gitee.qdbp.tools.utils.VersionCodeTools; /** @@ -41,37 +45,41 @@ public class SqlFragmentContainer { private final SqlBoot sqlBoot; /** 标签库 **/ private final Taglib taglib; + /** 扫描状态 (如果正在扫描, 则获取模板时需要加锁) **/ + private final ScanState scanState; /** 未指定数据库类型的SQL模板 **/ // key=sqlId - private final Map> untypedCache = new HashMap<>(); + private final Map untypedCache = new HashMap<>(); /** 指定了数据库类型的SQL模板 **/ - // key=sqlId(dbType), value=[{version,List[SqlMetaData]}], value按版本号从大到小排列, 无版本号的放最后 + // key=sqlId(dbType), value=[{version,SqlMetaDatas}], value按版本号从大到小排列, 无版本号的放最后 private final Map> typedCache = new HashMap<>(); public SqlFragmentContainer(SqlBoot sqlBoot) { this.sqlBoot = sqlBoot; + this.scanState = new ScanState(); this.taglib = sqlBoot.plugins().newSqlTaglib(); this.taglib.setDefaultTagFactory(new SqlTagFactory(sqlBoot)); } /** * 注册SQL模板标签元数据树 - * + * * @param scanTime 本次扫描时间 * @param sqlId SQL编号 * @param supports 支持哪些数据库版本 + * @param priority 优先级 * @param sqlFile SQL文件信息 * @param data 解析后的模板标签元数据树 */ - protected void register(long scanTime, String sqlId, String supports, SqlFile sqlFile, IMetaData data) { - SqlMetaData sqlMetaData = new SqlMetaData(scanTime, sqlId, supports, sqlFile, data); + protected void register(long scanTime, String sqlId, String supports, Integer priority, SqlFile sqlFile, + IMetaData data) { + SqlMetaData sqlMetaData = new SqlMetaData(scanTime, sqlId, supports, priority, sqlFile, data); if (supports == null || "*".equals(supports)) { // 存入未指定数据库类型的SQL模板容器 if (untypedCache.containsKey(sqlId)) { - mergeSqlMetaDatas(scanTime, sqlMetaData, untypedCache.get(sqlId)); + untypedCache.get(sqlId).addItem(sqlMetaData); } else { - List list = ConvertTools.toList(sqlMetaData); - untypedCache.put(sqlId, list); + untypedCache.put(sqlId, new SqlMetaDatas(scanState, sqlMetaData)); } } else { // mysql.8,mariadb.10.2.2,postgresql,db2,sqlserver,sqlite.3.8.3 @@ -103,21 +111,20 @@ public class SqlFragmentContainer { } } String cacheKey = sqlId + '(' + dbType + ')'; - TypedSqlMetaData value = new TypedSqlMetaData(version, sqlMetaData); + TypedSqlMetaData value = new TypedSqlMetaData(scanState, version, sqlMetaData); if (this.typedCache.containsKey(cacheKey)) { List sqlMetaDatas = this.typedCache.get(cacheKey); - mergeTagDatas(scanTime, version, sqlMetaData, sqlMetaDatas); + mergeTagDatas(version, sqlMetaData, sqlMetaDatas); } else { - List list = new ArrayList<>(); - list.add(value); - this.typedCache.put(cacheKey, list); + List sqlMetaDatas = ConvertTools.toList(value); + this.typedCache.put(cacheKey, sqlMetaDatas); } } } } // 合并SQL片段, 返回被替换的对象 - private SqlMetaData mergeTagDatas(long scanTime, String version, SqlMetaData newer, List list) { + private void mergeTagDatas(String version, SqlMetaData newer, List list) { // value按版本号从大到小排列, 无版本号的放最后 for (int index = 0; index < list.size(); index++) { TypedSqlMetaData target = list.get(index); @@ -125,57 +132,33 @@ public class SqlFragmentContainer { if (targetVersion == null || "*".equals(targetVersion)) { if (version == null || "*".equals(version)) { // 当前版本是*, 目标版本也是*, 合并到一起; 获取时会报冲突 - return mergeSqlMetaDatas(scanTime, newer, target.getMetaDatas()); + target.getMetaDatas().addItem(newer); + return; } else { // 当前版本不是*, 目标版本是*, 插入目标版本前面 - list.add(index, new TypedSqlMetaData(version, newer)); - return null; + list.add(index, new TypedSqlMetaData(scanState, version, newer)); + return; } } else if (version == null || "*".equals(version)) { // 当前版本是*, 目标版本不是*, 不匹配 continue; } else if (VersionCodeTools.compare(targetVersion, version) == 0) { // 遇到完全相同的版本, 合并到一起; 获取时会报冲突 - return mergeSqlMetaDatas(scanTime, newer, target.getMetaDatas()); + target.getMetaDatas().addItem(newer); + return; } else if (VersionCodeTools.compare(targetVersion, version) < 0) { // 插入首个小于当前版本的位置的前面 - list.add(index, new TypedSqlMetaData(version, newer)); - return null; + list.add(index, new TypedSqlMetaData(scanState, version, newer)); + return; } } // 没匹配上, 添加到最后 - list.add(new TypedSqlMetaData(version, newer)); - return null; - } - - // 合并SQL片段, 返回被替换的对象 - protected SqlMetaData mergeSqlMetaDatas(long scanTime, SqlMetaData newer, List metadatas) { - if (this.lastScanTime != null && !metadatas.isEmpty()) { - for (int i = 0, z = metadatas.size(); i < z; i++) { - SqlMetaData older = metadatas.get(i); - if (older.getAbsolutePath().equals(newer.getAbsolutePath())) { - // 绝对路径相同, 直接替换 - return metadatas.set(i, newer); - } - // 如果新SQL片段与原片段的相对路径相同 - if (older.getRelativePath().equals(newer.getRelativePath())) { - if (older.getScanTime() < scanTime) { - // 原片段不是来自本次扫描到的文件, 则替换原片段 - return metadatas.set(i, newer); - // } else { - // 绝对路径不同, 相对路径相同, 且都来自于本次扫描到的文件 - // 不可能出现这种情况, 因为在扫描那里已经处理了, 同一个相对路径只会取一个文件 - } - } - } - } - metadatas.add(newer); - return null; + list.add(new TypedSqlMetaData(scanState, version, newer)); } /** * 查找SQL模板标签元数据树 - * + * * @param sqlId SQL编号 * @return 标签元数据树 */ @@ -189,7 +172,7 @@ public class SqlFragmentContainer { String dbType = dbVersion.getDbType().name().toLowerCase(); String currVersion = dbVersion.getVersionCode(); - List found = null; + SqlMetaDatas found = null; // 记录下尝试过哪些模板, 用于匹配失败时输出日志 // key=dbType+dbVersion, value=location List tryedLocations = new ArrayList<>(); @@ -201,7 +184,7 @@ public class SqlFragmentContainer { // 逐一对比此模板的最低版本要求 for (TypedSqlMetaData item : typedList) { String minVersion = item.getMinVersion(); - List datas = item.getMetaDatas(); + SqlMetaDatas datas = item.getMetaDatas(); if (minVersion == null || "*".equals(minVersion)) { found = datas; // 没有最低要求, 则当前数据库为匹配 break; @@ -210,7 +193,7 @@ public class SqlFragmentContainer { found = datas; // 满足最低要求, 匹配成功 break; } - for (SqlMetaData temp : datas) { + for (SqlMetaData temp : datas.getItems()) { tryedLocations.add(new KeyString(dbType + '.' + minVersion, temp.getMetaData().getRealPath())); } } @@ -221,27 +204,33 @@ public class SqlFragmentContainer { } String versionString = dbVersion.toVersionString(); - if (found != null && !found.isEmpty()) { - if (found.size() == 1) { - return found.get(0).getMetaData(); + if (found != null) { + if (!found.isConflicting()) { + // 未冲突, 查找成功 + return found.getMajorItem().getMetaData(); } + // 有冲突, 输出日志 StringBuilder conflicts = new StringBuilder(); - for (SqlMetaData item : found) { + for (SqlMetaData item : found.getConflicts()) { conflicts.append("\n\t").append(item.getMetaData().getRealPath()); String supports = item.getSupports(); if (supports != null && !"*".equals(supports)) { conflicts.append(' ').append("").append(item.getSupports()).append(""); } + if (item.getPriority() != null) { + conflicts.append(' ').append('(').append(item.getPriority()).append(')'); + } } + int conflictSize = found.getConflicts().size(); StringBuilder details = new StringBuilder(); details.append("\nsqlId=").append(sqlId).append(", dbVersion=").append(versionString); - details.append("\nexpected single template but found ").append(found.size()).append(":"); + details.append("\nexpected single template but found ").append(conflictSize).append(":"); details.append(conflicts); if (log.isWarnEnabled()) { - log.warn("Sql template '{}({})' not found: {}", sqlId, versionString, details); + log.warn("Sql template '{}({})' conflict: {}", sqlId, versionString, details); } if (throwOnNotFound) { - throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_NOT_FOUND, details.toString()); + throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_CONFLICT, details.toString()); } else { return null; } @@ -291,7 +280,7 @@ public class SqlFragmentContainer { /** * 判断SQL模板是否存在 - * + * * @param sqlId SQL编号 * @return 是否存在 */ @@ -334,15 +323,10 @@ public class SqlFragmentContainer { } } - private enum TaskState { - IDLE, DOING - } - private final Lock initlock = new ReentrantLock(); private boolean inited = false; private final Lock scanlock = new ReentrantLock(); - private TaskState scanState = null; - private Long lastScanTime = null; + private Long lastScanTime; /** 初始化SQL模板文件 **/ public void initSqlFiles() { @@ -352,7 +336,7 @@ public class SqlFragmentContainer { this.initlock.lock(); try { if (!this.inited) { - this.scanState = TaskState.IDLE; + this.scanState.setScanning(false); this.scanSqlFiles(); this.inited = true; } @@ -363,22 +347,22 @@ public class SqlFragmentContainer { /** 扫描SQL模板文件 **/ public void scanSqlFiles() { - if (this.scanState == TaskState.DOING) { + if (this.scanState.isScanning()) { return; } if (this.scanlock.tryLock()) { try { - if (this.scanState == TaskState.DOING) { + if (this.scanState.isScanning()) { return; } - this.scanState = TaskState.DOING; + this.scanState.setScanning(true); try { long now = System.currentTimeMillis(); this.doScanSqlFiles(now); this.lastScanTime = now; } finally { - this.scanState = TaskState.IDLE; + this.scanState.setScanning(false); } } finally { this.scanlock.unlock(); @@ -404,10 +388,10 @@ public class SqlFragmentContainer { SqlFragmentParser parser = new SqlFragmentParser(taglib, sqlBoot.plugins()); List container = parser.parseSqlFiles(sqlFiles); - for (ParsedFragment item : container) { - this.register(scanTime, item.getSqlId(), item.getSupports(), item.getSqlFile(), item.getMetaData()); - if (item.getAlias() != null) { - this.register(scanTime, item.getAlias(), item.getSupports(), item.getSqlFile(), item.getMetaData()); + for (ParsedFragment i : container) { + register(scanTime, i.getSqlId(), i.getSupports(), i.getPriority(), i.getSqlFile(), i.getMetaData()); + if (i.getAlias() != null) { + register(scanTime, i.getAlias(), i.getSupports(), i.getPriority(), i.getSqlFile(), i.getMetaData()); } } @@ -425,27 +409,43 @@ public class SqlFragmentContainer { } } - protected static class SqlMetaData { + private static class ScanState { + private boolean scanning; + + public boolean isScanning() { + return scanning; + } + + public void setScanning(boolean scanning) { + this.scanning = scanning; + } + } + + protected static class SqlMetaData implements Comparable { /** SqlId **/ private final String sqlId; /** 支持的版本 **/ private final String supports; + /** 优先级 **/ + private final Integer priority; /** 绝对路径 **/ private final String absolutePath; /** 相对路径 **/ private final String relativePath; /** 扫描时间 **/ - private final Long scanTime; + private final long scanTime; /** 最后更新时间 **/ private final Long lastUpdateTime; /** 模板数据 **/ private final IMetaData metadata; - public SqlMetaData(long scanTime, String sqlId, String supports, SqlFile sqlFile, IMetaData metadata) { + public SqlMetaData(long scanTime, String sqlId, String supports, Integer priority, SqlFile sqlFile, + IMetaData metadata) { this.scanTime = scanTime; this.sqlId = sqlId; this.supports = supports; + this.priority = priority; this.absolutePath = sqlFile.getAbsolutePath(); this.relativePath = sqlFile.getRelativePath(); this.lastUpdateTime = sqlFile.getLastUpdateTime(); @@ -462,8 +462,12 @@ public class SqlFragmentContainer { return supports; } + public Integer getPriority() { + return priority; + } + /** 扫描时间 **/ - public Long getScanTime() { + public long getScanTime() { return scanTime; } @@ -487,18 +491,197 @@ public class SqlFragmentContainer { return metadata; } + public boolean isSamePriorityOf(SqlMetaData that) { + if (that == null) { + return false; + } + if (VerifyTools.notEquals(this.getPriority(), that.getPriority())) { + return false; + } + boolean thisInJar = this.isPathInJar(); + boolean thatInJar = that.isPathInJar(); + return thisInJar == thatInJar; + } + + @Override + public int compareTo(SqlMetaData that) { + if (this == that) { + return 0; + } + if (that == null) { + return -1; + } + + // 按优先级降序 + int priority = CompareTools.descCompare(this.getPriority(), that.getPriority()); + if (priority != 0) { + return priority; + } + + boolean thisInJar = this.isPathInJar(); + boolean thatInJar = that.isPathInJar(); + if (thisInJar != thatInJar) { + return thisInJar ? 1 : -1; + } + + return 0; + } + + protected boolean isPathInJar() { + // jar:file:/D:/qdbp-jdbc/jdbc-test/src/test/resources/libs/xxx.jar!/settings/sqls/select.001.sql + return getAbsolutePath().contains("jar:"); + } + + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(sqlId); + buffer.append('(').append(supports == null ? "*" : supports).append(')'); + if (priority != null) { + buffer.append('(').append(priority).append(')'); + } + buffer.append(' ').append(metadata.getRealPath()); + return buffer.toString(); + } + } + + // 只有同一SqlId相同DbType相同版本才会进入此集合 + // 如果正在扫描, 则获取模板时需要加锁, 以避免外界取到doAddItem执行到半途的数据 + protected static class SqlMetaDatas { + private final ScanState scanState; + private final Lock scanLock = new ReentrantLock(); + private final List metadatas = new ArrayList<>(); + private SqlMetaData majorItem; + private List conflictItems; + + public SqlMetaDatas(ScanState scanState, SqlMetaData metadata) { + this.scanState = scanState; + this.metadatas.add(metadata); + this.majorItem = metadata; + } + + public List getItems() { + boolean scanning = scanState.isScanning(); + if (scanning) { + this.scanLock.lock(); + } + try { + return metadatas; + } finally { + if (scanning) { + this.scanLock.unlock(); + } + } + } + + public SqlMetaData getMajorItem() { + boolean scanning = scanState.isScanning(); + if (scanning) { + this.scanLock.lock(); + } + try { + return majorItem; + } finally { + if (scanning) { + this.scanLock.unlock(); + } + } + } + + public boolean isConflicting() { + boolean scanning = scanState.isScanning(); + if (scanning) { + this.scanLock.lock(); + } + try { + return conflictItems != null; + } finally { + if (scanning) { + this.scanLock.unlock(); + } + } + } + + public List getConflicts() { + boolean scanning = scanState.isScanning(); + if (scanning) { + this.scanLock.lock(); + } + try { + return conflictItems; + } finally { + if (scanning) { + this.scanLock.unlock(); + } + } + } + + // 合并SQL片段到集合中 + public void addItem(SqlMetaData metadata) { + boolean scanning = scanState.isScanning(); + if (scanning) { + this.scanLock.lock(); + } + try { + this.doAddItem(metadata); + } finally { + if (scanning) { + this.scanLock.unlock(); + } + } + } + + private void doAddItem(SqlMetaData newer) { + // 删除之前扫描的同一文件中相同ID的SQL片段 + Iterator iterator = metadatas.iterator(); + while (iterator.hasNext()) { + SqlMetaData older = iterator.next(); + if (older.getAbsolutePath().equals(newer.getAbsolutePath())) { + if (older.getScanTime() < newer.getScanTime()) { + iterator.remove(); + } + } + } + metadatas.add(newer); + + // 检查冲突 + if (metadatas.size() == 1) { + conflictItems = null; + majorItem = metadatas.get(0); + } else if (metadatas.size() > 1) { + // 按优先级排序 + Collections.sort(metadatas); + // 取出优先级最高的第1个 + SqlMetaData first = metadatas.get(0); + // 检查有没有优先级与之相同的 + List same = new ArrayList<>(); + for (int i = 1; i < metadatas.size(); i++) { + SqlMetaData item = metadatas.get(i); + if (first.isSamePriorityOf(item)) { + same.add(item); + } + } + if (same.isEmpty()) { + conflictItems = null; + majorItem = first; + } else { // 存在冲突 + majorItem = null; + same.add(0, first); + conflictItems = same; + } + } + } } protected static class TypedSqlMetaData { /** 最低版本要求 **/ private final String minVersion; - private final List metadatas; + private final SqlMetaDatas metadatas; - public TypedSqlMetaData(String minVersion, SqlMetaData data) { + public TypedSqlMetaData(ScanState scanState, String minVersion, SqlMetaData metadata) { this.minVersion = minVersion; - this.metadatas = new ArrayList<>(); - this.metadatas.add(data); + this.metadatas = new SqlMetaDatas(scanState, metadata); } /** 最低版本要求 **/ @@ -506,7 +689,7 @@ public class SqlFragmentContainer { return minVersion; } - public List getMetaDatas() { + public SqlMetaDatas getMetaDatas() { return metadatas; } } @@ -516,13 +699,16 @@ public class SqlFragmentContainer { private final String sqlId; private final String alias; private final String supports; + private final Integer priority; private final SqlFile sqlFile; private final IMetaData metadata; - public ParsedFragment(String sqlId, String alias, String supports, SqlFile sqlFile, IMetaData data) { + public ParsedFragment(String sqlId, String alias, String supports, Integer priority, SqlFile sqlFile, + IMetaData data) { this.sqlId = sqlId; this.alias = alias; this.supports = supports; + this.priority = priority; this.sqlFile = sqlFile; this.metadata = data; } @@ -539,6 +725,10 @@ public class SqlFragmentContainer { return supports; } + public Integer getPriority() { + return priority; + } + public SqlFile getSqlFile() { return sqlFile; } @@ -552,6 +742,9 @@ public class SqlFragmentContainer { StringBuilder buffer = new StringBuilder(); buffer.append(sqlId); buffer.append('(').append(supports == null ? "*" : supports).append(')'); + if (priority != null) { + buffer.append('(').append(priority).append(')'); + } buffer.append(' ').append(metadata.getRealPath()); return buffer.toString(); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentParser.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentParser.java index e02df17329feaf1c2d85a048e9b5b35ad4e1dc84..5509cef94c97134a4d90787305ba98fb8bb81df4 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentParser.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlFragmentParser.java @@ -28,6 +28,7 @@ import com.gitee.qdbp.staticize.parse.TagParser; import com.gitee.qdbp.staticize.tags.base.Taglib; import com.gitee.qdbp.staticize.tags.base.TextTag; import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.RandomTools; import com.gitee.qdbp.tools.utils.StringTools; @@ -124,8 +125,9 @@ class SqlFragmentParser { String sqlId = data.getSqlId(); String sqlAlias = data.getSqlAlias(); String supports = data.getSupports(); + Integer priority = data.getPriority(); SqlFile sqlFile = data.getSqlFile(); - container.add(new ParsedFragment(sqlId, sqlAlias, supports, sqlFile, metadata)); + container.add(new ParsedFragment(sqlId, sqlAlias, supports, priority, sqlFile, metadata)); } catch (Exception e) { log.warn("Sql template parse error: {}", data.getReader().getRealPath(), e); continue; @@ -135,20 +137,21 @@ class SqlFragmentParser { private void registerSqlFragment(SqlCacheBox cacheBox, SqlFile sqlFile, SqlFragment sqlFragment) { String sqlPath = sqlFile.getAbsolutePath(); - SqlId result = parseSqlId(sqlPath, sqlFragment.getId(), sqlFragment.getSupports()); + SqlId result = parseSqlId(sqlPath, sqlFragment.getKey(), sqlFragment.getSupports()); // 如 fileId=user.manage, fragmentId=user.resource.query, supports=mysql // fragmentId和supports都有可能为空 // sqlId=fileId:fragmentId // sqlKey=fileId:fragmentId(supports) String fileId = result.getFileId(); String fragmentId = result.getFragmentId(); + Integer priority = result.getPriority(); String supports = result.getSupports() != null ? result.getSupports() : "*"; String sqlRelativePath = sqlFile.getRelativePath(); if (fragmentId == null) { // 只有文件名, 没有SqlId // sqlId = fileId; sqlLocation = sqlRelativePath IReader reader = new SimpleReader(sqlRelativePath, sqlFragment.getContent()); String cacheKey = RandomTools.generateUuid(); - cacheBox.register(cacheKey, new TmplData(fileId, null, supports, sqlFile, reader)); + cacheBox.register(cacheKey, new TmplData(fileId, null, supports, priority, sqlFile, reader)); } else { String sqlId = fileId + ':' + fragmentId; String sqlLocation = fragmentId + '@' + sqlRelativePath + ':' + sqlFragment.getLine(); @@ -158,7 +161,7 @@ class SqlFragmentParser { sqlAlias = fragmentId; } String cacheKey = RandomTools.generateUuid(); - cacheBox.register(cacheKey, new TmplData(sqlId, sqlAlias, supports, sqlFile, reader)); + cacheBox.register(cacheKey, new TmplData(sqlId, sqlAlias, supports, priority, sqlFile, reader)); } } @@ -200,13 +203,14 @@ class SqlFragmentParser { } String fragmentId = result.getFragmentId(); String supports = result.getSupports() != null ? result.getSupports() : "*"; + Integer priority = result.getPriority(); Map attrs = new LinkedHashMap<>(); attrs.put("namespace", namespace); attrs.put("commandType", commandType); if (fragmentId == null) { // 只有文件名, 没有SqlId // sqlId = fileId; sqlLocation = sqlRelativePath IMetaData data = root.wrapAsRoot(fileId, sqlRelativePath, node, commonImports, attrs); - container.add(new ParsedFragment(fileId, null, supports, sqlFile, data)); + container.add(new ParsedFragment(fileId, null, supports, priority, sqlFile, data)); } else { String sqlId = fileId + ':' + fragmentId; String sqlLocation = fragmentId + '@' + sqlRelativePath + ':' + node.getRow(); @@ -215,7 +219,7 @@ class SqlFragmentParser { sqlAlias = fragmentId; } IMetaData data = root.wrapAsRoot(sqlId, sqlLocation, node, commonImports, attrs); - container.add(new ParsedFragment(sqlId, sqlAlias, supports, sqlFile, data)); + container.add(new ParsedFragment(sqlId, sqlAlias, supports, priority, sqlFile, data)); } } } @@ -285,11 +289,13 @@ class SqlFragmentParser { private final String fileId; private final String fragmentId; private final String supports; + private final Integer priority; - public SqlId(String fileId, String fragmentId, String supports) { + public SqlId(String fileId, String fragmentId, String supports, Integer priority) { this.fileId = fileId; this.fragmentId = fragmentId; this.supports = supports; + this.priority = priority; } public String getFileId() { @@ -303,6 +309,10 @@ class SqlFragmentParser { public String getSupports() { return supports; } + + public Integer getPriority() { + return priority; + } } private SqlId parseSqlId(String sqlPath, String fragmentId, LineContent supports) { @@ -321,23 +331,37 @@ class SqlFragmentParser { } if (fragmentId == null) { String realSupports = resolveSupports(fileId, null, fileDbType, null, supports); - return new SqlId(fileId, null, realSupports); + return new SqlId(fileId, null, realSupports, null); } - // 如 user.resource.query 或 user.resource.query:* + // 冒号后面, 字符串是数据库类型, 数字是优先级 + // 如 user.resource.query 或 user.resource.query:* 或 user.resource.query:1000 // 或 user.resource.query:mysql 或 user.resource.query:mysql.8 + // 或 user.resource.query:mysql:1000 或 user.resource.query:mysql.8:1000 String originalId = fragmentId; String fragmentDbType = null; + Integer priority = null; int fragmentColonIndex = fragmentId.indexOf(':'); if (fragmentColonIndex >= 0) { - // user.resource.query:mysql 或 user.resource.query:* - fragmentId = originalId.substring(0, fragmentColonIndex).trim(); - fragmentDbType = originalId.substring(fragmentColonIndex + 1).trim(); + List parts = StringTools.splits(originalId, ':'); + fragmentId = parts.get(0); + for (int i = 1; i < parts.size(); i++) { + String part = parts.get(i); + if (StringTools.isDigit(part)) { + if (priority == null) { + priority = ConvertTools.toInteger(part); + } + } else if (part.length() > 0) { + if (fragmentDbType == null) { + fragmentDbType = part; + } + } + } } // fileId=user.manage, fragmentId=user.resource.query, fragmentDbType=mysql String realSupports = resolveSupports(fileId, fragmentId, fileDbType, fragmentDbType, supports); - return new SqlId(fileId, fragmentId, realSupports); + return new SqlId(fileId, fragmentId, realSupports, priority); } /** 取值顺序: supports/fragmentDbType/fileDbType; 如果有fragmentDbType, 将会从配置文件中读取supports作为追加内容 **/ @@ -713,18 +737,20 @@ class SqlFragmentParser { protected static class SqlFragment extends LineContent { - private final String id; + /** SQL标识, id:supports:priority **/ + private final String key; /** 版本支持声明语句 **/ private final LineContent supports; - public SqlFragment(String id, int line, LineContent supports, String content) { + public SqlFragment(String key, int line, LineContent supports, String content) { super(line, content); - this.id = id; + this.key = key; this.supports = supports; } - public String getId() { - return id; + /** SQL标识, id:supports:priority **/ + public String getKey() { + return key; } public LineContent getSupports() { @@ -737,14 +763,16 @@ class SqlFragmentParser { private final String sqlId; private final String sqlAlias; private final String supports; + private final Integer priority; private final SqlFile sqlFile; private final IReader reader; - public TmplData(String sqlId, String sqlAlias, String supports, SqlFile sqlFile, IReader reader) { + public TmplData(String sqlId, String sqlAlias, String supports, Integer priority, SqlFile sqlFile, IReader reader) { super(); this.sqlId = sqlId; this.sqlAlias = sqlAlias; this.supports = supports; + this.priority = priority; this.sqlFile = sqlFile; this.reader = reader; } @@ -765,6 +793,10 @@ class SqlFragmentParser { return supports; } + public Integer getPriority() { + return priority; + } + public IReader getReader() { return reader; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/ConfigValueTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/ConfigValueTag.java new file mode 100644 index 0000000000000000000000000000000000000000..f982ac41e214de3c18faaf0f0a4e9096359307cc --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/ConfigValueTag.java @@ -0,0 +1,49 @@ +package com.gitee.qdbp.jdbc.tags; + +import java.io.IOException; +import com.gitee.qdbp.staticize.exception.TagException; +import com.gitee.qdbp.staticize.tags.base.BaseTag; +import com.gitee.qdbp.staticize.tags.base.NextStep; + +/** + * 读取配置值的标签 + * + * @author zhaohuihua + * @version 20210914 + */ +public class ConfigValueTag extends BaseTag { + + /** 配置项KEY **/ + private String key; + /** 保存配置值的变量名 **/ + private String var; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getVar() { + return var; + } + + public void setVar(String var) { + this.var = var; + } + + @Override + public NextStep doHandle() throws TagException, IOException { + // ${db.config.get('recursive.keyword')} + Object value = this.getStackValue("db.config.get('" + key + "')", Object.class); + if (var == null) { + print(value); + return NextStep.SKIP_BODY; + } else { + this.addStackValue(var, value); + return NextStep.EVAL_BODY; + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/IncludeTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/IncludeTag.java index 7c3fb9e5667f526f63b066e0c2cf3652b4d1d063..2fd747e58bdc9f08cacf2b63282d33df633d943e 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/IncludeTag.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/IncludeTag.java @@ -1,31 +1,81 @@ package com.gitee.qdbp.jdbc.tags; -import com.gitee.qdbp.staticize.annotation.DynamicAttrSupport; +import java.io.IOException; +import com.gitee.qdbp.able.enums.WrapMode; +import com.gitee.qdbp.jdbc.api.SqlBoot; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.staticize.annotation.OutputWrapper; +import com.gitee.qdbp.staticize.common.IWriter; import com.gitee.qdbp.staticize.exception.TagException; -import com.gitee.qdbp.staticize.tags.base.BaseTag; import com.gitee.qdbp.staticize.tags.base.NextStep; /** * Include标签, 支持动态参数 - * + * * @author zhaohuihua * @version 140722 * @since 3.2.0 */ -@DynamicAttrSupport("param") -public class IncludeTag extends BaseTag { +@OutputWrapper +public class IncludeTag extends SqlCachingTag { - public IncludeTag() { - super(); + private final SqlBoot sqlBoot; + private String refid; + private String prefix; + private String suffix; + private String prefixOverrides; + private String suffixOverrides; + /** 是否添加括号 **/ + private WrapMode brackets; + + public IncludeTag(SqlBoot sqlBoot) { + super(sqlBoot); + this.sqlBoot = sqlBoot; + } + + public void setRefid(String refid) { + this.refid = refid; + } + + protected String getPrefix() { + return prefix; + } + + protected void setPrefix(String prefix) { + this.prefix = prefix; } - /** - * 获取src字段名 - * - * @return src字段名 - */ - public String getSrcField() { - return "src"; + protected String getSuffix() { + return suffix; + } + + protected void setSuffix(String suffix) { + this.suffix = suffix; + } + + protected String getPrefixOverrides() { + return prefixOverrides; + } + + protected void setPrefixOverrides(String prefixOverrides) { + this.prefixOverrides = prefixOverrides; + } + + protected String getSuffixOverrides() { + return suffixOverrides; + } + + protected void setSuffixOverrides(String suffixOverrides) { + this.suffixOverrides = suffixOverrides; + } + + protected WrapMode getBrackets() { + return brackets; + } + + protected void setBrackets(String brackets) { + this.brackets = WrapMode.of(brackets, "brackets value"); } /** {@inheritDoc} **/ @@ -33,4 +83,22 @@ public class IncludeTag extends BaseTag { public NextStep doHandle() throws TagException { return NextStep.EVAL_BODY; } + + @Override + public final void doEnded(SqlBuffer buffer, IWriter writer) throws TagException, IOException { + // 标签内不能包含SQL, 只能有标签, 因此用不到buffer + + SqlBuffer sql = sqlBoot.sqlContainer().generate(refid, context().stack()); + if (sql.isBlank()) { + return; + } + + SqlTools.wrap(sql, brackets, prefix, prefixOverrides, suffix, suffixOverrides); + String leadingBlank = clearLeadingBlank(); + if (leadingBlank != null) { + sql.shortcut().pd(leadingBlank); + } + + writer.write(sql); + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/OrderByTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/OrderByTag.java index 1f3e620787280757749629cb4d2d62ff01781f5b..555dee903f38d06214b7916e4ffd2ffda4e74e7f 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/OrderByTag.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/OrderByTag.java @@ -27,8 +27,13 @@ import com.gitee.qdbp.tools.utils.VerifyTools; */ public class OrderByTag extends BaseTag { + protected final SqlBoot sqlBoot; private Object value; + public OrderByTag(SqlBoot sqlBoot) { + this.sqlBoot = sqlBoot; + } + public Object getValue() { return value; } @@ -42,7 +47,6 @@ public class OrderByTag extends BaseTag { if (VerifyTools.isBlank(value)) { return NextStep.SKIP_BODY; } - SqlBoot sqlBoot = this.getStackValue("db.sqlBoot", SqlBoot.class); SqlBuffer sql; if (value instanceof String) { ColumnNameResolver resolver = new ColumnNamingConverter(sqlBoot.plugins()); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/PropertyTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/PropertyTag.java new file mode 100644 index 0000000000000000000000000000000000000000..32e135d7f837e04d31ae7232aa6cd8e8246dc6ca --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/PropertyTag.java @@ -0,0 +1,32 @@ +package com.gitee.qdbp.jdbc.tags; + +import com.gitee.qdbp.staticize.tags.core.SetVariableBase; + +/** + * PropertyTag + * + * @author zhaohuihua + * @version 20210911 + */ +public class PropertyTag extends SetVariableBase { + + /** 保存变量值的变量名 **/ + public String getName() { + return super.getVar(); + } + + /** 保存变量值的变量名 **/ + public void setName(String name) { + super.setVar(name); + } + + /** 获取变量值的表达式 **/ + public Object getValue() { + return super.getValue(); + } + + /** 获取变量值的表达式 **/ + public void setValue(Object value) { + super.setValue(value); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/xml/qdbc.mapper.1.0.dtd b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/xml/qdbc.mapper.1.0.dtd index ebbb5a71bf78ab40e0e19c344a272dc10db3f9a9..3a7d9801fcf6aea3232a4779f197da98a028e3cd 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/xml/qdbc.mapper.1.0.dtd +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/xml/qdbc.mapper.1.0.dtd @@ -17,7 +17,7 @@ --> - + @@ -32,7 +32,7 @@ name CDATA #REQUIRED value CDATA #IMPLIED > - + - + - + - + - + + + + + + + - + - + - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + @@ -244,14 +261,14 @@ name CDATA #REQUIRED value CDATA #IMPLIED > - + - + - + - - + + - + - + - + - + - + - + @DateTools = com.gitee.qdbp.tools.utils.DateTools diff --git a/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive2.xml b/jdbc-core/src/main/resources/settings/sqls/GlobalRecursive.xml similarity index 84% rename from jdbc-test/src/main/resources/settings/sqls/GlobalRecursive2.xml rename to jdbc-core/src/main/resources/settings/sqls/GlobalRecursive.xml index baee47279468b6eb8f90e1fb159c6100bb5ee876..52eb8d7848056d60518c293e07af53872cb2bbe0 100644 --- a/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive2.xml +++ b/jdbc-core/src/main/resources/settings/sqls/GlobalRecursive.xml @@ -1,6 +1,6 @@ - + - SELECT ${selectColumns} FROM ( + SELECT DISTINCT ${selectColumns} FROM ( SELECT T.ID__LIST FROM ( - SELECT @PIDS AS ID__LIST, ( - SELECT @PIDS := GROUP_CONCAT( ${codeColumn} ) + SELECT @IDS AS ID__LIST, ( + SELECT @IDS := GROUP_CONCAT( ${codeColumn} ) FROM ${tableName} - WHERE FIND_IN_SET( ${parentColumn}, @PIDS ) + WHERE FIND_IN_SET( ${parentColumn}, @IDS ) #{filterWhere} ) AS PID - FROM ${tableName}, ( SELECT @PIDS := #{@ConvertTools.joinToString(startCodes)} ) T + FROM ${tableName}, ( SELECT @IDS := #{@ConvertTools.joinToString(startCodes)} ) T ) T WHERE T.ID__LIST IS NOT NULL ) T1, ${tableName} T2 @@ -55,14 +55,14 @@ @@ -85,7 +85,7 @@ - SELECT ${selectColumns} FROM ( + SELECT DISTINCT ${selectColumns} FROM ( SELECT T.ID__LIST FROM ( - SELECT @ORGIDS AS ID__LIST, ( - SELECT @ORGIDS := GROUP_CONCAT(${parentColumn}) + SELECT @IDS AS ID__LIST, ( + SELECT @IDS := GROUP_CONCAT(${parentColumn}) FROM ${tableName} - WHERE FIND_IN_SET( ${codeColumn}, @ORGIDS ) + WHERE FIND_IN_SET( ${codeColumn}, @IDS ) #{filterWhere} - ) AS PID - FROM ${tableName}, ( SELECT @ORGIDS := '03105,02105' ) T + ) AS PID + FROM ${tableName}, ( SELECT @IDS := #{@ConvertTools.joinToString(startCodes)} ) T ) T WHERE T.ID__LIST IS NOT NULL ) T1, ${tableName} T2 @@ -125,14 +125,14 @@ diff --git a/jdbc-jdk8/pom.xml b/jdbc-jdk8/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..58cf854ea9487843da1a34c82e97384cf55d3fb2 --- /dev/null +++ b/jdbc-jdk8/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + + com.gitee.qdbp + qdbp-parent + 5.0.1 + + + qdbp-jdbc-jdk8 + 4.1.8 + jar + ${project.artifactId} + https://gitee.com/qdbp/qdbp-jdbc/ + qdbp jdbc core project + + + zhaohuihua + https://gitee.com/qdbp/qdbp-jdbc/ + + + + 1.8 + + + + + + com.gitee.qdbp + qdbp-jdbc-core + ${project.version} + + + + + diff --git a/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateConverter.java b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..95a2d43b5c9d147d6d7432d54efda435bdc8131d --- /dev/null +++ b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateConverter.java @@ -0,0 +1,93 @@ +package com.gitee.qdbp.jdbc.support.convert; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.converter.ConverterRegistry; + +/** + * 日期转换类 + * + * @author zhaohuihua + * @version 20211031 + */ +public class DateConverter { + + /** LocalDateTime转换为Date **/ + public static Date convertToDate(LocalDateTime source) { + ZoneId zoneId = ZoneId.systemDefault(); + // Combines this date-time with a time-zone to create a ZonedDateTime. + ZonedDateTime zdt = source.atZone(zoneId); + return Date.from(zdt.toInstant()); + } + + /** LocalDate转换为Date **/ + public static Date convertToDate(LocalDate source) { + ZoneId zoneId = ZoneId.systemDefault(); + // Combines this date-time with a time-zone to create a ZonedDateTime. + ZonedDateTime zdt = source.atStartOfDay().atZone(zoneId); + return Date.from(zdt.toInstant()); + } + + /** LocalTime转换为Date **/ + public static Date convertToDate(LocalTime source) { + LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(), source); + ZoneId zoneId = ZoneId.systemDefault(); + // Combines this date-time with a time-zone to create a ZonedDateTime. + ZonedDateTime zdt = localDateTime.atZone(zoneId); + return Date.from(zdt.toInstant()); + } + + /** Date转换为LocalDateTime **/ + public static LocalDateTime convertToLocalDateTime(Date source) { + Instant instant = source.toInstant(); + ZoneId zone = ZoneId.systemDefault(); + return LocalDateTime.ofInstant(instant, zone); + } + + /** Date转换为LocalDate **/ + public static LocalDate convertToLocalDate(Date source) { + Instant instant = source.toInstant(); + ZoneId zone = ZoneId.systemDefault(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone); + return localDateTime.toLocalDate(); + } + + /** Date转换为LocalTime **/ + public static LocalTime convertToLocalTime(Date source) { + Instant instant = source.toInstant(); + ZoneId zone = ZoneId.systemDefault(); + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone); + return localDateTime.toLocalTime(); + } + + /** 注册日期转换处理类到ConverterRegistry **/ + public static void registerConverters(ConversionService conversionService) { + if (conversionService instanceof ConverterRegistry) { + ConverterRegistry registry = (ConverterRegistry) conversionService; + if (!conversionService.canConvert(Date.class, LocalDateTime.class)) { + registry.addConverter(new DateToLocalDateTimeConverter()); + } + if (!conversionService.canConvert(Date.class, LocalDate.class)) { + registry.addConverter(new DateToLocalDateConverter()); + } + if (!conversionService.canConvert(Date.class, LocalTime.class)) { + registry.addConverter(new DateToLocalTimeConverter()); + } + if (!conversionService.canConvert(LocalDateTime.class, Date.class)) { + registry.addConverter(new LocalDateTimeToDateConverter()); + } + if (!conversionService.canConvert(LocalDate.class, Date.class)) { + registry.addConverter(new LocalDateToDateConverter()); + } + if (!conversionService.canConvert(LocalTime.class, Date.class)) { + registry.addConverter(new LocalTimeToDateConverter()); + } + } + } +} diff --git a/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalDateConverter.java b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalDateConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..22da0719b7debabbaa5f4e158825308a4ac43757 --- /dev/null +++ b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalDateConverter.java @@ -0,0 +1,21 @@ +package com.gitee.qdbp.jdbc.support.convert; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.springframework.core.convert.converter.Converter; + +/** + * Date转换为LocalDate + * + * @author zhaohuihua + * @version 20211031 + */ +public class DateToLocalDateConverter implements Converter { + + @Override + public LocalDate convert(Date source) { + return DateConverter.convertToLocalDate(source); + } +} diff --git a/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalDateTimeConverter.java b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalDateTimeConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..7a3bbe1b5608d6c6a0f8a385b40cb32ca7c6fb57 --- /dev/null +++ b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalDateTimeConverter.java @@ -0,0 +1,20 @@ +package com.gitee.qdbp.jdbc.support.convert; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Date; +import org.springframework.core.convert.converter.Converter; + +/** + * Date转换为LocalDateTime + * + * @author zhaohuihua + * @version 20211031 + */ +public class DateToLocalDateTimeConverter implements Converter { + + @Override + public LocalDateTime convert(Date source) { + return DateConverter.convertToLocalDateTime(source); + } +} diff --git a/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalTimeConverter.java b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalTimeConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..d520b9e7427c34192fcdfa4f7941575f8a5f0b04 --- /dev/null +++ b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/DateToLocalTimeConverter.java @@ -0,0 +1,20 @@ +package com.gitee.qdbp.jdbc.support.convert; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.Date; +import org.springframework.core.convert.converter.Converter; + +/** + * Date转换为LocalTime + * + * @author zhaohuihua + * @version 20211031 + */ +public class DateToLocalTimeConverter implements Converter { + + @Override + public LocalTime convert(Date source) { + return DateConverter.convertToLocalTime(source); + } +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java similarity index 67% rename from jdbc-test/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java rename to jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java index a94f5883f583e1f7b45743703810ee323d47f22c..97592ef00d14c253581ef47ba1989a4c5e93cd9e 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java +++ b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java @@ -16,9 +16,6 @@ public class LocalDateTimeToDateConverter implements Converter { + + @Override + public Date convert(LocalDate source) { + return DateConverter.convertToDate(source); + } +} diff --git a/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalTimeToDateConverter.java b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalTimeToDateConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..a271851b25c81773c1cc9a1257c57b39519c9420 --- /dev/null +++ b/jdbc-jdk8/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalTimeToDateConverter.java @@ -0,0 +1,23 @@ +package com.gitee.qdbp.jdbc.support.convert; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.springframework.core.convert.converter.Converter; + +/** + * LocalTime转换为Date + * + * @author zhaohuihua + * @version 20211031 + */ +public class LocalTimeToDateConverter implements Converter { + + @Override + public Date convert(LocalTime source) { + return DateConverter.convertToDate(source); + } +} diff --git a/jdbc-spring/pom.xml b/jdbc-spring/pom.xml index 36001ed96cc536cad642bffd0dff1d671f230f27..a00b37b8d48a984fe2382dc9421f952e4ed7253a 100644 --- a/jdbc-spring/pom.xml +++ b/jdbc-spring/pom.xml @@ -9,7 +9,7 @@ qdbp-jdbc-spring - 4.1.5 + 4.1.8 jar ${project.artifactId} https://gitee.com/qdbp/qdbp-jdbc/ diff --git a/jdbc-test/pom.xml b/jdbc-test/pom.xml index 2bd058ece1c204fa6d8dc5d0552425735f77666f..a3dea02d0af3bf8fd932b2d06732ba5ebf6d6328 100644 --- a/jdbc-test/pom.xml +++ b/jdbc-test/pom.xml @@ -9,7 +9,7 @@ qdbp-jdbc-test - 4.1.5 + 4.1.8 jar ${project.artifactId} https://gitee.com/qdbp/qdbp-jdbc/ @@ -34,6 +34,10 @@ mysql mysql + + mysql5 + mysql5 + oracle oracle @@ -51,10 +55,15 @@ qdbp-jdbc-spring ${project.version}
+ + com.gitee.qdbp + qdbp-jdbc-jdk8 + ${project.version} + com.gitee.qdbp qdbp-tools - 5.4.5 + 5.4.6 @@ -236,6 +245,7 @@ org.apache.maven.plugins maven-surefire-plugin + 2.12.4 src/test/resources/testng.xml diff --git a/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt b/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt index 1b4ca7ab40a6a4197ba45d7de3f2ee151948f09a..863260cf7e025026ddec6b3ec728f0107b5ebf4c 100644 --- a/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt +++ b/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt @@ -10,10 +10,14 @@ when = com.gitee.qdbp.staticize.tags.core.WhenTag otherwise = com.gitee.qdbp.staticize.tags.core.OtherwiseTag comment = com.gitee.qdbp.staticize.tags.core.CommentTag define = com.gitee.qdbp.staticize.tags.core.SetVariableTag +out = com.gitee.qdbp.staticize.tags.core.OutTag +property = com.gitee.qdbp.staticize.tags.core.SetVariableTag +config = com.gitee.qdbp.jdbc.tags.ConfigValueTag foreach = com.gitee.qdbp.jdbc.tags.SqlForEachTag include = com.gitee.qdbp.jdbc.tags.IncludeTag sql:include = com.gitee.qdbp.jdbc.tags.IncludeTag +property = com.gitee.qdbp.jdbc.tags.PropertyTag append = com.gitee.qdbp.jdbc.tags.AppendTag sql-append = com.gitee.qdbp.jdbc.tags.AppendTag sql:append = com.gitee.qdbp.jdbc.tags.AppendTag @@ -48,7 +52,7 @@ fmt-date = com.gitee.qdbp.staticize.tags.core.DateFormatTag fmt:date = com.gitee.qdbp.staticize.tags.core.DateFormatTag # 指定值栈包装类, 用于实现在业务侧自定义值栈自身的函数 -@StackWrapper = com.gitee.qdbp.staticize.common.StackWrapper +@StackWrapper = com.gitee.qdbp.able.model.reusable.StackWrapper # 全局静态类, 最常用的在这里指定, 省去 @DateTools = com.gitee.qdbp.tools.utils.DateTools diff --git a/jdbc-core/src/main/resources/settings/sqls/GlobalRecursive.sql b/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive2.sql similarity index 92% rename from jdbc-core/src/main/resources/settings/sqls/GlobalRecursive.sql rename to jdbc-test/src/main/resources/settings/sqls/GlobalRecursive2.sql index 582b0c1997672ca67dbafc3962274ae1373b61a0..8b5b559b44846e20cd31f20f0c22f85baf9bf7dc 100644 --- a/jdbc-core/src/main/resources/settings/sqls/GlobalRecursive.sql +++ b/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive2.sql @@ -9,7 +9,7 @@ -- <> 递归查询所有子节点, 标准递归语法 -- mysql.8,mariadb.10.2.2,postgresql,db2,h2,sqlserver,sqlite.3.8.3 -${db.config.get('recursive.keyword')} recursive_temp_table(temp_parent) AS ( + recursive_temp_table(temp_parent) AS ( SELECT ${codeColumn} temp_parent FROM ${tableName} WHERE #{filterWhere} @@ -27,7 +27,7 @@ WHERE ${codeColumn} IN ( SELECT temp_parent FROM recursive_temp_table ) -- <> 递归查询所有子节点, mysql专用递归语法 /*-- 可能需要调整GROUP_CONCAT的长度: group_concat_max_len = 40960 --*/ -SELECT ${selectColumns} FROM ( +SELECT DISTINCT ${selectColumns} FROM ( SELECT T.ID__LIST FROM ( SELECT @IDS AS ID__LIST, ( SELECT @IDS := GROUP_CONCAT( ${codeColumn} ) @@ -45,14 +45,14 @@ WHERE FIND_IN_SET( T2.${codeColumn}, T1.ID__LIST ) -- <> 递归查询所有子节点, oracle专用递归语法 -SELECT ${selectColumns} FROM ( +SELECT DISTINCT ${selectColumns} FROM ( SELECT * FROM ${tableName} START WITH CONNECT BY PRIOR ${codeColumn}=${parentColumn} #{filterWhere} + #{orderBy} ) #{searchWhere} -#{orderBy} -- <> 递归查询所有子节点, 使用存储过程实现的递归查询 @@ -71,7 +71,7 @@ CALL RECURSIVE_LIST_CHILDREN_QUERY ( -- <> 递归查询所有父节点, 标准递归语法 -- mysql.8,mariadb.10.2.2,postgresql,db2,h2,sqlserver,sqlite.3.8.3 -${db.config.get('recursive.keyword')} recursive_temp_table(temp_parent) AS ( + recursive_temp_table(temp_parent) AS ( SELECT ${codeColumn} temp_parent FROM ${tableName} WHERE #{filterWhere} @@ -89,7 +89,7 @@ WHERE ${codeColumn} IN ( SELECT temp_parent FROM recursive_temp_table ) -- <> 递归查询所有父节点, mysql专用递归语法 /*-- 可能需要调整GROUP_CONCAT的长度: group_concat_max_len = 40960 --*/ -SELECT ${selectColumns} FROM ( +SELECT DISTINCT ${selectColumns} FROM ( SELECT T.ID__LIST FROM ( SELECT @IDS AS ID__LIST, ( SELECT @IDS := GROUP_CONCAT(${parentColumn}) @@ -107,14 +107,14 @@ WHERE FIND_IN_SET( T2.${codeColumn}, T1.ID__LIST ) -- <> 递归查询所有父节点, oracle专用递归语法 -SELECT ${selectColumns} FROM ( +SELECT DISTINCT ${selectColumns} FROM ( SELECT * FROM ${tableName} START WITH CONNECT BY PRIOR ${parentColumn}=${codeColumn} #{filterWhere} + #{orderBy} ) #{searchWhere} -#{orderBy} -- <> 递归查询所有父节点, 使用存储过程实现的递归查询 diff --git a/jdbc-test/src/main/resources/settings/sqls/SqlIncludeTagTest.xml b/jdbc-test/src/main/resources/settings/sqls/SqlIncludeTagTest.xml new file mode 100644 index 0000000000000000000000000000000000000000..102721610f207d4b088b95af8c55c79247b288ef --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/SqlIncludeTagTest.xml @@ -0,0 +1,29 @@ + + + + + + + + diff --git a/jdbc-test/src/main/resources/settings/sqls/select.001.sql b/jdbc-test/src/main/resources/settings/sqls/select.001.sql index 0afd1bfc5f6d97a92df06a5d49744ef7ba82ea8f..df62b4e91aea8296eb7fd48f5e8361d978f508d2 100644 --- a/jdbc-test/src/main/resources/settings/sqls/select.001.sql +++ b/jdbc-test/src/main/resources/settings/sqls/select.001.sql @@ -16,3 +16,11 @@ AND r.DATA_STATE=1 WHERE ur.DATA_STATE=1 AND ur.USER_ID IN ( #{userIds} ) +-- << user.roles.query8 >> 查询用户有哪些角色 +SELECT * FROM TEST_USER_ROLE_REF ur +INNER JOIN TEST_ROLE_CORE_INFO r +ON ur.ROLE_ID=r.ID +AND r.DATA_STATE=1 +WHERE ur.DATA_STATE=1 +AND ur.USER_ID IN ( #{userIds} ) + diff --git a/jdbc-test/src/main/resources/settings/sqls/select.002.sql b/jdbc-test/src/main/resources/settings/sqls/select.002.sql index 4ef1440824daea89940d64984891948510dc5424..3f54953e22353d23fe7c0a1d18467d2232ce46d2 100644 --- a/jdbc-test/src/main/resources/settings/sqls/select.002.sql +++ b/jdbc-test/src/main/resources/settings/sqls/select.002.sql @@ -15,3 +15,11 @@ ON ur.ROLE_ID=r.ID AND r.DATA_STATE=1 WHERE ur.DATA_STATE=1 AND ur.USER_ID IN ( #{userIds} ) + +-- << user.roles.query8 >> 查询用户有哪些角色 +SELECT * FROM TEST_USER_ROLE_REF ur +INNER JOIN TEST_ROLE_CORE_INFO r +ON ur.ROLE_ID=r.ID +AND r.DATA_STATE=1 +WHERE ur.DATA_STATE=1 +AND ur.USER_ID IN ( #{userIds} ) diff --git a/jdbc-test/src/main/resources/settings/sqls/select.003.sql b/jdbc-test/src/main/resources/settings/sqls/select.003.sql index 079d30e748e6a69b4a2d4264471648feff8f4214..57cd82ba22e563c550baf915d74c5f899155f978 100644 --- a/jdbc-test/src/main/resources/settings/sqls/select.003.sql +++ b/jdbc-test/src/main/resources/settings/sqls/select.003.sql @@ -42,3 +42,13 @@ ON ur.ROLE_ID=r.ID AND r.DATA_STATE=1 WHERE ur.DATA_STATE=1 AND ur.USER_ID IN ( #{userIds} ) + + +-- << user.roles.query8:200 >> 查询用户有哪些角色 +SELECT * FROM TEST_USER_ROLE_REF ur +INNER JOIN TEST_ROLE_CORE_INFO r +ON ur.ROLE_ID=r.ID +AND r.DATA_STATE=1 +WHERE ur.DATA_STATE=1 +AND ur.USER_ID IN ( #{userIds} ) +AND r.TENANT_CODE='000000' diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/ConvertErrorTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/ConvertErrorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8b086d6412d18f0d4a6fa7f63516e007aafe43c7 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/ConvertErrorTest.java @@ -0,0 +1,376 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.util.Date; +import javax.persistence.Column; +import javax.persistence.Table; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.test.base.CommEntity; +import com.gitee.qdbp.jdbc.test.enums.AccountType; +import com.gitee.qdbp.jdbc.test.enums.Gender; +import com.gitee.qdbp.jdbc.test.enums.UserSource; +import com.gitee.qdbp.jdbc.test.enums.UserState; +import com.gitee.qdbp.jdbc.test.enums.UserType; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; +import com.gitee.qdbp.jdbc.test.model.UserCoreOptions; +import com.gitee.qdbp.tools.utils.DateTools; +import com.gitee.qdbp.tools.utils.JsonTools; + +/** + * ConvertErrorTest + * + * @author zhaohuihua + * @version 20211004 + */ +@Test +@ContextConfiguration(locations = {"classpath:settings/spring/spring.xml"}) +public class ConvertErrorTest extends AbstractTestNGSpringContextTests { + + private static final Logger log = LoggerFactory.getLogger(com.gitee.qdbp.jdbc.test.biz.SimpleCrudDaoTest.class); + + private static final String TACD = "ConvertError"; + @Autowired + private QdbcBoot qdbcBoot; + + @Test(priority = 1001) + public void testUserEntityDelete() { + DbWhere where = new DbWhere(); + where.on("tenantCode").equals(TACD); + CrudDao dao = qdbcBoot.crudDao(SysUserEntity.class); + dao.physicalDelete(where); + } + + @Test(priority = 1003) + public void testUserEntityCreate() { + CrudDao dao = qdbcBoot.crudDao(SysUserEntity.class); + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(TACD); + entity.setUserCode("super"); + entity.setSuperman(true); + entity.setDeptId("0"); + entity.setCity("beijing"); + entity.setGender(Gender.UNKNOWN); + entity.setBirthday(DateTools.parse("2018-02-03 15:25:35.456")); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + entity.setCreateTime(DateTools.parse("2018-05-15 20:30:40.599")); + + entity.setId("ConvertError001"); + entity.setUserType(UserType.ADMIN); + dao.insert(entity); + } + + @Test(priority = 2011) + public void testUserEntityQuery() { + try { + TestUserEntity user = qdbcBoot.crudStream(TestUserEntity.class) + .where("tenantCode").equals(TACD) + .and("id").equals("ConvertError001") + .end() + .find(); + log.debug("UserEntityQueryResult: {}", JsonTools.toLogString(user)); + } catch (ServiceException e) { + log.error("UserEntityQueryError", e); + Assert.assertEquals(e.getCode(), DbErrorCode.DB_COLUMN_VALUE_ERROR.getCode()); + Assert.assertTrue(e.getDetails().contains("TestUserEntity.city")); + } + } + + @Table(name = "TEST_USER_CORE_INFO") + public static class TestUserEntity extends CommEntity { + + /** 版本序列号 **/ + private static final long serialVersionUID = 1L; + + /** 租户编号 **/ + @Column + private String tenantCode; + /** 用户类型 **/ + @Column + private AccountType userType; + /** 部门ID **/ + @Column + private String deptId; + /** 账号/工号 **/ + @Column + private String userCode; + /** 登录用户名 **/ + @Column + private String userName; + /** 昵称 **/ + @Column + private String nickName; + /** 真实姓名 **/ + @Column + private String realName; + /** 电话 **/ + @Column + private String phone; + /** 邮箱 **/ + @Column + private String email; + /** 性别(0.未知|1.男|2.女) **/ + @Column + private Gender gender; + /** 头像 **/ + @Column + private String photo; + /** 生日 **/ + @Column + private Date birthday; + /** 城市 **/ + @Column + private Date city; + /** 身份证 **/ + @Column + private String identity; + /** 密码 **/ + @Column + private String password; + /** 是否为超级用户 **/ + @Column + private Boolean superman; + /** 选项 **/ + @Column + private UserCoreOptions options; + /** 状态(0.正常|1.锁定|2.待激活|3.注销) **/ + @Column + private UserState userState; + /** 注册来源 **/ + @Column + private UserSource userSource; + + /** 获取租户编号 **/ + public String getTenantCode() { + return tenantCode; + } + + /** 设置租户编号 **/ + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } + + /** 获取用户类型 **/ + public AccountType getUserType() { + return userType; + } + + /** 设置用户类型 **/ + public void setUserType(AccountType userType) { + this.userType = userType; + } + + /** 获取部门ID **/ + public String getDeptId() { + return deptId; + } + + /** 设置部门ID **/ + public void setDeptId(String deptId) { + this.deptId = deptId; + } + + /** 获取账号/工号 **/ + public String getUserCode() { + return userCode; + } + + /** 设置账号/工号 **/ + public void setUserCode(String userCode) { + this.userCode = userCode; + } + + /** 获取登录用户名 **/ + public String getUserName() { + return userName; + } + + /** 设置登录用户名 **/ + public void setUserName(String userName) { + this.userName = userName; + } + + /** 获取昵称 **/ + public String getNickName() { + return nickName; + } + + /** 设置昵称 **/ + public void setNickName(String nickName) { + this.nickName = nickName; + } + + /** 获取真实姓名 **/ + public String getRealName() { + return realName; + } + + /** 设置真实姓名 **/ + public void setRealName(String realName) { + this.realName = realName; + } + + /** 获取电话 **/ + public String getPhone() { + return phone; + } + + /** 设置电话 **/ + public void setPhone(String phone) { + this.phone = phone; + } + + /** 获取邮箱 **/ + public String getEmail() { + return email; + } + + /** 设置邮箱 **/ + public void setEmail(String email) { + this.email = email; + } + + /** 获取性别(0.未知|1.男|2.女) **/ + public Gender getGender() { + return gender; + } + + /** 设置性别(0.未知|1.男|2.女) **/ + public void setGender(Gender gender) { + this.gender = gender; + } + + /** 获取头像 **/ + public String getPhoto() { + return photo; + } + + /** 设置头像 **/ + public void setPhoto(String photo) { + this.photo = photo; + } + + /** 获取生日 **/ + public Date getBirthday() { + return birthday; + } + + /** 设置生日 **/ + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + + /** 获取城市 **/ + public Date getCity() { + return city; + } + + /** 设置城市 **/ + public void setCity(Date city) { + this.city = city; + } + + /** 获取身份证 **/ + public String getIdentity() { + return identity; + } + + /** 设置身份证 **/ + public void setIdentity(String identity) { + this.identity = identity; + } + + /** 获取密码 **/ + public String getPassword() { + return password; + } + + /** 设置密码 **/ + public void setPassword(String password) { + this.password = password; + } + + /** 获取是否为超级用户 **/ + public Boolean getSuperman() { + return superman; + } + + /** 设置是否为超级用户 **/ + public void setSuperman(Boolean superman) { + this.superman = superman; + } + + /** 获取状态(0.正常|1.锁定|2.待激活|3.注销) **/ + public UserState getUserState() { + return userState; + } + + /** 设置状态(0.正常|1.锁定|2.待激活|3.注销) **/ + public void setUserState(UserState userState) { + this.userState = userState; + } + + /** 获取选项 **/ + public UserCoreOptions getOptions() { + return options; + } + + /** 获取选项, force=是否强制返回非空选项 **/ + public UserCoreOptions getOptions(boolean force) { + if (options == null && force) { + options = new UserCoreOptions(); + } + return options; + } + + /** 设置选项 **/ + public void setOptions(UserCoreOptions options) { + this.options = options; + } + + /** 获取注册来源 **/ + public UserSource getUserSource() { + return userSource; + } + + /** 设置注册来源 **/ + public void setUserSource(UserSource userSource) { + this.userSource = userSource; + } + + /** 获取用户的显示名称 **/ + public String toDisplayName() { + if (nickName != null && nickName.length() > 0) { + return nickName; + } else if (realName != null && realName.length() > 0) { + return realName; + } else if (phone != null && phone.length() > 0) { + return phone; + } else if (email != null && email.length() > 0) { + return email; + } else if (userCode != null && userCode.length() > 0) { + return userCode; + } else if (userName != null && userName.length() > 0) { + return userName; + } else { + return null; + } + } + + @Override + public String toString() { + String name = toDisplayName(); + return name == null ? getId() : name; + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveFindChildrenTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveFindChildrenTest.java index d2f5539997dd11548f7b566c415fea0ba13bad53..6dc90c95dddf9cbb4346a88f3cfe505aeec712c7 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveFindChildrenTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveFindChildrenTest.java @@ -1,6 +1,7 @@ package com.gitee.qdbp.jdbc.test.biz; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import com.gitee.qdbp.able.jdbc.condition.DbWhere; import com.gitee.qdbp.able.jdbc.ordering.Orderings; @@ -76,4 +77,48 @@ public class RecursiveFindChildrenTest extends AbstractTestNGSpringContextTests // D010203是第3级, 每个3级有4个下级, 每个4级有5个下级 Assert.assertEquals(result.size(), 24, "testListDeptChildren"); } + + @Test + public void testListDeptParentsBean() { + CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); + String startCode = "D0101030403"; + DbWhere filter = new DbWhere().on("tenantCode").equals("depttest").end(); + DbWhere search = new DbWhere().on("deptCode").notEquals(startCode).end(); + Orderings orderings = Orderings.of("parentCode, sortIndex"); + List result = dao.listParents(startCode, "deptCode", "parentCode", filter, search, orderings); + Assert.assertEquals(result.size(), 4, "testListDeptParent"); + } + + @Test + public void testListDeptParentsBean2() { + CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); + List startCode = Arrays.asList("D0101020303", "D0101030403"); + DbWhere filter = new DbWhere().on("tenantCode").equals("depttest").end(); + DbWhere search = new DbWhere().on("deptCode").notIn(startCode).end(); + Orderings orderings = Orderings.of("parentCode, sortIndex"); + List result = dao.listParents(startCode, "deptCode", "parentCode", filter, search, orderings); + Assert.assertEquals(result.size(), 6, "testListDeptParent"); + } + + @Test + public void testListDeptParentCodes() { + CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); + String startCode = "D0101030403"; + DbWhere filter = new DbWhere().on("tenantCode").equals("depttest").end(); + DbWhere search = new DbWhere().on("deptCode").notEquals(startCode).end(); + Orderings orderings = Orderings.of("parentCode, sortIndex"); + List result = dao.listParentCodes(startCode, "deptCode", "parentCode", filter, search, orderings); + Assert.assertEquals(result.size(), 4, "testListDeptParent"); + } + + @Test + public void testListDeptParentCodes2() { + CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); + List startCode = Arrays.asList("D0101020303", "D0101030403"); + DbWhere filter = new DbWhere().on("tenantCode").equals("depttest").end(); + DbWhere search = new DbWhere().on("deptCode").notIn(startCode).end(); + Orderings orderings = Orderings.of("parentCode, sortIndex"); + List result = dao.listParentCodes(startCode, "deptCode", "parentCode", filter, search, orderings); + Assert.assertEquals(result.size(), 6, "testListDeptParent"); + } } diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveQueryTest.java index 7d18401756eb7b4d40e245a404a78c4c0dc682de..94fffe87a7091f3b14a0fab7bdb0e3452218543c 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveQueryTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveQueryTest.java @@ -5,6 +5,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; @@ -20,14 +22,17 @@ import com.gitee.qdbp.tools.excel.json.ExcelToJson; import com.gitee.qdbp.tools.excel.json.ToJsonMetadata; import com.gitee.qdbp.tools.excel.json.ToJsonProperties; import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.AssertTools; import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.PropertyTools; +import com.gitee.qdbp.tools.utils.StringTools; @Test @ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) public class RecursiveQueryTest extends AbstractTestNGSpringContextTests { - private static final String TACD = "DeptRecursive"; + private static final String TACD = "DeptRecursive"; + private static final Logger log = LoggerFactory.getLogger(RecursiveQueryTest.class); @Autowired private QdbcBoot qdbcBoot; private final Map> childrenMaps = new HashMap<>(); @@ -98,6 +103,22 @@ public class RecursiveQueryTest extends AbstractTestNGSpringContextTests { } private void testListChildren(String deptCode) { + testListChildrenCodes(deptCode); + testListChildrenEntities(deptCode); + } + + private void testListChildrenCodes(String deptCode) { + CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); + DbWhere filter = new DbWhere().on("tenantCode").equals(TACD).end(); + DbWhere search = DbWhere.NONE; + Orderings order = Orderings.of("parentCode, sortIndex"); + List children = dao.listChildrenCodes(deptCode, "deptCode", "parentCode", filter, search, order); + int actual = children.size(); + int expected = 1 + countChildren(childrenMaps, deptCode); + Assert.assertEquals(actual, expected, "Children size for '" + deptCode + "'"); + } + + private void testListChildrenEntities(String deptCode) { CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); DbWhere filter = new DbWhere().on("tenantCode").equals(TACD).end(); DbWhere search = DbWhere.NONE; @@ -110,54 +131,75 @@ public class RecursiveQueryTest extends AbstractTestNGSpringContextTests { @Test(priority = 21) public void testListParents100000() { - testListParents("100000", 1); + testListParents("100000", "100000"); } @Test(priority = 22) public void testListParents320000() { - testListParents("320000", 2); + testListParents("320000", "100000,320000"); } @Test(priority = 23) public void testListParents330000() { - testListParents("330000", 2); + testListParents("330000", "100000,330000"); } @Test(priority = 24) public void testListParents320100() { - testListParents("320100", 3); + testListParents("320100", "100000,320000,320100"); } @Test(priority = 25) public void testListParents340100() { - testListParents("340100", 3); + testListParents("340100", "100000,340000,340100"); } @Test(priority = 26) public void testListParents320102() { - testListParents("320102", 4); + testListParents("320102", "100000,320000,320100,320102"); } @Test(priority = 27) public void testListParents330110() { - testListParents("330110", 4); + testListParents("330110", "100000,330000,330100,330110"); + } + + private void testListParents(String deptCode, String expected) { + testListParentCodes(deptCode, expected); + testListParentEntities(deptCode, expected); } - private void testListParents(String deptCode, int expected) { + private void testListParentCodes(String deptCode, String expected) { CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); DbWhere filter = new DbWhere().on("tenantCode").equals(TACD).end(); DbWhere search = DbWhere.NONE; - Orderings order = Orderings.of("parentCode, sortIndex"); - List children = dao.listParents(deptCode, "deptCode", "parentCode", filter, search, order); - for (SysDeptEntity item : children) { + Orderings order = Orderings.NONE; + List codes = dao.listParentCodes(deptCode, "deptCode", "parentCode", filter, search, order); + log.debug("Parent codes of {}: {}", deptCode, ConvertTools.joinToString(codes)); + List expectedCodes = StringTools.splits(expected, ','); + Assert.assertEquals(codes.size(), expectedCodes.size(), "Parents size for '" + deptCode + "'"); + AssertTools.assertDeepEquals(codes, expectedCodes, "Parent codes"); + } + + private void testListParentEntities(String deptCode, String expected) { + CrudDao dao = qdbcBoot.crudDao(SysDeptEntity.class); + DbWhere filter = new DbWhere().on("tenantCode").equals(TACD).end(); + DbWhere search = DbWhere.NONE; + Orderings order = Orderings.NONE; + List result = dao.listParents(deptCode, "deptCode", "parentCode", filter, search, order); + List codes = new ArrayList<>(); + for (SysDeptEntity item : result) { + codes.add(item.getDeptCode()); Assert.assertNotNull(item.getId(), "listParents[result.id]"); Assert.assertNotNull(item.getDeptCode(), "listParents[result.deptCode]"); Assert.assertNotNull(item.getDeptName(), "listParents[result.deptName]"); Assert.assertNotNull(item.getParentCode(), "listParents[result.parentCode]"); Assert.assertNotNull(item.getDataState(), "listParents[result.dataState]"); } - int actual = children.size(); - Assert.assertEquals(actual, expected, "Parents size for '" + deptCode + "'"); + log.debug("Parent codes of {}: {}", deptCode, ConvertTools.joinToString(codes)); + List expectedCodes = StringTools.splits(expected, ','); + Assert.assertEquals(result.size(), expectedCodes.size(), "Parents size for '" + deptCode + "'"); + AssertTools.assertDeepEquals(codes, expectedCodes, "Parent codes"); } // 统计子节点数量, 返回结果不包含自身节点 diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/UserOrConditionTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/UserOrConditionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..588676d13b421d9d1d6d497488d797640cd270f8 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/UserOrConditionTest.java @@ -0,0 +1,105 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.beans.KeyString; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.test.enums.Gender; +import com.gitee.qdbp.jdbc.test.enums.UserSource; +import com.gitee.qdbp.jdbc.test.enums.UserState; +import com.gitee.qdbp.jdbc.test.enums.UserType; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; + +/** + * 复杂的OR查询条件 + * + * @author zhaohuihua + * @version 20210921 + */ +@Test +@ContextConfiguration(locations = {"classpath:settings/spring/spring.xml"}) +public class UserOrConditionTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "UserOrCondition"; + @Autowired + private QdbcBoot qdbcBoot; + + @Test(priority = 1001) + public void testUserEntityDelete() { + DbWhere where = new DbWhere(); + where.on("tenantCode").equals(TACD); + CrudDao dao = qdbcBoot.crudDao(SysUserEntity.class); + dao.physicalDelete(where); + } + + @Test(priority = 1002) + public void testUserEntityCreate() { + CrudDao dao = qdbcBoot.crudDao(SysUserEntity.class); + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(TACD); + entity.setSuperman(false); + entity.setGender(Gender.FEMALE); + entity.setUserType(UserType.USER); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + + entity.setId(null); + entity.setUserCode("kelly"); + entity.setDeptId("A001"); + entity.setCity("beijing"); + dao.insert(entity); + + entity.setId(null); + entity.setUserCode("evan"); + entity.setDeptId("A001"); + entity.setCity("nanjing"); + dao.insert(entity); + + entity.setId(null); + entity.setUserCode("coral"); + entity.setDeptId("A002"); + entity.setCity("nanjing"); + dao.insert(entity); + + entity.setId(null); + entity.setUserCode("jack"); + entity.setDeptId("A002"); + entity.setCity("beijing"); + dao.insert(entity); + } + + @Test(priority = 1003) + public void testOrConditionQuery() { + // deptId=A001且city=beijing 或 deptId=A002且city=nanjing + List conditions = new ArrayList<>(); + conditions.add(new KeyString("A001", "beijing")); + conditions.add(new KeyString("A002", "nanjing")); + + List userCodes = qdbcBoot.crudStream(SysUserEntity.class) + .where("tenantCode").equals(TACD) + .and() + .subCondition(where -> { + for (KeyString item : conditions) { + where.on().or().subCondition(w -> { + w.on("deptId").equals(item.getKey()) + .and("city").equals(item.getValue()); + }); + } + }) + .end() + .listFieldValues("userCode", false, String.class); + + Assert.assertEquals(userCodes.size(), 2); + Assert.assertTrue(userCodes.contains("kelly")); + Assert.assertFalse(userCodes.contains("even")); + Assert.assertTrue(userCodes.contains("coral")); + Assert.assertFalse(userCodes.contains("jack")); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlIncludeTagTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlIncludeTagTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a8d4ffb544bab1b2f53ed3614e31df32f7031d10 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlIncludeTagTest.java @@ -0,0 +1,46 @@ +package com.gitee.qdbp.jdbc.test.sql.build; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.AssertTools; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.annotations.Test; + +/** + * SqlOutTagTest + * + * @author zhaohuihua + * @version 20210911 + */ +@Test +@ContextConfiguration(locations = {"classpath:settings/spring/spring.xml"}) +public class SqlIncludeTagTest extends AbstractTestNGSpringContextTests { + + @Autowired + private QdbcBoot qdbcBoot; + + @Test + public void testSortIndex() throws IOException { + Map params = new HashMap<>(); + params.put("tableName", "TEST_DEPARTMENT_CORE_INFO"); + params.put("fieldName", "SORT_INDEX"); + params.put("parentField", "PARETN_CODE"); + params.put("parentValue", "A001"); + params.put("dataState", 1); + String sqlId = "SqlIncludeTagTest:queryMaxIndex"; + SqlBuffer sqlBuffer = qdbcBoot.sqlDao().getSqlContent(sqlId, params); + String sqlString = sqlBuffer.getLoggingSqlString(); + System.out.println(sqlString); + String sqlFile = "SqlIncludeTagTest.sql"; + + URL resultUrl = PathTools.findClassResource(SqlBuilderPrintTest.class, sqlFile); + AssertTools.assertTextLinesEquals(sqlString, resultUrl); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlIncludeTagTest.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlIncludeTagTest.sql new file mode 100644 index 0000000000000000000000000000000000000000..6b008c8aacddb6bcf1e66f00ed0a744c2d9bd835 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlIncludeTagTest.sql @@ -0,0 +1,5 @@ +SELECT MAX(SORT_INDEX) FROM TEST_DEPARTMENT_CORE_INFO + WHERE PARETN_CODE='A001'/*$1*/ + AND DATA_SATE=1/*$2*/ + AND TEST_SATE=2/*$3*/ + AND EFTFLAG='E'/*$4*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateConflictTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateConflictTest.java index dcdda2b2d3dd136a86f551f415bf26832dabcf18..3a9935838f625c8efc83f47e82f1deceb2fed0a3 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateConflictTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateConflictTest.java @@ -102,6 +102,18 @@ public class SqlTemplateConflictTest extends AbstractTestNGSpringContextTests { @Test public void testSqlIdConflict8() { + // 出现在select.001.sql/select.002.sql/select.003.sql, 003的优先级更高 + String sqlId = "user.roles.query8"; + Map params = new HashMap<>(); + params.put("userIds", "UB0000001"); + SqlBuffer sql = qdbcBoot.sqlDao().getSqlContent(sqlId, params); + // 001/002中的SQL没有TENANT_CODE条件 + String string = sql.getExecutableSqlString(); + Assert.assertTrue(string.contains("TENANT_CODE"), "sql 'user.roles.query8' contains 'TENANT_CODE'"); + } + + @Test + public void testSqlIdConflict20() { // 出现在select.dbversion.sql中 String sqlId = "select.dbversion"; SqlBuffer content = qdbcBoot.sqlContainer().generate(sqlId, null); diff --git a/jdbc-test/src/test/resources/jdbc.mysql5.properties b/jdbc-test/src/test/resources/jdbc.mysql5.properties new file mode 100644 index 0000000000000000000000000000000000000000..de1b12e7892aba7d401ab2b82026ab2bcf2416c0 --- /dev/null +++ b/jdbc-test/src/test/resources/jdbc.mysql5.properties @@ -0,0 +1,2 @@ +# jdbc +jdbc.sys = mysql:develop:dev888pwd@127.0.0.1:3306/qdbpdev