diff --git a/.gitignore b/.gitignore index bf54851f7092fd7e28f5e131b41c82992732d423..42d9215082e39ab98a70c6a77ec4376159ec5eb3 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ .mtj.tmp/ # Package Files # -*.jar *.war *.ear *.tar.gz @@ -36,3 +35,4 @@ target/ test-output/ .classpath .project +.setting.bat diff --git a/README.md b/README.md index 6b713e456720b9da172c7a25bf328c13d8d3ce76..e275f02c188c3b33340074e79f7732fff77fa754 100644 --- a/README.md +++ b/README.md @@ -7,19 +7,18 @@ https://github.com/zhaohuihua/qdbp-jdbc
https://yuque.com/zhaohuihua/qdbc 文档中心 # 存在的理由 -又造了一个轮子。 -为什么会有这个项目,因为现有的框架用得不够爽:(更多讨论见《[ORM框架的痛点讨论](https://yuque.com/zhaohuihua/qdbc/gebddu)》) +为什么会有这个项目,因为现有的框架用得不够爽:
+更多讨论见《[ORM框架的痛点讨论](https://yuque.com/zhaohuihua/qdbc/gebddu)》 * Hibernate太重量级,学习成本高,用得不好容易出现性能问题; * Spring的JdbcTemplate太基础,只做了预编译参数和结果映射; * MyBatis要生成一大堆xml文件,难以维护,尤其表结构变更时,重新生成的xml就需要跟以前的比对,很是麻烦; * 即使有MyBatisGenerator之类的工具来辅助生成代码,但由于代码是提前生成的,结构变更时依然麻烦; * 还有一个问题,所有的框架输出预编译参数的SQL日志都是用问号代替参数, -如果参数很多,根据日志到数据库中重现问题的时候简直痛苦。 + 如果参数很多,根据日志到数据库中重现问题的时候简直痛苦。 # 特点和独创内容 -* 特点:对于单表增删改查,以及涉及的大于/小于/不等于/like/in等条件,不需要写sql或xml!《[基本用法简介](https://yuque.com/zhaohuihua/qdbc/vfkzgg)》 -* 特点:多表关联基础查询,以及涉及的大于/小于/不等于/like/in等条件,不需要写sql或xml!《[表关联查询](https://yuque.com/zhaohuihua/qdbc/ziz5lh)》 +* 特点:对于单表增删改查,以及涉及的大于/小于/不等于/like/in等条件,不需要写sql或xml。《[基本用法简介](https://yuque.com/zhaohuihua/qdbc/vfkzgg)》 * 特点:集成全局业务处理接口
提供公共字段(创建人/创建时间/修改人/修改时间/租户隔离等)的赋值接口。
集成逻辑删除解决方案。《[逻辑删除数据状态填充策略](https://www.yuque.com/zhaohuihua/qdbc/ni9zz6)》
@@ -39,30 +38,29 @@ https://yuque.com/zhaohuihua/qdbc 文档中心 这方面也存在一些优化点: * MyBatis中大量的单表增删改查语句也要用到SQL模板,对DBA审查来说只是一种干扰,并不会过多关注; -* 多种数据库就需要多套模板,但实际情况往往是多套模板大体相同,只有少量差异; -* SQL模板首先应该是SQL,然后其中有一些XML的判断条件和循环语句,而不应该是XML格式。 +* 多种数据库就需要多套模板,但实际情况往往是多套模板大体相同,只有少量差异。 -详见《[SQL模板说明](https://yuque.com/zhaohuihua/qdbc/bvk5gy)》。 +详见《[SQL模板说明](https://www.yuque.com/zhaohuihua/qdbc/yoz8vk)》。 # 版本说明 JDK支持jdk1.7+
-数据库:MySQL、Oracle、DB2,做了较完整的测试。详见《[多数据库方言支持](https://www.yuque.com/zhaohuihua/qdbc/bdr1d3)》的【数据库类型测试】 +数据库:MySQL、Oracle、DB2、H2,做了较完整的测试。详见《[多数据库方言支持](https://www.yuque.com/zhaohuihua/qdbc/bdr1d3)》的【数据库类型测试】 # POM依赖 -https://mvnrepository.com/artifact/com.gitee.qdbp/qdbp-jdbc-spring +https://search.maven.org/artifact/com.gitee.qdbp/qdbp-jdbc-spring ```xml com.gitee.qdbp qdbp-jdbc-spring - 3.2.3 + 3.4.6 ``` 如果不是spring-web项目,可以单独使用qdbp-jdbc-core
-https://mvnrepository.com/artifact/com.gitee.qdbp/qdbp-jdbc-core +https://search.maven.org/artifact/com.gitee.qdbp/qdbp-jdbc-core ```xml com.gitee.qdbp qdbp-jdbc-core - 3.2.3 + 3.4.6 ``` diff --git a/jdbc-core/pom.xml b/jdbc-core/pom.xml index 7b087c79a809e96e43b571e0ef6eb440205e4a9f..18e284c43d763a51f5eb290505a2a28a65584842 100644 --- a/jdbc-core/pom.xml +++ b/jdbc-core/pom.xml @@ -5,11 +5,11 @@ com.gitee.qdbp qdbp-parent - 5.0.0 + 5.0.1 qdbp-jdbc-core - 3.2.9 + 3.4.6 jar ${project.artifactId} https://gitee.com/qdbp/qdbp-jdbc/ @@ -25,12 +25,12 @@ com.gitee.qdbp qdbp-able - 5.2.5 + 5.2.25 com.gitee.qdbp qdbp-staticize - 3.1.5 + ${project.version} com.github.jsqlparser @@ -57,10 +57,23 @@ + + com.alibaba + druid + 1.1.23 + true + + + com.zaxxer + HikariCP + 3.4.5 + true + + com.alibaba fastjson - 1.2.74 + 1.2.76 diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityDataFillService.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityDataFillService.java new file mode 100644 index 0000000000000000000000000000000000000000..7683562fd1e8012521ae3670f35f0db8015704c1 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityDataFillService.java @@ -0,0 +1,20 @@ +package com.gitee.qdbp.filling.api; + +import java.util.Map; + +/** + * 实体数据填充接口 + * + * @author zhaohuihua + * @version 20210113 + */ +public interface EntityDataFillService { + + /** + * 填充公共字段:
+ * entityDataService.fillFieldValue(data, "defaults", "taskState", ...); + * + * @param types 填充类型 + */ + void fillFieldValue(Iterable> data, String... options); +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityDataTypedFillService.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityDataTypedFillService.java new file mode 100644 index 0000000000000000000000000000000000000000..66b2c3956e1693ff901909a6e33fe663c36d1c82 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityDataTypedFillService.java @@ -0,0 +1,14 @@ +package com.gitee.qdbp.filling.api; + +import java.util.Map; + +/** + * 实体数据填充单一类型处理接口 + * + * @author zhaohuihua + * @version 20210113 + */ +public interface EntityDataTypedFillService { + + void fillFieldValue(Iterable> data, String options); +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..649c68272ae9f721fb06dd9bd33af65b67e431c1 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/api/EntityFillType.java @@ -0,0 +1,19 @@ +package com.gitee.qdbp.filling.api; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 实体填充类型 + * + * @author zhaohuihua + * @version 20210113 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface EntityFillType { + + String[] value(); +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..87572cc2aa8553a546d84265185447b3c3be8ea6 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillFieldValueService.java @@ -0,0 +1,206 @@ +package com.gitee.qdbp.filling.biz; + +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; + +/** + * 实体数据填充单一类型处理基础实现类
+ * options多个字段以逗号分隔, 默认的填充字段为源字段去掉Id/Code后缀, 加上Name
+ * -- createUser,updateUser
+ * -- 等于createUser-createUserName,updateUser-updateUserName
+ * 也可以自定义转换关系
+ * -- creatorId-createUser,updatorId-updateUser
+ * + * @author zhaohuihua + * @version 20210113 + */ +public abstract class BaseEntityDataFillFieldValueService implements EntityDataTypedFillService { + + @Override + public void fillFieldValue(Iterable> list, String option) { + List options = parseOptions(option); + + // # 收集需要填充的信息 + // key=sourceValue + Map> sourceMaps = collectFillInfos(list, options); + if (sourceMaps == null || sourceMaps.isEmpty()) { + return; + } + // # 根据sourceValue查询targetValue + List sourceValues = new ArrayList<>(sourceMaps.keySet()); // 复制副本以防修改 + Map targetMaps = findTargetValueMaps(sourceValues); + if (targetMaps == null || targetMaps.isEmpty()) { + return; + } + // # 将targetValue填充至originalData + for (Entry> entry : sourceMaps.entrySet()) { + Object sourceValue = entry.getKey(); + if (targetMaps.containsKey(sourceValue)) { + Object targetValue = targetMaps.get(sourceValue); + List fillors = entry.getValue(); + for (DataFillor fillor : fillors) { + fillor.fillValues(targetValue); + } + } + } + } + + protected abstract Map findTargetValueMaps(Collection sourceValues); + + protected boolean skipDataFill(Map data) { + return false; + } + + protected boolean skipSourceValue(Object 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; + } + 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 (!skipSourceValue(sourceValue)) { + fillor.addFillField(targetName, sourceValue); + } + 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 (!skipSourceValue(convertedSourceValue)) { + fillor.addFillField(convertedTargetName, convertedSourceValue); + } + 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 List> fillFields; + private Map originalData; + + public DataFillor(Map originalData) { + this.originalData = originalData; + this.fillFields = new ArrayList<>(); + } + + protected void addFillField(String targetName, Object 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/BaseEntityDataFillService.java b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillService.java new file mode 100644 index 0000000000000000000000000000000000000000..4183c09f9748c686dd22ce70440a09dd357f45e6 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/filling/biz/BaseEntityDataFillService.java @@ -0,0 +1,49 @@ +package com.gitee.qdbp.filling.biz; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.beans.KeyString; +import com.gitee.qdbp.filling.api.EntityDataFillService; +import com.gitee.qdbp.filling.api.EntityDataTypedFillService; + +/** + * 实体数据处理类基础实现类 + * + * @author zhaohuihua + * @version 20210113 + */ +public abstract class BaseEntityDataFillService implements EntityDataFillService { + + @Override + public void fillFieldValue(Iterable> data, String... options) { + List list = parseOptions(options); + for (KeyString item : list) { + EntityDataTypedFillService service = findService(item.getKey()); + if (service == null) { + continue; + } + service.fillFieldValue(data, item.getValue()); + } + } + + protected abstract EntityDataTypedFillService findService(String type); + + protected List parseOptions(String... options) { + List list = new ArrayList<>(); + for (String option : options) { + option = option.trim(); + int index = option.indexOf(':'); + if (index < 0) { + list.add(new KeyString(option, null)); + } else if (index > 0) { + String type = option.substring(0, index).trim(); + String args = option.substring(index + 1).trim(); + list.add(new KeyString(type, args)); + } else { + // 冒号开头? 忽略! + } + } + return list; + } +} 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 aad3e6fe0c6495f23cc2949fefdc9725643b1410..15e2e108a721e2c13a317394ffc61cd659ca44ac 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 @@ -41,6 +41,20 @@ public interface CrudDao { */ T findById(String id) throws ServiceException; + /** + * 根据主键编号获取对象
+ * 注意: 默认查询条件由entityFieldFillExecutor添加, 只查有效项
+ * SELECT {columnNames} FROM {tableName} WHERE ID={id} AND DATA_STATE='E' + * + * @param fields 查询的字段
+ * 全部字段传入Fields.ALL
+ * 指定字段传入IncludeFields对象, 如只查询ID,USER_CODE字段: new IncludeFields("id,userCode");
+ * 排除字段传入ExcludeFields对象, 如排除USER_REMARK字段: new ExcludeFields("userRemark");
+ * @param id 主键编号 + * @return 实体对象 + */ + T findById(Fields fields, String id) throws ServiceException; + /** * 根据查询条件获取对象
* 注意: 默认查询条件由entityFieldFillExecutor添加, 只查有效项
@@ -329,7 +343,7 @@ public interface CrudDao { Class valueClazz) throws ServiceException; /** - * 根据条件查询某个字段的值列表
+ * 根据条件分页查询某个字段的值列表
* 注意: 默认查询条件由entityFieldFillExecutor添加, 只查有效项
*
* SELECT {columnName} FROM {tableName}
@@ -360,7 +374,7 @@ public interface CrudDao { /** * 递归查询所有子节点
- * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在where条件排除
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
* MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
@@ -375,7 +389,7 @@ public interface CrudDao { * @param orderings 排序条件
* 不需要排序时应传入Orderings.NONE
* 由字符串构造排序条件: Orderings.of("name asc, createTime desc");
- * @return 子节点编号 + * @return 子节点列表 * @see ParseTools#parseBeanToDbWhere(Object) * @see ParseTools#parseParamsToDbWhere(Map, Class, boolean) * @see Orderings#of(String) @@ -385,7 +399,7 @@ public interface CrudDao { /** * 递归查询所有子节点
- * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在where条件排除
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
* MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
@@ -397,7 +411,7 @@ public interface CrudDao { * @param filterWhere 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
* @param searchWhere 结果搜索条件 (如用户输入的查询条件)
* 这些条件如果放在filterWhere中将无法生成完整的树
- * @return 子节点编号 + * @return 子节点列表 * @see ParseTools#parseBeanToDbWhere(Object) * @see ParseTools#parseParamsToDbWhere(Map, Class, boolean) */ @@ -406,7 +420,7 @@ public interface CrudDao { /** * 递归查询所有子节点编号
- * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在where条件排除
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
* MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
@@ -431,7 +445,7 @@ public interface CrudDao { /** * 递归查询所有子节点编号
- * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在where条件排除
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
* ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
* DB2/SqlServer: 使用WITH递归
* MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
@@ -454,6 +468,102 @@ public interface CrudDao { List listChildrenCodes(List startCodes, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings); + /** + * 递归查询所有父节点
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @param startCode 起始编号 + * @param codeField 编号字段名 + * @param parentField 上级编号字段名 + * @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) + * @see Orderings#of(String) + */ + List listParents(String startCode, String codeField, String parentField, DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings); + + /** + * 递归查询所有父节点
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @param startCodes 起始编号 + * @param codeField 编号字段名 + * @param parentField 上级编号字段名 + * @param filterWhere 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * @param searchWhere 结果搜索条件 (如用户输入的查询条件)
+ * 这些条件如果放在filterWhere中将无法生成完整的树
+ * @return 父节点列表 + * @see ParseTools#parseBeanToDbWhere(Object) + * @see ParseTools#parseParamsToDbWhere(Map, Class, boolean) + */ + List listParents(List startCodes, String codeField, String parentField, DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings); + + /** + * 递归查询所有父节点编号
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @param startCode 起始编号 + * @param codeField 编号字段名 + * @param parentField 上级编号字段名 + * @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) + * @see Orderings#of(String) + */ + List listParentCodes(String startCode, String codeField, String parentField, DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings); + + /** + * 递归查询所有父节点编号
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @param startCodes 起始编号 + * @param codeField 编号字段名 + * @param parentField 上级编号字段名 + * @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) + * @see Orderings#of(String) + */ + List listParentCodes(List startCodes, String codeField, String parentField, DbWhere filterWhere, + DbWhere searchWhere, Orderings orderings); + /** * 根据条件统计实体数量
* 注意: 默认查询条件由entityFieldFillExecutor添加, 只查有效项
@@ -480,7 +590,7 @@ public interface CrudDao { * 如果没有查询条件应传入DbWhere.NONE
* 由实体类转换为DbWhere: ParseTools.parseBeanToDbWhere(bean);
* 由请求参数转换为DbWhere: ParseTools.parseParamsToDbWhere(request.getParameterMap());
- * @return 列表数据 + * @return 分组统计结果 * @see ParseTools#parseBeanToDbWhere(Object) * @see ParseTools#parseParamsToDbWhere(Map, Class, boolean) */ @@ -494,7 +604,6 @@ public interface CrudDao { * INSERT INTO {tableName}({columnNames}) VALUES ({fieldValues}) * * @param entity 实体对象 - * @param fillCreateParams 是否自动填充创建参数(创建人/创建时间等) * @return 返回主键编号 * @throws ServiceException 操作失败 * @see DbConditionConverter#convertBeanToInsertMap(Object) 参数转换说明 @@ -755,8 +864,6 @@ public interface CrudDao { * UPDATE {tableName} SET DATA_STATE='D' WHERE ID={id} DATA_STATE='E' * * @param id 待删除的主键编号 - * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) - * @param errorOnUnaffected 受影响行数为0时是否抛异常 * @return 删除行数 * @throws ServiceException 删除失败 */ @@ -768,6 +875,8 @@ public interface CrudDao { * UPDATE {tableName} SET DATA_STATE='D' WHERE ID={id} DATA_STATE='E' * * @param id 待删除的主键编号 + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @param errorOnUnaffected 受影响行数为0时是否抛异常 * @return 删除行数 * @throws ServiceException 删除失败 */ @@ -938,4 +1047,7 @@ public interface CrudDao { * @throws ServiceException 操作失败 */ int physicalDelete(DbWhere where, boolean errorOnUnaffected) throws ServiceException; + + /** 获取BeanClass **/ + Class getBeanClass(); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/QdbcBoot.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/QdbcBoot.java index 60d60c0967136a061cfbe48969d17607071a6735..7b5330242b4b5e2b92af9edd52ca50ad83d7b85d 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/QdbcBoot.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/QdbcBoot.java @@ -4,6 +4,8 @@ import com.gitee.qdbp.able.jdbc.condition.TableJoin; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.build.CrudSqlBuilder; import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; +import com.gitee.qdbp.jdbc.stream.CrudStream; +import com.gitee.qdbp.jdbc.stream.SqlStream; /** * 数据库操作对象的构造器
@@ -18,64 +20,50 @@ import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; Date today = DateTools.toStartTime(new Date()); Date yesterday = DateTools.addDay(today, -1); { // 查询单个对象 - CrudDao<XxxBean> xxxDao = qdbcBoot.buildCrudDao(XxxBean.class); - XxxBean xxxBean = xxxDao.findById(id); + XxxBean xxxBean = qdbcBoot.crudStream(XxxBean.class) + .whereById(id) + .find(); } { // 查询列表(创建时间为今天的) - CrudDao<YyyBean> yyyDao = qdbcBoot.buildCrudDao(YyyBean.class); - DbWhere where = new DbWhere(); - where.on("createTime", ">=", today); - Orderings orderings = Orderings.of("createTime DESC"); - List<YyyBean> yyyBeans = yyyDao.list(where, orderings); + List<YyyBean> yyyBeans = qdbcBoot.crudStream(YyyBean.class) + .whereBy((where) -> { + where.on("createTime", ">=", today); + // where.andGreaterEqualsThen("createTime", today); + }) + .orderBy("createTime DESC") + .list(); } { // 分页查询(创建时间为昨天的第1页的10条记录) - CrudDao<YyyBean> yyyDao = qdbcBoot.buildCrudDao(YyyBean.class); - DbWhere where = new DbWhere(); - where.on("createTime", ">=", yesterday); - where.on("createTime", "<", today); - OrderPaging odpg = OrderPaging.of(new Paging(1, 10), "createTime ASC"); - PageList<YyyBean> yyyBeans = yyyDao.list(where, odpg); + PartList<YyyBean> yyyBeans = qdbcBoot.crudStream(YyyBean.class) + .whereBy((where) -> { + where.on("createTime", ">=", yesterday) + .on("createTime", "<", today); + // where.andGreaterEqualsThen("createTime", yesterday) + // .andLessThen("createTime", today); + }) + .orderBy("createTime ASC") + .pageBy(1, 10) + .list(); } { // 删除昨天待处理和失败的记录 - CrudDao<ZzzBean> zzzDao = qdbcBoot.buildCrudDao(ZzzBean.class); - DbWhere where = new DbWhere(); - where.on("state", "in", ZzzState.PENDING, ZzzState.ERROR); - where.on("handleTime", "between", yesterday, today); - zzzDao.physicalDelete(where); // 物理删除 - } - - { // 表关联查询 - TableJoin tables = ...; // 详见TableJoin的注释 - JoinQueryer<ZzzBean> zzzQuery = qdbcBoot.buildJoinQuery(tables, ZzzBean.class); - DbWhere where = new DbWhere(); - where.on("u.createTime", ">=", today); - OrderPaging odpg = OrderPaging.of(new Paging(1, 10), "u.createTime DESC"); - PageList<ZzzBean> zzzBeans = zzzQuery.list(where, odpg); + int rows = qdbcBoot.crudStream(ZzzBean.class) + .whereBy((where) -> { + where.on("state", "in", ZzzState.PENDING, ZzzState.ERROR); + .on("handleTime", "between", yesterday, today); + // where.andIn("state", ZzzState.PENDING, ZzzState.ERROR) + // .andBetween("handleTime", yesterday, today); + }) + .physicalDelete(); // 物理删除 } { // SQL模板查询 - Paging paging = new Paging(1, 10); - - DbWhere where = new DbWhere(); - where.on("ur.roleName", "like", keyword); - - Orderings orderings = Orderings.of("ur.roleName ASC"); - - TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); - QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); - - Map<String, Object> params = new HashMap<>(); - params.put("userIds", Arrays.asList(userId)); - if (VerifyTools.isNotBlank(where)) { - params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); - } - if (VerifyTools.isNotBlank(orderings)) { - params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); - } - + Map<String, Object> params = ...; String sqlId = "user.roles.query"; - SqlDao sqlDao = qdbcBoot.getSqlDao(); - PageList<SysRoleEntity> list = sqlDao.pageForObjects(sqlId, params, paging, SysRoleEntity.class); + List<SysRoleEntity> list = qdbcBoot.sqlStream("SysUserMapper:queryUserRoles") + .sqlId(sqlId).params(params) + .pageBy(1,10) + .resultAs(SysRoleEntity.class) + .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List } } * @@ -86,6 +74,73 @@ import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; */ public interface QdbcBoot { + /** + * 单表增删改查流式操作
+ * whereEquals|whereLike|whereIn|whereOn // 简单条件
+ * -- andEquals|andLike|andIn|andOn
+ * whereBy // 复杂条件
+ * -- logicalDelete|physicalDelete / find|findFieldValue / count|groupCount / list|listFieldValues
+ * -- orderBy
+ * ---- list|listFieldValues
+ * -- pageBy
+ * ---- list|listFieldValues
+ * select // 查询指定字段 (后续操作只能是查询,不能是删除和统计)
+ * -- list|listFieldValues
+ * -- whereEquals|whereLike|whereIn|whereOn // 简单条件
+ * ---- andEquals|andLike|andIn|andOn
+ * -- whereBy // 复杂条件
+ * ---- find|findFieldValue / list|listFieldValues
+ * ---- orderBy
+ * ------ list|listFieldValues
+ * ---- pageBy
+ * ------ list|listFieldValues
+ * entity // 指定实体类
+ * -- insert / update
+ * -- whereEquals|whereLike|whereIn|whereOn // 简单条件
+ * ---- andEquals|andLike|andIn|andOn
+ * -- whereBy // 复杂条件
+ * ---- update
+ * recursiveBy(codeField, parentField) // 递归查询
+ * -- startBy 指定开始节点编号
+ * ---- listChildren|listParents|listChildrenCodes|listParentCodes
+ * ---- filterOn // 简单条件
+ * ------ andEquals|andLike|andIn|andOn
+ * ---- filterBy // 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * ------ listChildren|listParents|listChildrenCodes|listParentCodes
+ * ---- searchOn // 简单条件
+ * ------ andEquals|andLike|andIn|andOn
+ * ---- searchBy // 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * ------ listChildren|listParents|listChildrenCodes|listParentCodes
+ * ---- orderBy // 排序条件
+ * ------ listChildren|listParents|listChildrenCodes|listParentCodes
+ * + * @param 单表对应的具体类型 + * @param clazz 单表对应的对象类型 + * @return 流式操作对象 + */ + CrudStream crudStream(Class clazz); + + /** + * Sql模板的流式操作
+ * sqlId // 设置SqlId
+ * -- find|list|insert|update|delete
+ * -- params // 设置参数
+ * ---- find|list|insert|update|delete
+ * ---- pageBy // 设置分页参数
+ * ------ find|list
+ * ------ resultAs // 结果转换
+ * -------- find|list // 查找数据/查找列表
+ * sqlId(queryId, countId) // 同时设置查询和统计SqlId (后续操作只能是分页查询)
+ * -- params // 设置参数
+ * ---- pageBy // 设置分页参数
+ * ------ list
+ * ------ resultAs // 结果转换
+ * -------- list // 查找数据/查找列表
+ * + * @return 流式操作对象 + */ + SqlStream sqlStream(); + /** * 构造单表增删改查对象 * diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlBufferJdbcOperations.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlBufferJdbcOperations.java index 46df22def4aebde447ed912909e0e6f15983f929..839be07c6e21e0a631363bb43b51f16a070e517c 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlBufferJdbcOperations.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlBufferJdbcOperations.java @@ -9,6 +9,7 @@ import org.springframework.jdbc.core.ResultSetExtractor; import org.springframework.jdbc.core.RowCallbackHandler; import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.support.KeyHolder; import org.springframework.jdbc.support.rowset.SqlRowSet; import com.gitee.qdbp.able.exception.ServiceException; @@ -142,6 +143,23 @@ public interface SqlBufferJdbcOperations { */ Map queryForMap(SqlBuffer sb) throws ServiceException; + /** + * Query given SQL to create a prepared statement from SQL and a + * list of arguments to bind to the query, expecting a result Map. + *

The query is expected to be a single row query; the result row will be + * mapped to a Map (one entry for each column, using the column name as the key). + * @param sb SqlBuffer + * @param rowMapper ResultSet row convert to map handler + * @return the result Map (one entry for each column, using the column name as the key) + * @throws org.springframework.dao.IncorrectResultSizeServiceException + * if the query does not return exactly one row + * @throws ServiceException if the query fails + * @see org.springframework.jdbc.core.JdbcTemplate#queryForMap(String) + * @see org.springframework.jdbc.core.ColumnMapRowMapper + */ + Map queryForMap(SqlBuffer sb, RowMapper> rowMapper) + throws ServiceException; + /** * Query given SQL to create a prepared statement from SQL and a * list of arguments to bind to the query, expecting a result list. @@ -157,6 +175,21 @@ public interface SqlBufferJdbcOperations { */ List queryForList(SqlBuffer sb, Class elementType) throws ServiceException; + /** + * Query given SQL to create a prepared statement from SQL and a + * list of arguments to bind to the query, expecting a result list. + *

The results will be mapped to a List (one entry for each row) of + * Maps (one entry for each column, using the column name as the key). + * Each element in the list will be of the form returned by this interface's + * {@code queryForMap} methods. + * @param sb SqlBuffer + * @param rowMapper ResultSet row convert to map handler + * @return a List that contains a Map per row + * @throws ServiceException if the query fails + * @see org.springframework.jdbc.core.JdbcTemplate#queryForList(String) + */ + List> queryForList(SqlBuffer sb, RowMapper> rowMapper) throws ServiceException; + /** * Query given SQL to create a prepared statement from SQL and a * list of arguments to bind to the query, expecting a result list. @@ -277,4 +310,7 @@ public interface SqlBufferJdbcOperations { * classic JDBC operations. */ JdbcOperations getJdbcOperations(); + + /** 获取NamedParameterJdbcOperations **/ + NamedParameterJdbcOperations getNamedParameterJdbcOperations(); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlDao.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlDao.java index 1d2c65aeb2a7a58ef854e15d82f4452f1b8bdb76..33e5ccae9ba6434ab818c86b01684f8d842d40d9 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlDao.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/api/SqlDao.java @@ -194,4 +194,13 @@ public interface SqlDao { * @return SQL内容 */ SqlBuffer getSqlContent(String sqlId, Object params); + + /** + * 渲染SQL模板内容 + * + * @param templateContent SQL模板内容 + * @param params params Sql参数 + * @return SQL内容 + */ + SqlBuffer renderSqlTemplate(String templateContent, Object params); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/BaseQueryerImpl.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/BaseQueryerImpl.java index 0eb2f386a2c0f692d2b627eb613890f7e8d53e70..5311ab7dc3724373268b12b73634a3dfc62d71e6 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/BaseQueryerImpl.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/BaseQueryerImpl.java @@ -16,6 +16,7 @@ import com.gitee.qdbp.able.jdbc.paging.PartList; import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations; import com.gitee.qdbp.jdbc.exception.DbErrorCode; import com.gitee.qdbp.jdbc.model.FieldScene; +import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillExecutor; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.result.KeyIntegerMapper; @@ -153,7 +154,7 @@ public abstract class BaseQueryerImpl { DbWhere readyWhere = checkWhere(where); entityFieldFillExecutor.fillQueryWhereDataState(readyWhere, getMajorTableAlias()); entityFieldFillExecutor.fillQueryWhereParams(readyWhere, getMajorTableAlias()); - PageList list = doListFieldValues(fieldName, false, readyWhere, null, valueClazz); + PageList list = doListFieldValues(fieldName, false, readyWhere, OrderPaging.NONE, valueClazz); return VerifyTools.isBlank(list) ? null : list.get(0); } @@ -182,7 +183,9 @@ public abstract class BaseQueryerImpl { if (odpg.isPaging() && odpg.isNeedCount()) { csb = sqlBuilder.buildCountSql(wsb); } - String columnName = sqlBuilder.helper().getColumnName(FieldScene.CONDITION, fieldName); + // 这里不能用getColumnName(), 否则JoinQueryer调用时会返回u.USER_NAME这种带表别名的列名, 导致取不到数据 + SimpleFieldColumn columnInfo = sqlBuilder.helper().getColumnInfo(FieldScene.CONDITION, fieldName); + String columnName = columnInfo.getColumnName(); RowMapper rowMapper = new SingleColumnMapper<>(columnName, valueClazz); PartList list = PagingQuery.queryForList(jdbc, qsb, csb, odpg, rowMapper); return list == null ? null : new PageList(list, list.getTotal()); 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 1d8e374b28bd36e24735775c510e26f847ca1fee..2c458363a55eb71cb989af1303e24bb36a602c24 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 @@ -4,6 +4,9 @@ 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 com.gitee.qdbp.able.convert.MapToBeanConverter; import com.gitee.qdbp.able.exception.ServiceException; import com.gitee.qdbp.able.jdbc.condition.DbUpdate; import com.gitee.qdbp.able.jdbc.condition.DbWhere; @@ -25,9 +28,7 @@ import com.gitee.qdbp.jdbc.plugins.DbConditionConverter; import com.gitee.qdbp.jdbc.plugins.EntityDataStateFillStrategy; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillExecutor; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillStrategy; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; import com.gitee.qdbp.jdbc.result.RowToBeanMapper; -import com.gitee.qdbp.jdbc.result.TableRowToBeanMapper; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.build.CrudSqlBuilder; import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; @@ -45,12 +46,13 @@ import com.gitee.qdbp.tools.utils.VerifyTools; */ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { + private static Logger log = LoggerFactory.getLogger(CrudDaoImpl.class); protected Class beanClass; /** 批量执行时的大小限制(0为无限制) **/ protected int defaultBatchSize = QdbcBootImpl.DEFAULT_BATCH_SIZE; CrudDaoImpl(Class c, SqlBufferJdbcOperations jdbc) { - super(newQuerySqlBuilder(c, jdbc), newEntityFieldFillExecutor(c), jdbc, newRowToBeanMapper(c)); + super(newQuerySqlBuilder(c, jdbc), newEntityFieldFillExecutor(c), jdbc, getRowToBeanMapper(c)); this.beanClass = c; } @@ -66,9 +68,8 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { return new EntityFieldFillExecutor(allFields, fieldFillStrategy, dataStateFillStrategy); } - private static RowToBeanMapper newRowToBeanMapper(Class clazz) { - MapToBeanConverter converter = DbTools.getMapToBeanConverter(); - return new TableRowToBeanMapper<>(clazz, converter); + private static RowToBeanMapper getRowToBeanMapper(Class clazz) { + return DbTools.getRowToBeanMapper(clazz); } @Override @@ -78,6 +79,11 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { @Override public T findById(String id) { + return findById(Fields.ALL, id); + } + + @Override + public T findById(Fields fields, String id) { VerifyTools.requireNotBlank(id, "id"); SimpleFieldColumn pk = getSqlBuilder().helper().getPrimaryKey(); if (pk == null) { // 没有找到主键字段 @@ -87,7 +93,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { String primaryField = pk.getFieldName(); DbWhere where = new DbWhere(); where.on(primaryField, "=", id); - return this.find(where); + return this.find(fields, where); } @Override @@ -136,38 +142,6 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { return doListChildren(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); } - // ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField} - // DB2: 使用WITH递归 - // MYSQL 8.0+: 使用WITH RECURSIVE递归 - // MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY - protected List doListChildren(List startCodes, String codeField, String parentField, - Fields selectFields, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings, Class resultType) - throws ServiceException { - CrudFragmentHelper sqlHelper = getSqlBuilder().helper(); - String codeColumn = sqlHelper.getColumnName(FieldScene.CONDITION, codeField); - String parentColumn = sqlHelper.getColumnName(FieldScene.CONDITION, parentField); - SqlBuffer selectColumns = sqlHelper.buildSelectFieldsSql(selectFields); - - Map params = new HashMap<>(); - params.put("codeColumn", codeColumn); - params.put("parentColumn", parentColumn); - params.put("tableName", sqlHelper.getTableName()); - params.put("selectColumns", selectColumns); - params.put("startCodes", startCodes); - if (filterWhere != null && !filterWhere.isEmpty()) { - params.put("filterWhere", sqlHelper.buildWhereSql(filterWhere, false)); - } - if (searchWhere != null && !searchWhere.isEmpty()) { - params.put("searchWhere", sqlHelper.buildWhereSql(searchWhere, false)); - } - if (orderings != null && !orderings.isEmpty()) { - params.put("orderBy", sqlHelper.buildOrderBySql(orderings, false)); - } - - String sqlId = "recursive.list.children.query"; - return jdbc.getSqlDao().listForObjects(sqlId, params, resultType); - } - @Override public List listChildrenCodes(String startCode, String codeField, String parentField, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { @@ -197,6 +171,106 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { return doListChildren(startCodes, codeField, parentField, fields, filter, search, orderings, String.class); } + @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()); + + List startCodes = ConvertTools.toList(startCode); + return doListParents(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); + } + + @Override + public List listParents(List startCodes, 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()); + + return doListParents(startCodes, codeField, parentField, Fields.ALL, filter, search, orderings, beanClass); + } + + @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()); + + List startCodes = ConvertTools.toList(startCode); + IncludeFields fields = new IncludeFields(codeField); + return doListParents(startCodes, codeField, parentField, fields, filter, search, orderings, String.class); + } + + @Override + public List listParentCodes(List startCodes, 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()); + + IncludeFields fields = new IncludeFields(codeField); + return doListParents(startCodes, codeField, parentField, fields, filter, search, orderings, String.class); + } + + // ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField}={parentField} + // DB2: 使用WITH递归 + // MYSQL 8.0+: 使用WITH RECURSIVE递归 + // MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + 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); + } + + 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); + } + + protected List doListRecursive(String type, List startCodes, String codeField, String parentField, + Fields selectFields, DbWhere filterWhere, DbWhere searchWhere, Orderings orderings, Class resultType) + throws ServiceException { + CrudFragmentHelper sqlHelper = getSqlBuilder().helper(); + String codeColumn = sqlHelper.getColumnName(FieldScene.CONDITION, codeField); + String parentColumn = sqlHelper.getColumnName(FieldScene.CONDITION, parentField); + SqlBuffer selectColumns = sqlHelper.buildSelectFieldsSql(selectFields); + + Map params = new HashMap<>(); + params.put("codeColumn", codeColumn); + params.put("parentColumn", parentColumn); + params.put("tableName", sqlHelper.getTableName()); + params.put("selectColumns", selectColumns); + params.put("startCodes", startCodes); + if (filterWhere != null && !filterWhere.isEmpty()) { + params.put("filterWhere", sqlHelper.buildWhereSql(filterWhere, false)); + } + if (searchWhere != null && !searchWhere.isEmpty()) { + params.put("searchWhere", sqlHelper.buildWhereSql(searchWhere, false)); + } + if (orderings != null && !orderings.isEmpty()) { + params.put("orderBy", sqlHelper.buildOrderBySql(orderings, false)); + } + + String sqlId = "global.recursive:recursive.list." + type + ".query"; + return jdbc.getSqlDao().listForObjects(sqlId, params, resultType); + } + protected Map copmareMapDifference(Map original, Map current) { Map diff = new HashMap<>(); for (Map.Entry entry : original.entrySet()) { @@ -205,8 +279,10 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { } } for (Map.Entry entry : current.entrySet()) { - if (VerifyTools.notEquals(entry.getValue(), original.get(entry.getKey()))) { - diff.put(entry.getKey(), entry.getValue()); + Object curvalue = DbTools.unwrapDbVariable(entry.getValue()); + Object orivalue = DbTools.unwrapDbVariable(original.get(entry.getKey())); + if (VerifyTools.notEquals(curvalue, orivalue)) { + diff.put(entry.getKey(), curvalue); } } return diff; @@ -445,8 +521,13 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { protected int doUpdate(DbUpdate readyEntity, DbWhere readyWhere, boolean errorOnUnaffected) throws ServiceException { SqlBuffer buffer = getSqlBuilder().buildUpdateSql(readyEntity, readyWhere); - - int rows = jdbc.update(buffer); + int rows; + if (buffer.isBlank()) { + rows = 0; + log.debug("Update set fields is empty, the update operation will be skipped."); + } else { + rows = jdbc.update(buffer); + } if (rows == 0 && errorOnUnaffected) { throw new ServiceException(DbErrorCode.DB_AFFECTED_ROWS_IS_ZERO); @@ -689,7 +770,12 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { entityFieldFillExecutor.fillLogicalDeleteParams(ud); } SqlBuffer buffer = getSqlBuilder().buildUpdateSql(ud, readyWhere); - rows = jdbc.update(buffer); + if (buffer.isBlank()) { + rows = 0; + log.debug("Update set fields is empty, the logical delete operation will be skipped."); + } else { + rows = jdbc.update(buffer); + } } else { // 不支持逻辑删除 String details = "UnsupportedLogicDelete, class=" + beanClass.getName(); throw new ServiceException(DbErrorCode.DB_UNSUPPORTED_LOGICAL_DELETE, details); @@ -768,7 +854,8 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { if (pk == null) { id = null; } else if (VerifyTools.isNotBlank(entity.get(pk.getFieldName()))) { - id = entity.get(pk.getFieldName()).toString(); + Object pkValue = DbTools.unwrapDbVariable(entity.get(pk.getFieldName())); + id = pkValue.toString(); } else { // 生成主键 entity.put(pk.getFieldName(), id = entityFieldFillExecutor.generatePrimaryKeyCode(tableName)); @@ -808,7 +895,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { // 比对修改后的字段值与原值的差异, 复制到原对象 Map diff = copmareMapDifference(original, readyEntity); MapToBeanConverter mapToBeanConverter = DbTools.getMapToBeanConverter(); - mapToBeanConverter.fill(diff, entity); + mapToBeanConverter.fillTo(diff, entity); return new PkEntity(id, readyEntity); } @@ -858,7 +945,7 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { } // 填充实体的修改参数(如修改人修改时间等) fillEntityUpdateParams(entity, fillUpdateParams); - return new PkEntity(pkValue.toString(), entity); + return new PkEntity(DbTools.unwrapDbVariable(pkValue).toString(), entity); } else if (beanClass.isAssignableFrom(object.getClass())) { @SuppressWarnings("unchecked") T entity = (T) object; @@ -945,11 +1032,12 @@ public class CrudDaoImpl extends BaseQueryerImpl implements CrudDao { // 比对修改后的字段值与原值的差异, 复制到原对象 Map diff = copmareMapDifference(original, readyEntity); MapToBeanConverter mapToBeanConverter = DbTools.getMapToBeanConverter(); - mapToBeanConverter.fill(diff, entity); + mapToBeanConverter.fillTo(diff, entity); return readyEntity; } /** 获取当前实例的Bean类型 **/ + @Override public Class getBeanClass() { return beanClass; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/JoinQueryerImpl.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/JoinQueryerImpl.java index 301d45c7614728e06266db513e6b12e5e0e15720..12360cca208fd4de583aa2654ad50844477ff5ed 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/JoinQueryerImpl.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/JoinQueryerImpl.java @@ -10,9 +10,7 @@ import com.gitee.qdbp.jdbc.model.AllFieldColumn; import com.gitee.qdbp.jdbc.plugins.EntityDataStateFillStrategy; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillExecutor; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillStrategy; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; import com.gitee.qdbp.jdbc.result.RowToBeanMapper; -import com.gitee.qdbp.jdbc.result.TablesRowToProperyMapper; import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; import com.gitee.qdbp.jdbc.sql.fragment.TableJoinFragmentHelper; import com.gitee.qdbp.jdbc.utils.DbTools; @@ -28,7 +26,7 @@ public class JoinQueryerImpl extends BaseQueryerImpl implements JoinQuerye private String majorTableAlias; public JoinQueryerImpl(TableJoin t, Class r, SqlBufferJdbcOperations jdbc) { - super(newQuerySqlBuilder(t, jdbc), newEntityFieldFillExecutor(t), jdbc, newRowToBeanMapper(t, r)); + super(newQuerySqlBuilder(t, jdbc), newEntityFieldFillExecutor(t), jdbc, getRowToBeanMapper(t, r)); this.majorTableAlias = t.getMajor().getTableAlias(); List joins = t.getJoins(); for (JoinItem item : joins) { @@ -51,9 +49,8 @@ public class JoinQueryerImpl extends BaseQueryerImpl implements JoinQuerye return new EntityFieldFillExecutor(allFields, fieldFillStrategy, dataStateFillStrategy); } - private static RowToBeanMapper newRowToBeanMapper(TableJoin tables, Class clazz) { - MapToBeanConverter converter = DbTools.getMapToBeanConverter(); - return new TablesRowToProperyMapper<>(tables, clazz, converter); + private static RowToBeanMapper getRowToBeanMapper(TableJoin tables, Class clazz) { + return DbTools.getRowToBeanMapper(tables, clazz); } /** {@inheritDoc} **/ diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/QdbcBootImpl.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/QdbcBootImpl.java index 9b5bf0f0158dcd22c95a2ffb55470eb67d2c90f6..f5261a9bea8950a97a25d528538578cc0f24d1f9 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/QdbcBootImpl.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/QdbcBootImpl.java @@ -19,6 +19,8 @@ import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; import com.gitee.qdbp.jdbc.sql.fragment.CrudFragmentHelper; import com.gitee.qdbp.jdbc.sql.fragment.TableCrudFragmentHelper; import com.gitee.qdbp.jdbc.sql.fragment.TableJoinFragmentHelper; +import com.gitee.qdbp.jdbc.stream.CrudStream; +import com.gitee.qdbp.jdbc.stream.SqlStream; /** * 基础增删改查对象的构造器 @@ -60,6 +62,18 @@ public class QdbcBootImpl implements QdbcBoot { } } + /** {@inheritDoc} **/ + @Override + public CrudStream crudStream(Class clazz) { + return new CrudStream(buildCrudDao(clazz)); + } + + /** {@inheritDoc} **/ + @Override + public SqlStream sqlStream() { + return new SqlStream(getSqlDao()); + } + /** {@inheritDoc} **/ @Override @SuppressWarnings("unchecked") diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlBufferJdbcTemplate.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlBufferJdbcTemplate.java index 673fa1a37ace3afa70ce57d5f4bfd00868b243f3..4d57e1591706b9588e271b0a13c83ecbf1c15899 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlBufferJdbcTemplate.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlBufferJdbcTemplate.java @@ -10,7 +10,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; -import org.springframework.jdbc.core.ColumnMapRowMapper; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.PreparedStatementCallback; import org.springframework.jdbc.core.ResultSetExtractor; @@ -32,11 +31,9 @@ import com.gitee.qdbp.able.result.ResultCode; import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations; import com.gitee.qdbp.jdbc.api.SqlDao; import com.gitee.qdbp.jdbc.model.DbVersion; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.result.FirstColumnMapper; import com.gitee.qdbp.jdbc.result.RowToBeanMapper; -import com.gitee.qdbp.jdbc.result.TableRowToBeanMapper; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; import com.gitee.qdbp.jdbc.utils.CountSqlParser; @@ -61,12 +58,10 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { private SqlDialect sqlDialect; private SqlDao sqlDao; private NamedParameterJdbcOperations namedParameterJdbcOperations; - protected RowMapper> mapResultConvert; protected CountSqlParser countSqlParser; public SqlBufferJdbcTemplate() { this.countSqlParser = new CountSqlParser(); - this.mapResultConvert = new ColumnMapRowMapper(); } public SqlBufferJdbcTemplate(NamedParameterJdbcOperations jdbcOperations) { @@ -74,53 +69,53 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { this.setNamedParameterJdbcOperations(jdbcOperations); } - protected SqlBufferJdbcTemplate(SqlBufferJdbcTemplate parent, RowMapper> mapResultConvert) { - this.namedParameterJdbcOperations = parent.getNamedParameterJdbcOperations(); - this.mapResultConvert = mapResultConvert != null ? mapResultConvert : parent.mapResultConvert; + protected SqlBufferJdbcTemplate(SqlBufferJdbcTemplate parent) { + this.namedParameterJdbcOperations = parent.namedParameterJdbcOperations; this.countSqlParser = parent.countSqlParser; - this.dbVersion = parent.getDbVersion(); - this.sqlDialect = parent.getSqlDialect(); - this.sqlDao = parent.getSqlDao(); - this.inited = true; - } - - public NamedParameterJdbcOperations getNamedParameterJdbcOperations() { - return namedParameterJdbcOperations; + this.dbVersion = parent.dbVersion; + this.sqlDialect = parent.sqlDialect; + this.sqlDao = parent.sqlDao; } public void setNamedParameterJdbcOperations(NamedParameterJdbcOperations namedParameterJdbcOperations) { this.namedParameterJdbcOperations = namedParameterJdbcOperations; + // 不能在系统初始化时执行dialect/sqlDao的初始化 + // 因为此时DbPluginContainer.init(container)还没有执行 + // 还没有配置DbVersionFinder + // this.doInitDialect(); + // this.doInitSqlDao(); } - private boolean inited = false; - - protected void init() { - if (this.inited) { - return; + private void initDialect() { + if (this.sqlDialect == null) { + this.doInitDialect(); } - this.doInit(); } - private synchronized void doInit() { - if (this.inited) { - return; + private void initSqlDao() { + if (this.sqlDao == null) { + this.doInitSqlDao(); } - this.inited = true; + } - JdbcOperations jdbcOperations = namedParameterJdbcOperations.getJdbcOperations(); - if (!(jdbcOperations instanceof JdbcAccessor)) { - throw new IllegalStateException("Unsupported JdbcOperations: " + jdbcOperations.getClass().getName()); - } - JdbcAccessor accessor = (JdbcAccessor) jdbcOperations; - DataSource datasource = accessor.getDataSource(); - if (datasource == null) { - throw new IllegalStateException("Datasource is null."); - } - if (dbVersion == null) { + private synchronized void doInitDialect() { + if (this.sqlDialect == null) { + JdbcOperations jdbcOperations = namedParameterJdbcOperations.getJdbcOperations(); + if (!(jdbcOperations instanceof JdbcAccessor)) { + throw new IllegalStateException("Unsupported JdbcOperations: " + jdbcOperations.getClass().getName()); + } + JdbcAccessor accessor = (JdbcAccessor) jdbcOperations; + DataSource datasource = accessor.getDataSource(); + if (datasource == null) { + throw new IllegalStateException("Datasource is null."); + } dbVersion = DbTools.findDbVersion(datasource); sqlDialect = DbTools.buildSqlDialect(dbVersion); log.debug("Database version: {}", dbVersion); } + } + + private synchronized void doInitSqlDao() { if (this.sqlDao == null) { SqlFragmentContainer container = SqlFragmentContainer.defaults(); this.sqlDao = new SqlDaoImpl(container, this); @@ -129,25 +124,28 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { @Override public DbVersion getDbVersion() { - this.init(); + initDialect(); return dbVersion; } @Override public SqlDialect getSqlDialect() { - this.init(); + initDialect(); return sqlDialect; } @Override public SqlDao getSqlDao() { - this.init(); + initSqlDao(); return sqlDao; } - protected RowToBeanMapper newRowToBeanMapper(Class clazz) { - MapToBeanConverter converter = DbTools.getMapToBeanConverter(); - return new TableRowToBeanMapper<>(clazz, converter, true, mapResultConvert); + protected RowMapper> getRowToMapConverter() { + return DbTools.getRowToMapConverter(); + } + + protected RowToBeanMapper getRowToBeanConverter(Class clazz) { + return DbTools.getRowToBeanMapper(clazz); } @Override @@ -158,6 +156,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql statement:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -174,8 +173,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return null; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -187,6 +185,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -204,8 +203,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return null; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -217,6 +215,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -227,8 +226,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { } } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -240,6 +238,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -251,8 +250,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return list; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -264,6 +262,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -281,8 +280,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return null; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -290,16 +288,17 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { public T queryForObject(SqlBuffer sb, Class resultType) throws ServiceException { VerifyTools.requireNotBlank(sb, "sqlBuffer"); if (!ReflectTools.isPrimitive(resultType, false)) { - return queryForObject(sb, newRowToBeanMapper(resultType)); + return queryForObject(sb, getRowToBeanConverter(resultType)); } long startTime = System.currentTimeMillis(); String logsql = null; if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); + String sql = sb.getPreparedSqlString(sqlDialect); + Map params = sb.getPreparedVariables(sqlDialect); try { - String sql = sb.getPreparedSqlString(sqlDialect); - Map params = sb.getPreparedVariables(sqlDialect); T result = namedParameterJdbcOperations.queryForObject(sql, params, resultType); if (log.isDebugEnabled()) { long time = System.currentTimeMillis() - startTime; @@ -314,8 +313,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return null; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -339,16 +337,24 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { @Override public Map queryForMap(SqlBuffer sb) throws ServiceException { + RowMapper> rowToMapConverter = getRowToMapConverter(); + return queryForMap(sb, rowToMapConverter); + } + + @Override + public Map queryForMap(SqlBuffer sb, RowMapper> rowMapper) + throws ServiceException { VerifyTools.requireNotBlank(sb, "sqlBuffer"); long startTime = System.currentTimeMillis(); String logsql = null; if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { - Map result = namedParameterJdbcOperations.queryForObject(sql, params, mapResultConvert); + Map result = namedParameterJdbcOperations.queryForObject(sql, params, rowMapper); if (log.isDebugEnabled()) { long time = System.currentTimeMillis() - startTime; log.debug("Sql query returns {} rows, elapsed time {}ms.", result == null ? 0 : 1, time); @@ -362,8 +368,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return null; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -375,6 +380,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -382,7 +388,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (ReflectTools.isPrimitive(elementType, false)) { list = namedParameterJdbcOperations.queryForList(sql, params, elementType); } else { - list = namedParameterJdbcOperations.query(sql, params, newRowToBeanMapper(elementType)); + list = namedParameterJdbcOperations.query(sql, params, getRowToBeanConverter(elementType)); } if (log.isDebugEnabled()) { long time = System.currentTimeMillis() - startTime; @@ -391,23 +397,30 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return list; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @Override public List> queryForList(SqlBuffer sb) throws ServiceException { + RowMapper> rowMapper = getRowToMapConverter(); + return queryForList(sb, rowMapper); + } + + @Override + public List> queryForList(SqlBuffer sb, RowMapper> rowMapper) + throws ServiceException { VerifyTools.requireNotBlank(sb, "sqlBuffer"); long startTime = System.currentTimeMillis(); String logsql = null; if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { - List> list = namedParameterJdbcOperations.query(sql, params, mapResultConvert); + List> list = namedParameterJdbcOperations.query(sql, params, rowMapper); if (log.isDebugEnabled()) { long time = System.currentTimeMillis() - startTime; log.debug("Sql query returns {} rows, elapsed time {}ms.", list == null ? 0 : list.size(), time); @@ -415,8 +428,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return list; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -428,6 +440,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql query:\n{}", logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -439,8 +452,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return result; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); } } @@ -474,6 +486,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql {}:\n{}", desc, logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -485,8 +498,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return rows; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(errorCode, details, e); + throw createServiceException(errorCode, details, e); } } @@ -497,6 +509,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql {}:\n{}", desc, logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); SqlParameterSource msps = new MapSqlParameterSource(params); @@ -509,8 +522,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return rows; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(errorCode, details, e); + throw createServiceException(errorCode, details, e); } } @@ -532,6 +544,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { if (log.isDebugEnabled()) { log.debug("Executing sql batch {}:\n{}", operate, logsql = getFormattedSqlString(sb, 1)); } + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getPreparedSqlString(sqlDialect); Map params = sb.getPreparedVariables(sqlDialect); try { @@ -543,31 +556,35 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return rows; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getFormattedSqlString(sb, 1)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(errorCode, details, e); + throw createServiceException(errorCode, details, e); } } protected String getFormattedSqlString(SqlBuffer sb, int indent) { // 未开启到TRACE级别的日志就执行省略模式 boolean omitMode = !log.isTraceEnabled(); + SqlDialect sqlDialect = getSqlDialect(); String sql = sb.getLoggingSqlString(sqlDialect, omitMode); return DbTools.formatSql(sql, 1); } // key=querySql, value=countSql - private static Map COUNT_SQL_MAPS = new ConcurrentHashMap<>(); + private static Map COUNT_SQL_MAPS = new ConcurrentHashMap<>(); @Override public int countByQuerySql(SqlBuffer querySql) { long startTime = System.currentTimeMillis(); + SqlDialect sqlDialect = getSqlDialect(); String namedQuerySql = querySql.getPreparedSqlString(sqlDialect); String countSql; Object[] paramArray; if (COUNT_SQL_MAPS.containsKey(namedQuerySql)) { - SqlAndParams item = COUNT_SQL_MAPS.get(namedQuerySql); - countSql = item.sql; - paramArray = item.params; + CountSqlItem item = COUNT_SQL_MAPS.get(namedQuerySql); + countSql = item.countSql; + ParsedSql parsedQuerySql = item.parsedSql; + Map paramMaps = querySql.getPreparedVariables(sqlDialect); + SqlParameterSource paramSource = new MapSqlParameterSource(paramMaps); + paramArray = NamedParameterUtils.buildValueArray(parsedQuerySql, paramSource, null); } else { // 解析查询语句生成统计语句 Map paramMaps = querySql.getPreparedVariables(sqlDialect); @@ -578,7 +595,7 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { countSql = countSqlParser.getSmartCountSql(actualQuerySql); - COUNT_SQL_MAPS.put(namedQuerySql, new SqlAndParams(countSql, paramArray)); + COUNT_SQL_MAPS.put(namedQuerySql, new CountSqlItem(parsedQuerySql, countSql)); } // 输出日志 @@ -601,19 +618,29 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { return total; } catch (DataAccessException e) { String details = "Sql:\n" + (logsql != null ? logsql : getLoggingSqlForHashParamsSql(countSql, paramArray)); - details = StringTools.concat('\n', details, e.getCause() == null ? null : e.getCause().getMessage()); - throw new ServiceException(ResultCode.DB_SELECT_ERROR, details, e); + throw createServiceException(ResultCode.DB_SELECT_ERROR, details, e); + } + } + + private ServiceException createServiceException(IResultMessage resultCode, String details, DataAccessException e) { + if (e.getCause() == null) { + return new ServiceException(resultCode, details, e); + } else { + details = StringTools.concat('\n', details, e.getCause().getMessage()); + // 异常Cause层级太深了, 而且很多重复信息, 跳过一层 + // return new ServiceException(resultCode, details, e); + return new ServiceException(resultCode, details, e.getCause()); } } - private static class SqlAndParams { + private static class CountSqlItem { - private String sql; - private Object[] params; + private ParsedSql parsedSql; + private String countSql; - public SqlAndParams(String sql, Object[] params) { - this.sql = sql; - this.params = params; + public CountSqlItem(ParsedSql parsedSql, String countSql) { + this.parsedSql = parsedSql; + this.countSql = countSql; } } @@ -673,4 +700,9 @@ public class SqlBufferJdbcTemplate implements SqlBufferJdbcOperations { public JdbcOperations getJdbcOperations() { return namedParameterJdbcOperations.getJdbcOperations(); } + + @Override + public NamedParameterJdbcOperations getNamedParameterJdbcOperations() { + return namedParameterJdbcOperations; + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlDaoImpl.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlDaoImpl.java index 33898a8c2dbdfcbe4e10921f8418eb363a35a9ee..7b00129d00bb245a38189d81b043cf9fcea95780 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlDaoImpl.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/biz/SqlDaoImpl.java @@ -6,16 +6,12 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.convert.BeanToMapConverter; import com.gitee.qdbp.able.jdbc.paging.PageList; import com.gitee.qdbp.able.jdbc.paging.Paging; import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations; import com.gitee.qdbp.jdbc.api.SqlDao; -import com.gitee.qdbp.jdbc.plugins.BeanToMapConverter; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; import com.gitee.qdbp.jdbc.plugins.SqlDialect; -import com.gitee.qdbp.jdbc.result.CamelNamingMapRowMapper; -import com.gitee.qdbp.jdbc.result.RowToBeanMapper; -import com.gitee.qdbp.jdbc.result.TableRowToBeanMapper; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; import com.gitee.qdbp.jdbc.utils.DbTools; @@ -35,16 +31,10 @@ public class SqlDaoImpl implements SqlDao { protected SqlFragmentContainer container; protected SqlBufferJdbcOperations jdbc; - protected SqlDialect dialect; public SqlDaoImpl(SqlFragmentContainer container, SqlBufferJdbcOperations jdbcOperations) { this.container = container; - this.dialect = jdbcOperations.getSqlDialect(); - if (jdbcOperations instanceof SqlBufferJdbcTemplate) { - this.jdbc = new MapSqlBufferJdbcTemplate((SqlBufferJdbcTemplate) jdbcOperations); - } else { - this.jdbc = jdbcOperations; - } + this.jdbc = jdbcOperations; } protected Map beanToMap(Object bean) { @@ -57,49 +47,49 @@ public class SqlDaoImpl implements SqlDao { @Override public T findForObject(String sqlId, Object params, Class resultType) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.queryForObject(sql, resultType); } @Override public T findForObject(String sqlId, Object params, RowMapper rowMapper) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.queryForObject(sql, rowMapper); } @Override public Map findForMap(String sqlId, Object params) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.queryForMap(sql); } @Override public List listForObjects(String sqlId, Object params, Class resultType) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.queryForList(sql, resultType); } @Override public List listForObjects(String sqlId, Object params, RowMapper rowMapper) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.query(sql, rowMapper); } @Override public List> listForMaps(String sqlId, Object params) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.queryForList(sql); } @Override public PageList pageForObjects(String sqlId, Object params, Paging paging, Class resultType) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); // 先查询总数据量 Integer total = paging.getTotal(); @@ -124,7 +114,7 @@ public class SqlDaoImpl implements SqlDao { @Override public PageList pageForObjects(String sqlId, Object params, Paging paging, RowMapper rowMapper) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); // 先查询总数据量 Integer total = paging.getTotal(); @@ -149,7 +139,7 @@ public class SqlDaoImpl implements SqlDao { @Override public PageList> pageForMaps(String sqlId, Object params, Paging paging) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); // 先查询总数据量 Integer total = paging.getTotal(); @@ -175,12 +165,12 @@ public class SqlDaoImpl implements SqlDao { public PageList pageForObjects(String queryId, String countId, Object params, Paging paging, Class resultType) { Map map = params == null ? null : beanToMap(params); - SqlBuffer querySql = container.render(queryId, map, dialect); + SqlBuffer querySql = doGetSqlContent(queryId, map); // 先查询总数据量 Integer total = paging.getTotal(); if (paging.isNeedCount()) { - SqlBuffer countSql = container.render(countId, map, dialect); + SqlBuffer countSql = doGetSqlContent(countId, map); total = jdbc.queryForObject(countSql, Integer.class); paging.setTotal(total); } @@ -202,12 +192,12 @@ public class SqlDaoImpl implements SqlDao { public PageList pageForObjects(String queryId, String countId, Object params, Paging paging, RowMapper rowMapper) { Map map = params == null ? null : beanToMap(params); - SqlBuffer querySql = container.render(queryId, map, dialect); + SqlBuffer querySql = doGetSqlContent(queryId, map); // 先查询总数据量 Integer total = paging.getTotal(); if (paging.isNeedCount()) { - SqlBuffer countSql = container.render(countId, map, dialect); + SqlBuffer countSql = doGetSqlContent(countId, map); total = jdbc.queryForObject(countSql, Integer.class); paging.setTotal(total); } @@ -228,12 +218,12 @@ public class SqlDaoImpl implements SqlDao { @Override public PageList> pageForMaps(String queryId, String countId, Object params, Paging paging) { Map map = params == null ? null : beanToMap(params); - SqlBuffer querySql = container.render(queryId, map, dialect); + SqlBuffer querySql = doGetSqlContent(queryId, map); // 先查询总数据量 Integer total = paging.getTotal(); if (paging.isNeedCount()) { - SqlBuffer countSql = container.render(countId, map, dialect); + SqlBuffer countSql = doGetSqlContent(countId, map); total = jdbc.queryForObject(countSql, Integer.class); paging.setTotal(total); } @@ -254,54 +244,47 @@ public class SqlDaoImpl implements SqlDao { @Override public int insert(String sqlId, Object params) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.insert(sql); } @Override public int update(String sqlId, Object params) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.update(sql); } @Override public int delete(String sqlId, Object params) { Map map = params == null ? null : beanToMap(params); - SqlBuffer sql = renderSqlTemplate(sqlId, map); + SqlBuffer sql = doGetSqlContent(sqlId, map); return jdbc.delete(sql); } @Override public boolean existSqlTemplate(String sqlId) { - return container.exist(sqlId, dialect.getDbVersion()); + return container.exist(sqlId, jdbc.getDbVersion()); } @Override public SqlBuffer getSqlContent(String sqlId, Object params) { Map map = params == null ? null : beanToMap(params); - return container.render(sqlId, map, dialect); + return doGetSqlContent(sqlId, map); } - protected SqlBuffer renderSqlTemplate(String sqlId, Map params) { - IMetaData tags = container.find(sqlId, dialect.getDbVersion()); + private SqlBuffer doGetSqlContent(String sqlId, Map params) { + IMetaData tags = container.find(sqlId, jdbc.getDbVersion()); if (log.isDebugEnabled()) { log.debug("Sql fragment from {}", tags.getRealPath()); } - return container.publish(tags, params, dialect); + return container.render(tags, params, jdbc.getSqlDialect()); } - /** 使用CamelNamingMapRowMapper **/ - private static class MapSqlBufferJdbcTemplate extends SqlBufferJdbcTemplate { - - public MapSqlBufferJdbcTemplate(SqlBufferJdbcTemplate parent) { - super(parent, new CamelNamingMapRowMapper()); - } - - @Override - protected RowToBeanMapper newRowToBeanMapper(Class clazz) { - MapToBeanConverter converter = DbTools.getMapToBeanConverter(); - return new TableRowToBeanMapper<>(clazz, converter, false, mapResultConvert); - } + @Override + public SqlBuffer renderSqlTemplate(String sqlContent, Object params) { + Map map = params == null ? null : beanToMap(params); + // 生成SQL语句 + return container.render(sqlContent, map, jdbc.getSqlDialect()); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/AllFieldColumn.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/AllFieldColumn.java index e98dbc3da4c22306bb95a8339860717410014c98..ffb0cb7e5c584ae147425db319c9b4012f34b90a 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/AllFieldColumn.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/AllFieldColumn.java @@ -41,6 +41,10 @@ public class AllFieldColumn implements Serializable return this.items.isEmpty(); } + public List items() { + return Collections.unmodifiableList(this.items); + } + /** * 查找主键字段 * diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/FunctionOrdering.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/FunctionOrdering.java new file mode 100644 index 0000000000000000000000000000000000000000..e02a9fd60e88fa707b8bc6b19f23962296f23e83 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/FunctionOrdering.java @@ -0,0 +1,16 @@ +package com.gitee.qdbp.jdbc.model; + +import com.gitee.qdbp.able.jdbc.ordering.Ordering; + +/** + * 带函数排序字段 + * + * @author zhaohuihua + * @version 20210509 + */ +public class FunctionOrdering extends Ordering { + + /** serialVersionUID **/ + private static final long serialVersionUID = 1L; + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/LikeValue.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/LikeValue.java deleted file mode 100644 index 4121343a4b7a97548bf55e4239e69df14995d259..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/LikeValue.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.gitee.qdbp.jdbc.model; - -import java.io.Serializable; - -/** - * Like Escape - * - * @author zhaohuihua - * @version 20201111 - */ -public class LikeValue implements Serializable { - - /** serialVersionUID **/ - private static final long serialVersionUID = 1L; - /** 转义字符 **/ - private final char escapeChar; - /** 包含特殊字符匹配的模式字符串: _匹配一个字符, %匹配零个或多个字符 **/ - private final String patternString; - - public LikeValue(String patternString) { - this.escapeChar = 0; - this.patternString = patternString; - } - - public LikeValue(char escapeChar, String patternString) { - this.escapeChar = escapeChar; - this.patternString = patternString; - } - - /** 转义字符 **/ - public char getEscapeChar() { - return escapeChar; - } - - /** 包含特殊字符匹配的模式字符串: _匹配一个字符, %匹配零个或多个字符 **/ - public String getPatternString() { - return patternString; - } - -} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SimpleFieldColumn.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SimpleFieldColumn.java index 1f1afe6d8c53df0e4ca58297b5aec671ecbbff11..7be28c2b64517b2ec6114cffc9c8a2ec380a3ddf 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SimpleFieldColumn.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SimpleFieldColumn.java @@ -25,27 +25,26 @@ public class SimpleFieldColumn implements Copyable, Serializable { private String columnText; /** Java字段类型 **/ private Class javaType; - /** SQL数据类型({@code java.sql.Types}) **/ - private Integer sqlType; + /** Jdbc数据类型 (java.sql.Types中的字段值) **/ + private Integer jdbcType; /** 是不是主键 **/ private boolean primaryKey; /** 新增时是否使用此字段 **/ private boolean columnInsertable = true; /** 修改时是否使用此字段 **/ private boolean columnUpdatable = true; - - // /** Whether the column is a unique key. **/ - // private boolean columnUnique; - // /** Whether the database column is nullable. **/ - // private boolean columnNullable = true; - // /** The SQL fragment that is used when generating the DDL for the column. **/ - // private String columnDefinition; - // /** The column length. (Applies only if a string-valued column is used.) **/ - // private int columnLength; - // /** The precision for a decimal (exact numeric) column. (Applies only if a decimal column is used.) **/ - // private int columnPrecision; - // /** (Optional) The scale for a decimal (exact numeric) column. (Applies only if a decimal column is used.) **/ - // private int columnScale; + /** Whether the column is a unique key. **/ + private boolean columnUnique = false; + /** Whether the database column is nullable. **/ + private boolean columnNullable = true; + /** The SQL fragment that is used when generating the DDL for the column. **/ + private String columnDefinition; + /** The column length. (Applies only if a string-valued column is used.) **/ + private Integer columnLength; + /** The precision for a decimal (exact numeric) column. (Applies only if a decimal column is used.) **/ + private Integer columnPrecision; + /** (Optional) The scale for a decimal (exact numeric) column. (Applies only if a decimal column is used.) **/ + private Integer columnScale; /** 默认值 **/ private Object columnDefault; @@ -113,15 +112,45 @@ public class SimpleFieldColumn implements Copyable, Serializable { this.javaType = javaType; } - /** SQL数据类型({@code java.sql.Types}) **/ - public Integer getSqlType() { - return sqlType; + /** + * Jdbc数据类型 + * + * @return jdbcType {@link java.sql.Types}中的字段值 + */ + public Integer getJdbcType() { + return jdbcType; } - /** SQL数据类型({@code java.sql.Types}) **/ - public void setSqlType(Integer sqlType) { + /** + * Jdbc数据类型 + * + * @param jdbcType {@link java.sql.Types}中的字段值 + */ + public void setJdbcType(Integer jdbcType) { checkReadonly(); - this.sqlType = sqlType; + this.jdbcType = jdbcType; + } + + /** + * Jdbc数据类型 + * + * @return jdbcType {@link java.sql.Types}中的字段值 + * @deprecated use {@link #getJdbcType()} + */ + @Deprecated + public Integer getSqlType() { + return this.getJdbcType(); + } + + /** + * Jdbc数据类型 + * + * @param jdbcType {@link java.sql.Types}中的字段值 + * @deprecated use {@link #setJdbcType(Integer)} + */ + @Deprecated + public void setSqlType(Integer jdbcType) { + this.setJdbcType(jdbcType); } /** 是不是主键 **/ @@ -168,6 +197,74 @@ public class SimpleFieldColumn implements Copyable, Serializable { this.columnUpdatable = columnUpdatable; } + /** 是不是唯一索引 **/ + public boolean isColumnUnique() { + return columnUnique; + } + + /** 是不是唯一索引 **/ + public void setColumnUnique(boolean columnUnique) { + this.columnUnique = columnUnique; + } + + /** 是否允许为空 **/ + public boolean isColumnNullable() { + return columnNullable; + } + + /** 是否允许为空 **/ + public void setColumnNullable(boolean columnNullable) { + this.columnNullable = columnNullable; + } + + /** 列定义信息 **/ + // columnDefinition="Decimal(10,2) default 1.00" + // columnDefinition="TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP" + // columnDefinition="VARCHAR(20) DEFAULT 'N/A'" // 字段串要用单引号括起来 + public String getColumnDefinition() { + return columnDefinition; + } + + /** 列定义信息 **/ + public void setColumnDefinition(String columnDefinition) { + this.columnDefinition = columnDefinition; + } + + /** 列内容长度 **/ + public Integer getColumnLength() { + return columnLength; + } + + /** 列内容长度 **/ + public void setColumnLength(Integer columnLength) { + this.columnLength = columnLength; + } + + /** 列数字精度, 如 DECIMAL(10,2)表示一共10位其中小数2位, precision=10, scale=2 **/ + public Integer getColumnPrecision() { + return columnPrecision; + } + + /** 列数字精度, 如 DECIMAL(10,2)表示一共10位其中小数2位, precision=10, scale=2 **/ + public void setColumnPrecision(Integer columnPrecision) { + this.columnPrecision = columnPrecision; + } + + /** 列小数位数, 如 DECIMAL(10,2)表示一共10位其中小数2位, precision=10, scale=2 **/ + public Integer getColumnScale() { + return columnScale; + } + + /** 列小数位数, 如 DECIMAL(10,2)表示一共10位其中小数2位, precision=10, scale=2 **/ + public void setColumnScale(Integer columnScale) { + this.columnScale = columnScale; + } + + /** 是否只读 **/ + public boolean isReadonly() { + return readonly; + } + /** * 与目标字段是否匹配(区分大小写) * @@ -218,7 +315,7 @@ public class SimpleFieldColumn implements Copyable, Serializable { instance.setColumnName(this.getColumnName()); instance.setColumnText(this.getColumnText()); instance.setJavaType(this.getJavaType()); - instance.setSqlType(this.getSqlType()); + instance.setJdbcType(this.getJdbcType()); instance.setPrimaryKey(this.isPrimaryKey()); instance.setColumnInsertable(this.isColumnInsertable()); instance.setColumnUpdatable(this.isColumnUpdatable()); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SqlFile.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SqlFile.java new file mode 100644 index 0000000000000000000000000000000000000000..7d12b4d327096a3b60324340893ee4995935251e --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/SqlFile.java @@ -0,0 +1,149 @@ +package com.gitee.qdbp.jdbc.model; + +import java.io.Serializable; +import java.net.URL; +import java.util.Comparator; + +/** + * SQL文件 + * + * @author zhaohuihua + * @version 20210206 + */ +public class SqlFile implements Serializable, Comparable { + + /** serialVersionUID **/ + private static final long serialVersionUID = 1L; + /** URL路径 **/ + private URL urlPath; + /** 绝对路径 **/ + private String absolutePath; + /** 相对路径 **/ + private String relativePath; + /** 更新时间 **/ + private Long lastUpdateTime; + + /** 绝对路径 **/ + public URL getUrlPath() { + return urlPath; + } + + /** URL路径 **/ + public void setUrlPath(URL urlPath) { + this.urlPath = urlPath; + } + + /** 绝对路径 **/ + public String getAbsolutePath() { + return absolutePath; + } + + /** 绝对路径 **/ + public void setAbsolutePath(String absolutePath) { + this.absolutePath = absolutePath; + } + + /** 相对路径 **/ + public String getRelativePath() { + return relativePath; + } + + /** 相对路径 **/ + public void setRelativePath(String relativePath) { + this.relativePath = relativePath; + } + + /** 最后更新时间 **/ + public Long getLastUpdateTime() { + return lastUpdateTime; + } + + /** 最后更新时间 **/ + public void setLastUpdateTime(Long updateTime) { + this.lastUpdateTime = updateTime; + } + + @Override + public String toString() { + return this.absolutePath; + } + + /** 排序: file优先于jar, 其余按更新时间降序 **/ + @Override + public int compareTo(SqlFile other) { + return DEFAULT_COMPARATOR.compare(this, other); + } + + private static Comparator DEFAULT_COMPARATOR = new DefaultComparator(); + + public static class DefaultComparator implements Comparator { + + protected String getUrlProtocol(SqlFile sqlFile) { + return sqlFile.getUrlPath().getProtocol(); + } + + @Override + public int compare(SqlFile curr, SqlFile that) { + if (curr == that) { + return 0; + } + if (that == null) { + return -1; + } + + // 相对路径升序 + int relativePathValue; + if (that.getRelativePath() == null && curr.getRelativePath() == null) { + relativePathValue = 0; + } else if (curr.getRelativePath() == null) { + relativePathValue = 1; + } else if (that.getRelativePath() == null) { + relativePathValue = -1; + } else { + relativePathValue = curr.getRelativePath().compareTo(that.getRelativePath()); + } + if (relativePathValue != 0) { + return relativePathValue; + } + + // file优先于jar + int absolutePathValue; + if (that.getUrlPath() == null && curr.getUrlPath() == null) { + absolutePathValue = 0; + } else if (that.getUrlPath() == null) { + absolutePathValue = -1; + } else if (curr.getUrlPath() == null) { + absolutePathValue = 1; + } else { + String currProtocol = getUrlProtocol(curr); + String thatProtocol = getUrlProtocol(that); + if (currProtocol.equals(thatProtocol)) { + absolutePathValue = 0; + } else if (currProtocol.contains("jar")) { + absolutePathValue = 1; // 下降 + } else if (thatProtocol.contains("jar")) { + absolutePathValue = -1; // 上升 + } else { + absolutePathValue = 0; + } + } + if (absolutePathValue != 0) { + return absolutePathValue; + } + + // 更新时间降序 + int updateTimeValue; + if (that.getLastUpdateTime() == null && curr.getLastUpdateTime() == null) { + updateTimeValue = 0; + } else if (curr.getLastUpdateTime() == null) { + updateTimeValue = 1; + } else if (that.getLastUpdateTime() == null) { + updateTimeValue = -1; + } else { + // 降序: 对方比本方 + updateTimeValue = that.getLastUpdateTime().compareTo(curr.getLastUpdateTime()); + } + return updateTimeValue; + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TablesFieldColumn.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TablesFieldColumn.java index 4539e47bd5c54c20da6b713150963bacba468b8e..43940adf5357775c8acbbd9e38d3c602a577e60d 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TablesFieldColumn.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TablesFieldColumn.java @@ -30,15 +30,15 @@ public class TablesFieldColumn extends SimpleFieldColumn { /** 构造函数, fieldName或columnName带有表别名将会拆分并保存到tableAlias字段 **/ public TablesFieldColumn(String fieldName, String columnName) { - this.setFieldName(fieldName); - this.setColumnName(columnName); + this.doSetFieldName(fieldName); + this.doSetColumnName(columnName); } /** 构造函数, fieldName或columnName带有表别名将会拆分并保存到tableAlias字段 **/ public TablesFieldColumn(String fieldName, String columnName, String resultField) { - this.setFieldName(fieldName); - this.setColumnName(columnName); - this.setResultField(resultField); + this.doSetFieldName(fieldName); + this.doSetColumnName(columnName); + this.resultField = resultField; } /** 表别名 **/ @@ -53,9 +53,14 @@ public class TablesFieldColumn extends SimpleFieldColumn { } /** 字段名(如果带有表别名, 会拆分并记录到tableAlias字段) **/ + @Override public void setFieldName(String fieldName) { - VerifyTools.requireNotBlank(fieldName, "fieldName"); checkReadonly(); + this.doSetFieldName(fieldName); + } + + private void doSetFieldName(String fieldName) { + VerifyTools.requireNotBlank(fieldName, "fieldName"); int dotIndex = fieldName.indexOf('.'); if (dotIndex < 0) { super.setFieldName(fieldName); @@ -68,9 +73,14 @@ public class TablesFieldColumn extends SimpleFieldColumn { } /** 数据表列名(如果带有表别名, 会拆分并记录到tableAlias字段) **/ + @Override public void setColumnName(String columnName) { - VerifyTools.requireNotBlank(columnName, "columnName"); checkReadonly(); + this.doSetColumnName(columnName); + } + + private void doSetColumnName(String columnName) { + VerifyTools.requireNotBlank(columnName, "columnName"); int dotIndex = columnName.indexOf('.'); if (dotIndex < 0) { super.setColumnName(columnName); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TypedDbVariable.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TypedDbVariable.java index 5f9a6694db8ef42c35774187fecfff8ccc28a008..e800822f3e02811152ec5d34c292f6eb7527461e 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TypedDbVariable.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/model/TypedDbVariable.java @@ -13,30 +13,60 @@ public class TypedDbVariable implements Serializable { /** serialVersionUID **/ private static final long serialVersionUID = 1L; - /** SQL数据类型({@code java.sql.Types}) **/ - private Integer sqlType; + /** Jdbc数据类型({@code java.sql.Types}) **/ + private Integer jdbcType; /** 变量值 **/ private Object value; /** * 构造函数 * - * @param sqlType SQL数据类型({@code java.sql.Types}) + * @param jdbcType Jdbc数据类型({@code java.sql.Types}) * @param value 变量值 */ - public TypedDbVariable(int sqlType, Object value) { - this.sqlType = sqlType; + public TypedDbVariable(int jdbcType, Object value) { + this.jdbcType = jdbcType; this.value = value; } - /** SQL数据类型({@code java.sql.Types}) **/ + /** + * Jdbc数据类型 + * + * @return jdbcType {@link java.sql.Types}中的字段值 + */ + public Integer getJdbcType() { + return jdbcType; + } + + /** + * Jdbc数据类型 + * + * @param jdbcType {@link java.sql.Types}中的字段值 + */ + public void setJdbcType(Integer jdbcType) { + this.jdbcType = jdbcType; + } + + /** + * Jdbc数据类型 + * + * @return jdbcType {@link java.sql.Types}中的字段值 + * @deprecated use {@link #getJdbcType()} + */ + @Deprecated public Integer getSqlType() { - return sqlType; + return this.getJdbcType(); } - /** SQL数据类型({@code java.sql.Types}) **/ - public void setSqlType(Integer sqlType) { - this.sqlType = sqlType; + /** + * Jdbc数据类型 + * + * @param jdbcType {@link java.sql.Types}中的字段值 + * @deprecated use {@link #setJdbcType(Integer)} + */ + @Deprecated + public void setSqlType(Integer jdbcType) { + this.setJdbcType(jdbcType); } /** 变量值 **/ diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryEndsWithOperator.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryEndsWithOperator.java index ff3c6775137fcb38d331071c344570377642d407..543182dbe612fdbd6ab77990acb38f24ed0e3dc4 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryEndsWithOperator.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryEndsWithOperator.java @@ -1,10 +1,12 @@ package com.gitee.qdbp.jdbc.operator.where; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; import com.gitee.qdbp.jdbc.operator.base.DbAbstractOperator; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.utils.DbTools; /** * 二元EndsWith运算符 @@ -23,7 +25,11 @@ public class DbBinaryEndsWithOperator extends DbAbstractOperator implements DbBi @Override public SqlBuffer buildSql(String columnName, Object columnValue, SqlDialect dialect) { - return new SqlBuilder().ad(columnName).ad(dialect.buildEndsWithSql(columnValue)).out(); + if (columnValue instanceof LikeValue) { + throw new IllegalArgumentException("EndsWith not support 'LikeValue', please use 'like'."); + } + String stringValue = columnValue == null ? null : DbTools.unwrapDbVariable(columnValue).toString(); + return new SqlBuilder().ad(columnName).ad(dialect.buildEndsWithSql(stringValue)).out(); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryLikeOperator.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryLikeOperator.java index 8b006bd893ca939a14cdbd82925b6e2bd5cc3e83..e0e9b2cc3e9a9c9859be541fb40acc47e66e18e0 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryLikeOperator.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryLikeOperator.java @@ -1,10 +1,12 @@ package com.gitee.qdbp.jdbc.operator.where; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; import com.gitee.qdbp.jdbc.operator.base.DbAbstractOperator; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.utils.DbTools; /** * 二元Like运算符 @@ -23,7 +25,13 @@ public class DbBinaryLikeOperator extends DbAbstractOperator implements DbBinary @Override public SqlBuffer buildSql(String columnName, Object columnValue, SqlDialect dialect) { - return new SqlBuilder().ad(columnName).ad(dialect.buildLikeSql(columnValue)).out(); + if (columnValue instanceof LikeValue) { + LikeValue likeValue = (LikeValue) columnValue; + return new SqlBuilder().ad(columnName).ad(dialect.buildLikeSql(likeValue)).out(); + } else { + String stringValue = columnValue == null ? null : DbTools.unwrapDbVariable(columnValue).toString(); + return new SqlBuilder().ad(columnName).ad(dialect.buildLikeSql(stringValue)).out(); + } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotEndsWithOperator.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotEndsWithOperator.java new file mode 100644 index 0000000000000000000000000000000000000000..d11f411176fcf2dc41552f18274ef23a7a1ac3e6 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotEndsWithOperator.java @@ -0,0 +1,36 @@ +package com.gitee.qdbp.jdbc.operator.where; + +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; +import com.gitee.qdbp.jdbc.operator.base.DbAbstractOperator; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.utils.DbTools; + +/** + * 二元NotEndsWith运算符 + * + * @author zhaohuihua + * @version 20201102 + * @since 3.2.6 + */ +public class DbBinaryNotEndsWithOperator extends DbAbstractOperator implements DbBinaryOperator { + + /** 版本序列号 **/ + private static final long serialVersionUID = 1L; + + public DbBinaryNotEndsWithOperator() { + super("Not Ends", "NotEnds", "Not Ends With", "NotEndsWith"); + } + + @Override + public SqlBuffer buildSql(String columnName, Object columnValue, SqlDialect dialect) { + if (columnValue instanceof LikeValue) { + throw new IllegalArgumentException("EndsWith not support 'LikeValue', please use 'like'."); + } + String stringValue = columnValue == null ? null : DbTools.unwrapDbVariable(columnValue).toString(); + return new SqlBuilder().ad(columnName).ad("NOT").ad(dialect.buildEndsWithSql(stringValue)).out(); + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotLikeOperator.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotLikeOperator.java index feac92f396f1bcfbdcebdb1019be5aa8c6f5abd5..b5478d34ea3507d8898105d610efaf43074e3416 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotLikeOperator.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotLikeOperator.java @@ -1,10 +1,12 @@ package com.gitee.qdbp.jdbc.operator.where; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; import com.gitee.qdbp.jdbc.operator.base.DbAbstractOperator; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.utils.DbTools; /** * 二元Like运算符 @@ -23,7 +25,13 @@ public class DbBinaryNotLikeOperator extends DbAbstractOperator implements DbBin @Override public SqlBuffer buildSql(String columnName, Object columnValue, SqlDialect dialect) { - return new SqlBuilder().ad(columnName).ad("NOT").ad(dialect.buildLikeSql(columnValue)).out(); + if (columnValue instanceof LikeValue) { + LikeValue likeValue = (LikeValue) columnValue; + return new SqlBuilder().ad(columnName).ad("NOT").ad(dialect.buildLikeSql(likeValue)).out(); + } else { + String stringValue = columnValue == null ? null : DbTools.unwrapDbVariable(columnValue).toString(); + return new SqlBuilder().ad(columnName).ad("NOT").ad(dialect.buildLikeSql(stringValue)).out(); + } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotStartsWithOperator.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotStartsWithOperator.java new file mode 100644 index 0000000000000000000000000000000000000000..a2b84a6106b4ec5fbafdd22dc2ea4fc78e0d653a --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryNotStartsWithOperator.java @@ -0,0 +1,36 @@ +package com.gitee.qdbp.jdbc.operator.where; + +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; +import com.gitee.qdbp.jdbc.operator.base.DbAbstractOperator; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.utils.DbTools; + +/** + * 二元NotStartsWith运算符 + * + * @author zhaohuihua + * @version 20201102 + * @since 3.2.6 + */ +public class DbBinaryNotStartsWithOperator extends DbAbstractOperator implements DbBinaryOperator { + + /** 版本序列号 **/ + private static final long serialVersionUID = 1L; + + public DbBinaryNotStartsWithOperator() { + super("Not Starts", "NotStarts", "Not Starts With", "NotStartsWith"); + } + + @Override + public SqlBuffer buildSql(String columnName, Object columnValue, SqlDialect dialect) { + if (columnValue instanceof LikeValue) { + throw new IllegalArgumentException("StartsWith not support 'LikeValue', please use 'like'."); + } + String stringValue = columnValue == null ? null : DbTools.unwrapDbVariable(columnValue).toString(); + return new SqlBuilder().ad(columnName).ad("NOT").ad(dialect.buildStartsWithSql(stringValue)).out(); + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryStartsWithOperator.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryStartsWithOperator.java index a5f46fedacd88ee1d89d33af900c06764c2603d4..359326ab8334dc9c9ef06d05e20e449e5f3f9c68 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryStartsWithOperator.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/operator/where/DbBinaryStartsWithOperator.java @@ -1,10 +1,12 @@ package com.gitee.qdbp.jdbc.operator.where; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; import com.gitee.qdbp.jdbc.operator.base.DbAbstractOperator; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.utils.DbTools; /** * 二元StartsWith运算符 @@ -23,7 +25,11 @@ public class DbBinaryStartsWithOperator extends DbAbstractOperator implements Db @Override public SqlBuffer buildSql(String columnName, Object columnValue, SqlDialect dialect) { - return new SqlBuilder().ad(columnName).ad(dialect.buildStartsWithSql(columnValue)).out(); + if (columnValue instanceof LikeValue) { + throw new IllegalArgumentException("StartsWith not support 'LikeValue', please use 'like'."); + } + String stringValue = columnValue == null ? null : DbTools.unwrapDbVariable(columnValue).toString(); + return new SqlBuilder().ad(columnName).ad(dialect.buildStartsWithSql(stringValue)).out(); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/BeanToMapConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/BeanToMapConverter.java deleted file mode 100644 index 2a6f662efd9afb4541fbc970e391bcd68cc318d9..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/BeanToMapConverter.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.gitee.qdbp.jdbc.plugins; - -import java.util.Map; - -/** - * JavaBean到Map的转换
- * 注意: 枚举和日期作为基本类型处理, 不作转换! - * - * @author zhaohuihua - * @version 20200901 - * @since 3.2.0 - */ -public interface BeanToMapConverter { - - /** - * 将Java对象转换为Map
- * 注意: 枚举和日期作为基本类型处理, 不作转换! - * - * @param bean Java对象 - * @return Map对象 - */ - Map convert(Object bean); - - /** - * 将Java对象转换为Map
- * 注意: 枚举和日期作为基本类型处理, 不作转换! - * - * @param bean Java对象 - * @param deep 是否递归转换子对象 - * @param clearBlankValue 是否清除空值 - * @return Map对象 - */ - Map convert(Object bean, boolean deep, boolean clearBlankValue); - -} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/ColumnFunctionSqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/ColumnFunctionSqlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..32a982becbe6de8639494f0709c1983e780460ad --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/ColumnFunctionSqlBuilder.java @@ -0,0 +1,29 @@ +package com.gitee.qdbp.jdbc.plugins; + +import com.gitee.qdbp.jdbc.sql.SqlBuffer; + +/** + * 列函数的SQL生成接口 + * + * @author zhaohuihua + * @version 20210511 + */ +public interface ColumnFunctionSqlBuilder { + + /** + * 获取支持的函数类型 + * + * @return 函数类型 + */ + String[] supported(); + + /** + * 生成SQL语句 + * + * @param columnName 列名 + * @param functionParams 函数参数 + * @param dialect SQL方言处理类 + * @return 排序SQL语句 + */ + SqlBuffer buildSql(String columnName, String functionParams, SqlDialect dialect); +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/ColumnNameResolver.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/ColumnNameResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..f804b5aee9ec4ff99843de05fe87e3f80fe684be --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/ColumnNameResolver.java @@ -0,0 +1,21 @@ +package com.gitee.qdbp.jdbc.plugins; + +import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; + +/** + * 字段名转列名 + * + * @author zhaohuihua + * @version 20210510 + */ +public interface ColumnNameResolver { + + /** + * 获取列名 + * + * @param fieldName 字段名 + * @return 列名, 如果不支持该字段将抛出异常 + * @throws UnsupportedFieldException 不支持的字段名 + */ + String getColumnName(String fieldName) throws UnsupportedFieldException; +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginContainer.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginContainer.java index 3eb3f288eaf929216b909d3b392c6c3cf45b481d..51586c17bbc946438b597e4d3e20da6aade6586b 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginContainer.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/DbPluginContainer.java @@ -12,39 +12,59 @@ import org.slf4j.LoggerFactory; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.support.DefaultConversionService; +import com.gitee.qdbp.able.convert.BeanToMapConverter; +import com.gitee.qdbp.able.convert.MapToBeanConverter; +import com.gitee.qdbp.able.convert.ObjectTypeConverter; +import com.gitee.qdbp.able.exception.ResourceNotFoundException; import com.gitee.qdbp.able.jdbc.base.OrderByCondition; import com.gitee.qdbp.able.jdbc.base.UpdateCondition; import com.gitee.qdbp.able.jdbc.base.WhereCondition; import com.gitee.qdbp.jdbc.model.DbType; +import com.gitee.qdbp.jdbc.model.DbVersion; import com.gitee.qdbp.jdbc.model.MainDbType; import com.gitee.qdbp.jdbc.plugins.impl.BatchInsertByMultiRowsExecutor; -import com.gitee.qdbp.jdbc.plugins.impl.BatchInsertByUnionAllFromDualExecutor; import com.gitee.qdbp.jdbc.plugins.impl.BatchOperateByForEachExecutor; +import com.gitee.qdbp.jdbc.plugins.impl.BatchOperateByMultiSqlExecutor; import com.gitee.qdbp.jdbc.plugins.impl.BatchUpdateByCaseWhenExecutor; import com.gitee.qdbp.jdbc.plugins.impl.BatchUpdateByJoinUsingExecutor; +import com.gitee.qdbp.jdbc.plugins.impl.ConfigableJdbcDataTypeResolver; import com.gitee.qdbp.jdbc.plugins.impl.DataSourceDbVersionFinder; import com.gitee.qdbp.jdbc.plugins.impl.FastJsonBeanToMapConverter; import com.gitee.qdbp.jdbc.plugins.impl.FastJsonDbConditionConverter; import com.gitee.qdbp.jdbc.plugins.impl.NoneEntityDataStateFillStrategy; +import com.gitee.qdbp.jdbc.plugins.impl.OrderByFunctionSqlBuilder; import com.gitee.qdbp.jdbc.plugins.impl.PersistenceAnnotationTableScans; import com.gitee.qdbp.jdbc.plugins.impl.SimpleDbOperatorContainer; import com.gitee.qdbp.jdbc.plugins.impl.SimpleEntityFieldFillStrategy; +import com.gitee.qdbp.jdbc.plugins.impl.SimpleJdbcNamingConverter; import com.gitee.qdbp.jdbc.plugins.impl.SimpleRawValueConverter; import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlDialect; -import com.gitee.qdbp.jdbc.plugins.impl.SpringSqlFileScanner; import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlFormatter; +import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlFragmentOptions; +import com.gitee.qdbp.jdbc.plugins.impl.SimpleTablesFieldColumnParser; import com.gitee.qdbp.jdbc.plugins.impl.SimpleVarToDbValueConverter; import com.gitee.qdbp.jdbc.plugins.impl.SpringMapToBeanConverter; +import com.gitee.qdbp.jdbc.plugins.impl.SpringSqlFileScanner; +import com.gitee.qdbp.jdbc.plugins.impl.SpringTypeConverter; +import com.gitee.qdbp.jdbc.result.CamelNamingRowToMapMapper; +import com.gitee.qdbp.jdbc.result.RowToBeanMapper.FactoryOfTable; +import com.gitee.qdbp.jdbc.result.RowToBeanMapper.FactoryOfTables; +import com.gitee.qdbp.jdbc.result.RowToMapMapper; +import com.gitee.qdbp.jdbc.result.TableRowToBeanMapper; +import com.gitee.qdbp.jdbc.result.TablesRowToProperyMapper; +import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; import com.gitee.qdbp.jdbc.support.ConversionServiceAware; 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.staticize.tags.base.Taglib; +import com.gitee.qdbp.tools.property.PropertyContainer; import com.gitee.qdbp.tools.utils.Config; import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.StringTools; /** - * 自定义插件容器 + * 插件中心 * * @author zhaohuihua * @version 190601 @@ -58,12 +78,22 @@ public class DbPluginContainer { /** 初始化全局实例, 应在第1次调用defaults()之前执行 **/ public static void init(DbPluginContainer container) { + if (DEFAULTS == container) { + return; + } if (DEFAULTS != null) { + // @formatter:off // 正确的操作顺序是先调DbPluginContainer.init(), 再使用DbPluginContainer.defaults() - log.debug("Default instance already initialized, executing again will override the global instance."); + log.debug("DbPluginContainer default instance already initialized, executing again will override the global instance."); + // @formatter:on } DEFAULTS = container; + // 检查和设置默认属性 checkAndSetDefaultPorperty(DEFAULTS); + // 初始化时扫描SQL模板 + if (DEFAULTS.getSqlFragmentOptions().isSqlTemplateScanOnStartup()) { + SqlFragmentContainer.defaults().initSqlFiles(); + } } /** @@ -77,7 +107,9 @@ public class DbPluginContainer { */ public static DbPluginContainer defaults() { if (DEFAULTS == null) { - log.debug("Default instance has not been initialized, the internal instance will be used."); + // @formatter:off + log.debug("DbPluginContainer default instance has not been initialized, the internal instance will be used."); + // @formatter:on DEFAULTS = InnerInstance.INSTANCE; } return DEFAULTS; @@ -121,15 +153,25 @@ public class DbPluginContainer { // 初始化默认的类型转换处理器 initDefaultConverter(plugins); + if (plugins.getNamingConverter() == null) { + plugins.setNamingConverter(new SimpleJdbcNamingConverter()); + } if (plugins.getAvailableDbTypes() == null) { plugins.addAvailableDbTypeClass(MainDbType.class); } if (plugins.getSqlTaglib() == null) { - plugins.setSqlTaglib(new Taglib("classpath:settings/dbtags/taglib.txt")); + plugins.setSqlTaglib(new Taglib("classpath:settings/qdbc/qdbc.taglib.txt")); + } + if (plugins.getJdbcDataTypeResolver() == null) { + String path = "classpath:settings/qdbc/qdbc.datatype.txt"; + plugins.setJdbcDataTypeResolver(new ConfigableJdbcDataTypeResolver(path)); } if (plugins.getTableInfoScans() == null) { plugins.setTableInfoScans(new PersistenceAnnotationTableScans()); } + if (plugins.getTablesFieldColumnParser() == null) { + plugins.setTablesFieldColumnParser(new SimpleTablesFieldColumnParser()); + } if (plugins.getEntityFieldFillStrategy() == null) { plugins.setEntityFieldFillStrategy(new SimpleEntityFieldFillStrategy()); } @@ -142,13 +184,23 @@ public class DbPluginContainer { if (plugins.getToDbValueConverter() == null) { plugins.setToDbValueConverter(new SimpleVarToDbValueConverter()); } + if (plugins.getObjectTypeConverter() == null) { + plugins.setObjectTypeConverter(new SpringTypeConverter()); + } + if (plugins.getRowToMapConverter() == null) { + plugins.setRowToMapConverter(new CamelNamingRowToMapMapper()); + } + if (plugins.getTableRowToBeanFactory() == null) { + plugins.setTableRowToBeanFactory(new TableRowToBeanMapper.Factory()); + } + if (plugins.getTablesRowToBeanFactory() == null) { + plugins.setTablesRowToBeanFactory(new TablesRowToProperyMapper.Factory()); + } if (plugins.getMapToBeanConverter() == null) { // 由于fastjson的TypeUtils.castToEnum()逻辑存在硬伤, 无法做到数字枚举值的自定义转换 // container.setMapToBeanConverter(new FastJsonMapToBeanConverter()); // 改为SpringMapToBeanConverter - SpringMapToBeanConverter converter = new SpringMapToBeanConverter(); - converter.setConversionService(plugins.getConversionService()); - plugins.setMapToBeanConverter(converter); + plugins.setMapToBeanConverter(new SpringMapToBeanConverter()); } if (plugins.getBeanToMapConverter() == null) { plugins.setBeanToMapConverter(new FastJsonBeanToMapConverter()); @@ -171,20 +223,34 @@ public class DbPluginContainer { if (plugins.getSqlFileScanner() == null) { plugins.setSqlFileScanner(new SpringSqlFileScanner()); } + if (plugins.getSqlFragmentOptions() == null) { + plugins.setSqlFragmentOptions(new SimpleSqlFragmentOptions()); + } + if (plugins.getOrderBySqlBuilders().isEmpty()) { + plugins.addOrderBySqlBuilder(new OrderByFunctionSqlBuilder()); + } if (plugins.getDefaultBatchInsertExecutor() == null) { plugins.setDefaultBatchInsertExecutor(new BatchOperateByForEachExecutor()); } if (plugins.getDefaultBatchUpdateExecutor() == null) { plugins.setDefaultBatchUpdateExecutor(new BatchOperateByForEachExecutor()); } - // 初始化公共的批量操作处理器(专用的放前面,通用的放后面) + // 初始化公共的批量操作处理器(通用的放前面,专用的放后面) if (plugins.getBatchInsertExecutors().isEmpty()) { - plugins.addBatchInsertExecutor(new BatchInsertByUnionAllFromDualExecutor()); - plugins.addBatchInsertExecutor(new BatchInsertByMultiRowsExecutor()); + plugins.addBatchInsertExecutor(new BatchOperateByMultiSqlExecutor()); // mysql,oracle + plugins.addBatchInsertExecutor(new BatchInsertByMultiRowsExecutor()); // mysql,db2 + + // BatchInsertByUnionAllFromDualExecutor有问题 + // Oracle的CLOB字段批量新增时, 如果CLOB字段值既有小于4000的也有大于4000的 + // 就会报错ORA-01790: 表达式必须具有与对应表达式相同的数据类型 + // 原因是JdbcTemplate对CLOB的处理, 小于4000的用ps.setString(), 超过4000的用ps.setClob() + // 详见StatementCreatorUtils.setValue() + // plugins.addBatchInsertExecutor(new BatchInsertByUnionAllFromDualExecutor()); } if (plugins.getBatchUpdateExecutors().isEmpty()) { - plugins.addBatchUpdateExecutor(new BatchUpdateByJoinUsingExecutor()); - plugins.addBatchUpdateExecutor(new BatchUpdateByCaseWhenExecutor()); + plugins.addBatchInsertExecutor(new BatchOperateByMultiSqlExecutor()); // mysql,oracle + plugins.addBatchUpdateExecutor(new BatchUpdateByCaseWhenExecutor()); // mysql,db2 + plugins.addBatchUpdateExecutor(new BatchUpdateByJoinUsingExecutor()); // mysql } // 设置插件的ConversionService @@ -219,27 +285,23 @@ public class DbPluginContainer { if (toDbValueConverter instanceof ConversionServiceAware) { ((ConversionServiceAware) toDbValueConverter).setConversionService(conversionService); } + ObjectTypeConverter typeConverter = plugins.getObjectTypeConverter(); + if (typeConverter instanceof ConversionServiceAware) { + ((ConversionServiceAware) typeConverter).setConversionService(conversionService); + } } } /** 数据库配置选项 **/ - private Config dbConfig; + private PropertyContainer dbConfig; /** 设置数据库配置选项 **/ public void setDbConfig(Properties config) { - this.dbConfig = new Config(config); - } - - /** 设置数据库配置选项 **/ - public void addDbConfig(String key, String value) { - if (dbConfig == null) { - dbConfig = new Config(); - } - this.dbConfig.put(key, value); + this.dbConfig = new Config(config != null ? config : new Properties()); } /** 获取数据库配置选项 **/ - public Config getDbConfig() { + public PropertyContainer getDbConfig() { return this.dbConfig; } @@ -249,7 +311,7 @@ public class DbPluginContainer { * @param force 是否强制返回非空对象 * @return 配置选项 */ - public Config getDbConfig(boolean force) { + public PropertyContainer getDbConfig(boolean force) { if (dbConfig == null) { dbConfig = new Config(); } @@ -362,7 +424,30 @@ public class DbPluginContainer { * @since 3.2.0 */ public void setSqlTaglibPath(String taglibPath) { - this.sqlTaglib = new Taglib(taglibPath); + try { + this.sqlTaglib = new Taglib(taglibPath); + } catch (ResourceNotFoundException e) { // 兼容旧版本 + String oldPath = "settings/dbtags/taglib.txt"; + String newPath = "settings/qdbc/qdbc.taglib.txt"; + if (!taglibPath.contains(oldPath)) { + throw e; + } else { + this.sqlTaglib = new Taglib(StringTools.replace(taglibPath, oldPath, newPath)); + } + } + } + + /** JDBC数据类型转换: 将代码中写的数据类型转换为java.sql.Types中的数据类型 **/ + private JdbcDataTypeResolver jdbcDataTypeResolver; + + /** JDBC数据类型转换: 将代码中写的数据类型转换为java.sql.Types中的数据类型 **/ + public JdbcDataTypeResolver getJdbcDataTypeResolver() { + return jdbcDataTypeResolver; + } + + /** JDBC数据类型转换: 将代码中写的数据类型转换为java.sql.Types中的数据类型 **/ + public void setJdbcDataTypeResolver(JdbcDataTypeResolver jdbcDataTypeResolver) { + this.jdbcDataTypeResolver = jdbcDataTypeResolver; } /** Spring的类型转换处理类 **/ @@ -391,6 +476,19 @@ public class DbPluginContainer { return tableInfoScans; } + /** 多表关联列信息的解析器 **/ + private TablesFieldColumnParser tablesFieldColumnParser; + + /** 多表关联列信息的解析器 **/ + public TablesFieldColumnParser getTablesFieldColumnParser() { + return tablesFieldColumnParser; + } + + /** 多表关联列信息的解析器 **/ + public void setTablesFieldColumnParser(TablesFieldColumnParser tablesFieldColumnParser) { + this.tablesFieldColumnParser = tablesFieldColumnParser; + } + /** 实体类字段数据填充策略 **/ private EntityFieldFillStrategy entityFieldFillStrategy; /** 实体类逻辑删除数据状态填充策略 **/ @@ -416,6 +514,29 @@ public class DbPluginContainer { this.dataStateFillStrategy = dataStateFillStrategy; } + /** 命名转换接口 **/ + private JdbcNamingConverter namingConverter; + + /** + * 命名转换接口 + * + * @return 命名转换接口 + * @since 3.3.0 + */ + public JdbcNamingConverter getNamingConverter() { + return namingConverter; + } + + /** + * 命名转换接口 + * + * @param namingConverter 命名转换接口 + * @since 3.3.0 + */ + public void setNamingConverter(JdbcNamingConverter namingConverter) { + this.namingConverter = namingConverter; + } + /** 数据库原生值的转换处理类(sysdate, CURRENT_TIMESTAMP等) **/ private RawValueConverter rawValueConverter; @@ -442,6 +563,88 @@ public class DbPluginContainer { return toDbValueConverter; } + /** 对象类型转换处理类 **/ + private ObjectTypeConverter objectTypeConverter; + + /** + * 对象类型转换处理类 + * + * @return 对象类型转换处理类 + * @since 3.3.0 + */ + public ObjectTypeConverter getObjectTypeConverter() { + return objectTypeConverter; + } + + /** + * 对象类型转换处理类 + * + * @param objectTypeConverter 对象类型转换处理类 + * @since 3.3.0 + */ + public void setObjectTypeConverter(ObjectTypeConverter objectTypeConverter) { + this.objectTypeConverter = objectTypeConverter; + } + + /** Row到Map的转换处理类 **/ + private RowToMapMapper rowToMapConverter; + + /** Row到Map的转换处理类 **/ + public void setRowToMapConverter(RowToMapMapper rowToMapConverter) { + this.rowToMapConverter = rowToMapConverter; + } + + /** Row到Map的转换处理类 **/ + public RowToMapMapper getRowToMapConverter() { + return rowToMapConverter; + } + + /** Row到Bean的转换处理类的工厂类 (单表) **/ + private FactoryOfTable tableRowToBeanFactory; + + /** + * Row到Bean的转换处理类的工厂类 (单表) + * + * @return Row到Bean处理类工厂 + * @since 3.3.0 + */ + public FactoryOfTable getTableRowToBeanFactory() { + return tableRowToBeanFactory; + } + + /** + * Row到Bean的转换处理类的工厂类 (单表) + * + * @param tableRowToBeanFactory Row到Bean处理类工厂 + * @since 3.3.0 + */ + public void setTableRowToBeanFactory(FactoryOfTable tableRowToBeanFactory) { + this.tableRowToBeanFactory = tableRowToBeanFactory; + } + + /** Row到Bean的转换处理类的工厂类 (多表关联) **/ + private FactoryOfTables tablesRowToBeanFactory; + + /** + * Row到Bean的转换处理类的工厂类 (多表关联) + * + * @return Row到Bean处理类工厂 + * @since 3.3.0 + */ + public FactoryOfTables getTablesRowToBeanFactory() { + return tablesRowToBeanFactory; + } + + /** + * Row到Bean的转换处理类的工厂类 (多表关联) + * + * @param tablesRowToBeanFactory Row到Bean处理类工厂 + * @since 3.3.0 + */ + public void setTablesRowToBeanFactory(FactoryOfTables tablesRowToBeanFactory) { + this.tablesRowToBeanFactory = tablesRowToBeanFactory; + } + /** Map到JavaBean的转换处理类 **/ private MapToBeanConverter mapToBeanConverter; @@ -566,40 +769,78 @@ public class DbPluginContainer { this.sqlFileScanner = sqlFileScanner; } + /** SQL片断的选项 **/ + private SqlFragmentOptions sqlFragmentOptions; + + /** + * 获取SQL片断选项 + * + * @return SQL片断选项 + * @since 3.2.10 + */ + public SqlFragmentOptions getSqlFragmentOptions() { + return sqlFragmentOptions; + } + + /** + * 设置SQL片断选项 + * + * @param sqlFragmentOptions SQL片断选项 + * @since 3.2.10 + */ + public void setSqlFragmentOptions(SqlFragmentOptions sqlFragmentOptions) { + this.sqlFragmentOptions = sqlFragmentOptions; + } + /** 默认的批量新增处理类 **/ private BatchInsertExecutor defaultBatchInsertExecutor; /** 默认的批量更新处理类 **/ private BatchUpdateExecutor defaultBatchUpdateExecutor; - /** 批量新增处理类列表 **/ + /** 批量新增处理类列表 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ private List batchInsertExecutors = new ArrayList<>(); - /** 批量更新处理类列表 **/ + /** 批量更新处理类列表 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ private List batchUpdateExecutors = new ArrayList<>(); - /** 获取默认的批量新增处理类 **/ + /** 获取默认的批量新增处理类 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ public BatchInsertExecutor getDefaultBatchInsertExecutor() { return defaultBatchInsertExecutor; } - /** 设置默认的批量新增处理类 **/ + /** 设置默认的批量新增处理类 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ public void setDefaultBatchInsertExecutor(BatchInsertExecutor batchInsertExecutor) { this.defaultBatchInsertExecutor = batchInsertExecutor; } - /** 获取批量新增处理类列表 **/ + /** 获取批量新增处理类列表 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ public List getBatchInsertExecutors() { return this.batchInsertExecutors; } - /** 设置批量新增处理类列表 **/ + /** 设置批量新增处理类列表 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ public void setBatchInsertExecutors(List batchInsertExecutors) { this.batchInsertExecutors = batchInsertExecutors; } - /** 增加新增处理类 **/ + /** 增加批量新增处理类 (后加入的优先匹配) **/ public void addBatchInsertExecutor(BatchInsertExecutor batchInsertExecutor) { this.batchInsertExecutors.add(batchInsertExecutor); } + /** 获取批量新增处理类 (后加入的优先匹配) **/ + public BatchInsertExecutor getBatchInsertExecutor(DbVersion version) { + DbPluginContainer plugins = DbPluginContainer.defaults(); + List executors = plugins.getBatchInsertExecutors(); + if (executors != null && !executors.isEmpty()) { + for (int i = executors.size() - 1; i >= 0; i--) { + BatchInsertExecutor item = executors.get(i); + if (item.supports(version)) { + return item; + } + } + } + return plugins.getDefaultBatchInsertExecutor(); + } + /** 获取默认的批量更新处理类 **/ public BatchUpdateExecutor getDefaultBatchUpdateExecutor() { return defaultBatchUpdateExecutor; @@ -610,21 +851,36 @@ public class DbPluginContainer { this.defaultBatchUpdateExecutor = batchUpdateExecutor; } - /** 获取批量更新处理类列表 **/ + /** 获取批量更新处理类列表 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ public List getBatchUpdateExecutors() { return this.batchUpdateExecutors; } - /** 设置批量更新处理类列表 **/ + /** 设置批量更新处理类列表 (通用的放前面,专用的放后面;反向匹配,后加入的优先匹配) **/ public void setBatchUpdateExecutors(List batchUpdateExecutors) { this.batchUpdateExecutors = batchUpdateExecutors; } - /** 增加更新处理类 **/ + /** 增加更新处理类 (后加入的优先匹配) **/ public void addBatchUpdateExecutor(BatchUpdateExecutor batchUpdateExecutor) { this.batchUpdateExecutors.add(batchUpdateExecutor); } + /** 获取批量修改处理类 (后加入的优先匹配) **/ + public BatchUpdateExecutor getBatchUpdateExecutor(DbVersion version) { + DbPluginContainer plugins = DbPluginContainer.defaults(); + List executors = plugins.getBatchUpdateExecutors(); + if (executors != null && !executors.isEmpty()) { + for (int i = executors.size() - 1; i >= 0; i--) { + BatchUpdateExecutor item = executors.get(i); + if (item.supports(version)) { + return item; + } + } + } + return plugins.getDefaultBatchUpdateExecutor(); + } + /** Where条件的自定义条件构造器 **/ private List> whereSqlBuilders = new ArrayList<>(); @@ -657,18 +913,23 @@ public class DbPluginContainer { return null; } + /** 获取Where条件的自定义条件构造器列表 **/ + public List> getWhereSqlBuilders() { + return whereSqlBuilders; + } + /** DbUpdate条件的自定义条件构造器 **/ - private List> UpdateSqlBuilders = new ArrayList<>(); + private List> updateSqlBuilders = new ArrayList<>(); /** 增加DbUpdate条件的自定义条件构造器 **/ public void addUpdateSqlBuilder(UpdateSqlBuilder builder) { - UpdateSqlBuilders.add(builder); + updateSqlBuilders.add(builder); } /** 设置DbUpdate条件的自定义条件构造器 **/ public void setUpdateSqlBuilders(List> builders) { - UpdateSqlBuilders.clear(); - UpdateSqlBuilders.addAll(builders); + updateSqlBuilders.clear(); + updateSqlBuilders.addAll(builders); } /** 获取DbUpdate条件的自定义条件构造器 **/ @@ -676,12 +937,12 @@ public class DbPluginContainer { // public > B getUpdateSqlBuilder(Class type) { @SuppressWarnings("unchecked") public > B getUpdateSqlBuilder(Class type) { - for (UpdateSqlBuilder item : UpdateSqlBuilders) { + for (UpdateSqlBuilder item : updateSqlBuilders) { if (item.supported() == type) { return (B) item; } } - for (UpdateSqlBuilder item : UpdateSqlBuilders) { + for (UpdateSqlBuilder item : updateSqlBuilders) { if (item.supported() != null && item.supported().isAssignableFrom(type)) { return (B) item; } @@ -689,33 +950,43 @@ public class DbPluginContainer { return null; } + /** 获取DbUpdate条件的自定义条件构造器列表 **/ + public List> getUpdateSqlBuilders() { + return updateSqlBuilders; + } + /** 排序条件的自定义条件构造器 **/ - private List> OrderBySqlBuilders = new ArrayList<>(); + private List> orderBySqlBuilders = new ArrayList<>(); /** 增加排序条件的自定义条件构造器 **/ public void addOrderBySqlBuilder(OrderBySqlBuilder builder) { - OrderBySqlBuilders.add(builder); + orderBySqlBuilders.add(builder); } /** 设置排序条件的自定义条件构造器 **/ public void setOrderBySqlBuilders(List> builders) { - OrderBySqlBuilders.clear(); - OrderBySqlBuilders.addAll(builders); + orderBySqlBuilders.clear(); + orderBySqlBuilders.addAll(builders); } /** 获取排序条件的自定义条件构造器 **/ @SuppressWarnings("unchecked") public > B getOrderBySqlBuilder(Class type) { - for (OrderBySqlBuilder item : OrderBySqlBuilders) { + for (OrderBySqlBuilder item : orderBySqlBuilders) { if (item.supported() == type) { return (B) item; } } - for (OrderBySqlBuilder item : OrderBySqlBuilders) { + for (OrderBySqlBuilder item : orderBySqlBuilders) { if (item.supported() != null && item.supported().isAssignableFrom(type)) { return (B) item; } } return null; } + + /** 获取排序条件的自定义条件构造器列表 **/ + public List> getOrderBySqlBuilders() { + return orderBySqlBuilders; + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityDataStateFillStrategy.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityDataStateFillStrategy.java index de5beb09e0f2782604f87a34a258c8fc959c30e6..8bda393ceb20e8cb97427ad7a6fa21e0dc468390 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityDataStateFillStrategy.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityDataStateFillStrategy.java @@ -46,6 +46,20 @@ import com.gitee.qdbp.jdbc.model.AllFieldColumn; */ public interface EntityDataStateFillStrategy { + /** + * 复杂的逻辑删除填充策略 (为了向后兼容, 增加此接口)
+ * 之前判断是否支持逻辑删除, 是在EntityFieldFillExecutor中判断扫描到的字段列表中是否存在逻辑删除字段名
+ * 这是一个错误, 应该由EntityDataStateFillStrategy来处理这个逻辑, 因为有些项目中可能某些表用A字段,另一些表用B字段
+ * + * @author zhaohuihua + * @version 20210120 + */ + interface Complex extends EntityDataStateFillStrategy { + + /** 是否支持逻辑删除 **/ + boolean supportedLogicalDelete(AllFieldColumn allFields); + } + /** 逻辑删除字段名 **/ String getLogicalDeleteField(); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityFieldFillExecutor.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityFieldFillExecutor.java index 27be363f095715b106e47469640be15cdabb4a78..ff012d6e2fbbb018292b7925dd8be03121e19f0b 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityFieldFillExecutor.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/EntityFieldFillExecutor.java @@ -26,7 +26,8 @@ public class EntityFieldFillExecutor { private EntityFieldFillStrategy fieldFillStrategy; private EntityDataStateFillStrategy dataStateFillStrategy; - public EntityFieldFillExecutor(AllFieldColumn allFields, EntityFieldFillStrategy fieldFillStrategy, EntityDataStateFillStrategy dataStateFillStrategy) { + public EntityFieldFillExecutor(AllFieldColumn allFields, EntityFieldFillStrategy fieldFillStrategy, + EntityDataStateFillStrategy dataStateFillStrategy) { this.allFields = allFields; if (this.allFields.isEmpty()) { throw new IllegalArgumentException("fields is empty"); @@ -46,6 +47,9 @@ public class EntityFieldFillExecutor { * @return 是否支持 */ public boolean supportedTableLogicalDelete() { + if (dataStateFillStrategy instanceof EntityDataStateFillStrategy.Complex) { + return ((EntityDataStateFillStrategy.Complex) dataStateFillStrategy).supportedLogicalDelete(allFields); + } String logicalDeleteField = dataStateFillStrategy.getLogicalDeleteField(); if (VerifyTools.isBlank(logicalDeleteField)) { return false; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/JdbcDataTypeResolver.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/JdbcDataTypeResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..101dc4ca26f0d5b52f6e7336c841d574a0544ff8 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/JdbcDataTypeResolver.java @@ -0,0 +1,38 @@ +package com.gitee.qdbp.jdbc.plugins; + +/** + * JDBC数据类型转换: 将代码中写的数据类型转换为java.sql.Types中的数据类型 + * + * @author zhaohuihua + * @version 20210430 + */ +public interface JdbcDataTypeResolver { + + /** + * 将代码中写的数据类型转换为java.sql.Types中的数据类型 + * + * @param dataType 代码中的数据类型
+ * 1.来自SQL模板中, 如#{createTime,jdbcType=TIMESTAMP}
+ * 2.来自实体类上的注解, 如@Column(columnDefinition="TIMESTAMP NOT NULL") + * @return JdbcType java.sql.Types中的数据类型 + * @see java.sql.Types + */ + Integer parseDataType(String dataType); + + /** + * 将代码中写的数据类型转换为java.sql.Types中的数据类型 + * + * @param dataType 代码中的数据类型 + * @return JdbcType字段名 + */ + String getJdbcType(String dataType); + + /** 是否支持定义精度 **/ + boolean supportDefinePrecision(String dataType); + + /** 是否支持定义小数位 **/ + boolean supportDefineScale(String dataType); + + /** 是否支持定义长度 **/ + boolean supportDefineLength(String dataType); +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/NameConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/JdbcNamingConverter.java similarity index 72% rename from jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/NameConverter.java rename to jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/JdbcNamingConverter.java index 9708fab1dea3787be36c9787cd331b9059a5c7de..d3c59e9a1914ec2687d7bf3f28321fcfbde5e01e 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/NameConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/JdbcNamingConverter.java @@ -1,13 +1,13 @@ package com.gitee.qdbp.jdbc.plugins; /** - * 名称转换处理接口
+ * 命名转换处理接口
* 每个项目的处理方式不一样, 抽象成接口由各项目提供实现类 * * @author zhaohuihua * @version 190601 */ -public interface NameConverter { +public interface JdbcNamingConverter { /** BeanName转换为表名 **/ String beanNameToTableName(String beanName); @@ -22,14 +22,14 @@ public interface NameConverter { String columnNameToFieldName(String columnName); /** - * 名称转换类设置接口 + * 命名转换类设置接口 * * @author zhaohuihua * @version 190727 */ public static interface Aware { - /** 设置名称转换类 **/ - void setNameConverter(NameConverter nameConverter); + /** 设置命名转换类 **/ + void setNamingConverter(JdbcNamingConverter namingConverter); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/MapToBeanConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/MapToBeanConverter.java deleted file mode 100644 index 6116f53d531a94b552296e9a5531c680c542047e..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/MapToBeanConverter.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.gitee.qdbp.jdbc.plugins; - -import java.util.Map; - -/** - * Map到JavaBean的转换 - * - * @author zhaohuihua - * @version 20200201 - */ -public interface MapToBeanConverter { - - /** - * 将Map转换为Java对象 - * - * @param 目标类型 - * @param map Map - * @param clazz 目标Java类 - * @return Java对象 - */ - T convert(Map map, Class clazz); - - /** - * 将Map内容设置到Java对象中 - * - * @param map Map - * @param bean 目标对象 - */ - void fill(Map map, Object bean); -} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/OrderBySqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/OrderBySqlBuilder.java index 17de97b136133b3c02b797cfef103c070ef88612..2714c2dc3fa4fecaa2b6674421e38c16eff13171 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/OrderBySqlBuilder.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/OrderBySqlBuilder.java @@ -3,7 +3,6 @@ package com.gitee.qdbp.jdbc.plugins; import com.gitee.qdbp.able.jdbc.base.OrderByCondition; import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; import com.gitee.qdbp.jdbc.sql.SqlBuffer; -import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; /** * OrderBy SQL Builder @@ -24,9 +23,13 @@ public interface OrderBySqlBuilder { * 生成OrderBy SQL语句 * * @param condition 条件 - * @param sqlBuilder SQL生成参数 + * @param columnResolver 列名转换处理类
+ * 如果能取到QueryFragmentHelper就用new ColumnNameHelper()
+ * 如果取不到, 就用ColumnNamingConverter.defaults(); + * @param dialect SQL方言 * @return SQL语句 * @throws UnsupportedFieldException */ - SqlBuffer buildSql(T condition, QueryFragmentHelper sqlBuilder) throws UnsupportedFieldException; + SqlBuffer buildSql(T condition, ColumnNameResolver columnResolver, SqlDialect dialect) + throws UnsupportedFieldException; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlDialect.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlDialect.java index d931669da96b666269017a82a9515bbf0033fcdf..92fef0b53bf0299e107ae00521392d7dab67e23b 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlDialect.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlDialect.java @@ -1,6 +1,9 @@ package com.gitee.qdbp.jdbc.plugins; import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.able.jdbc.paging.Paging; import com.gitee.qdbp.jdbc.model.DbVersion; import com.gitee.qdbp.jdbc.sql.SqlBuffer; @@ -60,23 +63,37 @@ public interface SqlDialect { * @param date 指定日期 * @return TO_TIMESTAMP SQL语句
* ORACLE: TO_TIMESTAMP('2019-06-01 12:34:56.789', 'YYYY-MM-DD HH24:MI:SS.FF')
- * MYSQL: '2019-06-01 12:34:56.789'
+ * MYSQL : '2019-06-01 12:34:56.789'
*/ String variableToString(Date variable); /** * 生成LIKE SQL语句
* 如果fieldValue中包含%等特殊字符, 会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如: buildLikeSql("xxx"); 表示查找包含xxx的数据, 生成SQL: LIKE ('%'|| 'xxx' ||'%')
+ * 如: buildLikeSql("10%"); 表示查找包含10%的数据, 生成SQL: LIKE ('%'|| '10#%' ||'%') ESCAPE '#'
* 如果希望自己处理ESCAPE逻辑, 可传入LikeValue对象:
- * 如: new LikeValue('#', "%10#%");表示查找以10%结尾的数据
- * 如: new LikeValue("%HOT%COOL%");表示查找含有HOT和COOL的数据
+ * 如: buildLikeSql(new LikeValue('#', "%10#%")); 表示查找以10%结尾的数据, 生成SQL: LIKE '%10#%' ESCAPE '#'
+ * 如: buildLikeSql(new LikeValue("%HOT%COOL%")); 表示查找含有HOT和COOL的数据, 生成SQL: LIKE '%HOT%COOL%'
* * @param fieldValue 字段值, String或LikeValue * @return LIKE SQL语句
* ORACLE: LIKE ('%'|| ? ||'%')
- * MYSQL: LIKE CONCAT('%', ?, '%')
+ * MYSQL : LIKE CONCAT('%', ?, '%')
*/ - SqlBuffer buildLikeSql(Object fieldValue); + SqlBuffer buildLikeSql(String fieldValue); + + /** + * 生成LIKE SQL语句
+ * 如: buildLikeSql(new LikeValue('#', "%10#%")); 表示查找以10%结尾的数据, 生成SQL: LIKE '%10#%' ESCAPE '#'
+ * 如: buildLikeSql(new LikeValue("%HOT%COOL%")); 表示查找含有HOT和COOL的数据, 生成SQL: LIKE '%HOT%COOL%'
+ * + * @param fieldValue 字段值, String或LikeValue + * @return LIKE SQL语句
+ * ORACLE: LIKE ? ESCAPE ?
+ * MYSQL : LIKE ? ESCAPE ?
+ */ + SqlBuffer buildLikeSql(LikeValue fieldValue); /** * 生成LIKE SQL语句 @@ -84,9 +101,9 @@ public interface SqlDialect { * @param fieldValue 字段值, 不支持LikeValue * @return LIKE SQL语句
* ORACLE: LIKE ( ? ||'%')
- * MYSQL: LIKE CONCAT( ?, '%')
+ * MYSQL : LIKE CONCAT( ?, '%')
*/ - SqlBuffer buildStartsWithSql(Object fieldValue); + SqlBuffer buildStartsWithSql(String fieldValue); /** * 生成LIKE SQL语句 @@ -94,9 +111,9 @@ public interface SqlDialect { * @param fieldValue 字段值, 不支持LikeValue * @return LIKE SQL语句
* ORACLE: LIKE ('%'|| ? )
- * MYSQL: LIKE CONCAT('%', ? )
+ * MYSQL : LIKE CONCAT('%', ? )
*/ - SqlBuffer buildEndsWithSql(Object fieldValue); + SqlBuffer buildEndsWithSql(String fieldValue); /** * SqlDialect创建接口 @@ -114,4 +131,31 @@ public interface SqlDialect { */ SqlDialect create(DbVersion version); } + + /** + * SqlDialect创建接口的SimpleSqlDialect实现类 + * + * @author zhaohuihua + * @version 20201018 + */ + public static abstract class CacheCreator implements SqlDialect.Creator { + + // key=DbVersionCode + private Map sqlDialectMaps = new HashMap<>(); + + @Override + public SqlDialect create(DbVersion version) { + String versionCode = version.toVersionString(); + if (sqlDialectMaps.containsKey(versionCode)) { + return sqlDialectMaps.get(versionCode); + } else { + SqlDialect dialect = newSqlDialect(version); + sqlDialectMaps.put(versionCode, dialect); + return dialect; + } + } + + /** 根据DbVersion生成SqlDialect **/ + protected abstract SqlDialect newSqlDialect(DbVersion version); + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFileScanner.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFileScanner.java index 1c50a563c80d97b44ec9ef8c9eb27dd9fb5f18a9..0ed106757416b7758479eb1c3e4db6f700d2285b 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFileScanner.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFileScanner.java @@ -1,8 +1,9 @@ package com.gitee.qdbp.jdbc.plugins; import java.io.IOException; -import java.net.URL; +import java.util.Date; import java.util.List; +import com.gitee.qdbp.jdbc.model.SqlFile; /** * SQL模板文件扫描接口 @@ -16,7 +17,8 @@ public interface SqlFileScanner { /** * 扫描SQL模板文件 * + * @param lastUpdateTime 最后更新时间, 如果不为空, 则只扫描该时间之后的文件 * @return SQL模板文件列表 */ - List scanSqlFiles() throws IOException; + List scanSqlFiles(Date lastUpdateTime) throws IOException; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFragmentOptions.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFragmentOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..00d0218e38dd80945581340c7d90e6febe9128db --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/SqlFragmentOptions.java @@ -0,0 +1,45 @@ +package com.gitee.qdbp.jdbc.plugins; + +/** + * SQL片断的选项 + * + * @author zhaohuihua + * @version 20201124 + * @since 3.2.10 + */ +public interface SqlFragmentOptions { + + /** + * 是否在系统启动时扫描SQL文件
+ * 如果启动时不扫描, 则首次获取SQL模板时会扫描(很慢)
+ * 所以, 一般情况下都应该配置为true, 除非系统中未使用到SQL模板 + * + * @return 是否在系统启动时扫描SQL文件 + */ + boolean isSqlTemplateScanOnStartup(); + + /** + * 是否允许通过fragmentId获取SQL片断
+ * 如果不允许通过fragmentId获取SQL片断, 那么只能通过 fileName:fragmentId 获取SQL片断
+ * 例如 global.recursive.sql 中存在 recursive.list.children.query 片断
+ * 如果不允许, 那么只能通过 global.recursive:recursive.list.children.query 获取
+ * 否则 global.recursive:recursive.list.children.query 和 recursive.list.children.query 都可以 + * + * @return 是否允许通过fragmentId获取SQL片断 + */ + boolean isUseFragmentIdQuery(); + + /** + * 是否输出SQL片断冲突日志
+ * 如果希望使用fileName:fragmentId来获取SQL片断, 则可能有很多fragmentId是相同的
+ * 例如 XxxDao.sql 和 YyyDao.sql 中都有list方法, 此时:
+ * 1. conflictLogsEnabled设置为false
+ * 2. 使用 XxxDao:list 和 YyyDao:list 来获取SQL片断
+ * + * @return 是否输出SQL片断冲突日志 + * @deprecated 已不存在该选项的应用场景:
+ * 实现机制修改为扫描时不检查冲突, 只有根据sqlId获取SQL片断时, 如果获取到多个才输出冲突日志 + */ + @Deprecated + boolean isConflictLogsEnabled(); +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/TablesFieldColumnParser.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/TablesFieldColumnParser.java new file mode 100644 index 0000000000000000000000000000000000000000..4427916cb6bac01c29afdbc6efc6072c737434e2 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/TablesFieldColumnParser.java @@ -0,0 +1,20 @@ +package com.gitee.qdbp.jdbc.plugins; + +import java.util.List; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.jdbc.model.TablesFieldColumn; + +/** + * 多表关联列信息的解析器 + * + * @author zhaohuihua + * @version 20210306 + */ +public interface TablesFieldColumnParser { + + /** 表别名与列别名的分隔符 **/ + String getTableAliasSeparator(); + + /** 解析多表关联的列信息 **/ + List parseFieldColumns(TableJoin tables); +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/UpdateSqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/UpdateSqlBuilder.java index 5a01b9b0d5a9d616c4a2f952018d98395e63090a..a9892d04de424f9d058c73d1c709ea209f1d1477 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/UpdateSqlBuilder.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/UpdateSqlBuilder.java @@ -3,7 +3,6 @@ package com.gitee.qdbp.jdbc.plugins; import com.gitee.qdbp.able.jdbc.base.UpdateCondition; import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; import com.gitee.qdbp.jdbc.sql.SqlBuffer; -import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; /** * Update SQL Builder @@ -21,13 +20,17 @@ public interface UpdateSqlBuilder { Class supported(); /** - * 生成Update SQL语句 + * 生成OrderBy SQL语句 * * @param condition 条件 - * @param sqlBuilder SQL生成参数 + * @param columnResolver 列名转换处理类
+ * 如果能取到QueryFragmentHelper就用new ColumnNameHelper()
+ * 如果取不到, 就用ColumnNamingConverter.defaults(); + * @param dialect SQL方言 * @return SQL语句 * @throws UnsupportedFieldException */ - SqlBuffer buildSql(T condition, QueryFragmentHelper sqlBuilder) throws UnsupportedFieldException; + SqlBuffer buildSql(T condition, ColumnNameResolver columnResolver, SqlDialect dialect) + throws UnsupportedFieldException; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/WhereSqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/WhereSqlBuilder.java index 02eaad9b59390955df0e1719440d1b1ad8f98212..44e933c3e2e279af5529b436f576c099bedc5c2e 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/WhereSqlBuilder.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/WhereSqlBuilder.java @@ -3,7 +3,6 @@ package com.gitee.qdbp.jdbc.plugins; import com.gitee.qdbp.able.jdbc.base.WhereCondition; import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; import com.gitee.qdbp.jdbc.sql.SqlBuffer; -import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; /** * Where SQL Builder @@ -24,10 +23,14 @@ public interface WhereSqlBuilder { * 生成Where SQL语句 * * @param condition 条件 - * @param sqlHelper SQL片段生成帮助类 + * @param columnResolver 列名转换处理类
+ * 如果能取到QueryFragmentHelper就用new ColumnNameHelper()
+ * 如果取不到, 就用ColumnNamingConverter.defaults(); + * @param dialect SQL方言 * @return SQL语句 * @throws UnsupportedFieldException */ - SqlBuffer buildSql(T condition, QueryFragmentHelper sqlHelper) throws UnsupportedFieldException; + SqlBuffer buildSql(T condition, ColumnNameResolver columnResolver, SqlDialect dialect) + throws UnsupportedFieldException; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseEntityDataStateFillStrategy.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseEntityDataStateFillStrategy.java index 49e9288b73295626859821ab748d31ceb8bfa456..ecf9cb0af47df65f250a45accdbfef5440dd22f4 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseEntityDataStateFillStrategy.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseEntityDataStateFillStrategy.java @@ -4,7 +4,10 @@ import java.util.List; import com.gitee.qdbp.able.jdbc.base.DbCondition; import com.gitee.qdbp.able.jdbc.condition.DbField; import com.gitee.qdbp.able.jdbc.condition.DbUpdate; +import com.gitee.qdbp.jdbc.model.AllFieldColumn; +import com.gitee.qdbp.jdbc.model.FieldScene; import com.gitee.qdbp.jdbc.plugins.EntityDataStateFillStrategy; +import com.gitee.qdbp.tools.utils.VerifyTools; /** * 实体的逻辑删除的数据状态填充策略基础类 @@ -12,7 +15,7 @@ import com.gitee.qdbp.jdbc.plugins.EntityDataStateFillStrategy; * @author zhaohuihua * @version 20200725 */ -public abstract class BaseEntityDataStateFillStrategy implements EntityDataStateFillStrategy { +public abstract class BaseEntityDataStateFillStrategy implements EntityDataStateFillStrategy.Complex { /** 逻辑删除字段名 **/ private String logicalDeleteField; @@ -27,6 +30,16 @@ public abstract class BaseEntityDataStateFillStrategy implements EntityDataS return logicalDeleteField; } + /** 是否支持逻辑删除 **/ + @Override + public boolean supportedLogicalDelete(AllFieldColumn allFields) { + if (VerifyTools.isBlank(logicalDeleteField)) { + return false; + } else { + return allFields.filter(FieldScene.UPDATE).containsByFieldName(logicalDeleteField); + } + } + /** 逻辑删除字段名 **/ public void setLogicalDeleteField(String logicalDeleteField) { this.logicalDeleteField = logicalDeleteField; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseJdbcNamingConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseJdbcNamingConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..1ee437a3eeb786f0d576eefd1302a5b1ddf1ecc8 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseJdbcNamingConverter.java @@ -0,0 +1,29 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.tools.utils.NamingTools; + +public abstract class BaseJdbcNamingConverter implements JdbcNamingConverter { + + /** 下划线名称是否使用大写 **/ + private boolean underlineUseUpperCase = true; + + /** 下划线名称是否使用大写 **/ + public boolean isUnderlineUseUpperCase() { + return underlineUseUpperCase; + } + + /** 下划线名称是否使用大写 **/ + public void setUnderlineUseUpperCase(boolean underlineUseUpperCase) { + this.underlineUseUpperCase = underlineUseUpperCase; + } + + protected String toUnderlineString(String string) { + return SqlTools.toUnderlineString(string, underlineUseUpperCase); + } + + protected String toCamelString(String name, boolean startsWithUpperCase) { + return NamingTools.toCamelString(name, startsWithUpperCase); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseNameConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseNameConverter.java deleted file mode 100644 index a411c9c865ef38231479b01ec8c104724280d7f8..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseNameConverter.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.gitee.qdbp.jdbc.plugins.impl; - -import com.gitee.qdbp.jdbc.plugins.NameConverter; -import com.gitee.qdbp.tools.utils.NamingTools; - -public abstract class BaseNameConverter implements NameConverter { - - private boolean columnNameUseUpperCase = true; - - public boolean isColumnNameUseUpperCase() { - return columnNameUseUpperCase; - } - - public void setColumnNameUseUpperCase(boolean columnNameUseUpperCase) { - this.columnNameUseUpperCase = columnNameUseUpperCase; - } - - protected String toUnderlineString(String string) { - String result = NamingTools.toUnderlineString(string); - return columnNameUseUpperCase ? result.toUpperCase() : result.toLowerCase(); - } - -} 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 b57aee30e376fa6fc7e089377dec6bbd7c1a345e..f834f8dc80eda9c452e00ed53a0cc1ba1bc2cbfe 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 @@ -2,13 +2,18 @@ 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.able.instance.ToStringComparator; +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.StringTools; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -24,54 +29,140 @@ public abstract class BaseSqlFileScanner implements SqlFileScanner { private static Logger log = LoggerFactory.getLogger(BaseSqlFileScanner.class); - /** 待扫描的文件夹, 多个以逗号分隔 **/ - private String folders; - /** 待扫描的文件名过滤规则, 多个以逗号分隔, 如 *.sql,*.stpl **/ - private String filters; + /** 文件路径扫描规则, 多个以逗号分隔 **/ + // settings/sqls/**/*.sql, com/gitee/qdbp/jdbc/test/**/*Dao.xml + private String patterns; public BaseSqlFileScanner() { } - public BaseSqlFileScanner(String folders, String filters) { - this.folders = folders; - this.filters = filters; + public BaseSqlFileScanner(String patterns) { + this.patterns = patterns; } - - protected abstract List scanSqlFiles(String[] folders, String filter) throws IOException; + + /** + * 扫描SQL文件 + * + * @param pattern 文件路径扫描规则 + * @return SQL文件URL路径 + * @throws IOException IO异常 + */ + protected abstract List scanSqlFiles(String pattern) throws IOException; @Override - public List scanSqlFiles() throws IOException { - String folder = VerifyTools.nvl(this.folders, "settings/sqls/"); - String[] folders = StringTools.split(folder, ','); - String filter = VerifyTools.nvl(this.filters, "*.sql"); + public List scanSqlFiles(Date lastUpdateTime) throws IOException { + String pattern = VerifyTools.nvl(this.patterns, "settings/sqls/**/*.sql"); + String[] patterns = StringTools.split(pattern, ','); Date startTime = new Date(); - List urls = scanSqlFiles(folders, filter); - Collections.sort(urls, ToStringComparator.INSTANCE); - if (log.isInfoEnabled()) { - String msg = "Success to scan sql templates, elapsed time {}, total of {} files were found."; - log.info(msg, ConvertTools.toDuration(startTime, true), urls.size()); + 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().toString() + " (ignored)"); + } + } + } + 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 results; + } + + protected List scanSqlFiles(Long minTime, String[] patterns) throws IOException { + List sqlFiles = new ArrayList<>(); + for (String pattern : patterns) { + List urls = scanSqlFiles(pattern); + if (urls != null && !urls.isEmpty()) { + for (URL url : urls) { + SqlFile sqlFile = parseSqlFile(pattern, url); + if (minTime == null) { + sqlFiles.add(sqlFile); + } else if (sqlFile.getLastUpdateTime() != null && sqlFile.getLastUpdateTime() > minTime) { + sqlFiles.add(sqlFile); + } + } + } + } + return sqlFiles; + } + + protected SqlFile parseSqlFile(String folder, URL url) { + String absolutePath = PathTools.toUriPath(url); + String relativePath = getSqlRelativePath(folder, url); + Date lastUpdateTime = PathTools.getLastModified(url); + SqlFile sqlFile = new SqlFile(); + sqlFile.setUrlPath(url); + sqlFile.setAbsolutePath(absolutePath); + sqlFile.setRelativePath(relativePath); + if (lastUpdateTime != null) { + sqlFile.setLastUpdateTime(lastUpdateTime.getTime()); } - return urls; + return sqlFile; } - /** 待扫描的文件夹, 多个以逗号分隔 **/ - public String getFolders() { - return folders; + 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); + } + } + // 同一路径存在多个文件时, 优先取classpath下的, 然后再取jar包中的 + for (Entry> entry : maps.entrySet()) { + if (entry.getValue().size() > 1) { + sortSqlFiles(entry.getKey(), entry.getValue()); + } + } + return maps; } - /** 待扫描的文件夹, 多个以逗号分隔 **/ - public void setFolders(String folders) { - this.folders = folders; + protected void sortSqlFiles(String relativePath, List sqlFiles) { + Collections.sort(sqlFiles); + } + + /** 获取SQL相对路径 **/ + protected String getSqlRelativePath(String pattern, URL url) { + String folder = null; + int starIndex = pattern.indexOf('*'); + if (starIndex > 0) { + folder = pattern.substring(0, starIndex); + } + String urlPath = url.toString(); + int folderIndex = folder == null ? -1 : urlPath.indexOf(folder); + if (folderIndex > 0) { + return StringTools.removeLeft(urlPath.substring(folderIndex), '/'); + } else { + return PathTools.getClasspathRelativePath(urlPath); + } } - /** 待扫描的文件名过滤规则, 多个以逗号分隔, 如 *.sql,*.stpl **/ - public String getFilters() { - return filters; + /** 文件路径扫描规则, 多个以逗号分隔 **/ + public String getPatterns() { + return patterns; } - /** 待扫描的文件名过滤规则, 多个以逗号分隔, 如 *.sql,*.stpl **/ - public void setFilters(String filters) { - this.filters = filters; + /** 文件路径扫描规则, 多个以逗号分隔 **/ + public void setPatterns(String patterns) { + this.patterns = patterns; } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableInfoScans.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableInfoScans.java index c2744af7b8f658ac544894a29c137d71bd31ecc5..c04d27b467421ae02a91f18b73350638c2e6c0be 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableInfoScans.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableInfoScans.java @@ -2,7 +2,9 @@ package com.gitee.qdbp.jdbc.plugins.impl; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.sql.Types; import java.util.ArrayList; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -12,6 +14,7 @@ import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; import com.gitee.qdbp.jdbc.plugins.CommonFieldResolver; import com.gitee.qdbp.jdbc.plugins.TableInfoScans; import com.gitee.qdbp.jdbc.plugins.TableNameScans; +import com.gitee.qdbp.tools.parse.StringParser; import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -106,9 +109,13 @@ public abstract class BaseTableInfoScans implements TableInfoScans { if (isIgnoreField(field, temp)) { continue; } - + // 扫描列信息 SimpleFieldColumn column = scanColumn(field, temp); + column.setJavaType(field.getType()); + if (column.getJdbcType() == null && Date.class.isAssignableFrom(field.getType())) { + column.setJdbcType(Types.TIMESTAMP); + } // 判断是不是主键 if (pkColumn == null && column.isPrimaryKey()) { pkColumn = column; @@ -145,7 +152,7 @@ public abstract class BaseTableInfoScans implements TableInfoScans { } /** 扫描@ColumnDefault注解声明的默认值 **/ - protected void scanColumnDefault(Field field, SimpleFieldColumn column) { + protected void scanColumnDefault(Field field, SimpleFieldColumn column, Class clazz) { ColumnDefault columnDefault = field.getAnnotation(ColumnDefault.class); if (columnDefault != null) { column.setColumnDefault(parseColumnDefault(columnDefault.value())); @@ -161,8 +168,10 @@ public abstract class BaseTableInfoScans implements TableInfoScans { return null; } defaultValue = defaultValue.trim(); - if (defaultValue.startsWith("'") && defaultValue.endsWith("'")) { - return defaultValue.substring(1, defaultValue.length() - 1); + if (StringParser.isWrappedBySingleQuotationMark(defaultValue)) { + return StringParser.unwrapSingleQuotationMark(defaultValue); + } else if (StringParser.isWrappedByDoubleQuotationMark(defaultValue)) { + return StringParser.unwrapDoubleQuotationMark(defaultValue); } if ("null".equalsIgnoreCase(defaultValue)) { return null; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableNameScans.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableNameScans.java index e036d7f2ebf4dc1109d2505b195f4b31700508e9..bc641dab6bd0420c87d5c02e3f667315960a5358 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableNameScans.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BaseTableNameScans.java @@ -1,6 +1,6 @@ package com.gitee.qdbp.jdbc.plugins.impl; -import com.gitee.qdbp.jdbc.plugins.NameConverter; +import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter; import com.gitee.qdbp.jdbc.plugins.TableNameScans; /** @@ -9,17 +9,17 @@ import com.gitee.qdbp.jdbc.plugins.TableNameScans; * @author zhaohuihua * @version 190727 */ -public abstract class BaseTableNameScans implements TableNameScans, NameConverter.Aware { +public abstract class BaseTableNameScans implements TableNameScans, JdbcNamingConverter.Aware { - /** 名称转换处理器 **/ - private NameConverter nameConverter; + /** 命名转换处理器 **/ + private JdbcNamingConverter namingConverter; @Override - public void setNameConverter(NameConverter nameConverter) { - this.nameConverter = nameConverter; + public void setNamingConverter(JdbcNamingConverter namingConverter) { + this.namingConverter = namingConverter; } - public NameConverter getNameConverter() { - return nameConverter; + public JdbcNamingConverter getNamingConverter() { + return namingConverter; } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByMultiRowsExecutor.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByMultiRowsExecutor.java index bfc0f66d2dd785c9faaa8ec0993d633cda648d44..ea46680fe7ea9f16d6ca94a77a0f9c5e5cd69b2e 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByMultiRowsExecutor.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByMultiRowsExecutor.java @@ -34,7 +34,7 @@ public class BatchInsertByMultiRowsExecutor implements BatchInsertExecutor { public boolean supports(DbVersion version) { String key = "qdbc.supports." + this.getClass().getSimpleName(); // SQL Server 需要2008以上版本 - String defvalue = "mysql,mariadb,db2,sqlserver.2008"; + String defvalue = "mysql,mariadb,db2,h2,sqlserver.2008,sqlite.3"; String options = DbTools.getDbConfig().getStringUseDefValue(key, defvalue); return version.matchesWith(options); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByUnionAllFromDualExecutor.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByUnionAllFromDualExecutor.java index f168012019f7aef18d97fc583b96856c1b056158..8f08642533eec6d7c47c406e2a1f13f93db0db9f 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByUnionAllFromDualExecutor.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchInsertByUnionAllFromDualExecutor.java @@ -30,6 +30,11 @@ import com.gitee.qdbp.jdbc.utils.DbTools; */ public class BatchInsertByUnionAllFromDualExecutor implements BatchInsertExecutor { + // Oracle的CLOB字段批量新增时, 如果CLOB字段值既有小于4000的也有大于4000的 + // 就会报错ORA-01790: 表达式必须具有与对应表达式相同的数据类型 + // 原因是JdbcTemplate对CLOB的处理, 小于4000的用ps.setString(), 超过4000的用ps.setClob() + // 详见StatementCreatorUtils.setValue() + /** 是否支持指定数据库 **/ @Override public boolean supports(DbVersion version) { diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchOperateByMultiSqlExecutor.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchOperateByMultiSqlExecutor.java index 95119c10e2a9d52f4b05d1952ac5af737f46680e..58c81d6e32cc3ba53c2ad3f3afa45c648213b62e 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchOperateByMultiSqlExecutor.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchOperateByMultiSqlExecutor.java @@ -8,6 +8,7 @@ import com.gitee.qdbp.able.jdbc.condition.DbWhere; import com.gitee.qdbp.able.jdbc.model.PkEntity; import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations; import com.gitee.qdbp.jdbc.model.DbVersion; +import com.gitee.qdbp.jdbc.model.MainDbType; import com.gitee.qdbp.jdbc.model.OmitStrategy; import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; import com.gitee.qdbp.jdbc.plugins.BatchInsertExecutor; @@ -17,10 +18,10 @@ import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.sql.build.CrudSqlBuilder; import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.jdbc.utils.DbTypes; /** * 生成多条SQL语句一起执行的批量处理接口实现类
- * 注意: 只有mysql支持, oracle/db2都不支持
* 注意: 如果是druid需要配置multiStatementAllow; 如果是mysql需要配置allowMultiQueries参数!
*

     <bean id="sysDataSource" ...>
@@ -55,7 +56,7 @@ public class BatchOperateByMultiSqlExecutor implements BatchInsertExecutor, Batc
     @Override
     public boolean supports(DbVersion version) {
         String key = "qdbc.supports." + this.getClass().getSimpleName();
-        String defvalue = "mysql,mariadb";
+        String defvalue = "mysql,mariadb,oracle";
         String options = DbTools.getDbConfig().getStringUseDefValue(key, defvalue);
         return version.matchesWith(options);
     }
@@ -70,11 +71,14 @@ public class BatchOperateByMultiSqlExecutor implements BatchInsertExecutor, Batc
      */
     @Override
     public List inserts(List entities, SqlBufferJdbcOperations jdbc, CrudSqlBuilder sqlBuilder) {
-        SqlBuilder sql = new SqlBuilder();
         List ids = new ArrayList<>();
         // 获取批量操作语句的省略策略配置项
         OmitStrategy omits = DbTools.getOmitSizeConfig("qdbc.batch.sql.omitStrategy", "8:3");
         int size = entities.size();
+        SqlBuilder sql = new SqlBuilder();
+        if (DbTypes.equals(jdbc.getDbVersion().getDbType(), MainDbType.Oracle)) {
+            sql.ad("BEGIN").ad('\n'); // ORACLE需要添加BEGIN/END
+        }
         for (int i = 0; i < size; i++) {
             PkEntity item = entities.get(i);
             String id = item.getPrimaryKey();
@@ -90,6 +94,9 @@ public class BatchOperateByMultiSqlExecutor implements BatchInsertExecutor, Batc
             SqlBuffer temp = sqlBuilder.buildInsertSql(entity);
             sql.ad(temp).ad(';');
         }
+        if (DbTypes.equals(jdbc.getDbVersion().getDbType(), MainDbType.Oracle)) {
+            sql.ad('\n').ad("END").ad(';'); // ORACLE需要添加BEGIN/END
+        }
 
         // 执行批量数据库插入
         jdbc.batchInsert(sql.out());
@@ -112,6 +119,9 @@ public class BatchOperateByMultiSqlExecutor implements BatchInsertExecutor, Batc
         // 获取批量操作语句的省略策略配置项
         OmitStrategy omits = DbTools.getOmitSizeConfig("qdbc.batch.sql.omitStrategy", "8:3");
         SqlBuilder sql = new SqlBuilder();
+        if (DbTypes.equals(jdbc.getDbVersion().getDbType(), MainDbType.Oracle)) {
+            sql.ad("BEGIN").ad('\n'); // ORACLE需要添加BEGIN/END
+        }
         int size = entities.size();
         for (int i = 0; i < size; i++) {
             PkEntity item = entities.get(i);
@@ -132,6 +142,9 @@ public class BatchOperateByMultiSqlExecutor implements BatchInsertExecutor, Batc
             SqlBuffer temp = sqlBuilder.buildUpdateSql(ud, where);
             sql.ad(temp).ad(';');
         }
+        if (DbTypes.equals(jdbc.getDbVersion().getDbType(), MainDbType.Oracle)) {
+            sql.ad('\n').ad("END").ad(';'); // ORACLE需要添加BEGIN/END
+        }
 
         // 执行批量数据库插入
         return jdbc.batchUpdate(sql.out());
diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByCaseWhenExecutor.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByCaseWhenExecutor.java
index bc1170cb3f065fad29b52e3ccff132b17e6a2b49..600fb605d351fa416459fbae799775dbf42473cc 100644
--- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByCaseWhenExecutor.java
+++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByCaseWhenExecutor.java
@@ -12,6 +12,7 @@ import com.gitee.qdbp.jdbc.model.FieldColumns;
 import com.gitee.qdbp.jdbc.model.FieldScene;
 import com.gitee.qdbp.jdbc.model.OmitStrategy;
 import com.gitee.qdbp.jdbc.model.SimpleFieldColumn;
+import com.gitee.qdbp.jdbc.model.TypedDbVariable;
 import com.gitee.qdbp.jdbc.plugins.BatchUpdateExecutor;
 import com.gitee.qdbp.jdbc.sql.SqlBuilder;
 import com.gitee.qdbp.jdbc.sql.build.CrudSqlBuilder;
@@ -43,7 +44,13 @@ public class BatchUpdateByCaseWhenExecutor implements BatchUpdateExecutor {
     @Override
     public boolean supports(DbVersion version) {
         String key = "qdbc.supports." + this.getClass().getSimpleName();
-        String defvalue = "mysql,mariadb,oracle,db2";
+        // 对Oracle的支持有问题 https://www.yuque.com/zhaohuihua/qdbc/mlxkha
+        // Oracle的CLOB字段批量新增时, 如果CLOB字段值既有小于4000的也有大于4000的
+        // 就会报错ORA-00932: 数据类型不一致: 应为 CHAR, 但却获得 CLOB
+        // 原因是JdbcTemplate对CLOB的处理, 小于4000的用ps.setString(), 超过4000的用ps.setClob()
+        // 详见StatementCreatorUtils.setValue()
+        // String defvalue = "mysql,mariadb,oracle,db2,h2";
+        String defvalue = "mysql,mariadb,db2,h2";
         String options = DbTools.getDbConfig().getStringUseDefValue(key, defvalue);
         return version.matchesWith(options);
     }
@@ -104,18 +111,19 @@ public class BatchUpdateByCaseWhenExecutor implements BatchUpdateExecutor {
                     sql.omit(i, size, omits.getKeepSize()); // 插入省略标记
                 }
                 Map entity = pkEntity.getEntity();
-                Object fieldValue = entity.get(fieldName);
+                Object idValue = DbTools.wrapDbVariable(pk, pkEntity.getPrimaryKey());
+                Object fieldValue = DbTools.wrapDbVariable(item, entity.get(fieldName));
                 // 不用调sqlHelper.convertFieldValue
                 // 因为不支持DbFieldName/DbFieldValue/DbRawValue, 只支持fieldName=fieldValue
                 // fieldValue = sqlHelper.convertFieldValue(fieldValue);
                 // WHEN {id1} THEN {field11}
-                sql.ad("WHEN").var(pkEntity.getPrimaryKey()).ad("THEN").var(fieldValue);
+                sql.ad("WHEN").var(idValue).ad("THEN").var(fieldValue);
             }
             // ELSE FIELD1 END)
             sql.newline().tab(-1).ad("ELSE").ad(item.getColumnName()).ad("END").ad(')');
         }
         // WHERE ID IN (
-        sql.newline().ad("WHERE").ad(pk.getColumnName()).ad("IN").ad('(').newline().tab();
+        sql.newline().ad("WHERE").ad(pk.getColumnName()).ad("IN").ad(' ').ad('(').newline().tab();
         // id1,id2,idn
         for (int i = 0; i < size; i++) {
             PkEntity item = entities.get(i);
@@ -126,7 +134,11 @@ public class BatchUpdateByCaseWhenExecutor implements BatchUpdateExecutor {
                 sql.omit(i, size, omits.getKeepSize()); // 插入省略标记
             }
             // 获取主键值
-            sql.var(item.getPrimaryKey());
+            Object idValue = item.getPrimaryKey();
+            if (pk.getJdbcType() != null) {
+                idValue = new TypedDbVariable(pk.getJdbcType(), idValue);
+            }
+            sql.var(idValue);
         }
         // )
         sql.newline().tab(-1).ad(')');
diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByJoinUsingExecutor.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByJoinUsingExecutor.java
index 59cb10b27a4bda469211305e1be746a754c818af..e071a4cb5e3dcb1137365133bb13c3e86f0558d9 100644
--- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByJoinUsingExecutor.java
+++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/BatchUpdateByJoinUsingExecutor.java
@@ -81,12 +81,13 @@ public class BatchUpdateByJoinUsingExecutor implements BatchUpdateExecutor {
                 sql.omit(i, size, omits.getKeepSize()); // 插入省略标记
             }
             // SELECT {id1} ID, {field11} FIELD11, {field12} FIELD12, ..., {field1n} FIELD1n
-            sql.ad("SELECT").var(item.getPrimaryKey()).ad(pk.getColumnName());
+            Object idValue = DbTools.wrapDbVariable(pk, item.getPrimaryKey());
+            sql.ad("SELECT").var(idValue).ad(pk.getColumnName());
             Map entity = item.getEntity();
             for (SimpleFieldColumn column : fieldColumns) {
                 String fieldName = column.getFieldName();
                 if (fieldMap.containsKey(fieldName) && !fieldName.equals(pk.getFieldName())) {
-                    Object fieldValue = entity.get(fieldName);
+                    Object fieldValue = DbTools.wrapDbVariable(column, entity.get(fieldName));
                     // 不用调sqlHelper.convertFieldValue
                     // 因为不支持DbFieldName/DbFieldValue/DbRawValue, 只支持fieldName=fieldValue
                     // fieldValue = sqlHelper.convertFieldValue(fieldValue);
diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ColumnNameHelper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ColumnNameHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..993f07513c75a4948a97e6f5dcce9e629569f6dd
--- /dev/null
+++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ColumnNameHelper.java
@@ -0,0 +1,31 @@
+package com.gitee.qdbp.jdbc.plugins.impl;
+
+import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException;
+import com.gitee.qdbp.jdbc.model.FieldScene;
+import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver;
+import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper;
+
+/**
+ * 字段名转列名实现类
+ *
+ * @author zhaohuihua
+ * @version 20210510
+ */
+public class ColumnNameHelper implements ColumnNameResolver {
+
+    private QueryFragmentHelper helper;
+    private FieldScene scene;
+
+    public ColumnNameHelper(QueryFragmentHelper helper, FieldScene scene) {
+        this.helper = helper;
+        this.scene = scene;
+    }
+
+    @Override
+    public String getColumnName(String fieldName) throws UnsupportedFieldException {
+        if (fieldName == null) {
+            return null;
+        }
+        return helper.getColumnName(scene, fieldName, true);
+    }
+}
diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ColumnNamingConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ColumnNamingConverter.java
new file mode 100644
index 0000000000000000000000000000000000000000..afe30a26feebcdf4e1fb1d61ae01eb00376675f1
--- /dev/null
+++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ColumnNamingConverter.java
@@ -0,0 +1,31 @@
+package com.gitee.qdbp.jdbc.plugins.impl;
+
+import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException;
+import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver;
+import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter;
+import com.gitee.qdbp.jdbc.utils.DbTools;
+
+/**
+ * 调用JdbcNamingConverter的ColumnNameResolver
+ *
+ * @author zhaohuihua
+ * @version 20210510
+ */
+public class ColumnNamingConverter implements ColumnNameResolver {
+
+    private static final ColumnNamingConverter DEFAULTS = new ColumnNamingConverter();
+
+    public static ColumnNamingConverter defaults() {
+        return DEFAULTS;
+    }
+
+    @Override
+    public String getColumnName(String fieldName) throws UnsupportedFieldException {
+        if (fieldName == null) {
+            return null;
+        }
+        JdbcNamingConverter converter = DbTools.getNamingConverter();
+        return converter.fieldNameToColumnName(fieldName);
+    }
+
+}
diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ConfigableJdbcDataTypeResolver.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ConfigableJdbcDataTypeResolver.java
new file mode 100644
index 0000000000000000000000000000000000000000..7e9f1b0dde4eb4b60054f0f65ef068c798fa9387
--- /dev/null
+++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/ConfigableJdbcDataTypeResolver.java
@@ -0,0 +1,129 @@
+package com.gitee.qdbp.jdbc.plugins.impl;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Properties;
+import com.gitee.qdbp.jdbc.plugins.JdbcDataTypeResolver;
+import com.gitee.qdbp.tools.utils.ConvertTools;
+import com.gitee.qdbp.tools.utils.PropertyTools;
+
+/**
+ * 从配置文件中读取转换关系的JdbcDataTypeResolver
+ * 默认配置文件: settings/qdbc/qdbc.datatype.txt + * + * @author zhaohuihua + * @version 20210430 + */ +public class ConfigableJdbcDataTypeResolver implements JdbcDataTypeResolver { + + private Properties properties; + private Map supportDefinePrecisionMaps; + private Map supportDefineScaleMaps; + private Map supportDefineLengthMaps; + private SimpleJdbcDataTypeResolver defaults; + + /** + * 构造函数 + * + * @param path 配置文件路径, 如: settings/qdbc/qdbc.datatype.txt + */ + public ConfigableJdbcDataTypeResolver(String path) { + this(PropertyTools.load(path, PropertyTools.DEFAULT_CHARSET, ConfigableJdbcDataTypeResolver.class)); + } + + /** + * 构造函数 + * + * @param properties 转换关系配置文件 + */ + public ConfigableJdbcDataTypeResolver(Properties properties) { + this.properties = properties; + this.defaults = new SimpleJdbcDataTypeResolver(); + if (properties != null) { + { // supportDefinePrecision + String key = "supportDefinePrecision.jdbcTypes"; + String[] values = PropertyTools.getArray(properties, key, false); + if (values != null && values.length > 0) { + this.supportDefinePrecisionMaps = ConvertTools.toMap(Arrays.asList(values)); + } + } + { // supportDefineScale + String key = "supportDefineScale.jdbcTypes"; + String[] values = PropertyTools.getArray(properties, key, false); + if (values != null && values.length > 0) { + this.supportDefineScaleMaps = ConvertTools.toMap(Arrays.asList(values)); + } + } + { // supportDefineLength + String key = "supportDefineLength.jdbcTypes"; + String[] values = PropertyTools.getArray(properties, key, false); + if (values != null && values.length > 0) { + this.supportDefineLengthMaps = ConvertTools.toMap(Arrays.asList(values)); + } + } + } + } + + public Properties getProperties() { + return properties; + } + + @Override + public Integer parseDataType(String dataType) { + String jdbcType = dataType; + if (properties != null && properties.containsKey(dataType)) { + jdbcType = properties.getProperty(dataType); + } + return JdbcDataTypeCache.getJdbcType(jdbcType); + } + + @Override + public String getJdbcType(String dataType) { + String jdbcType = dataType; + if (properties != null && properties.containsKey(dataType)) { + jdbcType = properties.getProperty(dataType); + } + return jdbcType; + } + + @Override + public boolean supportDefinePrecision(String dataType) { + String jdbcType = getJdbcType(dataType); + return doSupportDefinePrecision(jdbcType) || doSupportDefineScale(jdbcType); + } + + @Override + public boolean supportDefineScale(String dataType) { + String jdbcType = getJdbcType(dataType); + return doSupportDefineScale(jdbcType); + } + + private boolean doSupportDefinePrecision(String dataType) { + String jdbcType = getJdbcType(dataType); + if (supportDefinePrecisionMaps == null || supportDefinePrecisionMaps.isEmpty()) { + return this.defaults.supportDefinePrecision(dataType); + } else { + return supportDefinePrecisionMaps.containsKey(jdbcType); + } + } + + private boolean doSupportDefineScale(String dataType) { + String jdbcType = getJdbcType(dataType); + if (supportDefineScaleMaps == null || supportDefineScaleMaps.isEmpty()) { + return this.defaults.supportDefineScale(dataType); + } else { + return supportDefineScaleMaps.containsKey(jdbcType); + } + } + + @Override + public boolean supportDefineLength(String dataType) { + String jdbcType = getJdbcType(dataType); + if (supportDefineLengthMaps == null || supportDefineLengthMaps.isEmpty()) { + return this.defaults.supportDefineLength(dataType); + } else { + return supportDefineLengthMaps.containsKey(jdbcType); + } + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonBeanToMapConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonBeanToMapConverter.java index 4e80ad343d1342d5ca5975da725cab4b6167fadc..c59dd8e5c9d07e219d78f803bb9c8231e48f3161 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonBeanToMapConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonBeanToMapConverter.java @@ -1,7 +1,7 @@ package com.gitee.qdbp.jdbc.plugins.impl; import java.util.Map; -import com.gitee.qdbp.jdbc.plugins.BeanToMapConverter; +import com.gitee.qdbp.able.convert.BeanToMapConverter; /** * JavaBean到Map的转换
diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonDbConditionConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonDbConditionConverter.java index a3dd3972f2cb651fa888383e39203869dbe3aea6..23d1d1596f206efb417ceef69a2329511194da65 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonDbConditionConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonDbConditionConverter.java @@ -3,10 +3,10 @@ package com.gitee.qdbp.jdbc.plugins.impl; import java.util.HashMap; import java.util.List; import java.util.Map; +import com.gitee.qdbp.able.convert.BeanToMapConverter; import com.gitee.qdbp.able.jdbc.utils.FieldTools; import com.gitee.qdbp.jdbc.model.AllFieldColumn; import com.gitee.qdbp.jdbc.model.FieldScene; -import com.gitee.qdbp.jdbc.plugins.BeanToMapConverter; import com.gitee.qdbp.jdbc.utils.DbTools; import com.gitee.qdbp.tools.utils.VerifyTools; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonMapToBeanConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonMapToBeanConverter.java index 8e82f48e338aaba1417b5077c3e1a76c4f0f6943..3447198641b2cf17abb073bb4581ae5b05a68ae1 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonMapToBeanConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonMapToBeanConverter.java @@ -5,7 +5,7 @@ import java.util.List; import java.util.Map; import org.springframework.core.convert.ConversionService; import com.alibaba.fastjson.parser.ParserConfig; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; +import com.gitee.qdbp.able.convert.MapToBeanConverter; import com.gitee.qdbp.jdbc.support.ConversionServiceAware; import com.gitee.qdbp.jdbc.support.fastjson.UseSpringConversionDeserializer; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -36,7 +36,7 @@ public class FastJsonMapToBeanConverter implements MapToBeanConverter, Conversio } @Override - public void fill(Map map, Object bean) { + public void fillTo(Map map, Object bean) { VerifyTools.requireNonNull(map, "map"); VerifyTools.requireNonNull(bean, "bean"); FastJsonTools.mapFillToBean(map, bean); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTools.java index 5aa935795792482755d1ff96f23ff149b93a1672..b4a5de837f92f8e6f5e3cfa117f2d17274f94199 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTools.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTools.java @@ -24,11 +24,11 @@ import com.alibaba.fastjson.serializer.ObjectSerializer; import com.alibaba.fastjson.serializer.SerializeConfig; import com.alibaba.fastjson.util.FieldInfo; import com.alibaba.fastjson.util.TypeUtils; -import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.MapTools; import com.gitee.qdbp.tools.utils.ReflectTools; /** - * Json工具类
+ * FastJson工具类
* 为去除对qdbp-tools.jar的依赖, 复制自JsonTools * * @author zhaohuihua @@ -190,7 +190,7 @@ abstract class FastJsonTools { } Map map = getBeanFieldValuesMap(object, deep, SerializeConfig.getGlobalInstance()); if (clearBlankValue) { - ConvertTools.clearBlankValue(map); + MapTools.clearBlankValue(map); } return map; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTypeConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTypeConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..43c8ff566f22e9b2ee9bc35d320ba586855595df --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/FastJsonTypeConverter.java @@ -0,0 +1,18 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import com.alibaba.fastjson.util.TypeUtils; +import com.gitee.qdbp.able.convert.ObjectTypeConverter; + +/** + * 通过FastJson实现的类型转换类 + * + * @author zhaohuihua + * @version 20210308 + */ +public class FastJsonTypeConverter implements ObjectTypeConverter { + + @Override + public T convert(Object value, Class clazz) { + return TypeUtils.castToJavaBean(value, clazz); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/JdbcDataTypeCache.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/JdbcDataTypeCache.java new file mode 100644 index 0000000000000000000000000000000000000000..9b8973ef4d64eff7c2c3285ca664a1d871dc093f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/JdbcDataTypeCache.java @@ -0,0 +1,40 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.sql.Types; +import java.util.HashMap; +import java.util.Map; +import com.gitee.qdbp.tools.utils.ReflectTools; + +/** + * 读取java.sql.Types中声明的静态字段值并缓存 + * + * @author zhaohuihua + * @version 20210429 + */ +class JdbcDataTypeCache { + + public static Integer getJdbcType(String dataType) { + return cache.get(dataType); + } + + private static Map cache = new HashMap<>(); + static { + Field[] fields = ReflectTools.getAllFields(Types.class, true, false, false); + for (Field field : fields) { + if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isStatic(field.getModifiers())) { + continue; + } + if (field.getType() != int.class) { + continue; + } + try { + int value = field.getInt(null); + cache.put(field.getName(), value); + } catch (Exception e) { + continue; + } + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/OrderByFunctionSqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/OrderByFunctionSqlBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..e46d007f32444657d8b63afe207e7d1e74e265a0 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/OrderByFunctionSqlBuilder.java @@ -0,0 +1,168 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import com.gitee.qdbp.able.jdbc.ordering.OrderType; +import com.gitee.qdbp.able.jdbc.ordering.Ordering; +import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; +import com.gitee.qdbp.jdbc.model.FunctionOrdering; +import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver; +import com.gitee.qdbp.jdbc.plugins.OrderBySqlBuilder; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; +import com.gitee.qdbp.tools.parse.StringParser; +import com.gitee.qdbp.tools.utils.NamingTools; +import com.gitee.qdbp.tools.utils.ReflectTools; +import com.gitee.qdbp.tools.utils.StringTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * OrderBy 函数SQL生成处理类 + * + * @author zhaohuihua + * @version 20210510 + */ +public class OrderByFunctionSqlBuilder implements OrderBySqlBuilder { + + @Override + public Class supported() { + return FunctionOrdering.class; + } + + @Override + public SqlBuffer buildSql(FunctionOrdering ordering, ColumnNameResolver columnResolver, SqlDialect dialect) + throws UnsupportedFieldException { + if (ordering.isEmpty()) { + return new SqlBuffer(); // 没有指定任何隔离字段, 没法加限制条件 + } + if (VerifyTools.isNotBlank(ordering.getFunctionName())) { + return buildFunctionSql(ordering, columnResolver, dialect); + } else { + String columnName = columnResolver.getColumnName(ordering.getOrderBy()); + SqlBuilder buffer = new SqlBuilder(); + buffer.ad(columnName); + OrderType orderType = ordering.getOrderType(); + if (orderType == OrderType.ASC) { + buffer.ad("ASC"); + } else if (orderType == OrderType.DESC) { + buffer.ad("DESC"); + } + return buffer.out(); + } + } + + private Map methodMaps = new HashMap<>(); + + protected Method getFunctionHandlerMethod(String methodName) { + String camelName = NamingTools.toCamelString(methodName); + if (methodMaps.containsKey(camelName)) { + return methodMaps.get(camelName); + } + Class[] types = new Class[] { Ordering.class, ColumnNameResolver.class, SqlDialect.class }; + Method method = ReflectTools.findMethod(this.getClass(), camelName, types); + if (method == null) { + String fmt = "OrderBy function [%s] not found for %s.%s(Ordering, ColumnNameResolver, SqlDialect)"; + String msg = String.format(fmt, methodName, this.getClass().getSimpleName(), camelName); + throw new IllegalArgumentException(msg); + } + + Class result = method.getReturnType(); + if (result != SqlBuffer.class) { + String fmt = "OrderBy function [%s] method %s.%s(Ordering, ColumnNameResolver, SqlDialect) " + + "must be return SqlBuffer"; + String msg = String.format(fmt, methodName, this.getClass().getSimpleName(), camelName); + throw new IllegalArgumentException(msg); + } + + methodMaps.put(camelName, method); + return method; + } + + protected SqlBuffer buildFunctionSql(FunctionOrdering ordering, ColumnNameResolver columnResolver, + SqlDialect dialect) { + if ("pinyin".equalsIgnoreCase(ordering.getOrderBy())) { + // 兼容旧版本 userName(pinyin), 新版本应该是 pinyin(userName) + if (ordering.getFunctionParams().size() == 0) { + if (!StringTools.isWordString(ordering.getFunctionName())) { + // u.userName(pinyin) // functionName都不是单词, 说明确实是旧版本写法 + ordering.setOrderBy(ordering.getFunctionName()); + ordering.setFunctionName("pinyin"); + } else { + Class[] types = new Class[] { Ordering.class, ColumnNameResolver.class, SqlDialect.class }; + String camelName = NamingTools.toCamelString(ordering.getFunctionName()); + Method method = ReflectTools.findMethod(this.getClass(), camelName, types); + if (method == null) { // functionName找不到方法, 说明确实是旧版本写法 + ordering.setOrderBy(ordering.getFunctionName()); + ordering.setFunctionName("pinyin"); + } + } + } + } + String functionName = ordering.getFunctionName(); + Method method = getFunctionHandlerMethod(functionName); + return ReflectTools.invokeMethod(this, method, ordering, columnResolver, dialect); + } + + /** + * 拼音函数, 使用方法: Orderings.of("pinyin(userName) asc")
+ * Oracle: ORDER BY USER_NAME ASC
+ * MySQL : ORDER BY CONVERT(USER_NAME USING GBK) ASC + * + * @param ordering 排序对象 + * @param columnResolver 列名转换对象 + * @param dialect SQL方言处理类 + * @return 排序SQL语句 + */ + public SqlBuffer pinyin(Ordering ordering, ColumnNameResolver columnResolver, SqlDialect dialect) { + if (VerifyTools.isBlank(ordering.getOrderBy())) { + String fmt = "OrderBy function [%s] format error, missing 'orderBy'. %s"; + String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal()); + throw new IllegalArgumentException(msg); + } + if (ordering.getFunctionParams().size() != 0) { + String fmt = "OrderBy function [%s] format error, too many params. %s"; + String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal()); + throw new IllegalArgumentException(msg); + } + String columnName = columnResolver.getColumnName(ordering.getOrderBy()); + SqlBuilder buffer = new SqlBuilder(dialect.toPinyinOrderByExpression(columnName)); + OrderType orderType = ordering.getOrderType(); + if (orderType == OrderType.ASC) { + buffer.ad("ASC"); + } else if (orderType == OrderType.DESC) { + buffer.ad("DESC"); + } + return buffer.out(); + } + + /** + * include函数, 使用方法: Orderings.of("include(u.userType,'GlobalDecode.userType') asc")
+ * + * @param ordering 排序对象 + * @param columnResolver 列名转换对象 + * @param dialect SQL方言处理类 + * @return 排序SQL语句 + */ + public SqlBuffer include(Ordering ordering, ColumnNameResolver columnResolver, SqlDialect dialect) { + if (VerifyTools.isBlank(ordering.getOrderBy())) { + String fmt = "OrderBy function [%s] format error, missing 'orderBy'. %s"; + String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal()); + throw new IllegalArgumentException(msg); + } + String stringParam = ordering.getOnlyStringParam(); + // 'a.b'最少也得5个字符 + if (VerifyTools.isBlank(stringParam) || stringParam.length() < 5) { + String fmt = "OrderBy function [%s] format error, params value error. %s"; + String msg = String.format(fmt, ordering.getFunctionName(), ordering.getOriginal()); + throw new IllegalArgumentException(msg); + } + String sqlId = StringParser.unwrapQuotationMark(stringParam); + String columnName = columnResolver.getColumnName(ordering.getOrderBy()); + Map params = new HashMap<>(); + params.put("columnName", columnName); + return SqlFragmentContainer.defaults().render(sqlId, params, dialect); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/PersistenceAnnotationTableScans.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/PersistenceAnnotationTableScans.java index 11402aeb9f1b0705f7fb529ee54f2deee544a074..a2f54a5ec5d3f9629192e1c06f22973bc3ff8fc6 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/PersistenceAnnotationTableScans.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/PersistenceAnnotationTableScans.java @@ -1,47 +1,42 @@ package com.gitee.qdbp.jdbc.plugins.impl; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.List; import javax.persistence.Column; import javax.persistence.Id; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.gitee.qdbp.able.matches.StringMatcher; import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; -import com.gitee.qdbp.jdbc.plugins.NameConverter; -import com.gitee.qdbp.jdbc.plugins.TableNameScans; +import com.gitee.qdbp.jdbc.plugins.JdbcDataTypeResolver; +import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter; +import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.tools.parse.StringAccess; +import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.StringTools; import com.gitee.qdbp.tools.utils.VerifyTools; /** * 扫描@Table/@Column/@Id注解
* 没有@Column注解的字段, 不会出现在新增/修改的字段列表中, 但会作为查询结果集映射字段
* 但如果所有字段都没有@Column注解, 则将insertable/updatable全部设置为true
- * 也就是说, 要么完全没有@Column注解; 只要出现了一个没有注解的字段, 则所有数据库对应字段都需要有注解
- * 因为表关联查询所定义的结果类, 一般是不会有@Column字段的, 此时应认为是查询全部字段
+ * 也就是说, 要么完全没有@Column注解; 只要出现了一个没有注解的字段, 则所有数据库对应字段都需要有注解
+ * 因为表关联查询所定义的结果类, 一般是不会有@Column字段的, 此时应认为是查询全部字段
* * @author zhaohuihua * @version 190601 */ public class PersistenceAnnotationTableScans extends BaseTableInfoScans { - /** 名称转换处理器 **/ - private NameConverter nameConverter; + private static Logger log = LoggerFactory.getLogger(PersistenceAnnotationTableScans.class); + /** 查找主键字段的处理器 **/ private StringMatcher primaryKeyMatcher; /** 默认构造函数 **/ public PersistenceAnnotationTableScans() { this.setTableNameScans(new SimpleTableNameScans()); - this.setNameConverter(new SimpleNameConverter()); - } - - /** 名称转换处理器 **/ - public NameConverter getNameConverter() { - return nameConverter; - } - - /** 名称转换处理器 **/ - public void setNameConverter(NameConverter nameConverter) { - this.nameConverter = nameConverter; - this.handleNameConverterAware(); } /** 查找ID的处理器 **/ @@ -54,20 +49,6 @@ public class PersistenceAnnotationTableScans extends BaseTableInfoScans { this.primaryKeyMatcher = primaryKeyMatcher; } - /** 设置表名扫描类 **/ - public void setTableNameScans(TableNameScans tableNameScans) { - super.setTableNameScans(tableNameScans); - this.handleNameConverterAware(); - } - - /** 处理NameConverterAware **/ - protected void handleNameConverterAware() { - TableNameScans tableNameScans = this.getTableNameScans(); - if (this.nameConverter != null && tableNameScans instanceof NameConverter.Aware) { - ((NameConverter.Aware) tableNameScans).setNameConverter(this.nameConverter); - } - } - @Override protected void onAfterScanColumns(List allColumns) { super.onAfterScanColumns(allColumns); @@ -135,24 +116,27 @@ public class PersistenceAnnotationTableScans extends BaseTableInfoScans { String columnName = annotation == null ? null : annotation.name(); if (VerifyTools.isBlank(columnName)) { columnName = fieldName; - if (nameConverter != null) { - columnName = nameConverter.fieldNameToColumnName(fieldName); + JdbcNamingConverter namingConverter = DbTools.getNamingConverter(); + if (namingConverter != null) { + columnName = namingConverter.fieldNameToColumnName(fieldName); } } // 生成列信息对象 FieldColumn column = new FieldColumn(fieldName, columnName, annotation); - // 判断是不是主键 - scanPrimaryKey(column, field, clazz); - // 扫描@ColumnDefault注解声明的默认值 - scanColumnDefault(field, column); // 解析@Column注解中声明的信息 if (annotation != null) { - parseColumnAnnotation(column, annotation); + parseColumnAnnotation(column, annotation, clazz); } + // 判断是不是主键 + scanPrimaryKey(field, column, clazz); + // 扫描@ColumnDefault注解声明的默认值 + scanColumnDefault(field, column, clazz); + // 扫描校验框架的@Null,@NotNull注解 + scanValidNullable(field, column, clazz); return column; } - protected void scanPrimaryKey(FieldColumn column, Field field, Class clazz) { + protected void scanPrimaryKey(Field field, FieldColumn column, Class clazz) { String fieldName = field.getName(); Id idAnnotation = field.getAnnotation(Id.class); if (idAnnotation != null || primaryKeyMatcher != null && primaryKeyMatcher.matches(fieldName)) { @@ -160,8 +144,20 @@ public class PersistenceAnnotationTableScans extends BaseTableInfoScans { } } + protected void scanValidNullable(Field field, FieldColumn column, Class clazz) { + // 扫描校验框架的@Null,@NotNull注解 + Annotation[] annotations = field.getAnnotations(); + for (Annotation annotation : annotations) { + if (annotation.getClass().getName().equals("javax.validation.constraints.NotNull")) { + column.setColumnNullable(false); + } else if (annotation.getClass().getName().equals("javax.validation.constraints.Null")) { + column.setColumnNullable(true); + } + } + } + /** 解析@Column注解中声明的信息 **/ - protected void parseColumnAnnotation(SimpleFieldColumn column, Column annotation) { + protected void parseColumnAnnotation(SimpleFieldColumn column, Column annotation, Class clazz) { if (annotation == null) { return; } @@ -170,16 +166,82 @@ public class PersistenceAnnotationTableScans extends BaseTableInfoScans { // column.setColumnLength(annotation.length()); // column.setColumnPrecision(annotation.precision()); // column.setColumnScale(annotation.scale()); - if (VerifyTools.isNotBlank(annotation.columnDefinition())) { - parseColumnDefinition(column, annotation.columnDefinition()); + String columnDefinition = annotation.columnDefinition(); + if (VerifyTools.isNotBlank(columnDefinition)) { + column.setColumnDefinition(columnDefinition); + parseColumnDefinition(column, columnDefinition, clazz); } } /** 从列定义中解析列属性 **/ + // column_name type [NOT NULL|NULL] [DEFAULT default_value] + // [AUTO_INCREMENT] [UNIQUE [KEY]|[PRIMARY] KEY] [COMMENT 'string'] // columnDefinition="Decimal(10,2) default 1.00" // columnDefinition="TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP" // columnDefinition="VARCHAR(20) DEFAULT 'N/A'" // 字段串要用单引号括起来 - protected void parseColumnDefinition(SimpleFieldColumn column, String columnDefinition) { - // TODO 从列定义中解析列属性 + // columnDefinition="INT(10) UNSIGNED NOT NULL AUTO_INCREMENT" // UNSIGNED AUTO_INCREMENT + protected void parseColumnDefinition(SimpleFieldColumn column, String columnDefinition, Class clazz) { + JdbcDataTypeResolver resolver = DbTools.getJdbcDataTypeResolver(); + // 从列定义中解析列属性 + StringAccess sa = new StringAccess(columnDefinition.trim()); + String dataType = sa.readWord(); // 读取一个单词 + if (dataType == null) { + return; // 不是以单词开头, 无法识别 + } + // Decimal or TIMESTAMP or VARCHAR or INT + String jdbcTypeString = resolver.getJdbcType(dataType.toUpperCase()); + Integer jdbcTypeNumber = resolver.parseDataType(jdbcTypeString); + if (jdbcTypeNumber != null) { + column.setJdbcType(jdbcTypeNumber); + } else { + String msg = "Can't resolve jdbcType '{}', from @Column(columnDefinition=\"{}\") of {}.{}, " + + "Please add mapping relationship to configuration file 'qdbc.datatype.txt'."; + log.warn(msg, dataType, columnDefinition, clazz.getSimpleName(), column.getFieldName()); + } + char typeNext = sa.skipWhitespace().peekChar(); + if (typeNext == 0) { + return; + } + // (10,2) or null or (20) or (10) + List paramItems = sa.readMethodParams(); + if (paramItems != null && !paramItems.isEmpty()) { + if (paramItems.size() == 1) { + // VARCHAR(20) + String length = paramItems.get(0); + if (StringTools.isDigit(length)) { + if (resolver.supportDefinePrecision(jdbcTypeString)) { + column.setColumnPrecision(ConvertTools.toInteger(length)); + } else if (resolver.supportDefineLength(jdbcTypeString)) { + column.setColumnLength(ConvertTools.toInteger(length)); + } + } + } else if (paramItems.size() >= 2) { + // DECIMAL(10,2) + String precision = paramItems.get(0); + String scale = paramItems.get(1); + if (StringTools.isDigit(precision) && resolver.supportDefinePrecision(jdbcTypeString)) { + column.setColumnPrecision(ConvertTools.toInteger(precision)); + } + if (StringTools.isDigit(scale) && resolver.supportDefineScale(jdbcTypeString)) { + column.setColumnScale(ConvertTools.toInteger(scale)); + } + } + } + while (sa.isReadable() && sa.isLastReadSuccess()) { + String nextWord = sa.skipWhitespace().readWord(); // 读取一个单词 + if ("NOT".equalsIgnoreCase(nextWord)) { + String serialWord = sa.skipWhitespace().readWord(); // 再读取一个单词 + if ("NULL".equals(serialWord)) { + column.setColumnNullable(false); + } + } else if ("NULL".equalsIgnoreCase(nextWord)) { + column.setColumnNullable(true); + } else if ("DEFAULT".equalsIgnoreCase(nextWord)) { + String defvalue = sa.skipWhitespace().readUniterm(' ', '\t', '\r', '\n'); + if (defvalue != null) { + column.setColumnDefault(parseColumnDefault(defvalue)); + } + } + } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleDbOperatorContainer.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleDbOperatorContainer.java index 03f8288f5b4db96e5e97b559e2e372f68cf4a6b6..ca803f9074a13edc22d9f281a62ee0a48453db5d 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleDbOperatorContainer.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleDbOperatorContainer.java @@ -14,8 +14,10 @@ import com.gitee.qdbp.jdbc.operator.where.DbBinaryGreaterThenOperator; import com.gitee.qdbp.jdbc.operator.where.DbBinaryLessEqualsThenOperator; import com.gitee.qdbp.jdbc.operator.where.DbBinaryLessThenOperator; import com.gitee.qdbp.jdbc.operator.where.DbBinaryLikeOperator; +import com.gitee.qdbp.jdbc.operator.where.DbBinaryNotEndsWithOperator; import com.gitee.qdbp.jdbc.operator.where.DbBinaryNotEqualsOperator; import com.gitee.qdbp.jdbc.operator.where.DbBinaryNotLikeOperator; +import com.gitee.qdbp.jdbc.operator.where.DbBinaryNotStartsWithOperator; import com.gitee.qdbp.jdbc.operator.where.DbBinaryStartsWithOperator; import com.gitee.qdbp.jdbc.operator.where.DbMultivariateInOperator; import com.gitee.qdbp.jdbc.operator.where.DbMultivariateNotInOperator; @@ -50,7 +52,9 @@ public class SimpleDbOperatorContainer implements DbOperatorContainer { registerWhereOperator(new DbBinaryLikeOperator()); registerWhereOperator(new DbBinaryNotLikeOperator()); registerWhereOperator(new DbBinaryStartsWithOperator()); + registerWhereOperator(new DbBinaryNotStartsWithOperator()); registerWhereOperator(new DbBinaryEndsWithOperator()); + registerWhereOperator(new DbBinaryNotEndsWithOperator()); registerWhereOperator(new DbTernaryBetweenOperator()); registerWhereOperator(new DbTernaryNotBetweenOperator()); registerWhereOperator(new DbMultivariateInOperator()); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleEntityFieldFillStrategy.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleEntityFieldFillStrategy.java index baa994d445e05ac24659bf1dcac1042298e893a4..e1645e9946a008e56f27bad5f438aa6b1ba59786 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleEntityFieldFillStrategy.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleEntityFieldFillStrategy.java @@ -5,8 +5,8 @@ import java.util.Map; import com.gitee.qdbp.able.jdbc.condition.DbUpdate; import com.gitee.qdbp.able.jdbc.condition.DbWhere; import com.gitee.qdbp.jdbc.model.AllFieldColumn; -import com.gitee.qdbp.jdbc.plugins.EntityFillBizResolver; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillStrategy; +import com.gitee.qdbp.jdbc.plugins.EntityFillBizResolver; import com.gitee.qdbp.tools.utils.RandomTools; /** diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleJdbcDataTypeResolver.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleJdbcDataTypeResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..6821c10d47eff39920bc33a0fd7e64c13611289a --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleJdbcDataTypeResolver.java @@ -0,0 +1,56 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import java.util.Map; +import com.gitee.qdbp.jdbc.plugins.JdbcDataTypeResolver; +import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.StringTools; + +/** + * JDBC数据类型转换: 将代码中写的数据类型转换为java.sql.Types中的数据类型 + * + * @author zhaohuihua + * @version 20210430 + */ +public class SimpleJdbcDataTypeResolver implements JdbcDataTypeResolver { + + private Map supportDefinePrecisionMaps; + private Map supportDefineScaleMaps; + private Map supportDefineLengthMaps; + + public SimpleJdbcDataTypeResolver() { + String precisions = "BIT|TINYINT|SMALLINT|INTEGER|BIGINT"; + String scales = "FLOAT|DOUBLE|REAL|NUMERIC|DECIMAL"; + String lengths = "CHAR|VARCHAR|LONGVARCHAR|NCHAR|NVARCHAR"; + this.supportDefinePrecisionMaps = ConvertTools.toMap(StringTools.splits(precisions)); + this.supportDefineScaleMaps = ConvertTools.toMap(StringTools.splits(scales)); + this.supportDefineLengthMaps = ConvertTools.toMap(StringTools.splits(lengths)); + } + + @Override + public Integer parseDataType(String dataType) { + return JdbcDataTypeCache.getJdbcType(dataType); + } + + @Override + public String getJdbcType(String dataType) { + return dataType; + } + + @Override + public boolean supportDefinePrecision(String dataType) { + String jdbcType = getJdbcType(dataType); + return supportDefinePrecisionMaps.containsKey(jdbcType) || supportDefineScaleMaps.containsKey(jdbcType); + } + + @Override + public boolean supportDefineScale(String dataType) { + String jdbcType = getJdbcType(dataType); + return supportDefineScaleMaps.containsKey(jdbcType); + } + + @Override + public boolean supportDefineLength(String dataType) { + String jdbcType = getJdbcType(dataType); + return supportDefineLengthMaps.containsKey(jdbcType); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleJdbcNamingConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleJdbcNamingConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..35b0edb6da533f36201c267cf878026d9aa09f28 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleJdbcNamingConverter.java @@ -0,0 +1,31 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +/** + * 命名转换处理器 + * + * @author zhaohuihua + * @version 190601 + */ +public class SimpleJdbcNamingConverter extends BaseJdbcNamingConverter { + + @Override + public String beanNameToTableName(String beanName) { + return toUnderlineString(beanName); + } + + @Override + public String tableNameToBeanName(String tableName) { + return toCamelString(tableName, true); + } + + @Override + public String fieldNameToColumnName(String fieldName) { + return toUnderlineString(fieldName); + } + + @Override + public String columnNameToFieldName(String columnName) { + return toCamelString(columnName, false); + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleNameConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleNameConverter.java index 08a7dc066ede50e5d31291500d751f033646f705..8739496e6ebe35fdf636c1fe2eab6ae57869a1a9 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleNameConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleNameConverter.java @@ -1,33 +1,12 @@ -package com.gitee.qdbp.jdbc.plugins.impl; - -import com.gitee.qdbp.tools.utils.NamingTools; - -/** - * 名称转换处理器 - * - * @author zhaohuihua - * @version 190601 - */ -public class SimpleNameConverter extends BaseNameConverter { - - @Override - public String beanNameToTableName(String beanName) { - return toUnderlineString(beanName); - } - - @Override - public String tableNameToBeanName(String tableName) { - return NamingTools.toCamelString(tableName, true); - } - - @Override - public String fieldNameToColumnName(String fieldName) { - return toUnderlineString(fieldName); - } - - @Override - public String columnNameToFieldName(String columnName) { - return NamingTools.toCamelString(columnName, false); - } - -} +package com.gitee.qdbp.jdbc.plugins.impl; + +/** + * 命名转换处理器 + * + * @author zhaohuihua + * @version 190601 + * @deprecated rename to {@link SimpleJdbcNamingConverter} + */ +@Deprecated +public class SimpleNameConverter extends SimpleJdbcNamingConverter { +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlDialect.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlDialect.java index f0b9e59d1c9414e5adb4210dcba13e2491126efd..1b3e8f8fe118814f05f70a70043a53a338abc2c8 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlDialect.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlDialect.java @@ -3,17 +3,16 @@ package com.gitee.qdbp.jdbc.plugins.impl; import java.util.Date; import java.util.HashMap; import java.util.Map; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.able.jdbc.paging.Paging; import com.gitee.qdbp.jdbc.model.DbType; import com.gitee.qdbp.jdbc.model.DbVersion; -import com.gitee.qdbp.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.model.MainDbType; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.utils.DbTypes; import com.gitee.qdbp.tools.utils.DateTools; -import com.gitee.qdbp.tools.utils.ReflectTools; import com.gitee.qdbp.tools.utils.VerifyTools; /** @@ -68,6 +67,9 @@ public class SimpleSqlDialect implements SqlDialect { /** {@inheritDoc} **/ @Override public void processPagingSql(SqlBuffer buffer, Paging paging) { + if (paging == null || !paging.isPaging()) { + return; + } DbType dbType = dbVersion.getDbType(); if (DbTypes.equals(dbType, MainDbType.Oracle)) { processPagingForOracle(buffer, paging); @@ -83,10 +85,15 @@ public class SimpleSqlDialect implements SqlDialect { processPagingForSqlite(buffer, paging); } else { // throw new UnsupportedOperationException("Unsupported db type: " + dbType); - processPagingForLimitOffset(buffer, paging); + processPagingForOtherwise(buffer, paging); } } + /** 如遇到不支持的数据库, 可覆盖此方法 ( 通过DbPluginContainer.setSqlDialectCreator()注册至插件中心 ) **/ + protected void processPagingForOtherwise(SqlBuffer buffer, Paging paging) { + processPagingForLimitOffset(buffer, paging); + } + protected void processPagingForMySql(SqlBuffer buffer, Paging paging) { SqlBuilder sql = buffer.shortcut(); if (paging.getStart() <= 0) { @@ -126,14 +133,14 @@ public class SimpleSqlDialect implements SqlDialect { protected void processPagingForOracle(SqlBuffer buffer, Paging paging) { // 逻辑参考自: org.hibernate.dialect.OracleDialect if (paging.getStart() <= 0) { - buffer.indentAll(1, true); + buffer.tabAll(1, true); // SELECT T_T.* FROM ( // {sql} // ) T_T WHERE ROWNUM <= {end} buffer.prepend("SELECT T_T.* FROM (\n"); buffer.append("\n) T_T\nWHERE ROWNUM <= ").addVariable(paging.getEnd()); } else { - buffer.indentAll(2, true); + buffer.tabAll(2, true); // SELECT * FROM ( // SELECT T_T.*, ROWNUM R_N FROM ( // {sql} @@ -153,7 +160,7 @@ public class SimpleSqlDialect implements SqlDialect { String end = String.valueOf(paging.getEnd()); buffer.append('\n').append("FETCH FIRST").append(' ', end, ' ').append("ROWS ONLY"); } else { - buffer.indentAll(2, true); + buffer.tabAll(2, true); // SELECT * FROM ( // SELECT T_T.*, ROWNUMBER() OVER(ORDER BY ORDER OF T_T) AS R_N // FROM ( @@ -208,6 +215,13 @@ public class SimpleSqlDialect implements SqlDialect { sb.append("'YYYY-MM-DD HH24:MI:SS.FF'"); sb.append(')'); return sb.toString(); + } else if (DbTypes.equals(dbType, MainDbType.PostgreSQL)) { + sb.append("TO_TIMESTAMP").append('('); + sb.append("'").append(DateTools.toNormativeString(date)).append("'"); + sb.append(','); + sb.append("'YYYY-MM-DD HH24:MI:SS.MS'"); + sb.append(')'); + return sb.toString(); } else if (DbTypes.equals(dbType, MainDbType.H2)) { sb.append("PARSEDATETIME").append('('); sb.append("'").append(DateTools.toNormativeString(date)).append("'"); @@ -223,17 +237,19 @@ public class SimpleSqlDialect implements SqlDialect { /** {@inheritDoc} **/ @Override - public SqlBuffer buildLikeSql(Object fieldValue) { - if (fieldValue instanceof LikeValue) { - LikeValue likeValue = parseLikeValue(fieldValue); - SqlBuilder buffer = new SqlBuilder("LIKE").var(likeValue.getPatternString()); - if (likeValue.getEscapeChar() != 0) { - buffer.ad("ESCAPE").var(likeValue.getEscapeChar()); - } - return buffer.out(); + public SqlBuffer buildLikeSql(LikeValue fieldValue) { + SqlBuilder buffer = new SqlBuilder("LIKE").var(fieldValue.getPatternString()); + if (fieldValue.getEscapeChar() != 0) { + buffer.ad("ESCAPE").var(fieldValue.getEscapeChar()); } - LikeValue likeValue = parseLikeValue(fieldValue); + return buffer.out(); + } + + /** {@inheritDoc} **/ + @Override + public SqlBuffer buildLikeSql(String fieldValue) { SqlBuilder buffer = new SqlBuilder("LIKE"); + LikeValue likeValue = parseLikeString(fieldValue); Object pattern = likeValue == null ? fieldValue : likeValue.getPatternString(); DbType dbType = dbVersion.getDbType(); if (DbTypes.exists(dbType, MainDbType.Oracle, MainDbType.DB2, MainDbType.PostgreSQL)) { @@ -253,12 +269,9 @@ public class SimpleSqlDialect implements SqlDialect { /** {@inheritDoc} **/ @Override - public SqlBuffer buildStartsWithSql(Object fieldValue) { - if (fieldValue instanceof LikeValue) { - throw new IllegalArgumentException("StartsWith not support 'LikeEscapeItem', please use 'like'."); - } - LikeValue likeValue = parseLikeValue(fieldValue); + public SqlBuffer buildStartsWithSql(String fieldValue) { SqlBuilder buffer = new SqlBuilder("LIKE"); + LikeValue likeValue = parseLikeString(fieldValue); Object pattern = likeValue == null ? fieldValue : likeValue.getPatternString(); DbType dbType = dbVersion.getDbType(); if (DbTypes.exists(dbType, MainDbType.Oracle, MainDbType.DB2, MainDbType.PostgreSQL)) { @@ -278,12 +291,9 @@ public class SimpleSqlDialect implements SqlDialect { /** {@inheritDoc} **/ @Override - public SqlBuffer buildEndsWithSql(Object fieldValue) { - if (fieldValue instanceof LikeValue) { - throw new IllegalArgumentException("EndsWith not support 'LikeEscapeItem', please use 'like'."); - } - LikeValue likeValue = parseLikeValue(fieldValue); + public SqlBuffer buildEndsWithSql(String fieldValue) { SqlBuilder buffer = new SqlBuilder("LIKE"); + LikeValue likeValue = parseLikeString(fieldValue); Object pattern = likeValue == null ? fieldValue : likeValue.getPatternString(); DbType dbType = dbVersion.getDbType(); if (DbTypes.exists(dbType, MainDbType.Oracle, MainDbType.DB2, MainDbType.PostgreSQL)) { @@ -301,23 +311,9 @@ public class SimpleSqlDialect implements SqlDialect { return buffer.out(); } - protected LikeValue parseLikeValue(Object likeValue) { - if (likeValue == null) { - return null; - } - if (likeValue instanceof LikeValue) { - return (LikeValue) likeValue; - } else if (likeValue instanceof String) { - return parseLikeString((String) likeValue); - } else if (ReflectTools.isPrimitive(likeValue.getClass(), false)) { - return null; - } else { - return parseLikeString(likeValue.toString()); - } - } - private static char[] ESCAPE_CHARS = "#!:$^-+".toCharArray(); + // 如果不存在特殊字符, 返回null, 否则返回LikeValue对象 protected LikeValue parseLikeString(String likeString) { // 判断字符串中有没有%和_这类特殊字符 char[] likeChars = likeString.toCharArray(); @@ -364,26 +360,11 @@ public class SimpleSqlDialect implements SqlDialect { * @author zhaohuihua * @version 20201018 */ - public static class Creator implements SqlDialect.Creator { + public static class Creator extends CacheCreator { @Override - public SqlDialect create(DbVersion version) { - return buildSqlDialect(version); - } - - // key=DbVersionCode - private Map sqlDialectMaps = new HashMap<>(); - - /** 根据DbVersion生成SqlDialect **/ - public SqlDialect buildSqlDialect(DbVersion version) { - String versionCode = version.toVersionString(); - if (sqlDialectMaps.containsKey(versionCode)) { - return sqlDialectMaps.get(versionCode); - } else { - SqlDialect dialect = new SimpleSqlDialect(version); - sqlDialectMaps.put(versionCode, dialect); - return dialect; - } + protected SqlDialect newSqlDialect(DbVersion version) { + return new SimpleSqlDialect(version); } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFileScanner.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFileScanner.java index 74f462a106883b1e2922c7e2bf52664b860eaf6c..67497060ffb394dbe2dc2e34110835ddf1160dd4 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFileScanner.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFileScanner.java @@ -1,15 +1,19 @@ package com.gitee.qdbp.jdbc.plugins.impl; import java.net.URL; -import java.util.ArrayList; import java.util.List; import com.gitee.qdbp.able.matches.FileMatcher; import com.gitee.qdbp.able.matches.StringMatcher.LogicType; import com.gitee.qdbp.able.matches.WrapFileMatcher; import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.StringTools; /** - * SQL模板文件扫描接口的简单实现类 + * SQL模板文件扫描接口的简单实现类
+ * 只会取第1个星号前面的部分作为文件夹名称, 最后的文件名作为文件匹配规则
+ * 如: settings/sqls/**/*.sql
+ * 拆解为查找settings/sqls/文件夹下所有的*.sql文件
+ * 即第1个星号后面到文件名之间, 无论配置什么都是不起作用的
* * @author zhaohuihua * @version 20200830 @@ -21,20 +25,15 @@ public class SimpleSqlFileScanner extends BaseSqlFileScanner { super(); } - public SimpleSqlFileScanner(String folders, String filters) { - super(folders, filters); + public SimpleSqlFileScanner(String patterns) { + super(patterns); } @Override - protected List scanSqlFiles(String[] folders, String filter) { + protected List scanSqlFiles(String pattern) { + String folder = StringTools.removeSuffixAt(pattern, '*'); + String filter = PathTools.getFileName(pattern); FileMatcher matcher = WrapFileMatcher.parseMatchers(filter, LogicType.OR, "name", "ant", ',', '\n'); - List urls = new ArrayList<>(); - for (String item : folders) { - List temp = PathTools.scanResources(item, matcher); - if (temp != null && !temp.isEmpty()) { - urls.addAll(temp); - } - } - return urls; + return PathTools.scanResources(folder, matcher); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFormatter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFormatter.java index 513185db3fa80c338d45b5394afc67218b2ab38c..7abade116f76fce2eb355fa106ec1f6965945489 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFormatter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFormatter.java @@ -19,7 +19,7 @@ public class SimpleSqlFormatter implements SqlFormatter { if (indent <= 0) { return sql; } else { - return IndentTools.indentAll(sql, indent); + return IndentTools.space.tabAll(sql, indent); } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFragmentOptions.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFragmentOptions.java new file mode 100644 index 0000000000000000000000000000000000000000..feff23ca3cdd3d8cced41358ea80074af0bd59df --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleSqlFragmentOptions.java @@ -0,0 +1,64 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import com.gitee.qdbp.jdbc.plugins.SqlFragmentOptions; + +/** + * SQL片断的选项 + * + * @author zhaohuihua + * @version 20201124 + * @since 3.2.10 + */ +public class SimpleSqlFragmentOptions implements SqlFragmentOptions { + + /** 是否在系统启动时扫描SQL文件 **/ + private boolean sqlTemplateScanOnStartup = true; + /** 是否允许通过fragmentId获取SQL片断 **/ + private boolean useFragmentIdQuery = true; + + /** 是否在系统启动时扫描SQL文件 **/ + @Override + public boolean isSqlTemplateScanOnStartup() { + return sqlTemplateScanOnStartup; + } + + /** 是否在系统启动时扫描SQL文件 **/ + public void setSqlTemplateScanOnStartup(boolean sqlTemplateScanOnStartup) { + this.sqlTemplateScanOnStartup = sqlTemplateScanOnStartup; + } + + /** 是否允许通过fragmentId获取SQL片断 **/ + @Override + public boolean isUseFragmentIdQuery() { + return useFragmentIdQuery; + } + + /** 是否允许通过fragmentId获取SQL片断 **/ + public void setUseFragmentIdQuery(boolean useFragmentIdQuery) { + this.useFragmentIdQuery = useFragmentIdQuery; + } + + /** + * 是否输出SQL片断冲突日志 + * + * @return 是否输出SQL片断冲突日志 + * @deprecated 已不存在该选项的应用场景:
+ * 实现机制修改为扫描时不检查冲突, 只有根据sqlId获取SQL片断时, 如果获取到多个才输出冲突日志 + */ + @Deprecated + @Override + public boolean isConflictLogsEnabled() { + return true; + } + + /** + * 是否输出SQL片断冲突日志 + * + * @param conflictLogsEnabled 是否输出冲突日志 + * @deprecated 已不存在该选项的应用场景:
+ * 实现机制修改为扫描时不检查冲突, 只有根据sqlId获取SQL片断时, 如果获取到多个才输出冲突日志 + */ + @Deprecated + public void setConflictLogsEnabled(boolean conflictLogsEnabled) { + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableInfoScans.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableInfoScans.java index 8bb75b5f5f8f41b76e214edea44a3ca33b662db7..6752d86b68b5cc95a6796046b53b23e8544e81d2 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableInfoScans.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableInfoScans.java @@ -4,8 +4,8 @@ import java.lang.reflect.Field; import com.gitee.qdbp.able.matches.EqualsStringMatcher; import com.gitee.qdbp.able.matches.StringMatcher; import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; -import com.gitee.qdbp.jdbc.plugins.NameConverter; -import com.gitee.qdbp.jdbc.plugins.TableNameScans; +import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter; +import com.gitee.qdbp.jdbc.utils.DbTools; /** * 提取全部字段, 而不是扫描注解 @@ -17,13 +17,10 @@ public class SimpleTableInfoScans extends BaseTableInfoScans { /** 查找ID的处理器 **/ private StringMatcher primaryKeyMatcher = new EqualsStringMatcher("id"); - /** 名称转换处理器 **/ - private NameConverter nameConverter = new SimpleNameConverter(); /** 默认构造函数 **/ public SimpleTableInfoScans() { this.setTableNameScans(new SimpleTableNameScans()); - this.setNameConverter(new SimpleNameConverter()); } /** 查找主键字段的处理器 **/ @@ -36,47 +33,23 @@ public class SimpleTableInfoScans extends BaseTableInfoScans { this.primaryKeyMatcher = primaryKeyMatcher; } - /** 名称转换处理器 **/ - public NameConverter getNameConverter() { - return nameConverter; - } - - /** 名称转换处理器 **/ - public void setNameConverter(NameConverter nameConverter) { - this.nameConverter = nameConverter; - this.handleNameConverterAware(); - } - - /** 设置表名扫描类 **/ - public void setTableNameScans(TableNameScans tableNameScans) { - super.setTableNameScans(tableNameScans); - this.handleNameConverterAware(); - } - - /** 处理NameConverterAware **/ - protected void handleNameConverterAware() { - TableNameScans tableNameScans = this.getTableNameScans(); - if (this.nameConverter != null && tableNameScans instanceof NameConverter.Aware) { - ((NameConverter.Aware) tableNameScans).setNameConverter(this.nameConverter); - } - } - @Override protected SimpleFieldColumn scanColumn(Field field, Class clazz) { + JdbcNamingConverter namingConverter = DbTools.getNamingConverter(); // 获取字段名 String fieldName = field.getName(); // 获取列名 - String columnName = nameConverter.fieldNameToColumnName(fieldName); + String columnName = namingConverter.fieldNameToColumnName(fieldName); // 生成列信息对象 SimpleFieldColumn column = new SimpleFieldColumn(fieldName, columnName); // 判断是不是主键 - scanPrimaryKey(column, field, clazz); + scanPrimaryKey(field, column, clazz); // 扫描@ColumnDefault注解声明的默认值 - scanColumnDefault(field, column); + scanColumnDefault(field, column, clazz); return column; } - protected void scanPrimaryKey(SimpleFieldColumn column, Field field, Class clazz) { + protected void scanPrimaryKey(Field field, SimpleFieldColumn column, Class clazz) { String fieldName = field.getName(); if (primaryKeyMatcher.matches(fieldName)) { column.setPrimaryKey(true); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableNameScans.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableNameScans.java index 079463032cf72cae24320d73f7af7c9189cc402c..000b97f6f0dae95de6c127a9a64dbd582d0607cc 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableNameScans.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTableNameScans.java @@ -1,7 +1,7 @@ package com.gitee.qdbp.jdbc.plugins.impl; import javax.persistence.Table; -import com.gitee.qdbp.jdbc.plugins.NameConverter; +import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter; import com.gitee.qdbp.tools.utils.VerifyTools; /** @@ -18,8 +18,8 @@ public class SimpleTableNameScans extends BaseTableNameScans { if (annotation != null && VerifyTools.isNotBlank(annotation.name())) { return annotation.name(); } else { - NameConverter nameConverter = this.getNameConverter(); - return nameConverter.beanNameToTableName(clazz.getSimpleName()); + JdbcNamingConverter namingConverter = this.getNamingConverter(); + return namingConverter.beanNameToTableName(clazz.getSimpleName()); } } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTablesFieldColumnParser.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTablesFieldColumnParser.java new file mode 100644 index 0000000000000000000000000000000000000000..b42eb3ef2d4e7ea65f19bbb27633e9d9556e2650 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleTablesFieldColumnParser.java @@ -0,0 +1,86 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.able.jdbc.condition.TableJoin.JoinItem; +import com.gitee.qdbp.able.jdbc.condition.TableJoin.TableItem; +import com.gitee.qdbp.jdbc.model.AllFieldColumn; +import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; +import com.gitee.qdbp.jdbc.model.TablesFieldColumn; +import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser; +import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 多表关联列信息的解析器 + * + * @author zhaohuihua + * @version 20210306 + */ +public class SimpleTablesFieldColumnParser implements TablesFieldColumnParser { + + private String tableAliasSeparator = "__"; + + @Override + public String getTableAliasSeparator() { + return tableAliasSeparator; + } + + @Override + public List parseFieldColumns(TableJoin tables) { + TableItem major = tables.getMajor(); + List joins = tables.getJoins(); + List all = new ArrayList<>(); + { // 添加主表的字段 + List fields = scanColumnList(major); + all.addAll(fields); + } + if (VerifyTools.isNotBlank(joins)) { + // 添加关联表的字段 + for (JoinItem item : joins) { + List fields = scanColumnList(item); + all.addAll(fields); + } + } + // 处理重名字段: 设置columnAlias + // 1.先统计字段出现次数 + Map countMaps = new HashMap<>(); + for (SimpleFieldColumn field : all) { + String fieldName = field.getFieldName(); + if (countMaps.containsKey(fieldName)) { + countMaps.put(fieldName, countMaps.get(fieldName) + 1); + } else { + countMaps.put(fieldName, 1); + } + } + // 2.如果出现多次则设置columnAlias=tableAlias__columnName + for (TablesFieldColumn field : all) { + String fieldName = field.getFieldName(); + if (countMaps.get(fieldName) > 1) { + String tableAlias = field.getTableAlias().toUpperCase(); + String columnName = field.getColumnName(); + field.setColumnAlias(tableAlias + tableAliasSeparator + columnName); + field.setAmbiguous(true); + } + } + return all; + } + + private List scanColumnList(TableItem table) { + AllFieldColumn all = DbTools.parseAllFieldColumns(table.getTableType()); + String tableAlias = table.getTableAlias(); + String resultField = table.getResultField(); + List fields = all.items(); + List result = new ArrayList<>(fields.size()); + for (SimpleFieldColumn item : fields) { + TablesFieldColumn copied = item.to(TablesFieldColumn.class); + copied.setTableAlias(tableAlias); + copied.setResultField(resultField); + result.add(copied); + } + return result; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleVarToDbValueConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleVarToDbValueConverter.java index 922cb1e1e417ab72d01f504c4a5bb1882b214700..2442f7256ed08fcf4688e5e7a07515dc219144f7 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleVarToDbValueConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SimpleVarToDbValueConverter.java @@ -76,7 +76,7 @@ public class SimpleVarToDbValueConverter implements VariableToDbValueConverter { if (value == null) { return null; } - Integer sqlType = variable.getSqlType(); + Integer sqlType = variable.getJdbcType(); if (sqlType == null) { return convert(value); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringMapToBeanConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringMapToBeanConverter.java index 38e9de39e1a01f2f3e1b403e72948cbee8329e26..e5ecc128c973662f35780ca436e2137251c5446a 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringMapToBeanConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringMapToBeanConverter.java @@ -22,7 +22,7 @@ import org.springframework.util.ClassUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.util.TypeUtils; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; +import com.gitee.qdbp.able.convert.MapToBeanConverter; import com.gitee.qdbp.jdbc.support.ConversionServiceAware; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -52,7 +52,7 @@ public class SpringMapToBeanConverter } @Override - public void fill(Map map, Object bean) { + public void fillTo(Map map, Object bean) { VerifyTools.requireNonNull(map, "map"); VerifyTools.requireNonNull(bean, "bean"); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringSqlFileScanner.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringSqlFileScanner.java index 4a990a38273a81e1a8be33a19b8027eca0952b2e..db362051b7c99fe807d2caf6c7bfaf8104506e58 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringSqlFileScanner.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringSqlFileScanner.java @@ -5,10 +5,11 @@ import java.net.URL; import java.util.ArrayList; import java.util.List; import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.StringTools; /** * SQL模板文件扫描接口的Spring实现类 @@ -23,25 +24,53 @@ public class SpringSqlFileScanner extends BaseSqlFileScanner { super(); } - public SpringSqlFileScanner(String folders, String filters) { - super(folders, filters); + /** + * 构造函数 + * + * @param patterns 扫描规则, 多个以逗号分隔
+ * 如: settings/sqls/**/*.sql, com/xxx/**/*Dao.xml
+ */ + public SpringSqlFileScanner(String patterns) { + super(patterns); + } + + /** + * 构造函数 + * + * @param folder 文件夹 + * @param filter 文件名过滤条件 + * @deprecated 作废, 改为{@link SpringSqlFileScanner#SpringSqlFileScanner(String patterns)}
+ * 因为按照此配置: folder=a,b; filter=*.sql,*.xml
+ * 文件夹a和b下的.sql和.xml都会被扫描; 无法做到a下只扫描.sql, b下只扫描.xml
+ */ + @Deprecated + public SpringSqlFileScanner(String folder, String filter) { + super(parsePatterns(folder, filter)); } @Override - protected List scanSqlFiles(String[] folders, String filter) throws IOException { + protected List scanSqlFiles(String pattern) throws IOException { List urls = new ArrayList<>(); - for (String item : folders) { - String resourcePath = "classpath*:" + PathTools.concat(item, "**", filter); - ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver(); - ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(resourceLoader); - Resource[] resources = resolver.getResources(resourcePath); - if (resources != null && resources.length > 0) { - for (Resource resource : resources) { - urls.add(resource.getURL()); - } + String resourcePath = "classpath*:" + pattern; + ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources(resourcePath); + if (resources != null && resources.length > 0) { + for (Resource resource : resources) { + urls.add(resource.getURL()); } } return urls; } + private static String parsePatterns(String folder, String filter) { + List patterns = new ArrayList<>(); + String[] folders = StringTools.split(folder, ','); + String[] filters = StringTools.split(filter, ','); + for (String fo : folders) { + for (String fi : filters) { + patterns.add(PathTools.concat(fo, "**", fi)); + } + } + return ConvertTools.joinToString(patterns, ','); + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringTypeConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringTypeConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..40f5217ed1089305890a48cd079ffad33cae03e7 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringTypeConverter.java @@ -0,0 +1,33 @@ +package com.gitee.qdbp.jdbc.plugins.impl; + +import org.springframework.core.convert.ConversionService; +import com.gitee.qdbp.able.convert.ObjectTypeConverter; +import com.gitee.qdbp.jdbc.support.ConversionServiceAware; + +/** + * 通过Spring实现的类型转换类 + * + * @author zhaohuihua + * @version 20210308 + */ +public class SpringTypeConverter implements ObjectTypeConverter, ConversionServiceAware { + + /** Spring的类型转换处理类 **/ + private ConversionService conversionService; + + @Override + public T convert(Object value, Class clazz) { + return conversionService.convert(value, clazz); + } + + /** Spring的类型转换处理类 **/ + public ConversionService getConversionService() { + return conversionService; + } + + /** Spring的类型转换处理类 **/ + @Override + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringVarToDbValueConverter.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringVarToDbValueConverter.java index 6cd59982d78699c73d6e419682f5acf95e620fb1..fe2364a60f8f65b6d67631408423f918660f3069 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringVarToDbValueConverter.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/plugins/impl/SpringVarToDbValueConverter.java @@ -134,6 +134,7 @@ public class SpringVarToDbValueConverter extends ConfigableVarToDbValueConverter } /** Spring的类型转换处理类 **/ + @Override public void setConversionService(ConversionService conversionService) { this.conversionService = conversionService; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/CamelNamingRowToMapMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/CamelNamingRowToMapMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..9db4d5c3f6ce248bac37c6bfd384970f1c00762a --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/CamelNamingRowToMapMapper.java @@ -0,0 +1,19 @@ +package com.gitee.qdbp.jdbc.result; + +import com.gitee.qdbp.tools.utils.NamingTools; + +/** + * KEY由驼峰命名法转换的ColumnMapRowMapper
+ * 如果存在同名字段, 以先出现的为准, 忽略后出现的 + * + * @author zhaohuihua + * @version 20201109 + */ +public class CamelNamingRowToMapMapper extends SimpleRowToMapMapper { + + @Override + protected String columnNameToFieldName(String columnName) { + return NamingTools.toCamelString(columnName); + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToBeanMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToBeanMapper.java index b2731ade175ce7ba18b0e67ddb5eaf92524b3add..92c92601d2991ea81f8164164e879384029529ac 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToBeanMapper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToBeanMapper.java @@ -1,6 +1,7 @@ package com.gitee.qdbp.jdbc.result; import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; /** * 结果集行数据到JavaBean的转换工具 @@ -10,4 +11,29 @@ import org.springframework.jdbc.core.RowMapper; */ public interface RowToBeanMapper extends RowMapper { + /** + * Row到Bean转换处理工厂类 (单表) + * + * @author zhaohuihua + * @version 20210308 + * @serial 3.3.0 + */ + interface FactoryOfTable { + + /** 获取Row到Bean的转换处理类 (单表) **/ + RowToBeanMapper getRowToBeanMapper(Class mappedClass); + } + + /** + * Row到Bean转换处理工厂类 (多表关联) + * + * @author zhaohuihua + * @version 20210308 + * @serial 3.3.0 + */ + interface FactoryOfTables { + + /** 获取Row到Bean的转换处理类 (多表关联) **/ + RowToBeanMapper getRowToBeanMapper(TableJoin tables, Class mappedClass); + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToMapMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToMapMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..6414389129867ed257f763cc3d8573c2325e1993 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/RowToMapMapper.java @@ -0,0 +1,18 @@ +package com.gitee.qdbp.jdbc.result; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import org.springframework.jdbc.core.RowMapper; + +/** + * Row到Map的转换类 + * + * @author zhaohuihua + * @version 20210303 + */ +public interface RowToMapMapper extends RowMapper> { + + Map mapRow(ResultSet rs, int rowNum) throws SQLException; + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/CamelNamingMapRowMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SimpleRowToMapMapper.java similarity index 46% rename from jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/CamelNamingMapRowMapper.java rename to jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SimpleRowToMapMapper.java index a412686379f7b93b9b7e73fe73e15611a86c199f..92a2130ca1557188f936a148234c4d80e2620c57 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/CamelNamingMapRowMapper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SimpleRowToMapMapper.java @@ -3,57 +3,53 @@ package com.gitee.qdbp.jdbc.result; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.util.LinkedCaseInsensitiveMap; -import com.gitee.qdbp.tools.utils.NamingTools; +import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; +import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser; /** - * KEY由驼峰命名法转换的ColumnMapRowMapper
+ * Row转换为Map
* 如果存在同名字段, 以先出现的为准, 忽略后出现的 * * @author zhaohuihua * @version 20201109 */ -public class CamelNamingMapRowMapper implements RowMapper> { +public class SimpleRowToMapMapper implements RowToMapMapper { + + public SimpleRowToMapMapper() { + } @Override public Map mapRow(ResultSet rs, int rowNum) throws SQLException { ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); - Map mapOfColumnValues = createColumnMap(columnCount); + Map mapOfColumnValues = new LinkedHashMap<>(columnCount); for (int i = 1; i <= columnCount; i++) { - String key = getColumnKey(JdbcUtils.lookupColumnName(rsmd, i)); - if (mapOfColumnValues.containsKey(key)) { + String fieldName = getFieldName(JdbcUtils.lookupColumnName(rsmd, i)); + if (mapOfColumnValues.containsKey(fieldName)) { continue; } Object object = getColumnValue(rs, i); - mapOfColumnValues.put(key, object); + mapOfColumnValues.put(fieldName, object); } return mapOfColumnValues; } - /** - * Create a Map instance to be used as column map.
- * By default, a linked case-insensitive Map will be created. - * - * @param columnCount the column count, to be used as initial capacity for the Map - * @return the new Map instance - * @see org.springframework.util.LinkedCaseInsensitiveMap - */ - protected Map createColumnMap(int columnCount) { - return new LinkedCaseInsensitiveMap(columnCount); - } - - protected String getColumnKey(String columnName) { - // 表别名与列别名的分隔符, 见DbTools.parseAllFieldColumns(); - String tableAliasSeparator = "__"; + protected String getFieldName(String columnName) { + TablesFieldColumnParser parser = DbPluginContainer.defaults().getTablesFieldColumnParser(); + // 表别名与列别名的分隔符 + String tableAliasSeparator = parser.getTableAliasSeparator(); int tableAliasIndex = columnName.indexOf(tableAliasSeparator); if (tableAliasIndex >= 0) { columnName = columnName.substring(tableAliasIndex + tableAliasSeparator.length()); } - return NamingTools.toCamelString(columnName); + return columnNameToFieldName(columnName); + } + + protected String columnNameToFieldName(String columnName) { + return columnName; } protected Object getColumnValue(ResultSet rs, int index) throws SQLException { diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SingleColumnMapper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SingleColumnMapper.java index 557ae7aebb7b04f2fad883c0f04deaeae55a6b87..fa53a89a2baf6d44c5ba0c32f5020c9e00bdf4c4 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SingleColumnMapper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/result/SingleColumnMapper.java @@ -1,9 +1,12 @@ package com.gitee.qdbp.jdbc.result; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; -import com.alibaba.fastjson.util.TypeUtils; +import org.springframework.jdbc.support.JdbcUtils; +import com.gitee.qdbp.able.convert.ObjectTypeConverter; +import com.gitee.qdbp.jdbc.utils.DbTools; /** * 获取单列数据 @@ -14,16 +17,26 @@ import com.alibaba.fastjson.util.TypeUtils; public class SingleColumnMapper implements RowMapper { private String columnName; - private Class clazz; + private Class valueClazz; - public SingleColumnMapper(String columnName, Class clazz) { + public SingleColumnMapper(String columnName, Class valueClazz) { this.columnName = columnName; - this.clazz = clazz; + this.valueClazz = valueClazz; } @Override public T mapRow(ResultSet rs, int rowNum) throws SQLException { - Object object = rs.getObject(columnName); - return TypeUtils.castToJavaBean(object, clazz); + ResultSetMetaData rsmd = rs.getMetaData(); + int columnCount = rsmd.getColumnCount(); + Object value = null; + for (int i = 1; i <= columnCount; i++) { + String columnName = JdbcUtils.lookupColumnName(rsmd, i); + if (columnName.equals(this.columnName)) { + value = JdbcUtils.getResultSetValue(rs, i, valueClazz); + break; + } + } + ObjectTypeConverter converter = DbTools.getObjectTypeConverter(); + return converter.convert(value, valueClazz); } } 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 fd66fbabba5546f09c149691557c09cfd7cde2db..bf35cfe8f39d59ef21609b50415ada471546c62b 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 @@ -1,16 +1,17 @@ package com.gitee.qdbp.jdbc.result; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.jdbc.core.ColumnMapRowMapper; -import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.JdbcUtils; 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.MapToBeanConverter; +import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; +import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser; import com.gitee.qdbp.jdbc.utils.DbTools; /** @@ -22,51 +23,68 @@ import com.gitee.qdbp.jdbc.utils.DbTools; */ public class TableRowToBeanMapper implements RowToBeanMapper { - private boolean useColumnName; - private Class resultType; - private MapToBeanConverter converter; - private RowMapper> mapper; + private Class mappedClass; - public TableRowToBeanMapper(Class resultType, MapToBeanConverter converter) { - this(resultType, converter, true, new ColumnMapRowMapper()); + public TableRowToBeanMapper(Class mappedClass) { + this.mappedClass = mappedClass; } - public TableRowToBeanMapper(Class resultType, MapToBeanConverter converter, boolean useColumnName, - RowMapper> mapper) { - this.resultType = resultType; - this.converter = converter; - this.useColumnName = useColumnName; - this.mapper = mapper; + protected final Class getMappedClass() { + return this.mappedClass; } @Override public T mapRow(ResultSet rs, int rowNum) throws SQLException { - Map map = mapper.mapRow(rs, rowNum); - - // 1. 获取列名与字段名的对应关系 - AllFieldColumn all = DbTools.parseAllFieldColumns(resultType); - if (all == null || all.isEmpty()) { - return null; - } + // 将ResultSet的行数据转换为Map + Map map = rowToMap(rs, rowNum); + // 利用工具类进行Map到JavaBean的转换 + return DbTools.getMapToBeanConverter().convert(map, getMappedClass()); + } - // 2. ResultSet是列名与字段值的对应关系, 转换为字段名与字段值的对应关系 + protected Map rowToMap(ResultSet rs, int rowNum) throws SQLException { + // 获取结果类的所有字段信息 + AllFieldColumn all = DbTools.parseAllFieldColumns(getMappedClass()); FieldColumns fieldColumns = all.filter(FieldScene.RESULT); - Map result = new HashMap(); - for (Map.Entry entry : map.entrySet()) { - SimpleFieldColumn field; - if (useColumnName) { - String columnName = entry.getKey(); - field = fieldColumns.findByColumnAlias(columnName); - } else { - String fieldName = entry.getKey(); - field = fieldColumns.findByFieldName(fieldName); - } + 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) { - result.put(field.getFieldName(), entry.getValue()); + String fieldName = field.getFieldName(); + Class fieldType = field.getJavaType(); + Object value = JdbcUtils.getResultSetValue(rs, i, fieldType); + mapOfColumnValues.put(fieldName, value); } } - // 3. 利用工具类进行Map到JavaBean的转换 - return converter.convert(result, resultType); + return mapOfColumnValues; + } + + protected String getColumnName(String columnName) { + TablesFieldColumnParser parser = DbPluginContainer.defaults().getTablesFieldColumnParser(); + // 表别名与列别名的分隔符 + String tableAliasSeparator = parser.getTableAliasSeparator(); + int tableAliasIndex = columnName.indexOf(tableAliasSeparator); + if (tableAliasIndex >= 0) { + columnName = columnName.substring(tableAliasIndex + tableAliasSeparator.length()); + } + return columnName; } + /** + * TableRowToBeanMapper的工厂类 + * + * @author zhaohuihua + * @version 20210306 + * @serial 3.3.0 + */ + public static class Factory implements FactoryOfTable { + + @Override + public RowToBeanMapper getRowToBeanMapper(Class mappedClass) { + return new TableRowToBeanMapper(mappedClass); + } + } } 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 f33d704cb2cf24dbc47caba442bb2d30685f1631..095b6adc0906b163c2aa1a2e23bdfc95a32188e2 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 @@ -1,17 +1,18 @@ package com.gitee.qdbp.jdbc.result; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; -import org.springframework.jdbc.core.ColumnMapRowMapper; +import org.springframework.jdbc.support.JdbcUtils; import com.gitee.qdbp.able.jdbc.condition.TableJoin; import com.gitee.qdbp.able.jdbc.condition.TableJoin.TableItem; 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.TablesFieldColumn; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; import com.gitee.qdbp.jdbc.utils.DbTools; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -32,45 +33,58 @@ import com.gitee.qdbp.tools.utils.VerifyTools; public class TablesRowToProperyMapper implements RowToBeanMapper { private TableJoin tables; - private Class resultType; - private MapToBeanConverter converter; - private ColumnMapRowMapper mapper = new ColumnMapRowMapper(); + private Class mappedClass; - public TablesRowToProperyMapper(TableJoin tables, Class resultType, MapToBeanConverter converter) { + public TablesRowToProperyMapper(TableJoin tables, Class mappedClass) { this.tables = tables; - this.resultType = resultType; - this.converter = converter; + this.mappedClass = mappedClass; + } + + protected final TableJoin getTables() { + return this.tables; + } + + protected final Class getMappedClass() { + return this.mappedClass; } @Override public T mapRow(ResultSet rs, int rowNum) throws SQLException { - Map map = mapper.mapRow(rs, rowNum); + // 将ResultSet的行数据转换为Map + Map map = rowToMap(rs, rowNum); + // 利用工具类进行Map到JavaBean的转换 + return DbTools.getMapToBeanConverter().convert(map, getMappedClass()); + } + protected Map rowToMap(ResultSet rs, int rowNum) throws SQLException { // 1. 获取列名与字段名的对应关系 - AllFieldColumn all = DbTools.parseAllFieldColumns(tables); - if (all == null || all.isEmpty()) { - return null; - } + // 获取结果类的所有字段信息 + AllFieldColumn all = DbTools.parseAllFieldColumns(getTables()); + FieldColumns fieldColumns = all.filter(FieldScene.RESULT); - Map result = new HashMap<>(); // 结果容器 + Map result = new LinkedHashMap<>(); // 结果容器 Map> subs = new HashMap<>(); // 子对象容器 // 2. 根据TableJoin的resultField生成子Map对象 String majorField = tables.getMajor().getResultField(); if (VerifyTools.isNotBlank(majorField) && !majorField.equals("this")) { - subs.put(majorField, new HashMap()); + subs.put(majorField, new LinkedHashMap()); } for (TableItem item : tables.getJoins()) { String itemField = item.getResultField(); if (VerifyTools.isNotBlank(itemField) && !itemField.equals("this")) { - subs.put(itemField, new HashMap()); + subs.put(itemField, new LinkedHashMap()); } } + if (!subs.isEmpty()) { + result.putAll(subs); + } - FieldColumns fieldColumns = all.filter(FieldScene.RESULT); // 3. 根据列别名查找字段信息, 再找到resultField, 根据字段名填充数据 - for (Map.Entry entry : map.entrySet()) { + ResultSetMetaData rsmd = rs.getMetaData(); + int columnCount = rsmd.getColumnCount(); + for (int i = 1; i <= columnCount; i++) { // 根据列别名查找字段信息 - String columnAlias = entry.getKey(); + String columnAlias = JdbcUtils.lookupColumnName(rsmd, i); TablesFieldColumn field = fieldColumns.findByColumnAlias(columnAlias); if (field == null) { continue; // 结果集的列在结果容器类中找不到对应的字段 @@ -80,19 +94,34 @@ public class TablesRowToProperyMapper implements RowToBeanMapper { if (VerifyTools.isBlank(resultField)) { 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(field.getFieldName(), entry.getValue()); + result.put(fieldName, value); } else { - subs.get(resultField).put(field.getFieldName(), entry.getValue()); + subs.get(resultField).put(fieldName, value); } } - if (!subs.isEmpty()) { - result.putAll(subs); - } - // 4. 利用工具类进行Map到JavaBean的转换 - return converter.convert(result, resultType); + return result; } + /** + * TablesRowToProperyMapper的工厂类 + * + * @author zhaohuihua + * @version 20210306 + */ + public static class Factory implements FactoryOfTables { + + @Override + public RowToBeanMapper getRowToBeanMapper(TableJoin tables, Class mappedClass) { + return new TablesRowToProperyMapper(tables, mappedClass); + } + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuffer.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuffer.java index 5859c89552d6d6cfdd12ef0b079f757aef52042e..c694d779f45a50d0b90810a1ceed1ce117fc6fa9 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuffer.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuffer.java @@ -355,9 +355,9 @@ public class SqlBuffer implements Serializable { } /** 删除左侧空白 **/ - private void trimLeft() { + public SqlBuffer trimLeft() { if (buffer.isEmpty()) { - return; + return this; } for (int i = 0, last = buffer.size() - 1; i <= last; i++) { Item item = buffer.get(i); @@ -368,14 +368,15 @@ public class SqlBuffer implements Serializable { continue; } } - return; + return this; } + return this; } /** 删除右侧空白 **/ - private void trimRight() { + public SqlBuffer trimRight() { if (buffer.isEmpty()) { - return; + return this; } for (int i = buffer.size() - 1; i >= 0; i--) { Item item = buffer.get(i); @@ -386,8 +387,9 @@ public class SqlBuffer implements Serializable { continue; } } - return; + return this; } + return this; } /** @@ -404,7 +406,7 @@ public class SqlBuffer implements Serializable { if (buffer.isEmpty() || VerifyTools.isAllBlank(prefix, prefixOverrides)) { return false; } - for (int i = 0, last = buffer.size() - 1; i <= last; i++) { + for (int i = 0; i < buffer.size(); i++) { Item item = buffer.get(i); if (item instanceof StringItem) { StringItem stringItem = (StringItem) item; @@ -418,10 +420,11 @@ public class SqlBuffer implements Serializable { } StringItem element = new StringItem(); element.append(prefix); - if (!SqlTextTools.endsWithSqlSymbol(prefix, ')')) { + // 如果前缀的结尾不是符号, 就要加空格; 符号排除了右括号, 因为右括号后面也要加空格 + if (!SqlTools.Text.endsWithSqlSymbol(prefix, ')')) { element.append(' '); } - buffer.add(i, element); + buffer.add(i++, element); return true; } else if (item instanceof OmitItem) { continue; @@ -439,7 +442,7 @@ public class SqlBuffer implements Serializable { * 如果suffix为空, 又没有找到suffixOverrides, 也会返回false * * @param suffix 待插入的后缀 - * @param suffixOverrides 待替换的后缀, 不区分大小写, 支持以|拆分的多个前缀, 如AND|OR + * @param suffixOverrides 待替换的后缀, 不区分大小写, 支持以|拆分的多个后缀, 如AND|OR * @return 是否有变更 */ public boolean insertSuffix(String suffix, String suffixOverrides) { @@ -459,7 +462,7 @@ public class SqlBuffer implements Serializable { return false; } StringItem element = new StringItem(); - if (!SqlTextTools.startsWithSqlSymbol(suffix)) { + if (!SqlTools.Text.startsWithSqlSymbol(suffix)) { element.append(' '); } element.append(suffix); @@ -489,14 +492,17 @@ public class SqlBuffer implements Serializable { if (stringValue.trim().length() > 0) { return false; } - } else if (item instanceof VariableItem) { - return false; } else if (item instanceof RawValueItem) { RawValueItem rawValueItem = (RawValueItem) item; String stringValue = rawValueItem.getValue(); if (stringValue.trim().length() > 0) { return false; } + } else if (item instanceof VariableItem) { + VariableItem variableItem = (VariableItem) item; + if (variableItem.getValue() != null) { + return false; + } } else if (item instanceof OmitItem) { continue; } else { @@ -565,41 +571,110 @@ public class SqlBuffer implements Serializable { return this; } - /** 所有内容缩进1个TAB **/ - public SqlBuffer indentAll() { - return indentAll(1, true); + /** + * 所有缩进增加或减少n个TAB + * + * @param size 缩进量: 正数为增加, 负数为减少 + * @return 返回当前SQL容器用于连写 + */ + public SqlBuffer tabAll(int size) { + return this.tabAll(size, true); } /** - * 所有内容缩进n个TAB + * 所有缩进增加或减少n个TAB * - * @param size 缩进多少个TAB + * @param size 缩进量: 正数为增加, 负数为减少 * @param leading 开头要不要缩进 * @return 返回当前SQL容器用于连写 */ - public SqlBuffer indentAll(int size, boolean leading) { - if (size <= 0 || this.buffer.isEmpty()) { + public SqlBuffer tabAll(int size, boolean leading) { + if (size == 0) { return this; } - char[] tabs = IndentTools.getIndenTabs(size); + + boolean first = true; + boolean needPrepend = false; // 以VariableItem开头, 且leading=true且size>0时, 需要在前面添加TAB for (Item item : this.buffer) { - if (item instanceof StringItem) { - StringItem stringItem = (StringItem) item; - stringItem.indentAll(tabs); + if (item == null) { + continue; } - } - if (leading) { - Item first = this.buffer.get(0); - if (first instanceof StringItem) { - StringItem stringItem = (StringItem) first; - stringItem.prepend(tabs); + if (item instanceof StringItem) { + StringItem temp = (StringItem) item; + if (temp.value.length() == 0) { + continue; + } + // blankLineIndent=空行要不要缩进, 一定要传true + // 因为这里的value只是片段, 后面可能还有内容, 并不能判断空行 + IndentTools.space.tabAll(temp.value, size, first && leading, true); + first = false; + } else if (item instanceof RawValueItem) { + RawValueItem temp = (RawValueItem) item; + if (temp.value.length() == 0) { + continue; + } + temp.setValue(IndentTools.space.tabAll(temp.value, size, first && leading, true)); + first = false; + } else if (item instanceof VariableItem) { + if (first) { + first = false; + if (leading && size > 0) { + needPrepend = true; + } + } + } else if (item instanceof OmitItem) { + continue; } else { - this.prepend(tabs); + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); } } + + if (needPrepend) { + this.prepend(IndentTools.getIndenTabs(size)); + } + return this; } + /** + * 所有缩进设置为1个TAB, 以最小的缩进为基准 + * + * @return 返回当前SQL容器用于连写 + * @deprecated 含义不明晰
+ * 如果是设置为1个TAB, 应改为indentAll(1);
+ * 如果是在原有的基础上缩进1个TAB, 应改为tabAll(1); + */ + @Deprecated + public SqlBuffer indentAll() { + return indentAll(1, true); + } + + /** + * 所有缩进设置为n个TAB, 以最小的缩进为基准 + * + * @param size 缩进量: 必须为正数或0, 负数视为0 + * @return 返回当前SQL容器用于连写 + */ + public SqlBuffer indentAll(int size) { + return this.indentAll(size, true); + } + + /** + * 所有缩进设置为n个TAB, 以最小的缩进为基准 + * + * @param size 缩进量: 必须为正数或0, 负数视为0 + * @param leading 开头要不要缩进 + * @return 返回当前SQL容器用于连写 + */ + public SqlBuffer indentAll(int size, boolean leading) { + // minIndent<0表示未找到缩进, 只有leading=false才有可能<0 + int minIndent = SqlTools.Indent.countMinIndentSize(this, leading); + if (minIndent < 0 || size == minIndent) { + return this; + } + return this.tabAll(size - minIndent, leading); + } + /** * 复制 * @@ -742,7 +817,7 @@ public class SqlBuffer implements Serializable { * @return SQL语句 */ public String getLoggingSqlString(DbVersion version, boolean omitMode) { - return getLoggingSqlString(DbTools.buildSqlDialect(version), omitMode); + return getLoggingSqlString(DbTools.buildSqlDialect(version), omitMode, true); } /** @@ -755,6 +830,10 @@ public class SqlBuffer implements Serializable { * @return SQL语句 */ public String getLoggingSqlString(SqlDialect dialect, boolean omitMode) { + return getLoggingSqlString(dialect, omitMode, true); + } + + private String getLoggingSqlString(SqlDialect dialect, boolean omitMode, boolean format) { int valueLimit = 100; StringBuilder sql = new StringBuilder(); int charCount = 0; @@ -828,7 +907,7 @@ public class SqlBuffer implements Serializable { if (!omitStacks.isEmpty() && charCount > 0) { // 插入省略信息 insertOmittedDetails(sql, charCount, lineCount, -1); } - return sqlFormatToString(sql); + return format ? sqlFormatToString(sql) : sql.toString(); } /** 替换\t为4个空格, 替换\r\n为\n, 替换单独的\r为\n; 清除末尾的空白 **/ @@ -856,7 +935,7 @@ public class SqlBuffer implements Serializable { // /* 10000 chars, 100 lines, 50 items are omitted here ... */ StringBuilder msg = generateOmittedDetails(charCount, lineCount, itemCount); // 在最后一个换行符之后插入省略信息 - IndentTools.insertMessageAfterLastNewline(sql, msg); + IndentTools.space.insertMessageAfterLastNewline(sql, msg); } // 生成省略掉的行数和字符数详细描述 @@ -904,7 +983,7 @@ public class SqlBuffer implements Serializable { public String toString() { SqlDialect dialect = DbTools.buildSqlDialect(new DbVersion(MainDbType.Oracle)); - return getLoggingSqlString(dialect, true); + return getLoggingSqlString(dialect, true, false); } protected List items() { @@ -922,8 +1001,12 @@ public class SqlBuffer implements Serializable { if (this.isEmpty() || part.isEmpty()) { return this; } + if (SqlTools.Text.startsWithChar(part, '(') && IndentTools.countNewlineChars(part) > 0) { + this.append(' '); + return this; + } // 左侧是除右括号以外的空白或符号, 或者右侧是空白或符号, 不需要加空格 - if (SqlTextTools.endsWithSqlSymbol(this, ')') || SqlTextTools.startsWithSqlSymbol(part)) { + if (SqlTools.Text.endsWithSqlSymbol(this, ')') || SqlTools.Text.startsWithSqlSymbol(part)) { return this; } this.append(' '); @@ -942,8 +1025,12 @@ public class SqlBuffer implements Serializable { if (this.isEmpty() || part.isEmpty()) { return this; } + if (SqlTools.Text.startsWithChar(part, '(') && SqlTools.Text.countNewlineChars(part) > 0) { + this.append(' '); + return this; + } // 左侧是除右括号以外的空白或符号, 或者右侧是空白或符号, 不需要加空格 - if (SqlTextTools.endsWithSqlSymbol(this, ')') || SqlTextTools.startsWithSqlSymbol(part)) { + if (SqlTools.Text.endsWithSqlSymbol(this, ')') || SqlTools.Text.startsWithSqlSymbol(part)) { return this; } this.append(' '); @@ -955,7 +1042,7 @@ public class SqlBuffer implements Serializable { if (this.isEmpty()) { return this; } - if (SqlTextTools.endsWithSqlSymbol(this, ')')) { + if (SqlTools.Text.endsWithSqlSymbol(this, ')')) { return this; } this.append(' '); @@ -969,8 +1056,12 @@ public class SqlBuffer implements Serializable { if (this.isEmpty() || part.isEmpty()) { return this; } + if (SqlTools.Text.startsWithChar(this, '(') && SqlTools.Text.countNewlineChars(this) > 0) { + this.prepend(' '); + return this; + } // 左侧是除右括号以外的空白或符号, 或者右侧是空白或符号, 不需要加空格 - if (SqlTextTools.endsWithSqlSymbol(part, ')') || SqlTextTools.startsWithSqlSymbol(this)) { + if (SqlTools.Text.endsWithSqlSymbol(part, ')') || SqlTools.Text.startsWithSqlSymbol(this)) { return this; } this.prepend(' '); @@ -983,22 +1074,26 @@ public class SqlBuffer implements Serializable { if (this.isEmpty() || part.isEmpty()) { return this; } + if (SqlTools.Text.startsWithChar(this, '(') && SqlTools.Text.countNewlineChars(this) > 0) { + this.prepend(' '); + return this; + } // 左侧是除右括号以外的空白或符号, 或者右侧是空白或符号, 不需要加空格 - if (SqlTextTools.endsWithSqlSymbol(part, ')') || SqlTextTools.startsWithSqlSymbol(this)) { + if (SqlTools.Text.endsWithSqlSymbol(part, ')') || SqlTools.Text.startsWithSqlSymbol(this)) { return this; } this.prepend(' '); return this; } - /** 查找最后的缩进量 **/ - protected int findLastIndentSize() { - return SqlTextTools.findLastIndentSize(this); + /** 清除最前面的缩进空白(返回清除了几个缩进量) **/ + protected int clearLeadingIndentWhitespace() { + return SqlTools.Indent.clearLeadingIndentWhitespace(this); } /** 清除最后的文字后面的缩进空白(返回清除了几个缩进量) **/ protected int clearTrailingIndentWhitespace() { - return SqlTextTools.clearTrailingIndentWhitespace(this); + return SqlTools.Indent.clearTrailingIndentWhitespace(this); } protected static interface Item { @@ -1163,15 +1258,6 @@ public class SqlBuffer implements Serializable { return value.length(); } - /** 缩进TAB(只在换行符后面增加TAB) **/ - public void indentAll(char[] tabs) { - for (int i = value.length() - 1; i >= 0; i--) { - if (value.charAt(i) == '\n') { - value.insert(i + 1, tabs); - } - } - } - /** * 在第1个非空字符前插入前缀
* 删除prefixOverrides, 插入prefix
@@ -1221,7 +1307,7 @@ public class SqlBuffer implements Serializable { value.insert(position, prefix); } else { String suffix = value.substring(position, Math.min(position + 10, value.length())); - if (SqlTextTools.endsWithSqlSymbol(prefix, ')') || SqlTextTools.startsWithSqlSymbol(suffix)) { + if (SqlTools.Text.endsWithSqlSymbol(prefix, ')') || SqlTools.Text.startsWithSqlSymbol(suffix)) { value.insert(position, prefix); } else { value.insert(position, prefix + ' '); @@ -1282,7 +1368,7 @@ public class SqlBuffer implements Serializable { value.insert(position, suffix); } else { String prefix = value.substring(Math.max(position - 10, 0), position); - if (SqlTextTools.endsWithSqlSymbol(prefix, ')') || SqlTextTools.startsWithSqlSymbol(suffix)) { + if (SqlTools.Text.endsWithSqlSymbol(prefix, ')') || SqlTools.Text.startsWithSqlSymbol(suffix)) { value.insert(position, suffix); } else { value.insert(position, ' ' + suffix); @@ -1304,8 +1390,8 @@ public class SqlBuffer implements Serializable { } // 如果prefix是单词结尾, 则下一个字符必须不是单词 // 例如替换OR, 遇到ORG_ID, 应判定为不匹配 - if (i + index < string.length() && SqlTextTools.isSqlWordChar(prefix.charAt(prefix.length() - 1))) { - if (SqlTextTools.isSqlWordChar(string.charAt(i + index))) { + if (i + index < string.length() && StringTools.isWordChar(prefix.charAt(prefix.length() - 1))) { + if (StringTools.isWordChar(string.charAt(i + index))) { return false; } } @@ -1323,8 +1409,8 @@ public class SqlBuffer implements Serializable { } } // 如果suffix是单词开头, 则前一个字符必须不是单词 - if (si > 0 && SqlTextTools.isSqlWordChar(suffix.charAt(0))) { - if (SqlTextTools.isSqlWordChar(string.charAt(si - 1))) { + if (si > 0 && StringTools.isWordChar(suffix.charAt(0))) { + if (StringTools.isWordChar(string.charAt(si - 1))) { return false; } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuilder.java index f14e50fabe265e6f34063165c88da8157450c6e2..227c20ba8514670b5e4e0c19c452a9e9cf2b84aa 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuilder.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlBuilder.java @@ -2,6 +2,7 @@ package com.gitee.qdbp.jdbc.sql; import java.io.Serializable; import com.gitee.qdbp.tools.utils.IndentTools; +import com.gitee.qdbp.tools.utils.StringTools; /** * SQL生成器, 会自动追加空格
@@ -39,6 +40,19 @@ public class SqlBuilder implements Serializable { return this.buffer; } + /** + * 如果容器不是空的, 就追加SQL片断 + * + * @param part SQL片断 + * @return 返回当前SQL容器用于连写 + */ + public SqlBuilder adIf(String part) { + if (!this.isBlank()) { + this.ad(part); + } + return this; + } + /** * ad=append: 将指定SQL片段追加到SQL后面, 将会在前后及每项之间自动追加空格 * @@ -48,7 +62,27 @@ public class SqlBuilder implements Serializable { public SqlBuilder ad(String... parts) { if (parts != null) { for (String part : parts) { - this.buffer.autoAppendWhitespace(part).append(part); + int lastIndent = SqlTools.Indent.findLastIndentSize(this.buffer); + if (lastIndent == 0 || IndentTools.countNewlineChars(part) == 0) { + // 不能自动处理, 因为有可能本意就是想简单的追加 + this.buffer.autoAppendWhitespace(part).append(part); + } else { // 只有两个片段都有换行才自动处理 + // minIndent<0表示未找到缩进, 只有leading=false才有可能<0 + int minIndent = IndentTools.countMinIndentSize(part, true); + if (minIndent < 0 || lastIndent == minIndent) { + this.buffer.autoAppendWhitespace(part).append(part); + } else { + // 左侧以空行结尾, 右侧首行空白后面会清除(不用额外判断,加不加无所谓) + // 左侧是文字结尾, 右侧以空行开头, 则右侧首行不用加缩进 + boolean leading = !IndentTools.startsWithBlankline(part); + String temp = IndentTools.tabs.tabAll(part, lastIndent - minIndent, leading); + if (SqlTools.Text.endsWithBlankline(this.buffer)) { + // 左侧以空行结束且最后有缩进(lastIndent>0), 则右侧首行缩进需要清除掉 + temp = StringTools.removeLeft(temp, ' ', '\t'); + } + this.buffer.autoAppendWhitespace(temp).append(temp); + } + } } } return this; @@ -78,6 +112,23 @@ public class SqlBuilder implements Serializable { if (parts != null) { for (int i = parts.length - 1; i >= 0; i--) { String part = parts[i]; + int lastIndent = IndentTools.findLastIndentSize(part); + if (lastIndent == 0 || SqlTools.Text.countNewlineChars(this.buffer) == 0) { + // 不能自动处理, 因为有可能本意就是想简单的追加 + } else { // 只有两个片段都有换行才自动处理 + // minIndent<0表示未找到缩进, 只有leading=false才有可能<0 + int minIndent = SqlTools.Indent.countMinIndentSize(this.buffer, true); + if (minIndent >= 0 && lastIndent != minIndent) { + // 左侧以空行结尾, 右侧首行空白后面会清除(不用额外判断,加不加无所谓) + // 左侧是文字结尾, 右侧以空行开头, 则右侧首行不用加缩进 + boolean leading = !SqlTools.Text.startsWithBlankline(this.buffer); + this.buffer.tabAll(lastIndent - minIndent, leading); + if (IndentTools.endsWithBlankline(part)) { + // 左侧以空行结束且最后有缩进(lastIndent>0), 则右侧首行缩进需要清除掉 + this.buffer.clearLeadingIndentWhitespace(); + } + } + } this.buffer.autoPrependWhitespace(part).prepend(part); } } @@ -106,11 +157,25 @@ public class SqlBuilder implements Serializable { */ public SqlBuilder ad(SqlBuffer buffer) { if (buffer != null && !buffer.isEmpty()) { - int indent = this.buffer.findLastIndentSize(); - if (indent > 0) { - buffer.indentAll(indent, false); + int lastIndent = SqlTools.Indent.findLastIndentSize(this.buffer); + SqlBuffer temp = buffer; + if (lastIndent == 0 || SqlTools.Text.countNewlineChars(buffer) == 0) { + // 不能自动处理, 因为有可能本意就是想简单的追加 + } else { // 只有两个片段都有换行才自动处理 + // minIndent<0表示未找到缩进, 只有leading=false才有可能<0 + int minIndent = SqlTools.Indent.countMinIndentSize(temp, true); + if (minIndent >= 0 && lastIndent != minIndent) { + // 左侧以空行结尾, 右侧首行空白后面会清除(不用额外判断,加不加无所谓) + // 左侧是文字结尾, 右侧以空行开头, 则右侧首行不用加缩进 + boolean leading = !SqlTools.Text.startsWithBlankline(buffer); + temp = buffer.copy().tabAll(lastIndent - minIndent, leading); + if (SqlTools.Text.endsWithBlankline(this.buffer)) { + // 左侧以空行结束且最后有缩进(lastIndent>0), 则右侧首行缩进需要清除掉 + temp.clearLeadingIndentWhitespace(); + } + } } - this.buffer.autoAppendWhitespace(buffer).append(buffer); + this.buffer.autoAppendWhitespace(temp).append(temp); } return this; } @@ -133,9 +198,22 @@ public class SqlBuilder implements Serializable { */ public SqlBuilder pd(SqlBuffer buffer) { if (buffer != null && !buffer.isEmpty()) { - int indent = buffer.findLastIndentSize(); - if (indent > 0) { - this.buffer.indentAll(indent, false); + int indent = SqlTools.Indent.findLastIndentSize(buffer); + if (indent == 0 || SqlTools.Text.countNewlineChars(this.buffer) == 0) { + // 不能自动处理, 因为有可能本意就是想简单的追加 + } else { // 只有两个片段都有换行才自动处理 + // minIndent<0表示未找到缩进, 只有leading=false才有可能<0 + int minIndent = SqlTools.Indent.countMinIndentSize(this.buffer, true); + if (minIndent >= 0 && indent != minIndent) { + // 左侧以空行结尾, 右侧首行空白后面会清除(不用额外判断,加不加无所谓) + // 左侧是文字结尾, 右侧以空行开头, 则右侧首行不用加缩进 + boolean leading = !SqlTools.Text.startsWithBlankline(this.buffer); + this.buffer.tabAll(indent - minIndent, leading); + if (SqlTools.Text.endsWithBlankline(buffer)) { + // 左侧以空行结束且最后有缩进(lastIndent>0), 则右侧首行缩进需要清除掉 + this.buffer.clearLeadingIndentWhitespace(); + } + } } this.buffer.autoPrependWhitespace(buffer).prepend(buffer); } @@ -180,11 +258,11 @@ public class SqlBuilder implements Serializable { */ public SqlBuilder newline() { this.buffer.append('\n'); - int size = this.buffer.findLastIndentSize(); + int size = SqlTools.Indent.findLastIndentSize(this.buffer); if (size > 0) { this.buffer.append(IndentTools.getIndenTabs(size)); } - return this.tab(0); + return this; } /** @@ -199,7 +277,7 @@ public class SqlBuilder implements Serializable { /** * 在现有的基础上增加或减少缩进 * - * @param size 正数为增加, 负数为减少 + * @param size 缩进量: 正数为增加, 负数为减少 * @return 返回当前SQL容器用于连写 */ public SqlBuilder tab(int size) { @@ -222,7 +300,7 @@ public class SqlBuilder implements Serializable { * indent()是设置缩进量
* tab()是在当前缩进量的基础上增加或减少缩进
* - * @param size 必须为正数, 负数和0会报错 + * @param size 缩进量: 必须为正数或0, 负数视为0 * @return 返回当前SQL容器用于连写 */ public SqlBuilder indent(int size) { @@ -233,10 +311,36 @@ public class SqlBuilder implements Serializable { return this; } + /** + * 所有内容缩进n个TAB + * + * @param size 缩进量: 正数为增加, 负数为减少 + * @return 返回当前SQL容器用于连写 + */ + public SqlBuilder tabAll(int size) { + this.buffer.tabAll(size, true); + return this; + } + + /** + * 所有内容设置为缩进n个TAB, 以最小的缩进为准 + * + * @param size 必须为正数或0, 负数视为0 + * @return 返回当前SQL容器用于连写 + */ + public SqlBuilder indentAll(int size) { + this.buffer.indentAll(size, true); + return this; + } + public boolean isEmpty() { return buffer.isEmpty(); } + public boolean isBlank() { + return buffer.isBlank(); + } + /** * 超级长的SQL在输出日志时可以省略掉一部分
* 省略哪一部分, 用startOmit()/endOmit()来标识起止位置 diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTextTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTextTools.java deleted file mode 100644 index 6eacb6e140bdf4a136f2c6cf92c571bc8cce1d6c..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTextTools.java +++ /dev/null @@ -1,394 +0,0 @@ -package com.gitee.qdbp.jdbc.sql; - -import com.gitee.qdbp.jdbc.sql.SqlBuffer.Item; -import com.gitee.qdbp.jdbc.sql.SqlBuffer.OmitItem; -import com.gitee.qdbp.jdbc.sql.SqlBuffer.RawValueItem; -import com.gitee.qdbp.jdbc.sql.SqlBuffer.StringItem; -import com.gitee.qdbp.jdbc.sql.SqlBuffer.VariableItem; -import com.gitee.qdbp.tools.utils.IndentTools; - -/** - * 内部使用的SQL文本工具类 - * - * @author zhaohuihua - * @version 20200718 - */ -class SqlTextTools { - - /** - * 是不是SQL中的单词字符 - * - * @param c 字符 - * @return 是不是单词字符 - */ - public static boolean isSqlWordChar(char c) { - return c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '$'; - } - - /** - * 查找最后的缩进量
-     * 例如: \r\n=换行符, \t=TAB符, \s=空格
-     * \n\tABC\n\t\tDEF\t\t\t --> 这里要找的是DEF之前的那个换行符之后的空白字符, 即缩进量为2
-     * \n\tABC\n\t\tDEF\n     --> 最后一个字符就是换行符, 即刚刚换行完, 要找的仍然是DEF之前的那个换行符
-     * \n\tABC\n\t\tDEF\n\n   --> 最后连续多个换行符, 要找的仍然是DEF之前的那个换行符
-     * \n\tABC\n\t\t          --> 这里应返回ABC后面的换行符之后的缩进量2
-     * \tABC --> 这里应返回首行的缩进量1
- * - * @param sql SQL语句 - * @return 缩进量 - */ - public static int findLastIndentSize(SqlBuffer sql) { - if (sql.items().isEmpty()) { - return 0; - } - // 先从后向前查找带有换行符的字符串 - // 取换行符之后的子串 - // 如果子串全是空白字符, 再向后取前置的空白字符 - int size = sql.items().size(); - for (int i = size - 1; i >= 0; i--) { - String suffixAfterNewline; - { // 查找带有换行符的字符串, 并获取换行符之后的子串 - CharSequence string = getItemStringValue(sql.items().get(i)); - if (string == null) { - continue; - } - int lastIndex = string.length() - 1; - if (string.length() == 0) { - suffixAfterNewline = null; - } else { - if (i == size - 1) { // 如果是最后一项, 移除最后的连续多个换行符 - lastIndex = getIndexOfBeforeTrailingChars(string, '\r', '\n'); - } - // 获取换行符之后的子串 - suffixAfterNewline = getSubstringOfAfterLastNewline(string, lastIndex); - } - if (suffixAfterNewline == null && i == 0) { - // 在没有换行符的情况下, 如果是第一项, 则将整个字符串视为换行符之后的子串 - // +1是因为getIndexOfBeforeTrailingChars返回是的换行符之前的那个位置 - // +1才能将那个位置的字符包含进来 - suffixAfterNewline = string.toString().substring(0, lastIndex + 1); - } - if (suffixAfterNewline == null) { - continue; // 未找到换行符 - } - } - // 已找到换行符, 判断是否全为空白字符 - if (!isAllWhitespace(suffixAfterNewline)) { - String leadingWhitespace = getLeadingWhitespace(suffixAfterNewline); - return IndentTools.calcSpacesToTabSize(leadingWhitespace); - } else { - // 向后取前置的空白字符 - StringBuilder buffer = new StringBuilder(); - buffer.append(suffixAfterNewline); - for (int j = i + 1; j < size; j++) { - CharSequence string = getItemStringValue(sql.items().get(j)); - if (string != null && isAllWhitespace(string)) { - buffer.append(string); - continue; // 后面仍然全是空白, 再继续向后查找 - } else { - if (string != null && string.length() > 0) { - String leadingWhitespace = getLeadingWhitespace(string); - buffer.append(leadingWhitespace); - } - break; // 遇到非string类型的item, 或者不全是空白, 结束查找 - } - } - return IndentTools.calcSpacesToTabSize(buffer); - } - } - return 0; - } - - /** - * 清除最后的文字后面的缩进空白 - * - * @param sql SQL语句 - * @return 清除了多少个缩进量 - */ - public static int clearTrailingIndentWhitespace(SqlBuffer sql) { - if (sql.items().isEmpty()) { - return 0; - } - // 先从后向前查找带有换行符的字符串 - // 取换行符之后的子串 - // 如果子串全是空白字符, 再向后取前置的空白字符 - int size = sql.items().size(); - StringBuilder allCleared = new StringBuilder(); - for (int i = size - 1; i >= 0; i--) { - Item item = sql.items().get(i); - if (item instanceof StringItem) { - StringItem stringItem = (StringItem) item; - String cleared = clearTrailingIndentWhitespace(stringItem); - if (cleared != null) { - allCleared.append(cleared); - } - if (stringItem.getValue().length() > 0) { - break; // 清除完之后如果还有字符, 结束查找 - } - } else if (item instanceof VariableItem) { - break; // 遇到非string类型的item, 结束查找 - } else if (item instanceof RawValueItem) { - RawValueItem stringItem = (RawValueItem) item; - String cleared = clearTrailingIndentWhitespace(stringItem); - if (cleared != null) { - allCleared.append(cleared); - } - if (stringItem.getValue().length() > 0) { - break; // 清除完之后如果还有字符, 结束查找 - } - } else if (item instanceof OmitItem) { - break; // 遇到非string类型的item, 结束查找 - } else { - throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); - } - } - return IndentTools.calcSpacesToTabSize(allCleared); - } - - private static String clearTrailingIndentWhitespace(StringItem item) { - // +1是因为getIndexOfBeforeTrailingChars返回是的空白之前的那个位置 - // +1才能将那个位置的字符包含进来 - int index = getIndexOfBeforeTrailingChars(item.getValue(), ' ', '\t') + 1; - if (index >= item.getValue().length()) { - return null; - } else { - String cleared = item.getValue().substring(index); - item.getValue().setLength(index); - return cleared; - } - } - - private static String clearTrailingIndentWhitespace(RawValueItem item) { - // +1是因为getIndexOfBeforeTrailingChars返回是的空白之前的那个位置 - // +1才能将那个位置的字符包含进来 - int index = getIndexOfBeforeTrailingChars(item.getValue(), ' ', '\t') + 1; - if (index >= item.getValue().length()) { - return null; - } else { - String cleared = item.getValue().substring(index); - item.setValue(index == 0 ? "" : item.getValue().substring(0, index)); - return cleared; - } - } - - private static CharSequence getItemStringValue(Item item) { - if (item instanceof StringItem) { - return ((StringItem) item).getValue(); - } else if (item instanceof VariableItem) { - return null; - } else if (item instanceof RawValueItem) { - return ((RawValueItem) item).getValue(); - } else if (item instanceof OmitItem) { - return null; - } else { - throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); - } - } - - /** 获取最后一个换行符之后的子字符串 **/ - // ABC\n -- "" - // ABC\n\t\t -- \t\t - // \n\t\tABC\t\t\t -- \t\t (ABC前面的2个TAB) - // \n\t\tABC\n\t\t\t -- \t\t\t (ABC后面的3个TAB) - // \t\tABC -- null (没有换行符) - // "" -- null (没有换行符) - private static String getSubstringOfAfterLastNewline(CharSequence string, int lastIndex) { - for (int i = lastIndex; i >= 0; i--) { - char c = string.charAt(i); - if (c == '\r' || c == '\n') { - return string.toString().substring(i + 1); - } - } - return null; - } - - /** 获取前置空白字符 **/ - private static String getLeadingWhitespace(CharSequence string) { - for (int i = 0; i < string.length(); i++) { - char c = string.charAt(i); - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { - continue; - } else { - return i == 0 ? "" : string.toString().substring(0, i); - } - } - return string.toString(); - } - - /** 获取指定字符之前的位置 **/ - private static int getIndexOfBeforeTrailingChars(CharSequence string, char... chars) { - int lastIndex = string.length() - 1; - for (int i = lastIndex; i >= 0; i--) { - char c = string.charAt(i); - if (isInChars(c, chars)) { - lastIndex--; // 移除最后连续的换行符 - } else { - break; - } - } - return lastIndex; - } - - /** 是不是全都是空白字符 **/ - private static boolean isAllWhitespace(CharSequence string) { - for (int i = 0; i < string.length(); i++) { - char c = string.charAt(i); - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { - continue; - } else { - return false; - } - } - return true; - } - - // 符号: ASCII码表顺序, 去掉了'`_$ - private static char[] SYMBOLS = "\t\r\n !\"#%&()*+,-./:;<=>?@[\\]^{|}~".toCharArray(); - - /** - * 是不是SQL符号 - * - * @param c 指定字符 - * @return 是不是符号 - */ - private static boolean isSqlSymbol(char c, char... excludeSymbols) { - if (isInChars(c, excludeSymbols)) { - return false; - } - for (int i = 0; i < SYMBOLS.length; i++) { - if (c == SYMBOLS[i]) { - return true; - } - } - return false; - } - - private static boolean isInChars(char c, char... chars) { - if (chars != null && chars.length > 0) { - for (int i = 0; i < chars.length; i++) { - if (c == chars[i]) { - return true; - } - } - } - return false; - } - - /** 是不是以SQL符号开头 **/ - public static boolean startsWithSqlSymbol(CharSequence string, char... excludeSymbols) { - if (string == null || string.length() == 0) { - return false; - } - return isSqlSymbol(string.charAt(0), excludeSymbols); - } - - /** 是不是以SQL符号开头 **/ - public static boolean startsWithSqlSymbol(SqlBuffer sql, char... excludeSymbols) { - if (sql.items().isEmpty()) { - return false; - } - for (Item item : sql.items()) { - if (item instanceof StringItem) { - return startsWithSqlSymbol(((StringItem) item).getValue(), excludeSymbols); - } else if (item instanceof VariableItem) { - return false; - } else if (item instanceof RawValueItem) { - return startsWithSqlSymbol(((RawValueItem) item).getValue(), excludeSymbols); - } else if (item instanceof OmitItem) { - continue; - } else { - throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); - } - } - return false; - } - - /** 是不是以SQL符号结尾 **/ - public static boolean endsWithSqlSymbol(CharSequence string, char... excludeSymbols) { - if (string == null || string.length() == 0) { - return false; - } - return isSqlSymbol(string.charAt(string.length() - 1), excludeSymbols); - } - - /** 是不是以SQL符号结尾 **/ - public static boolean endsWithSqlSymbol(SqlBuffer sql, char... excludeSymbols) { - if (sql.items().isEmpty()) { - return false; - } - for (int i = sql.items().size() - 1; i >= 0; i--) { - Item item = sql.items().get(i); - if (item instanceof StringItem) { - return endsWithSqlSymbol(((StringItem) item).getValue(), excludeSymbols); - } else if (item instanceof VariableItem) { - return false; - } else if (item instanceof RawValueItem) { - return endsWithSqlSymbol(((RawValueItem) item).getValue(), excludeSymbols); - } else if (item instanceof OmitItem) { - continue; - } else { - throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); - } - } - return false; - } - - /** 是不是以指定字符开头 **/ - public static boolean startsWithChar(CharSequence string, char c) { - if (string == null || string.length() == 0) { - return false; - } - return c == string.charAt(0); - } - - /** 是不是以指定字符开头 **/ - public static boolean startsWithChar(SqlBuffer sql, char c) { - if (sql.items().isEmpty()) { - return false; - } - for (Item item : sql.items()) { - if (item instanceof StringItem) { - return startsWithChar(((StringItem) item).getValue(), c); - } else if (item instanceof VariableItem) { - return false; - } else if (item instanceof RawValueItem) { - return startsWithChar(((RawValueItem) item).getValue(), c); - } else if (item instanceof OmitItem) { - continue; - } else { - throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); - } - } - return false; - } - - /** 是不是以指定字符结尾 **/ - public static boolean endsWithChar(CharSequence string, char c) { - if (string == null || string.length() == 0) { - return false; - } - return c == string.charAt(string.length() - 1); - } - - /** 是不是以指定字符结尾 **/ - public static boolean endsWithChar(SqlBuffer sql, char c) { - if (sql.items().isEmpty()) { - return false; - } - for (int i = sql.items().size() - 1; i >= 0; i--) { - Item item = sql.items().get(i); - if (item instanceof StringItem) { - return endsWithChar(((StringItem) item).getValue(), c); - } else if (item instanceof VariableItem) { - return false; - } else if (item instanceof RawValueItem) { - return endsWithChar(((RawValueItem) item).getValue(), c); - } else if (item instanceof OmitItem) { - continue; - } else { - throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); - } - } - return false; - } -} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTools.java index 56955c82bcc81a21405c02bffe6d38200f6ed9f3..e26f1e5c5dfd148e8a095181aa548f772561819f 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTools.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/SqlTools.java @@ -5,10 +5,33 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import com.gitee.qdbp.able.enums.WrapMode; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.able.jdbc.fields.ExcludeFields; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.ordering.OrderType; +import com.gitee.qdbp.able.jdbc.ordering.Ordering; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; +import com.gitee.qdbp.jdbc.model.FunctionOrdering; import com.gitee.qdbp.jdbc.model.OmitStrategy; +import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver; +import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; +import com.gitee.qdbp.jdbc.plugins.OrderBySqlBuilder; import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser; +import com.gitee.qdbp.jdbc.sql.SqlBuffer.Item; +import com.gitee.qdbp.jdbc.sql.SqlBuffer.OmitItem; +import com.gitee.qdbp.jdbc.sql.SqlBuffer.RawValueItem; +import com.gitee.qdbp.jdbc.sql.SqlBuffer.StringItem; +import com.gitee.qdbp.jdbc.sql.SqlBuffer.VariableItem; +import com.gitee.qdbp.jdbc.sql.fragment.CrudFragmentHelper; +import com.gitee.qdbp.jdbc.sql.fragment.TableCrudFragmentHelper; +import com.gitee.qdbp.jdbc.sql.fragment.TableJoinFragmentHelper; import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.tools.utils.IndentTools; +import com.gitee.qdbp.tools.utils.NamingTools; +import com.gitee.qdbp.tools.utils.StringTools; import com.gitee.qdbp.tools.utils.VerifyTools; /** @@ -17,7 +40,135 @@ import com.gitee.qdbp.tools.utils.VerifyTools; * @author zhaohuihua * @version 190601 */ -public abstract class SqlTools { +public class SqlTools { + + /** 工具类私有构造方法 **/ + private SqlTools() { + } + + /** + * 生成Select字段列表SQL语句 + * + * @param clazz 目标类名 + * @param dialect 数据库方言 + * @return SQL语句 + */ + public static SqlBuffer buildSelectFieldsSql(Class clazz, SqlDialect dialect) { + return buildSelectFieldsSql(clazz, null, null, dialect); + } + + /** + * 生成Select字段列表SQL语句 + * + * @param clazz 目标类名 + * @param alias 表别名 + * @param dialect 数据库方言 + * @return SQL语句 + */ + public static SqlBuffer buildSelectFieldsSql(Class clazz, String alias, SqlDialect dialect) { + return buildSelectFieldsSql(clazz, alias, null, dialect); + } + + /** + * 生成Select字段列表SQL语句 + * + * @param clazz 目标类名 + * @param alias 表别名 + * @param excludes 排除指定字段名 + * @param dialect 数据库方言 + * @return SQL语句 + */ + public static SqlBuffer buildSelectFieldsSql(Class clazz, String alias, String excludes, SqlDialect dialect) { + Fields fields = VerifyTools.isBlank(excludes) ? Fields.ALL : new ExcludeFields(excludes); + if (VerifyTools.isBlank(alias)) { + CrudFragmentHelper sqlHelper = new TableCrudFragmentHelper(clazz, dialect); + return sqlHelper.buildSelectFieldsSql(fields); + } else { + TableJoin tables = TableJoin.of(clazz, alias); + TableJoinFragmentHelper sqlHelper = new TableJoinFragmentHelper(tables, dialect); + return sqlHelper.buildSelectFieldsSql(fields); + } + } + + /** + * 生成OrderBy排序SQL语句 + * + * @param orderings OrderBy列表 + * @param columnResolver 列名转换处理类
+ * 如果能取到QueryFragmentHelper就用new ColumnNameHelper()
+ * 如果取不到, 就用ColumnNamingConverter.defaults(); + * @return SQL语句 + */ + public static SqlBuffer buildOrderBySql(Orderings orderings, ColumnNameResolver columnResolver, + SqlDialect dialect) { + if (VerifyTools.isBlank(orderings)) { + return null; + } + SqlBuilder buffer = new SqlBuilder(); + boolean first = true; + for (Ordering item : orderings) { + if (first) { + first = false; + } else { + buffer.ad(','); + } + buffer.ad(buildOrderBySql(item, columnResolver, dialect)); + } + return buffer.out(); + } + + public static SqlBuffer buildOrderBySql(Ordering ordering, ColumnNameResolver columnResolver, SqlDialect dialect) { + String functionName = ordering.getFunctionName(); + if (VerifyTools.isNotBlank(functionName)) { + FunctionOrdering functionOrdering = new FunctionOrdering(); + ordering.copyTo(functionOrdering); + OrderBySqlBuilder builder = DbTools.getOrderBySqlBuilder(functionOrdering); + return builder.buildSql(functionOrdering, columnResolver, dialect); + } else { + String columnName = columnResolver.getColumnName(ordering.getOrderBy()); + SqlBuilder buffer = new SqlBuilder(); + buffer.ad(columnName); + OrderType orderType = ordering.getOrderType(); + if (orderType == OrderType.ASC) { + buffer.ad("ASC"); + } else if (orderType == OrderType.DESC) { + buffer.ad("DESC"); + } + return buffer.out(); + } + } + + /** + * 转换为下划线命名字符串
+ * 如 u.userName -- u.USER_NAME + * + * @param fieldName 字段名 + * @param useUpperCase 是否使用大写 + * @return 下划线命名字符串 + */ + public static String toUnderlineString(String fieldName, boolean useUpperCase) { + int dotIndex = fieldName.indexOf('.'); + if (dotIndex <= 0) { + String underlineString = NamingTools.toUnderlineString(fieldName); + return useUpperCase ? underlineString.toUpperCase() : underlineString; + } else { + String prefix = fieldName.substring(0, dotIndex); + String suffix = fieldName.substring(dotIndex + 1); + String underlineSuffix = NamingTools.toUnderlineString(suffix); + return prefix + '.' + (useUpperCase ? underlineSuffix.toUpperCase() : underlineSuffix); + } + } + + public static String getFieldName(String columnName) { + TablesFieldColumnParser parser = DbPluginContainer.defaults().getTablesFieldColumnParser(); + // 表别名与列别名的分隔符 + String tableAliasSeparator = parser.getTableAliasSeparator(); + int tableAliasIndex = columnName.indexOf(tableAliasSeparator); + if (tableAliasIndex >= 0) { + columnName = columnName.substring(tableAliasIndex + tableAliasSeparator.length()); + } + return NamingTools.toCamelString(columnName); + } /** * 生成IN语句
@@ -77,7 +228,7 @@ public abstract class SqlTools { String operate = matches ? "IN" : "NOT IN"; int inItemLimit = dialect.getInItemLimit(); if (inItemLimit <= 0 || values.size() <= inItemLimit) { - sql.ad(columnName).ad(operate).ad('('); + sql.ad(columnName).ad(operate).ad(' ').ad('('); for (int i = 0, count = values.size(); i < count; i++) { Object value = values.get(i); if (i > 0) { @@ -100,7 +251,7 @@ public abstract class SqlTools { if (g > 0) { sql.ad(logic); } - sql.ad(columnName).ad(operate).ad('('); + sql.ad(columnName).ad(operate).ad(' ').ad('('); List list = groups.get(g); for (int i = 0, count = list.size(); i < count; i++) { Object value = list.get(i); @@ -145,4 +296,924 @@ public abstract class SqlTools { } return list; } + + /** + * 前后增加括号 + * + * @param sql SQL语句 + * @param brackets 括号处理方式 + */ + public static void wrap(SqlBuffer sql, WrapMode brackets) { + wrap(sql, brackets, null, null, null, null, true); + } + + /** + * 增加前缀/后缀 + * + * @param sql SQL语句 + * @param brackets 括号处理方式 + * @param prefix 待插入的前缀 + * @param suffix 待插入的后缀 + */ + public static void wrap(SqlBuffer sql, WrapMode brackets, String prefix, String suffix) { + wrap(sql, brackets, prefix, null, suffix, null, true); + } + + /** + * 增加前缀/后缀 + * + * @param sql SQL语句 + * @param brackets 括号处理方式 + * @param prefix 待插入的前缀 + * @param prefixOverrides 待替换的前缀, 不区分大小写, 支持以|拆分的多个前缀, 如AND|OR + * @param suffix 待插入的后缀 + * @param suffixOverrides 待替换的后缀, 不区分大小写, 支持以|拆分的多个后缀, 如AND|OR + */ + public static void wrap(SqlBuffer sql, WrapMode brackets, String prefix, String prefixOverrides, String suffix, + String suffixOverrides) { + wrap(sql, brackets, prefix, prefixOverrides, suffix, suffixOverrides, true); + } + + /** + * 增加前缀/后缀 + * + * @param sql SQL语句 + * @param brackets 括号处理方式 + * @param prefix 待插入的前缀 + * @param prefixOverrides 待替换的前缀, 不区分大小写, 支持以|拆分的多个前缀, 如AND|OR + * @param suffix 待插入的后缀 + * @param suffixOverrides 待替换的后缀, 不区分大小写, 支持以|拆分的多个后缀, 如AND|OR + * @param addLeadingNewline 要不要添加前置换行符 + */ + public static void wrap(SqlBuffer sql, WrapMode brackets, String prefix, String prefixOverrides, String suffix, + String suffixOverrides, boolean addLeadingNewline) { + if (sql.isBlank()) { + return; + } + boolean wrapBrackets = false; + Boolean useNewline = null; + if (brackets == WrapMode.FORCE) { + wrapBrackets = true; + } else if (brackets == WrapMode.AUTO) { // 自动识别是否添加括号 + if (VerifyTools.isNotBlank(prefix) && StringTools.existsWord(prefix, "OR")) { + wrapBrackets = true; // 前缀条件是OR + } else if (SqlTools.Text.exists(sql, "\n", true)) { + wrapBrackets = true; // SQL中存在换行符 + useNewline = true; + } else if (SqlTools.Text.exists(sql, "OR")) { + wrapBrackets = true; // SQL中含有OR条件 + } + } + + if (!wrapBrackets) { + //\\\\\\\\\\\\\\\\\\\\ + // 不需要加括号 + ////////////////////// + if (VerifyTools.isNotBlank(prefix) || VerifyTools.isNotBlank(prefixOverrides)) { + sql.insertPrefix(prefix, prefixOverrides); + } + if (VerifyTools.isNotBlank(suffix) || VerifyTools.isNotBlank(suffixOverrides)) { + sql.insertSuffix(suffix, suffixOverrides); + } + return; + } + + //\\\\\\\\\\\\\\\\\\\\ + // 需要加括号 + ////////////////////// + // 判断要不要换行 + if (useNewline == null) { + if (SqlTools.Text.exists(sql, "\n", true)) { + useNewline = true; + } else if (SqlTools.Text.exists(sql, "IN")) { + useNewline = true; + } else { + useNewline = false; + } + } + + if (!useNewline) { + //\\\\\\\\\\\\\\\\\\\\ + // 不需要换行 + ////////////////////// + // 前缀后缀加括号 + if (VerifyTools.isBlank(prefix)) { + prefix = "( "; + } else if (prefix.indexOf('(') >= 0) { // 前缀中存在左括号 + prefix += " "; + } else { + prefix += " ( "; + } + if (VerifyTools.isBlank(suffix)) { + suffix = " )"; + } else if (suffix.indexOf(')') >= 0) { // 后缀中存在右括号 + prefix = " " + prefix; + } else { + prefix += " )"; + } + + sql.insertPrefix(prefix, prefixOverrides); + sql.insertSuffix(suffix, suffixOverrides); + return; + } + + //\\\\\\\\\\\\\\\\\\\\ + // 需要换行 + ////////////////////// + // 先处理prefixOverrides/suffixOverrides + if (VerifyTools.isNotBlank(prefixOverrides)) { + sql.insertPrefix(null, prefixOverrides); + } + if (VerifyTools.isNotBlank(suffixOverrides)) { + sql.insertSuffix(null, suffixOverrides); + } + + // 计算buffer有多少个缩进 + int indent = Indent.countMinIndentSize(sql, false, 0); + + if (!Text.startsWithBlankline(sql)) { + sql.prepend('\n'); + } + // SQL内容增加缩进 + sql.tabAll(1, false); + + // 前缀后缀加括号和换行符 + String tabs = new String(IndentTools.space.getIndenTabs(indent)); + if (addLeadingNewline) { + if (VerifyTools.isBlank(prefix)) { + prefix = "\n" + tabs + "("; + } else if (prefix.indexOf('(') >= 0) { // 前缀中存在左括号 + prefix = "\n" + tabs + prefix; + } else { + prefix = "\n" + tabs + prefix + " ("; + } + } else { + if (VerifyTools.isBlank(prefix)) { + prefix = tabs + "("; + } else if (prefix.indexOf('(') >= 0) { // 前缀中存在左括号 + prefix = tabs + prefix; + } else { + prefix = tabs + prefix + " ("; + } + } + if (VerifyTools.isBlank(suffix)) { + suffix = "\n" + tabs + ")"; + } else if (suffix.indexOf(')') >= 0) { // 后缀中存在右括号 + suffix = "\n" + tabs + suffix; + } else { + suffix = "\n" + tabs + ") " + suffix; + } + + sql.prepend(prefix); + sql.append(suffix); + } + + public abstract static class Indent { + + /** + * 查找最后的缩进量
+         * 例如: \r\n=换行符, \t=TAB符, \s=空格
+         * \n\tABC\n\t\tDEF\t\t\t --> 这里要找的是DEF之前的那个换行符之后的空白字符, 即缩进量为2
+         * \n\tABC\n\t\tDEF\n     --> 最后一个字符就是换行符, 即刚刚换行完, 要找的仍然是DEF之前的那个换行符
+         * \n\tABC\n\t\tDEF\n\n   --> 最后连续多个换行符, 要找的仍然是DEF之前的那个换行符
+         * \n\tABC\n\t\t          --> 这里应返回ABC后面的换行符之后的缩进量2
+         * \tABC --> 这里应返回首行的缩进量1
+ * + * @param sql SQL语句 + * @return 缩进量 + */ + public static int findLastIndentSize(SqlBuffer sql) { + if (sql.items().isEmpty()) { + return 0; + } + // 先从后向前查找带有换行符的字符串 + // 取换行符之后的子串 + // 如果子串全是空白字符, 再向后取前置的空白字符 + int size = sql.items().size(); + for (int i = size - 1; i >= 0; i--) { + String suffixAfterNewline; + { // 查找带有换行符的字符串, 并获取换行符之后的子串 + CharSequence string = getItemStringValue(sql.items().get(i)); + if (string == null) { + continue; + } + int lastIndex = string.length() - 1; + if (string.length() == 0) { + suffixAfterNewline = null; + } else { + if (i == size - 1) { // 如果是最后一项, 移除最后的连续多个换行符 + lastIndex = getIndexOfBeforeTrailingChars(string, '\r', '\n'); + } + // 获取换行符之后的子串 + suffixAfterNewline = getSubstringOfAfterLastNewline(string, lastIndex); + } + if (suffixAfterNewline == null && i == 0) { + // 在没有换行符的情况下, 如果是第一项, 则将整个字符串视为换行符之后的子串 + // +1是因为getIndexOfBeforeTrailingChars返回是的换行符之前的那个位置 + // +1才能将那个位置的字符包含进来 + suffixAfterNewline = string.toString().substring(0, lastIndex + 1); + } + if (suffixAfterNewline == null) { + continue; // 未找到换行符 + } + } + // 已找到换行符, 判断是否全为空白字符 + if (!isAllWhitespace(suffixAfterNewline)) { + String leadingWhitespace = getLeadingWhitespace(suffixAfterNewline); + return IndentTools.calcSpacesToTabSize(leadingWhitespace); + } else { + // 向后取前置的空白字符 + StringBuilder buffer = new StringBuilder(); + buffer.append(suffixAfterNewline); + for (int j = i + 1; j < size; j++) { + CharSequence string = getItemStringValue(sql.items().get(j)); + if (string != null && isAllWhitespace(string)) { + buffer.append(string); + continue; // 后面仍然全是空白, 再继续向后查找 + } else { + if (string != null && string.length() > 0) { + String leadingWhitespace = getLeadingWhitespace(string); + buffer.append(leadingWhitespace); + } + break; // 遇到非string类型的item, 或者不全是空白, 结束查找 + } + } + return IndentTools.calcSpacesToTabSize(buffer); + } + } + return 0; + } + + /** + * 清除最前面的缩进空白 (遇到换行或非空白字符就结束) + * + * @param sql SQL语句 + * @return 清除了多少个缩进量 + */ + public static int clearLeadingIndentWhitespace(SqlBuffer sql) { + if (sql.items().isEmpty()) { + return 0; + } + // 先从前向后查找字符串 + // 取前置空白字符, 如果整个字符串全是空白字符, 再继续向后读取 + int size = sql.items().size(); + StringBuilder allCleared = new StringBuilder(); + for (int i = 0; i < size; i++) { + Item item = sql.items().get(i); + if (item instanceof StringItem) { + StringItem stringItem = (StringItem) item; + String cleared = clearLeadingIndentWhitespace(stringItem); + if (cleared != null) { + allCleared.append(cleared); + } + if (stringItem.getValue().length() > 0) { + break; // 清除完之后如果还有字符, 结束查找 + } + } else if (item instanceof VariableItem) { + break; // 遇到非string类型的item, 结束查找 + } else if (item instanceof RawValueItem) { + RawValueItem stringItem = (RawValueItem) item; + String cleared = clearLeadingIndentWhitespace(stringItem); + if (cleared != null) { + allCleared.append(cleared); + } + if (stringItem.getValue().length() > 0) { + break; // 清除完之后如果还有字符, 结束查找 + } + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return IndentTools.calcSpacesToTabSize(allCleared); + } + + private static String clearLeadingIndentWhitespace(StringItem item) { + int index = 0; + for (int i = 0, z = item.getValue().length() - 1; i < z; i++) { + char c = item.getValue().charAt(i); + if (Text.isInChars(c, ' ', '\t')) { + index++; + } else { + break; + } + } + if (index == 0) { + return null; + } + String cleared = item.getValue().substring(0, index); + item.getValue().delete(0, index); + return cleared; + } + + private static String clearLeadingIndentWhitespace(RawValueItem item) { + int index = 0; + for (int i = 0, z = item.getValue().length() - 1; i < z; i++) { + char c = item.getValue().charAt(i); + if (Text.isInChars(c, ' ', '\t')) { + index++; + } else { + break; + } + } + if (index == 0) { + return null; + } + String cleared = item.getValue().substring(0, index); + item.setValue(item.getValue().substring(index)); + return cleared; + } + + /** + * 清除最后的文字后面的缩进空白 (遇到换行或非空白字符就结束) + * + * @param sql SQL语句 + * @return 清除了多少个缩进量 + */ + public static int clearTrailingIndentWhitespace(SqlBuffer sql) { + if (sql.items().isEmpty()) { + return 0; + } + // 先从后向前查找带有换行符的字符串 + // 取换行符之后的子串 + // 如果子串全是空白字符, 再向后取前置的空白字符 + int size = sql.items().size(); + StringBuilder allCleared = new StringBuilder(); + for (int i = size - 1; i >= 0; i--) { + Item item = sql.items().get(i); + if (item instanceof StringItem) { + StringItem stringItem = (StringItem) item; + String cleared = clearTrailingIndentWhitespace(stringItem); + if (cleared != null) { + allCleared.append(cleared); + } + if (stringItem.getValue().length() > 0) { + break; // 清除完之后如果还有字符, 结束查找 + } + } else if (item instanceof VariableItem) { + break; // 遇到非string类型的item, 结束查找 + } else if (item instanceof RawValueItem) { + RawValueItem stringItem = (RawValueItem) item; + String cleared = clearTrailingIndentWhitespace(stringItem); + if (cleared != null) { + allCleared.append(cleared); + } + if (stringItem.getValue().length() > 0) { + break; // 清除完之后如果还有字符, 结束查找 + } + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return IndentTools.calcSpacesToTabSize(allCleared); + } + + private static String clearTrailingIndentWhitespace(StringItem item) { + // +1是因为getIndexOfBeforeTrailingChars返回是的空白之前的那个位置 + // +1才能将那个位置的字符包含进来 + int index = getIndexOfBeforeTrailingChars(item.getValue(), ' ', '\t') + 1; + if (index >= item.getValue().length()) { + return null; + } else { + String cleared = item.getValue().substring(index); + item.getValue().setLength(index); + return cleared; + } + } + + private static String clearTrailingIndentWhitespace(RawValueItem item) { + // +1是因为getIndexOfBeforeTrailingChars返回是的空白之前的那个位置 + // +1才能将那个位置的字符包含进来 + int index = getIndexOfBeforeTrailingChars(item.getValue(), ' ', '\t') + 1; + if (index >= item.getValue().length()) { + return null; + } else { + String cleared = item.getValue().substring(index); + item.setValue(index == 0 ? "" : item.getValue().substring(0, index)); + return cleared; + } + } + + /** 统计最小的缩进数, -1表示未找到缩进 **/ + public static int countMinIndentSize(SqlBuffer sql) { + return countMinIndentSize(sql, true, -1); + } + + /** 统计最小的缩进数, -1表示未找到缩进, defaults=未找到时的默认值 **/ + public static int countMinIndentSize(SqlBuffer sql, int defaults) { + return countMinIndentSize(sql, true, defaults); + } + + /** + * 统计最小的缩进数, -1表示未找到缩进 + * + * @param sql SQL语句 + * @param leadingIndent 要不要统计开头的缩进 + * @return 缩进数 + */ + public static int countMinIndentSize(SqlBuffer sql, boolean leadingIndent) { + return countMinIndentSize(sql, leadingIndent, -1); + } + + /** + * 统计最小的缩进数, -1表示未找到缩进 + * + * @param sql SQL语句 + * @param leadingIndent 要不要统计开头的缩进 + * @param defaultSize 未找到时的默认值 + * @return 缩进数 + */ + public static int countMinIndentSize(SqlBuffer sql, boolean leadingIndent, int defaultSize) { + if (sql.items().isEmpty()) { + return 0; + } + int minIndent = -1; + boolean first = true; + for (Item item : sql.items()) { + if (item == null) { + continue; + } + CharSequence string; + if (item instanceof StringItem) { + string = ((StringItem) item).getValue(); + } else if (item instanceof RawValueItem) { + string = ((RawValueItem) item).getValue(); + } else if (item instanceof VariableItem) { + if (first) { + return 0; + } else { + continue; + } + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + if (string == null || string.length() == 0) { + continue; + } + // blankLineIndent=是否统计空行的缩进, 一定要传true + // 因为这里的string只是片段, 后面可能还有内容, 并不能判断空行 + int indent = IndentTools.countMinIndentSize(string, first && leadingIndent, true); + first = false; + if (indent < 0) { + continue; + } else if (indent == 0) { + return 0; // 已找到最小缩进数量, 可以提前结束 + } else { + if (minIndent < 0 || minIndent > indent) { + minIndent = indent; + } + } + } + return minIndent < 0 ? defaultSize : minIndent; + } + + private static CharSequence getItemStringValue(Item item) { + if (item instanceof StringItem) { + return ((StringItem) item).getValue(); + } else if (item instanceof VariableItem) { + return null; + } else if (item instanceof RawValueItem) { + return ((RawValueItem) item).getValue(); + } else if (item instanceof OmitItem) { + return null; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + + /** 获取最后一个换行符之后的子字符串 **/ + // ABC\n -- "" + // ABC\n\t\t -- \t\t + // \n\t\tABC\t\t\t -- \t\t (ABC前面的2个TAB) + // \n\t\tABC\n\t\t\t -- \t\t\t (ABC后面的3个TAB) + // \t\tABC -- null (没有换行符) + // "" -- null (没有换行符) + private static String getSubstringOfAfterLastNewline(CharSequence string, int lastIndex) { + for (int i = lastIndex; i >= 0; i--) { + char c = string.charAt(i); + if (c == '\r' || c == '\n') { + return string.toString().substring(i + 1); + } + } + return null; + } + + /** 获取前置空白字符 **/ + private static String getLeadingWhitespace(CharSequence string) { + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + continue; + } else { + return i == 0 ? "" : string.toString().substring(0, i); + } + } + return string.toString(); + } + + /** 获取指定字符之前的位置 **/ + private static int getIndexOfBeforeTrailingChars(CharSequence string, char... chars) { + int lastIndex = string.length() - 1; + for (int i = lastIndex; i >= 0; i--) { + char c = string.charAt(i); + if (Text.isInChars(c, chars)) { + lastIndex--; // 移除最后连续的换行符 + } else { + break; + } + } + return lastIndex; + } + + /** 是不是全都是空白字符 **/ + private static boolean isAllWhitespace(CharSequence string) { + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); + if (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + continue; + } else { + return false; + } + } + return true; + } + } + + public abstract static class Text { + + // 符号: ASCII码表顺序, 去掉了'`_$ + private static char[] SYMBOLS = "\t\r\n !\"#%&()*+,-./:;<=>?@[\\]^{|}~".toCharArray(); + + /** + * 是不是SQL符号 + * + * @param c 指定字符 + * @return 是不是符号 + */ + private static boolean isSqlSymbol(char c, char... excludeSymbols) { + if (isInChars(c, excludeSymbols)) { + return false; + } + for (int i = 0; i < SYMBOLS.length; i++) { + if (c == SYMBOLS[i]) { + return true; + } + } + return false; + } + + private static boolean isInChars(char c, char... chars) { + if (chars != null && chars.length > 0) { + for (int i = 0; i < chars.length; i++) { + if (c == chars[i]) { + return true; + } + } + } + return false; + } + + /** 统计文本中有多少个换行符 **/ + public static int countNewlineChars(SqlBuffer sql) { + if (sql.items().isEmpty()) { + return 0; + } + int count = 0; + for (Item item : sql.items()) { + if (item == null) { + continue; + } + CharSequence string; + if (item instanceof StringItem) { + string = ((StringItem) item).getValue(); + } else if (item instanceof RawValueItem) { + string = ((RawValueItem) item).getValue(); + } else if (item instanceof VariableItem) { + continue; + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + if (string == null || string.length() == 0) { + continue; + } + count += IndentTools.countNewlineChars(string); + } + return count; + } + + /** + * 判断是不是以空行开头, 即判断前几个字符是不是换行符加空白 + * + * @param sql SQL语句 + * @return 是不是以空行开头 + */ + public static boolean startsWithBlankline(SqlBuffer sql) { + return startsWithBlankline(sql, false); + } + + /** + * 判断是不是以空行开头, 即判断前几个字符是不是换行符加空白 + * + * @param sql SQL语句 + * @param allBlankValue 全部空白算不算以空行开头 + * @return 是不是以空行开头 + */ + public static boolean startsWithBlankline(SqlBuffer sql, boolean allBlankValue) { + if (sql.items().isEmpty()) { + return allBlankValue; + } + int size = sql.items().size(); + // 从前往后找, 空白字符继续; 先遇到换行符返回true, 先遇到非空白字符返回false + for (int i = 0; i < size; i++) { + Item item = sql.items().get(i); + if (item instanceof StringItem || item instanceof RawValueItem) { + String string; + if (item instanceof StringItem) { + string = ((StringItem) item).getValue().toString(); + } else { + string = ((RawValueItem) item).getValue(); + } + for (int s = 0; s < string.length(); s++) { + char c = string.charAt(s); + if (c == ' ' || c == '\t') { + continue; + } + if (c == '\r' || c == '\n') { + return true; + } else { + return false; + } + } + } else if (item instanceof VariableItem) { + return false; // 遇到变量, 返回false + } else if (item instanceof OmitItem) { + continue; // 继续查找 + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return allBlankValue; // 从前到后都是空白 + } + + /** + * 判断是不是以空行结尾, 即判断最后是不是换行符加空白 + * + * @param sql SQL语句 + * @return 是不是以空行结尾 + */ + public static boolean endsWithBlankline(SqlBuffer sql) { + return endsWithBlankline(sql, false); + } + + /** + * 判断是不是以空行结尾, 即判断最后是不是换行符加空白 + * + * @param sql SQL语句 + * @param allBlankValue 全部空白算不算以空行结尾 + * @return 是不是以空行结尾 + */ + public static boolean endsWithBlankline(SqlBuffer sql, boolean allBlankValue) { + if (sql.items().isEmpty()) { + return allBlankValue; + } + int size = sql.items().size(); + // 从后往前找, 空白字符继续; 先遇到换行符返回true, 先遇到非空白字符返回false + for (int i = size - 1; i >= 0; i--) { + Item item = sql.items().get(i); + if (item instanceof StringItem || item instanceof RawValueItem) { + String string; + if (item instanceof StringItem) { + string = ((StringItem) item).getValue().toString(); + } else { + string = ((RawValueItem) item).getValue(); + } + for (int s = string.length() - 1; s >= 0; s--) { + char c = string.charAt(s); + if (c == ' ' || c == '\t') { + continue; + } + if (c == '\r' || c == '\n') { + return true; + } else { + return false; + } + } + } else if (item instanceof VariableItem) { + return false; // 遇到变量, 返回false + } else if (item instanceof OmitItem) { + continue; // 继续查找 + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return allBlankValue; // 从后一直找到第1个字符都是空白 + } + + /** 是不是以SQL符号开头 **/ + public static boolean startsWithSqlSymbol(CharSequence string, char... excludeSymbols) { + if (string == null || string.length() == 0) { + return false; + } + return isSqlSymbol(string.charAt(0), excludeSymbols); + } + + /** 是不是以SQL符号开头 **/ + public static boolean startsWithSqlSymbol(SqlBuffer sql, char... excludeSymbols) { + if (sql.items().isEmpty()) { + return false; + } + for (Item item : sql.items()) { + if (item instanceof StringItem) { + return startsWithSqlSymbol(((StringItem) item).getValue(), excludeSymbols); + } else if (item instanceof VariableItem) { + return false; + } else if (item instanceof RawValueItem) { + return startsWithSqlSymbol(((RawValueItem) item).getValue(), excludeSymbols); + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return false; + } + + /** 是不是以SQL符号结尾 **/ + public static boolean endsWithSqlSymbol(CharSequence string, char... excludeSymbols) { + if (string == null || string.length() == 0) { + return false; + } + return isSqlSymbol(string.charAt(string.length() - 1), excludeSymbols); + } + + /** 是不是以SQL符号结尾 **/ + public static boolean endsWithSqlSymbol(SqlBuffer sql, char... excludeSymbols) { + if (sql.items().isEmpty()) { + return false; + } + for (int i = sql.items().size() - 1; i >= 0; i--) { + Item item = sql.items().get(i); + if (item instanceof StringItem) { + return endsWithSqlSymbol(((StringItem) item).getValue(), excludeSymbols); + } else if (item instanceof VariableItem) { + return false; + } else if (item instanceof RawValueItem) { + return endsWithSqlSymbol(((RawValueItem) item).getValue(), excludeSymbols); + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return false; + } + + /** 是不是以指定字符开头 **/ + public static boolean startsWithChar(CharSequence string, char c) { + if (string == null || string.length() == 0) { + return false; + } + return c == string.charAt(0); + } + + /** 是不是以指定字符开头 **/ + public static boolean startsWithChar(SqlBuffer sql, char c) { + if (sql.items().isEmpty()) { + return false; + } + for (Item item : sql.items()) { + if (item instanceof StringItem) { + return startsWithChar(((StringItem) item).getValue(), c); + } else if (item instanceof VariableItem) { + return false; + } else if (item instanceof RawValueItem) { + return startsWithChar(((RawValueItem) item).getValue(), c); + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return false; + } + + /** 是不是以指定字符结尾 **/ + public static boolean endsWithChar(CharSequence string, char c) { + if (string == null || string.length() == 0) { + return false; + } + return c == string.charAt(string.length() - 1); + } + + /** 是不是以指定字符结尾 **/ + public static boolean endsWithChar(SqlBuffer sql, char c) { + if (sql.items().isEmpty()) { + return false; + } + for (int i = sql.items().size() - 1; i >= 0; i--) { + Item item = sql.items().get(i); + if (item instanceof StringItem) { + return endsWithChar(((StringItem) item).getValue(), c); + } else if (item instanceof RawValueItem) { + return endsWithChar(((RawValueItem) item).getValue(), c); + } else if (item instanceof VariableItem) { + return false; + } else if (item instanceof OmitItem) { + continue; + } else { + throw new UnsupportedOperationException("Unsupported item: " + item.getClass()); + } + } + return false; + } + + /** + * 判断SQL中是否存在指定的关键字 + * + * @param sql SQL语句 + * @param keyword 指定的关键字: 如果是单词, 按整词匹配; 如果包含符号或空格, 按contains匹配 + * @return 是否存在 + */ + public static boolean exists(SqlBuilder sql, String keyword) { + return exists(sql.out(), keyword, false); + } + + /** + * 判断SQL中是否存在指定的关键字, 只会从SQL文件中查找, 不会查找SQL变量 + * + * @param sql SQL语句 + * @param keyword 指定的关键字: 如果是单词, 按整词匹配; 如果包含符号或空格, 按contains匹配 + * @return 是否存在 + */ + public static boolean exists(SqlBuilder sql, String keyword, boolean skipLeadingBlank) { + return exists(sql.out(), keyword, skipLeadingBlank); + } + + /** + * 判断SQL中是否存在指定的关键字 + * + * @param sql SQL语句 + * @param keyword 指定的关键字: 如果是单词, 按整词匹配; 如果包含符号或空格, 按contains匹配 + * @return 是否存在 + */ + public static boolean exists(SqlBuffer sql, String keyword) { + return exists(sql, keyword, false); + } + + /** + * 判断SQL中是否存在指定的关键字, 只会从SQL文件中查找, 不会查找SQL变量 + * + * @param sql SQL语句 + * @param keyword 指定的关键字: 如果是单词, 按整词匹配; 如果包含符号或空格, 按contains匹配 + * @return 是否存在 + */ + public static boolean exists(SqlBuffer sql, String keyword, boolean skipLeadingBlank) { + VerifyTools.requireNotBlank(keyword, "keyword"); + if (sql.isEmpty()) { + return false; + } + boolean isword = StringTools.isWordString(keyword); + boolean first = true; + for (Item item : sql.items()) { + if (item == null) { + continue; + } + String stringValue; + if (item instanceof StringItem) { + stringValue = ((StringItem) item).getValue().toString(); + } else if (item instanceof RawValueItem) { + stringValue = ((RawValueItem) item).getValue(); + } else if (item instanceof OmitItem) { + continue; + } else { + first = false; + continue; + } + if (stringValue.length() == 0) { + continue; + } + if (first && skipLeadingBlank) { + stringValue = StringTools.trimLeft(stringValue); + } + first = false; + if (isword) { + if (StringTools.existsWord(stringValue, keyword)) { + return true; + } + } else { + if (stringValue.contains(keyword)) { + return true; + } + } + } + return false; + } + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/CrudSqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/CrudSqlBuilder.java index 11e0b7fb2b2352f087e41619afad99303bad4c18..3fcc1b3bdca915df58da7dab7330c4e6b7177a44 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/CrudSqlBuilder.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/CrudSqlBuilder.java @@ -4,9 +4,11 @@ import java.util.Map; import java.util.Set; import com.gitee.qdbp.able.jdbc.condition.DbUpdate; import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.sql.fragment.CrudFragmentHelper; +import com.gitee.qdbp.jdbc.sql.fragment.TableCrudFragmentHelper; import com.gitee.qdbp.tools.utils.VerifyTools; /** @@ -22,6 +24,10 @@ public class CrudSqlBuilder extends QuerySqlBuilder { super(sqlHelper); } + public CrudSqlBuilder(Class clazz, SqlDialect dialect) { + this(new TableCrudFragmentHelper(clazz, dialect)); + } + public CrudFragmentHelper helper() { return (CrudFragmentHelper) sqlHelper; } @@ -42,13 +48,26 @@ public class CrudSqlBuilder extends QuerySqlBuilder { return buffer.out(); } + /** + * 生成UPDATE语句
+ * 如果没有任何UPDATE SET字段, 将返回空的SqlBuffer对象 + * + * @param entity 待更新的实体类 + * @param where WHERE条件 + * @return UPDATE语句 + */ public SqlBuffer buildUpdateSql(DbUpdate entity, DbWhere where) { CrudFragmentHelper sqlHelper = helper(); - String tableName = sqlHelper.getTableName(); + SqlBuffer updateSetSql = sqlHelper.buildUpdateSetSql(entity, true); + if (updateSetSql.isBlank()) { + return new SqlBuffer(); + } + + String tableName = sqlHelper.getTableName(); SqlBuilder buffer = new SqlBuilder(); buffer.ad("UPDATE").ad(tableName); - buffer.newline().ad(sqlHelper.buildUpdateSetSql(entity, true)); + buffer.newline().ad(updateSetSql); if (VerifyTools.isNotBlank(where)) { buffer.newline().ad(sqlHelper.buildWhereSql(where, true)); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/QuerySqlBuilder.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/QuerySqlBuilder.java index d3a7287ac5f221b4bad222febc2cd2c40ee551fd..827fd055181f619e693cbca87161d52577e26b79 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/QuerySqlBuilder.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/build/QuerySqlBuilder.java @@ -2,12 +2,16 @@ package com.gitee.qdbp.jdbc.sql.build; import com.gitee.qdbp.able.exception.ServiceException; import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; import com.gitee.qdbp.able.jdbc.fields.Fields; import com.gitee.qdbp.able.jdbc.fields.IncludeFields; import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; +import com.gitee.qdbp.jdbc.sql.fragment.TableCrudFragmentHelper; +import com.gitee.qdbp.jdbc.sql.fragment.TableJoinFragmentHelper; import com.gitee.qdbp.tools.utils.VerifyTools; /** @@ -25,6 +29,14 @@ public class QuerySqlBuilder { this.sqlHelper = sqlHelper; } + public QuerySqlBuilder(Class clazz, SqlDialect dialect) { + this(new TableCrudFragmentHelper(clazz, dialect)); + } + + public QuerySqlBuilder(TableJoin tables, SqlDialect dialect) { + this(new TableJoinFragmentHelper(tables, dialect)); + } + public QueryFragmentHelper helper() { return sqlHelper; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/QueryFragmentHelper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/QueryFragmentHelper.java index 9e9a22b9edabeca689df5d1f3237fa2912d7f4d3..0d59f3d08ac096e4fc82ee02b988f3fc0432e8b0 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/QueryFragmentHelper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/QueryFragmentHelper.java @@ -199,6 +199,27 @@ public interface QueryFragmentHelper { */ String getColumnName(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) throws UnsupportedFieldException; + /** + * 获取列信息 + * + * @param scene 字段使用场景 + * @param fieldName 字段名 + * @return 列信息, 如果不支持该字段将抛出异常 + * @throws UnsupportedFieldException 不支持的字段名 + */ + SimpleFieldColumn getColumnInfo(FieldScene scene, String fieldName) throws UnsupportedFieldException; + + /** + * 获取列信息 + * + * @param scene 字段使用场景 + * @param fieldName 字段名 + * @param throwOnUnsupportedField 如果不支持该字段是否抛出异常 + * @return 列信息 + * @throws UnsupportedFieldException 不支持的字段名 + */ + SimpleFieldColumn getColumnInfo(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) throws UnsupportedFieldException; + /** * 获取字段名列表 * @@ -233,11 +254,4 @@ public interface QueryFragmentHelper { */ Object convertSpecialFieldValue(Object fieldValue); - /** - * 转换特殊的字段值, 如DbFieldName转换为DbRawValue(columnName) - * - * @param fieldValues 字段值列表 - * @return 转换后的字段值列表 - */ - List convertSpecialFieldValues(List fieldValues); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableCrudFragmentHelper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableCrudFragmentHelper.java index da0056b5fef39905ca04a13ddfae459dd854392d..1d63f9e799334e0b31d42b474c5421fda0d636b6 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableCrudFragmentHelper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableCrudFragmentHelper.java @@ -15,8 +15,10 @@ 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.operator.DbBaseOperator; +import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.plugins.UpdateSqlBuilder; +import com.gitee.qdbp.jdbc.plugins.impl.ColumnNameHelper; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.utils.DbTools; @@ -77,9 +79,9 @@ public class TableCrudFragmentHelper extends TableQueryFragmentHelper implements if (!buffer.isEmpty()) { buffer.ad(','); } - Object fieldValue = entity.get(item.getFieldName()); + Object fieldValue = DbTools.wrapDbVariable(item, entity.get(item.getFieldName())); if (VerifyTools.isBlank(fieldValue) && VerifyTools.isNotBlank(item.getColumnDefault())) { - fieldValue = item.getColumnDefault(); + fieldValue = DbTools.wrapDbVariable(item, item.getColumnDefault()); } buffer.var(convertSpecialFieldValue(fieldValue)); } @@ -96,31 +98,35 @@ public class TableCrudFragmentHelper extends TableQueryFragmentHelper implements Iterator iterator = entity.iterator(); while (iterator.hasNext()) { DbCondition condition = iterator.next(); - if (!buffer.isEmpty()) { - buffer.ad(','); - } + SqlBuffer sqlFragments; try { if (condition instanceof UpdateCondition) { UpdateCondition subCondition = (UpdateCondition) condition; - SqlBuffer subSql = buildUpdateSql(subCondition, false); - if (!subSql.isEmpty()) { - buffer.ad(subSql); - } + sqlFragments = buildUpdateSql(subCondition, false); } else if (condition instanceof DbField) { DbField item = (DbField) condition; - SqlBuffer fieldSql = buildUpdateSql(item, false); - buffer.ad(fieldSql); + sqlFragments = buildUpdateSql(item, false); } else { unsupported.add(condition.getClass().getSimpleName() + "#UnsupportedCondition"); + continue; } } catch (UnsupportedFieldException e) { unsupported.addAll(e.getFields()); + continue; } + + if (sqlFragments.isBlank()) { + continue; + } + if (!buffer.isBlank()) { + buffer.ad(','); + } + buffer.ad(sqlFragments); } if (!unsupported.isEmpty()) { throw ufe("update values sql", unsupported); } - if (whole && !buffer.isEmpty()) { + if (whole && !buffer.isBlank()) { buffer.pd("SET"); } return buffer.out(); @@ -136,7 +142,7 @@ public class TableCrudFragmentHelper extends TableQueryFragmentHelper implements if (VerifyTools.isBlank(fieldName)) { throw ufe("build update sql", "fieldName:MustNotBe" + (fieldName == null ? "Null" : "Empty")); } - String columnName = getColumnName(FieldScene.UPDATE, fieldName); + SimpleFieldColumn column = getColumnInfo(FieldScene.UPDATE, fieldName); // 查找Update运算符处理类 DbBaseOperator operator = DbTools.getUpdateOperator(operateType); @@ -145,7 +151,7 @@ public class TableCrudFragmentHelper extends TableQueryFragmentHelper implements } // 由运算符处理类生成子SQL String desc = "build update sql unsupported field"; - SqlBuffer buffer = buildOperatorSql(fieldName, columnName, operateType, operator, fieldValue, desc); + SqlBuffer buffer = buildOperatorSql(column, operateType, operator, fieldValue, desc); if (whole && !buffer.isEmpty()) { buffer.shortcut().pd("SET"); @@ -165,8 +171,9 @@ public class TableCrudFragmentHelper extends TableQueryFragmentHelper implements if (builder == null) { throw ufe("update sql", condition.getClass().getSimpleName() + "#SqlBuilderNotFound"); } - SqlBuffer buffer = builder.buildSql(condition, this); - if (whole && !buffer.isEmpty()) { + ColumnNameResolver columnResolver = new ColumnNameHelper(this, FieldScene.CONDITION); + SqlBuffer buffer = builder.buildSql(condition, columnResolver, this.getSqlDialect()); + if (whole && !buffer.isBlank()) { buffer.shortcut().pd("SET"); } return buffer; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableJoinFragmentHelper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableJoinFragmentHelper.java index de2b12353d205bfc18ba9327b4397af0b0f8b271..d51c6fdabcd49b6a33253568d362d77ab9c3f6a5 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableJoinFragmentHelper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableJoinFragmentHelper.java @@ -53,6 +53,21 @@ public class TableJoinFragmentHelper extends TableQueryFragmentHelper { /** {@inheritDoc} **/ @Override public String getColumnName(FieldScene scene, String fieldName) throws UnsupportedFieldException { + SimpleFieldColumn column = getColumnInfo(scene, fieldName); + return column == null ? null : column.toTableColumnName(); + } + + /** {@inheritDoc} **/ + @Override + public String getColumnName(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) + throws UnsupportedFieldException { + SimpleFieldColumn column = getColumnInfo(scene, fieldName, throwOnUnsupportedField); + return column == null ? null : column.toTableColumnName(); + } + + /** {@inheritDoc} **/ + @Override + public SimpleFieldColumn getColumnInfo(FieldScene scene, String fieldName) throws UnsupportedFieldException { List fields = this.columns.filter(scene).findAllByFieldName(fieldName); if (fields.isEmpty()) { throw ufe("unsupported field", fieldName); @@ -60,13 +75,13 @@ public class TableJoinFragmentHelper extends TableQueryFragmentHelper { String fieldsDesc = ConvertTools.joinToString(fields); throw ufe("unsupported field", "AmbiguousField:" + fieldName + '(' + fieldsDesc + ')'); } else { - return fields.get(0).toTableColumnName(); + return fields.get(0); } } /** {@inheritDoc} **/ @Override - public String getColumnName(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) + public SimpleFieldColumn getColumnInfo(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) throws UnsupportedFieldException { List fields = this.columns.filter(scene).findAllByFieldName(fieldName); if (fields.isEmpty()) { @@ -83,7 +98,7 @@ public class TableJoinFragmentHelper extends TableQueryFragmentHelper { return null; } } else { - return fields.get(0).toTableColumnName(); + return fields.get(0); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableQueryFragmentHelper.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableQueryFragmentHelper.java index 867addb3f71618feb506e28325a473d9a57f1e0d..63c9dcdd95fd9cabd12648596b33c766224fd86b 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableQueryFragmentHelper.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/fragment/TableQueryFragmentHelper.java @@ -7,11 +7,17 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import com.gitee.qdbp.able.enums.LogicType; +import com.gitee.qdbp.able.enums.MatchType; +import com.gitee.qdbp.able.enums.WrapMode; import com.gitee.qdbp.able.exception.ServiceException; import com.gitee.qdbp.able.jdbc.base.DbCondition; +import com.gitee.qdbp.able.jdbc.base.LogicCondition; +import com.gitee.qdbp.able.jdbc.base.MatchCondition; import com.gitee.qdbp.able.jdbc.base.WhereCondition; import com.gitee.qdbp.able.jdbc.condition.DbField; import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.condition.DbWhere.BizCondition; import com.gitee.qdbp.able.jdbc.condition.SubWhere; import com.gitee.qdbp.able.jdbc.fields.AllFields; import com.gitee.qdbp.able.jdbc.fields.DistinctFields; @@ -21,7 +27,6 @@ import com.gitee.qdbp.able.jdbc.fields.IncludeFields; import com.gitee.qdbp.able.jdbc.model.DbFieldName; import com.gitee.qdbp.able.jdbc.model.DbFieldValue; import com.gitee.qdbp.able.jdbc.model.DbRawValue; -import com.gitee.qdbp.able.jdbc.ordering.OrderType; import com.gitee.qdbp.able.jdbc.ordering.Ordering; import com.gitee.qdbp.able.jdbc.ordering.Orderings; import com.gitee.qdbp.able.result.ResultCode; @@ -36,8 +41,10 @@ import com.gitee.qdbp.jdbc.operator.DbBinaryOperator; import com.gitee.qdbp.jdbc.operator.DbMultivariateOperator; import com.gitee.qdbp.jdbc.operator.DbTernaryOperator; import com.gitee.qdbp.jdbc.operator.DbUnaryOperator; +import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.plugins.WhereSqlBuilder; +import com.gitee.qdbp.jdbc.plugins.impl.ColumnNameHelper; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.sql.SqlTools; @@ -81,14 +88,8 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { return null; } - String logicType = "AND"; - if (where instanceof SubWhere) { - logicType = ((SubWhere) where).getLogicType(); - } - SqlBuilder buffer = new SqlBuilder(); List unsupported = new ArrayList(); - boolean first = true; // 逐一遍历条件项, 生成where语句 Iterator iterator = where.iterator(); while (iterator.hasNext()) { @@ -97,35 +98,50 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { continue; } - if (first) { - first = false; - } else { - buffer.ad(logicType); - } - + SqlBuffer sqlFragments; try { - if (condition instanceof WhereCondition) { // 自定义条件 - WhereCondition subCondition = (WhereCondition) condition; - SqlBuffer subSql = buildWhereSql(subCondition, false); - if (!subSql.isEmpty()) { - buffer.ad("( ").ad(subSql).ad(" )"); - } + if (condition instanceof DbField) { // 字段条件 + DbField item = (DbField) condition; + sqlFragments = buildWhereSql(item, false); } else if (condition instanceof SubWhere) { // 子条件 SubWhere subWhere = (SubWhere) condition; - SqlBuffer subSql = buildWhereSql(subWhere, false); - if (!subSql.isEmpty()) { - buffer.ad(subSql); + sqlFragments = buildWhereSql(subWhere, false); + } else if (condition instanceof BizCondition) { // 自定义条件 + BizCondition bizCondition = (BizCondition) condition; + WhereCondition subCondition = bizCondition.getWhereCondition(); + MatchType matchType = bizCondition.getMatchType(); + sqlFragments = buildWhereSql(subCondition, matchType, false); + } else if (condition instanceof WhereCondition) { // 自定义条件 + WhereCondition subCondition = (WhereCondition) condition; + MatchType matchType = null; + if (condition instanceof MatchCondition) { + matchType = ((MatchCondition) condition).getMatchType(); } - } else if (condition instanceof DbField) { // 字段条件 - DbField item = (DbField) condition; - SqlBuffer fieldSql = buildWhereSql(item, false); - buffer.ad(fieldSql); + sqlFragments = buildWhereSql(subCondition, matchType, false); } else { unsupported.add("UnsupportedCondition:" + condition.getClass().getSimpleName()); + continue; } } catch (UnsupportedFieldException e) { unsupported.addAll(e.getFields()); + continue; + } + if (sqlFragments.isBlank()) { + continue; + } + + if (!buffer.isBlank()) { // 如果SQL不为空, 添加逻辑连接符号 + LogicType logicType = null; + if (condition instanceof LogicCondition) { + logicType = ((LogicCondition) condition).getLogicType(); + } + if (SqlTools.Text.countNewlineChars(sqlFragments) > 0) { + buffer.newline(); + } + buffer.ad(logicType == null ? LogicType.AND.name() : logicType.name()); } + // 添加条件SQL片断 + buffer.ad(sqlFragments); } if (!unsupported.isEmpty()) { // 存在异常字段 @@ -139,18 +155,29 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { throw ufe("build where sql unsupported fields", unsupported); } - if (!buffer.isEmpty()) { - // 后期处理, 如果不是肯定条件则前面加上NOT, 如果whole=true则在前面加上WHERE - if (where instanceof SubWhere) { - // 子SQL要用括号括起来 - buffer.pd("( ").ad(" )"); - if (!((SubWhere) where).isPositive()) { - buffer.pd("NOT"); + if (buffer.isBlank()) { + return buffer.out(); + } + + // 后期处理, 如果不是肯定条件则前面加上NOT, 如果whole=true则在前面加上WHERE + if (where instanceof SubWhere) { // 如果是子条件, 添加括号 + // 添加括号: 如果是NOT条件; 或OR条件; 或SQL中存在OR语句 + if (where instanceof MatchCondition && ((MatchCondition) where).getMatchType() == MatchType.Negative) { + SqlTools.wrap(buffer.out(), WrapMode.FORCE, "NOT", null, null, null, false); + } else { + LogicType logicType = null; + if (where instanceof LogicCondition) { + logicType = ((LogicCondition) where).getLogicType(); + } + if (logicType == LogicType.OR) { + SqlTools.wrap(buffer.out(), WrapMode.FORCE, null, null, null, null, false); + } else { + SqlTools.wrap(buffer.out(), WrapMode.AUTO, null, null, null, null, false); } } - if (whole) { - buffer.pd("WHERE"); - } + } + if (whole) { + buffer.pd("WHERE"); } return buffer.out(); } @@ -168,9 +195,9 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { if (VerifyTools.isBlank(fieldName)) { throw ufe("build where sql", "fieldName:MustNotBe" + (fieldName == null ? "Null" : "Empty")); } - String columnName; + SimpleFieldColumn column; try { - columnName = getColumnName(FieldScene.CONDITION, fieldName); + column = getColumnInfo(FieldScene.CONDITION, fieldName); } catch (UnsupportedFieldException e) { e.setMessage("build where sql unsupported field"); throw e; @@ -182,9 +209,9 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { } // 由运算符处理类生成子SQL String desc = "build where sql unsupported fields"; - SqlBuffer buffer = buildOperatorSql(fieldName, columnName, operateType, operator, fieldValue, desc); + SqlBuffer buffer = buildOperatorSql(column, operateType, operator, fieldValue, desc); - if (whole && !buffer.isEmpty()) { + if (whole && !buffer.isBlank()) { buffer.shortcut().pd("WHERE"); } return buffer; @@ -194,6 +221,15 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { @Override public SqlBuffer buildWhereSql(T condition, boolean whole) throws UnsupportedFieldException { + MatchType matchType = null; + if (condition instanceof MatchCondition) { + matchType = ((MatchCondition) condition).getMatchType(); + } + return buildWhereSql(condition, matchType, whole); + } + + private SqlBuffer buildWhereSql(T condition, MatchType matchType, boolean whole) + throws UnsupportedFieldException { if (condition == null || condition.isEmpty()) { return null; } @@ -202,11 +238,30 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { if (builder == null) { throw ufe("build where sql", "SqlBuilderNotFound:" + condition.getClass().getSimpleName()); } - SqlBuffer buffer = builder.buildSql(condition, this); - if (whole && !buffer.isEmpty()) { - buffer.shortcut().pd("WHERE"); + ColumnNameResolver columnResolver = new ColumnNameHelper(this, FieldScene.CONDITION); + SqlBuffer buffer = builder.buildSql(condition, columnResolver, this.getSqlDialect()); + if (buffer.isBlank()) { + return buffer; } - return buffer; + SqlBuilder sql = buffer.shortcut(); + // 添加括号: 如果是NOT条件; 或OR条件; 或SQL中存在OR语句 + if (matchType == MatchType.Negative) { + SqlTools.wrap(buffer, WrapMode.FORCE, "NOT", null, null, null, false); + } else { + LogicType logicType = null; + if (condition instanceof LogicCondition) { + logicType = ((LogicCondition) condition).getLogicType(); + } + if (logicType == LogicType.OR) { + SqlTools.wrap(buffer, WrapMode.FORCE, null, null, null, null, false); + } else { + SqlTools.wrap(buffer, WrapMode.AUTO, null, null, null, null, false); + } + } + if (whole) { + sql.pd("WHERE"); + } + return sql.out(); } /** {@inheritDoc} **/ @@ -221,7 +276,7 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { throw e; } SqlBuffer buffer = SqlTools.buildInSql(columnName, fieldValues, dialect); - if (whole && !buffer.isEmpty()) { + if (whole && !buffer.isBlank()) { buffer.shortcut().pd("WHERE"); } return buffer; @@ -239,24 +294,16 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { throw e; } SqlBuffer buffer = SqlTools.buildNotInSql(columnName, fieldValues, dialect); - if (whole && !buffer.isEmpty()) { + if (whole && !buffer.isBlank()) { buffer.shortcut().pd("WHERE"); } return buffer; } - protected SqlBuffer buildOperatorSql(String fieldName, String columnName, String operateType, Object fieldValue, - String desc) { - // 查找Where运算符处理类 - DbBaseOperator operator = DbTools.getWhereOperator(operateType); - if (operator == null) { - throw ufe("build where sql", "UnsupportedOperator:(" + fieldName + ' ' + operateType + " ...)"); - } - return buildOperatorSql(fieldName, columnName, operateType, operator, fieldValue, desc); - } - - protected SqlBuffer buildOperatorSql(String fieldName, String columnName, String operateType, - DbBaseOperator operator, Object fieldValue, String desc) { + protected SqlBuffer buildOperatorSql(SimpleFieldColumn column, String operateType, DbBaseOperator operator, + Object fieldValue, String desc) { + String fieldName = column.getFieldName(); + String columnName = column.toTableColumnName(); if (operator instanceof DbUnaryOperator) { if (VerifyTools.isBlank(fieldValue)) { return ((DbUnaryOperator) operator).buildSql(columnName, dialect); @@ -264,13 +311,13 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { throw ufe(desc, "TooManyArguments:(" + fieldName + ' ' + operateType + ")"); } } else if (operator instanceof DbBinaryOperator) { - Object value = convertSpecialFieldValue(fieldValue); + Object value = convertSpecialFieldValue(DbTools.wrapDbVariable(column, fieldValue)); return ((DbBinaryOperator) operator).buildSql(columnName, value, dialect); } else if (operator instanceof DbTernaryOperator) { - List values = convertSpecialFieldValues(ConvertTools.parseListIfNullToEmpty(fieldValue)); + List values = ConvertTools.parseListIfNullToEmpty(fieldValue); if (values.size() == 2) { - Object first = convertSpecialFieldValue(values.get(0)); - Object second = convertSpecialFieldValue(values.get(1)); + Object first = convertSpecialFieldValue(DbTools.wrapDbVariable(column, values.get(0))); + Object second = convertSpecialFieldValue(DbTools.wrapDbVariable(column, values.get(1))); return ((DbTernaryOperator) operator).buildSql(columnName, first, second, dialect); } else if (values.size() < 2) { // 参数不够 throw ufe(desc, "MissArguments:(" + fieldName + ' ' + operateType + " arg1 arg2)"); @@ -278,9 +325,13 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { throw ufe(desc, "TooManyArguments:(" + fieldName + ' ' + operateType + " arg1 arg2)"); } } else if (operator instanceof DbMultivariateOperator) { - List values = convertSpecialFieldValues(ConvertTools.parseListIfNullToEmpty(fieldValue)); + List values = ConvertTools.parseListIfNullToEmpty(fieldValue); if (!values.isEmpty()) { - return ((DbMultivariateOperator) operator).buildSql(columnName, values, dialect); + List realValues = new ArrayList<>(values.size()); + for (Object item : values) { + realValues.add(convertSpecialFieldValue(DbTools.wrapDbVariable(column, item))); + } + return ((DbMultivariateOperator) operator).buildSql(columnName, realValues, dialect); } else { // 参数不能为空 throw ufe(desc, "MissArguments:(" + fieldName + ' ' + operateType + " ...)"); } @@ -317,67 +368,36 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { return fieldValue; } - /** {@inheritDoc} **/ - @Override - public List convertSpecialFieldValues(List fieldValues) { - List result = new ArrayList<>(); - if (fieldValues != null) { - for (Object fieldValue : fieldValues) { - result.add(convertSpecialFieldValue(fieldValue)); - } - } - return result; - } - - private static String PINYIN_SUFFIX = "(PINYIN)"; - /** {@inheritDoc} **/ @Override public SqlBuffer buildOrderBySql(Orderings orderings, boolean whole) throws UnsupportedFieldException { if (VerifyTools.isBlank(orderings)) { return null; } - SqlBuilder buffer = new SqlBuilder(); List unsupported = new ArrayList(); - boolean first = true; for (Ordering item : orderings) { String fieldName = item.getOrderBy(); - // 汉字按拼音排序: userName(PINYIN) - boolean usePinyin = false; - if (fieldName.toUpperCase().endsWith(PINYIN_SUFFIX)) { - usePinyin = true; - fieldName = fieldName.substring(0, fieldName.length() - PINYIN_SUFFIX.length()).trim(); - } - String columnName; - try { - columnName = getColumnName(FieldScene.CONDITION, fieldName); - } catch (UnsupportedFieldException e) { - unsupported.addAll(e.getFields()); - continue; - } - if (usePinyin) { // 根据数据库类型转换为拼音排序表达式 - columnName = dialect.toPinyinOrderByExpression(columnName); - } - if (first) { - first = false; + String funcName = item.getFunctionName(); + if ("pinyin".equalsIgnoreCase(fieldName) && VerifyTools.isNotBlank(funcName) + && getColumnName(FieldScene.CONDITION, funcName, false) != null) { + // 兼容旧版本 userName(pinyin), 新版本应该是 pinyin(userName) + item.setOrderBy(funcName); + item.setFunctionName(fieldName); } else { - buffer.ad(','); - } - buffer.ad(columnName); - OrderType orderType = item.getOrderType(); - if (orderType == OrderType.ASC) { - buffer.ad("ASC"); - } else if (orderType == OrderType.DESC) { - buffer.ad("DESC"); + if (getColumnName(FieldScene.CONDITION, fieldName, false) == null) { + unsupported.add(fieldName); + } } } if (!unsupported.isEmpty()) { throw ufe("build order by sql unsupported fields", unsupported); } - if (whole && !buffer.isEmpty()) { - buffer.pd("ORDER BY"); + ColumnNameResolver columnResolver = new ColumnNameHelper(this, FieldScene.CONDITION); + SqlBuffer buffer = SqlTools.buildOrderBySql(orderings, columnResolver, dialect); + if (whole && !buffer.isBlank()) { + buffer.shortcut().pd("ORDER BY"); } - return buffer.out(); + return buffer; } /** {@inheritDoc} **/ @@ -507,7 +527,7 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { SqlBuilder buffer = new SqlBuilder(); FieldColumns fieldColumns = this.columns.filter(scene); for (SimpleFieldColumn item : fieldColumns) { - if (!buffer.isEmpty()) { + if (!buffer.isBlank()) { buffer.ad(','); } buffer.ad(columnAlias ? item.toFullColumnName() : item.toTableColumnName()); @@ -548,7 +568,7 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { FieldColumns fieldColumns = this.columns.filter(scene); for (SimpleFieldColumn item : fieldColumns) { if (fieldMap.containsKey(item.getFieldName()) == isWhitelist) { - if (!buffer.isEmpty()) { + if (!buffer.isBlank()) { buffer.ad(','); } buffer.ad(columnAlias ? item.toFullColumnName() : item.toTableColumnName()); @@ -663,10 +683,24 @@ public abstract class TableQueryFragmentHelper implements QueryFragmentHelper { @Override public String getColumnName(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) throws UnsupportedFieldException { + SimpleFieldColumn column = getColumnInfo(scene, fieldName, throwOnUnsupportedField); + return column == null ? null : column.toTableColumnName(); + } + + /** {@inheritDoc} **/ + @Override + public SimpleFieldColumn getColumnInfo(FieldScene scene, String fieldName) throws UnsupportedFieldException { + return getColumnInfo(scene, fieldName, true); + } + + /** {@inheritDoc} **/ + @Override + public SimpleFieldColumn getColumnInfo(FieldScene scene, String fieldName, boolean throwOnUnsupportedField) + throws UnsupportedFieldException { FieldColumns fieldColumns = this.columns.filter(scene); for (SimpleFieldColumn item : fieldColumns) { if (item.matchesByFieldName(fieldName)) { - return item.toTableColumnName(); + return item; } } if (throwOnUnsupportedField) { diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlBufferContext.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlBufferContext.java index 8dca9532243d2922a15ce011c6835fc344bfaea1..0e13317d9304409ad859634aedbd9dc624c3ca92 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlBufferContext.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlBufferContext.java @@ -1,12 +1,13 @@ package com.gitee.qdbp.jdbc.sql.parse; +import com.gitee.qdbp.jdbc.model.TypedDbVariable; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; import com.gitee.qdbp.jdbc.utils.DbTools; import com.gitee.qdbp.staticize.exception.TagException; import com.gitee.qdbp.staticize.publish.BaseContext; -import com.gitee.qdbp.staticize.utils.OgnlTools; +import com.gitee.qdbp.staticize.utils.OgnlUtils; import com.gitee.qdbp.tools.utils.StringTools; /** @@ -45,12 +46,33 @@ class SqlBufferContext extends BaseContext { @Override public Object doGetValue(String prefix, String expression) throws TagException { if ("#".equals(prefix)) { // 预编译参数 - Object value = OgnlTools.getValue(stack(), expression); - return value == null ? null : new SqlBuffer().addVariable(value); + // #{where.createTime,jdbcType=TIMESTAMP} + JdbcExpression result = parseJdbcExpression(expression); + Object value; + if (result == null) { + // #{where.createTime} + value = OgnlUtils.evaluate(expression, true, stack()); + } else { + // #{where.createTime,jdbcType=TIMESTAMP} + Object temp = OgnlUtils.evaluate(result.getExpression(), true, stack()); + value = new TypedDbVariable(result.getJdbcType(), temp); + } + return new SqlBuffer().addVariable(value); } else if ("$".equals(prefix)) { // 拼写式参数 + // 如果userName=zhh + // -- USER_NAME=${userName} 将输出USER_NAME=zhh (错误) + // -- USER_NAME=${sql:userName} 将输出USER_NAME='zhh' (正确) + // 如果tableName=SYS_USER_INFO + // -- FROM ${tableName} 将输出FROM SYS_USER_INFO (正确) + // -- FROM ${sql:tableName} 将输出FROM 'SYS_USER_INFO' (错误) + // 再比如日期类型 + // -- ${createTime} 将会输出 createTime.toString() + // -- 而 ${sql:createTime} 将会根据数据库类型转换为对应的SQL片断 + // ---- 在mysql中将会输出 '2019-06-01 12:34:56.789' + // ---- 在oracle中将会输出 TO_TIMESTAMP('2019-06-01 12:34:56.789', 'YYYY-MM-DD HH24:MI:SS.FF') if (expression.trim().startsWith("sql:")) { String exp = StringTools.removePrefix(expression.trim(), "sql:"); - Object value = OgnlTools.getValue(stack(), exp); + Object value = OgnlUtils.evaluate(exp, true, stack()); if (value instanceof SqlBuffer) { return ((SqlBuffer) value).getExecutableSqlString(dialect); } else if (value instanceof SqlBuilder) { @@ -59,7 +81,7 @@ class SqlBufferContext extends BaseContext { return DbTools.variableToString(value, dialect); } } else { - Object value = OgnlTools.getValue(stack(), expression); + Object value = OgnlUtils.evaluate(expression, true, stack()); if (value == null) { return null; } else if (value instanceof SqlBuffer) { @@ -74,4 +96,48 @@ class SqlBufferContext extends BaseContext { throw new TagException("表达式前缀有误"); } } + + /** 解析带jdbcType的表达式 **/ + // string = where.createTime,jdbcType=TIMESTAMP + private JdbcExpression parseJdbcExpression(String string) { + int index = string.indexOf(','); + if (index <= 0) { + return null; + } + String prefix = string.substring(0, index); + String suffix = string.substring(index + 1).trim(); + if (suffix.length() == 0) { + return null; + } + String[] array = StringTools.split(suffix, '='); + if (array.length != 2 || !"jdbcType".equals(array[0])) { + return null; + } + String jdbcType = array[1]; + Integer value = DbTools.parseJdbcDataType(jdbcType); + if (value == null) { + return null; + } + return new JdbcExpression(prefix, value); + } + + // #{where.createTime,jdbcType=TIMESTAMP} + private static class JdbcExpression { + + private int jdbcType; + private String expression; + + public JdbcExpression(String expression, int jdbcType) { + this.expression = expression; + this.jdbcType = jdbcType; + } + + public int getJdbcType() { + return jdbcType; + } + + public String getExpression() { + return expression; + } + } } 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 88fa505ebc0e1753f48e20b5f71ad6745a8ae0fc..a04b19a51477934652c6eafe62fbc15e067ce000 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 @@ -1,12 +1,13 @@ package com.gitee.qdbp.jdbc.sql.parse; import java.io.IOException; -import java.net.URL; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.gitee.qdbp.able.beans.KeyString; @@ -14,14 +15,15 @@ import com.gitee.qdbp.able.exception.ServiceException; import com.gitee.qdbp.jdbc.exception.DbErrorCode; import com.gitee.qdbp.jdbc.model.DbType; import com.gitee.qdbp.jdbc.model.DbVersion; +import com.gitee.qdbp.jdbc.model.SqlFile; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.plugins.SqlFileScanner; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.utils.DbTools; 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.files.PathTools; import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.StringTools; import com.gitee.qdbp.tools.utils.VersionCodeTools; @@ -35,7 +37,7 @@ import com.gitee.qdbp.tools.utils.VersionCodeTools; */ public class SqlFragmentContainer { - private static Logger log = LoggerFactory.getLogger(SqlFileScanner.class); + private static Logger log = LoggerFactory.getLogger(SqlFragmentContainer.class); private static final SqlFragmentContainer DEFAULTS = new SqlFragmentContainer(); @@ -45,10 +47,10 @@ public class SqlFragmentContainer { /** 未指定数据库类型的SQL模板 **/ // key=sqlId - private Map untypedCache = new HashMap<>(); + private Map> untypedCache = new HashMap<>(); /** 指定了数据库类型的SQL模板 **/ - // key=sqlId(dbType), value=[{version,IMetaData}], value按版本号从大到小排列, 无版本号的放最后 - private Map> typedCache = new HashMap<>(); + // key=sqlId(dbType), value=[{version,List[SqlMetaData]}], value按版本号从大到小排列, 无版本号的放最后 + private Map> typedCache = new HashMap<>(); private SqlFragmentContainer() { } @@ -56,15 +58,21 @@ public class SqlFragmentContainer { /** * 注册SQL模板标签元数据树 * + * @param scanTime 本次扫描时间 * @param sqlId SQL编号 * @param supports 支持哪些数据库版本 + * @param sqlFile SQL文件信息 * @param data 解析后的模板标签元数据树 */ - protected void register(String sqlId, String supports, IMetaData data) { + protected void register(long scanTime, String sqlId, String supports, SqlFile sqlFile, IMetaData data) { + SqlMetaData sqlMetaData = new SqlMetaData(scanTime, sqlId, supports, sqlFile, data); if (supports == null || "*".equals(supports)) { - if (!untypedCache.containsKey(sqlId)) { - // 存入未指定数据库类型的SQL模板容器 - untypedCache.put(sqlId, data); + // 存入未指定数据库类型的SQL模板容器 + if (untypedCache.containsKey(sqlId)) { + mergeSqlMetaDatas(scanTime, sqlMetaData, untypedCache.get(sqlId)); + } else { + List list = ConvertTools.toList(sqlMetaData); + untypedCache.put(sqlId, list); } } else { // mysql.8,mariadb.10.2.2,postgresql,db2,sqlserver,sqlite.3.8.3 @@ -81,47 +89,74 @@ public class SqlFragmentContainer { } else if (dotIndex == 0) { // .10.2.2? continue; } else { // 带版本号, 如mariadb.10.2.2 - dbType = supportItem.substring(0, dotIndex).toLowerCase(); - version = supportItem.substring(dotIndex + 1); + dbType = supportItem.substring(0, dotIndex).trim().toLowerCase(); + version = supportItem.substring(dotIndex + 1).trim(); + if (version.length() == 0) { + version = null; + } } - String sqlKey = sqlId + '(' + dbType + ')'; - TagData value = new TagData(version, data); - if (this.typedCache.containsKey(sqlKey)) { - mergeTagDatas(value, this.typedCache.get(sqlKey)); + String cacheKey = sqlId + '(' + dbType + ')'; + TypedSqlMetaData value = new TypedSqlMetaData(version, sqlMetaData); + if (this.typedCache.containsKey(cacheKey)) { + List sqlMetaDatas = this.typedCache.get(cacheKey); + mergeTagDatas(scanTime, version, sqlMetaData, sqlMetaDatas); } else { - List list = new ArrayList<>(); + List list = new ArrayList<>(); list.add(value); - this.typedCache.put(sqlKey, list); + this.typedCache.put(cacheKey, list); } } - } } - private static void mergeTagDatas(TagData item, List list) { - String version = item.getMinVersion(); - if (version == null || "*".equals(version)) { - list.add(item); - return; - } - boolean inserted = false; + private SqlMetaData mergeTagDatas(long scanTime, String version, SqlMetaData newer, List list) { + // value按版本号从大到小排列, 无版本号的放最后 for (int index = 0; index < list.size(); index++) { - TagData target = list.get(index); + TypedSqlMetaData target = list.get(index); String targetVersion = target.getMinVersion(); if (targetVersion == null || "*".equals(targetVersion)) { - list.add(index, item); - inserted = true; - break; + if (version == null || "*".equals(version)) { + return mergeSqlMetaDatas(scanTime, newer, target.getMetaDatas()); + } else { + list.add(index, new TypedSqlMetaData("*", newer)); + return null; + } + } else if (VersionCodeTools.compare(targetVersion, version) == 0) { + return mergeSqlMetaDatas(scanTime, newer, target.getMetaDatas()); } else if (VersionCodeTools.compare(targetVersion, version) < 0) { // 插入首个小于当前版本的位置的前面 - list.add(index, item); - inserted = true; - break; + list.add(index, new TypedSqlMetaData(version, newer)); + return null; } } - if (!inserted) { - list.add(item); + // 没匹配上, 添加到最后 + 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; } /** @@ -135,77 +170,93 @@ public class SqlFragmentContainer { return find(sqlId, dbVersion, true); } - // key=sqlId(dbType.version) - private Map tmplFondCache = new HashMap<>(); - // key=sqlId(dbType.version), value=details message - private Map tmplErrorCache = new HashMap<>(); - protected IMetaData find(String sqlId, DbVersion dbVersion, boolean throwOnNotFound) { - String dbType = dbVersion.getDbType().name().toLowerCase(); String currVersion = dbVersion.getVersionCode(); - String cacheKey = sqlId + '(' + dbType + '.' + currVersion + ')'; - if (tmplFondCache.containsKey(cacheKey)) { - return tmplFondCache.get(cacheKey); - } - if (tmplErrorCache.containsKey(cacheKey)) { - if (throwOnNotFound) { - String details = tmplErrorCache.get(cacheKey); - throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_NOT_FOUND, details); - } else { - return null; - } - } - - IMetaData found = null; + List found = null; // 记录下尝试过哪些模板, 用于匹配失败时输出日志 // key=dbType+dbVersion, value=location List tryedLocations = new ArrayList<>(); - { // 开始查找模板 - this.scanSqlFiles(); - String sqlKey = sqlId + '(' + dbType + ')'; - List typedList = typedCache.get(sqlKey); - if (typedList != null && !typedList.isEmpty()) { - for (TagData item : typedList) { - // 此模板的最低版本要求 - String minVersion = item.getMinVersion(); - tryedLocations.add(new KeyString(dbType + '.' + minVersion, item.getMetaData().getRealPath())); - if (minVersion == null || "*".equals(minVersion)) { - found = item.getMetaData(); // 没有最低要求, 则当前数据库为匹配 - break; - } - if (VersionCodeTools.compare(currVersion, minVersion) >= 0) { - found = item.getMetaData(); // 满足最低要求, 匹配成功 - break; - } - } - } - // 带版本的模板未匹配成功, 判断是否存在不带版本要求的模板 - if (found == null && untypedCache.containsKey(sqlId)) { - found = untypedCache.get(sqlId); + // 开始查找模板 + this.initSqlFiles(); + String cacheKey = sqlId + '(' + dbType + ')'; + List typedList = typedCache.get(cacheKey); + if (typedList != null && !typedList.isEmpty()) { + // 逐一对比此模板的最低版本要求 + for (TypedSqlMetaData item : typedList) { + String minVersion = item.getMinVersion(); + List datas = item.getMetaDatas(); + if (minVersion == null || "*".equals(minVersion)) { + found = datas; // 没有最低要求, 则当前数据库为匹配 + break; + } + if (VersionCodeTools.compare(currVersion, minVersion) >= 0) { + found = datas; // 满足最低要求, 匹配成功 + break; + } + for (SqlMetaData temp : datas) { + tryedLocations.add(new KeyString(dbType + '.' + minVersion, temp.getMetaData().getRealPath())); + } } } + // 带版本的模板未匹配成功, 判断是否存在不带版本要求的模板 + if (found == null && untypedCache.containsKey(sqlId)) { + found = untypedCache.get(sqlId); + } - if (found != null) { - tmplFondCache.put(cacheKey, found); - return found; + String versionString = dbVersion.toVersionString(); + if (found != null && !found.isEmpty()) { + if (found.size() == 1) { + return found.get(0).getMetaData(); + } + StringBuilder conflicts = new StringBuilder(); + for (SqlMetaData item : found) { + conflicts.append("\n\t").append(item.getMetaData().getRealPath()); + String supports = item.getSupports(); + if (supports != null && !"*".equals(supports)) { + conflicts.append(' ').append("").append(item.getSupports()).append(""); + } + } + 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(conflicts); + if (log.isWarnEnabled()) { + log.warn("Sql template '{}({})' not found: {}", sqlId, versionString, details.toString()); + } + if (throwOnNotFound) { + throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_NOT_FOUND, details.toString()); + } else { + return null; + } } // 未匹配成功 - StringBuilder details = new StringBuilder(); if (tryedLocations.isEmpty()) { - details.append("sqlId=").append(sqlId).append(", dbVersion=").append(dbVersion.toVersionString()); + StringBuilder details = new StringBuilder(); + details.append("sqlId=").append(sqlId).append(", dbVersion=").append(versionString); + if (log.isWarnEnabled()) { + log.warn("Sql template not found: {}", details.toString()); + } + if (throwOnNotFound) { + throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_NOT_FOUND, details.toString()); + } else { + return null; + } } else { - details.append("\nsqlId=").append(sqlId).append(", dbVersion=").append(dbVersion.toVersionString()); + StringBuilder details = new StringBuilder(); + details.append("\nsqlId=").append(sqlId).append(", dbVersion=").append(versionString); details.append("\nmatches tryed locations:\n").append(locationsToString(tryedLocations)); - } - tmplErrorCache.put(cacheKey, details.toString()); - if (throwOnNotFound) { - throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_NOT_FOUND, details.toString()); - } else { - return null; + if (log.isWarnEnabled()) { + log.warn("Sql template '{}({})' not found: {}", sqlId, versionString, details.toString()); + } + if (throwOnNotFound) { + throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_NOT_FOUND, details.toString()); + } else { + return null; + } } } @@ -221,6 +272,7 @@ public class SqlFragmentContainer { if (buffer.length() > 0) { buffer.append('\n'); } + buffer.append('\t'); buffer.append(StringTools.pad(item.getKey(), ' ', false, maxVersionLength)); buffer.append(" --> "); buffer.append(item.getValue()); @@ -236,7 +288,7 @@ public class SqlFragmentContainer { * @return 是否存在 */ public boolean exist(String sqlId, DbVersion dbVersion) { - this.scanSqlFiles(); + this.initSqlFiles(); if (untypedCache.containsKey(sqlId)) { return true; } @@ -245,23 +297,29 @@ public class SqlFragmentContainer { } /** 根据SqlId从缓存中获取SQL模板, 渲染为SqlBuffer对象 **/ - public SqlBuffer render(String sqlId, Map data, SqlDialect dialect) { + public SqlBuffer generate(String sqlId, Map data, SqlDialect dialect) { IMetaData tags = find(sqlId, dialect.getDbVersion()); - return publish(tags, data, dialect); + return render(tags, data, dialect); } /** 解析SQL模板内容, 渲染为SqlBuffer对象 **/ - public SqlBuffer parse(String sqlString, Map data, SqlDialect dialect) { - IMetaData tags = SqlStringParser.parseSqlString(sqlString); - return publish(tags, data, dialect); + public SqlBuffer render(String sqlString, Map data, SqlDialect dialect) { + Taglib taglib = DbTools.getSqlTaglib(); + IMetaData tags; + try { + tags = TemplateTools.parse(sqlString, taglib); + } catch (Exception e) { + throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_PARSE_ERROR, e); + } + return render(tags, data, dialect); } /** 将SQL模板渲染为SqlBuffer对象 **/ - public SqlBuffer publish(IMetaData tags, Map data, SqlDialect dialect) { + public SqlBuffer render(IMetaData tags, Map data, SqlDialect dialect) { SqlBufferPublisher publisher = new SqlBufferPublisher(tags); try { SqlBuffer buffer = publisher.publish(data, dialect); - return buffer.trim(); + return buffer.indentAll(0).trim(); } catch (TagException e) { throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_RENDER_ERROR, e); } catch (IOException e) { @@ -269,69 +327,162 @@ public class SqlFragmentContainer { } } - private boolean scaned = false; + private static enum TaskState { + IDLE, DOING; + } + + private Lock scanlock = new ReentrantLock(); + private TaskState scanState = null; + private Long lastScanTime = null; + + /** 初始化SQL模板文件 **/ + public void initSqlFiles() { + if (this.scanState == null) { + this.scanState = TaskState.IDLE; + this.scanSqlFiles(); + } + } /** 扫描SQL模板文件 **/ public void scanSqlFiles() { - if (this.scaned) { + if (this.scanState == TaskState.DOING) { return; } - this.doScanSqlFiles(); + + if (this.scanlock.tryLock()) { + try { + if (this.scanState == TaskState.DOING) { + return; + } + this.scanState = TaskState.DOING; + try { + long now = System.currentTimeMillis(); + this.doScanSqlFiles(now); + this.lastScanTime = now; + } finally { + this.scanState = TaskState.IDLE; + } + } finally { + this.scanlock.unlock(); + } + } } /** 扫描SQL模板文件 **/ - private synchronized void doScanSqlFiles() { - if (this.scaned) { - return; - } - this.scaned = true; + private void doScanSqlFiles(long scanTime) { SqlFileScanner scanner = DbTools.getSqlFileScanner(); - List urls; + List sqlFiles; try { - urls = scanner.scanSqlFiles(); + Date lastScanTime = this.lastScanTime == null ? null : new Date(this.lastScanTime); + sqlFiles = scanner.scanSqlFiles(lastScanTime); } catch (IOException e) { - log.warn("Failed to scan sql template file:", e); + log.warn("Failed to scan sql template file", e); return; } Date startTime = new Date(); List dbTypes = DbTools.getAvailableDbTypes(); Taglib taglib = DbTools.getSqlTaglib(); + + // 解析SQL文件 SqlFragmentParser parser = new SqlFragmentParser(taglib, dbTypes); - for (URL url : urls) { - String absolutePath = PathTools.toUriPath(url); - try { - String sqlContent = PathTools.downloadString(url); - parser.parseSqlContent(absolutePath, sqlContent); - } catch (IOException e) { - log.warn("Failed to read sql template: {}", absolutePath, e); - } catch (Exception e) { - log.warn("Failed to parse sql template: {}", absolutePath, e); + 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()); } } - List tagDatas = parser.parseCachedSqlFragments(); - if (log.isInfoEnabled()) { - String msg = "Success to parse sql templates, elapsed time {}, total of {} files and {} fragments."; - log.info(msg, ConvertTools.toDuration(startTime, true), urls.size(), tagDatas.size()); - } - for (ParsedFragment item : tagDatas) { - this.register(item.getSqlId(), item.getSupports(), item.getMetaData()); - if (item.getAlias() != null) { - this.register(item.getAlias(), item.getSupports(), item.getMetaData()); + if (log.isTraceEnabled() && !container.isEmpty()) { + List fragments = new ArrayList<>(); + for (ParsedFragment item : container) { + fragments.add(item.toString()); } + String details = ConvertTools.joinToString(fragments, "\n\t"); + String msg = "Success to parse sql templates, elapsed time {}, total of {} fragments in {} files.\n\t{}"; + log.trace(msg, ConvertTools.toDuration(startTime, true), container.size(), sqlFiles.size(), details); + } else if (log.isInfoEnabled()) { + String msg = "Success to parse sql templates, elapsed time {}, total of {} fragments in {} files."; + log.info(msg, ConvertTools.toDuration(startTime, true), container.size(), sqlFiles.size()); + } + } + + protected static class SqlMetaData { + + /** SqlId **/ + private final String sqlId; + /** 支持的版本 **/ + private final String supports; + /** 绝对路径 **/ + private final String absolutePath; + /** 相对路径 **/ + private final String relativePath; + /** 扫描时间 **/ + private final Long scanTime; + /** 最后更新时间 **/ + private final Long lastUpdateTime; + /** 模板数据 **/ + private IMetaData metadata; + + public SqlMetaData(long scanTime, String sqlId, String supports, SqlFile sqlFile, IMetaData metadata) { + this.scanTime = scanTime; + this.sqlId = sqlId; + this.supports = supports; + this.absolutePath = sqlFile.getAbsolutePath(); + this.relativePath = sqlFile.getRelativePath(); + this.lastUpdateTime = sqlFile.getLastUpdateTime(); + this.metadata = metadata; + } + + /** SqlId **/ + public String getSqlId() { + return sqlId; + } + + /** 支持的版本 **/ + public String getSupports() { + return supports; + } + + /** 扫描时间 **/ + public Long getScanTime() { + return scanTime; + } + + /** 绝对路径 **/ + public String getAbsolutePath() { + return absolutePath; + } + + /** 相对路径 **/ + public String getRelativePath() { + return relativePath; + } + + /** 最后更新时间 **/ + public Long getLastUpdateTime() { + return lastUpdateTime; + } + + /** 模板数据 **/ + public IMetaData getMetaData() { + return metadata; } + } - protected static class TagData { + protected static class TypedSqlMetaData { /** 最低版本要求 **/ private final String minVersion; - private final IMetaData metadata; + private final List metadatas; - public TagData(String minVersion, IMetaData data) { + public TypedSqlMetaData(String minVersion, SqlMetaData data) { this.minVersion = minVersion; - this.metadata = data; + this.metadatas = new ArrayList<>(); + this.metadatas.add(data); } /** 最低版本要求 **/ @@ -339,8 +490,8 @@ public class SqlFragmentContainer { return minVersion; } - public IMetaData getMetaData() { - return metadata; + public List getMetaDatas() { + return metadatas; } } @@ -349,12 +500,14 @@ public class SqlFragmentContainer { private final String sqlId; private final String alias; private final String supports; + private final SqlFile sqlFile; private final IMetaData metadata; - public ParsedFragment(String sqlId, String alias, String supports, IMetaData data) { + public ParsedFragment(String sqlId, String alias, String supports, SqlFile sqlFile, IMetaData data) { this.sqlId = sqlId; this.alias = alias; this.supports = supports; + this.sqlFile = sqlFile; this.metadata = data; } @@ -366,13 +519,25 @@ public class SqlFragmentContainer { return alias; } + public String getSupports() { + return supports; + } + + public SqlFile getSqlFile() { + return sqlFile; + } + public IMetaData getMetaData() { return metadata; } - public String getSupports() { - return supports; + @Override + public String toString() { + StringBuilder buffer = new StringBuilder(); + buffer.append(sqlId); + buffer.append('(').append(supports == null ? "*" : supports).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 222e0469fce3b132bb31776a644c1a8d3d1e3a36..054ca8d730e4d4d0fa815170100731a2263db9f9 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 @@ -1,26 +1,34 @@ package com.gitee.qdbp.jdbc.sql.parse; +import java.io.IOException; +import java.net.URL; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.gitee.qdbp.able.beans.KeyString; import com.gitee.qdbp.able.exception.ResourceNotFoundException; import com.gitee.qdbp.jdbc.model.DbType; +import com.gitee.qdbp.jdbc.model.SqlFile; import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer.ParsedFragment; +import com.gitee.qdbp.jdbc.tags.mapper.MapperTag; +import com.gitee.qdbp.jdbc.tags.mapper.SqlBoxTag; +import com.gitee.qdbp.jdbc.utils.DbTools; import com.gitee.qdbp.staticize.common.IMetaData; import com.gitee.qdbp.staticize.common.IReader; +import com.gitee.qdbp.staticize.common.NodeMetaData; +import com.gitee.qdbp.staticize.exception.TagException; import com.gitee.qdbp.staticize.io.IReaderCreator; import com.gitee.qdbp.staticize.io.SimpleReader; +import com.gitee.qdbp.staticize.io.UrlReaderCreator; 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; /** @@ -41,16 +49,6 @@ class SqlFragmentParser { private String supportsTagName = "supports"; private Taglib taglib; private Map dbTypes = new HashMap<>(); - /** 是否允许通过fragmentId获取SQL片断 **/ - private boolean useFragmentIdQuery = true; - - private CacheBox cacheBox = new CacheBox(); - - // sqlKey=fileId:fragmentId(supports) - /** 已加入缓存中的内容, key=sqlKeySorted, value={sqlKeyOriginal, location} **/ - private Map cachedMaps = new HashMap<>(); - /** 冲突列表, key=sqlKeySorted, value=[{sqlKeyOriginal, location}] **/ - private Map> conflicts = new HashMap<>(); public SqlFragmentParser(Taglib taglib, List dbTypes) { this.taglib = taglib; @@ -59,72 +57,79 @@ class SqlFragmentParser { } } - public void parseSqlContent(String sqlPath, String sqlContent) { + /** 解析SQL文件 **/ + public List parseSqlFiles(List sqlFiles) { + SqlCacheBox sqlCache = new SqlCacheBox(); + IReaderCreator xmlCache = new UrlReaderCreator(); + + // *.sql模板文件是先拆分再解析的 + // *.xml模板文件是先解析再拆分的 + List container = new ArrayList<>(); + for (SqlFile sqlFile : sqlFiles) { + URL url = sqlFile.getUrlPath(); + String path = url.toString(); + String extension = PathTools.getExtension(path, false); + try { + if ("xml".equalsIgnoreCase(extension)) { // XML格式模板 + TagParser xmlParser = new TagParser(taglib, xmlCache); + TagParser.Options options = new TagParser.Options(); + options.setClearXmlComment(true).setParseJspScript(false); + IMetaData tags = xmlParser.parse(path, options); + collectXmlParsedFragment(container, sqlFile, tags); + } else { // SQL格式模板 + String sqlContent = PathTools.downloadString(url); + registerSqlFragment(sqlCache, sqlFile, sqlContent); + } + } catch (IOException e) { + log.warn("Failed to read sql template: {}", sqlFile.getAbsolutePath(), e); + } catch (Exception e) { + log.warn("Failed to parse sql template: {}", sqlFile.getAbsolutePath(), e); + } + } + // 上面的循环遇到SQL格式模板只是拆分并注册到sqlCache中 + // 此处再集中解析SQL格式模板 + parseCachedSqlFragments(container, sqlCache); + return container; + } + + private void registerSqlFragment(SqlCacheBox cacheBox, SqlFile sqlFile, String sqlContent) { StringBuilder buffer = new StringBuilder(sqlContent); clearCommentContent(buffer, '<' + commentTagName + '>', '<' + '/' + commentTagName + '>', true); clearCommentContent(buffer, "/*--", "--*/", true); clearCommentContent(buffer, "<%--", "--%>", true); - List sqlFragments = splitSqlFile(sqlPath, buffer.toString()); + List sqlFragments = splitSqlFile(sqlFile.getAbsolutePath(), buffer.toString()); for (SqlFragment sqlFragment : sqlFragments) { - registerSqlFragment(sqlPath, sqlFragment); + registerSqlFragment(cacheBox, sqlFile, sqlFragment); } } - public List parseCachedSqlFragments() { - this.printConflictLogs(); - // sqlKey=fileId:fragmentId(supports) - List sqlKeys = cacheBox.getSqlKeys(); - List tagDatas = new ArrayList<>(); - for (String sqlKey : sqlKeys) { - TmplData data = cacheBox.getSqlFragment(sqlKey); + private void parseCachedSqlFragments(List container, SqlCacheBox cacheBox) { + List cacheKeys = cacheBox.getCacheKeys(); + TagParser.Options options = new TagParser.Options(); + options.setClearXmlComment(true); + options.setParseJspScript(false); + for (String cacheKey : cacheKeys) { + TmplData data = cacheBox.getSqlFragment(cacheKey); TagParser parser = new TagParser(taglib, cacheBox); try { - IMetaData metadata = parser.parse(sqlKey); - tagDatas.add(new ParsedFragment(data.getSqlId(), data.getSqlAlias(), data.getSupports(), metadata)); + IMetaData metadata = parser.parse(cacheKey, options); + String sqlId = data.getSqlId(); + String sqlAlias = data.getSqlAlias(); + String supports = data.getSupports(); + SqlFile sqlFile = data.getSqlFile(); + container.add(new ParsedFragment(sqlId, sqlAlias, supports, sqlFile, metadata)); } catch (Exception e) { log.warn("Sql template parse error: {}", data.getReader().getRealPath(), e); continue; } } - return tagDatas; } - public CacheBox getCacheBox() { - return this.cacheBox; - } - - private void printConflictLogs() { - if (conflicts.isEmpty() || !log.isWarnEnabled()) { - return; - } - // 输出冲突日志 - StringBuilder buffer = new StringBuilder(); - List sqlKeySorteds = new ArrayList<>(conflicts.keySet()); - Collections.sort(sqlKeySorteds); - for (String sqlKeySorted : sqlKeySorteds) { - if (buffer.length() > 0) { - buffer.append('\n'); - } - List conflictContents = conflicts.get(sqlKeySorted); - String sqlKeyOriginal = conflictContents.get(0).getKey(); - List conflictLocations = getValuesOfKeyStringList(conflictContents); - buffer.append(sqlKeyOriginal).append(' ').append("conflict with:"); - buffer.append("\n ").append(ConvertTools.joinToString(conflictLocations, "\n ")); - } - log.warn("Sql template conflict list:\n{}", buffer.toString()); - } - - private List getValuesOfKeyStringList(List list) { - List values = new ArrayList<>(); - for (KeyString item : list) { - values.add(item.getValue()); - } - return values; - } - - private void registerSqlFragment(String sqlPath, SqlFragment sqlFragment) { - SqlId result = parseSqlId(sqlPath, sqlFragment); + private void registerSqlFragment(SqlCacheBox cacheBox, SqlFile sqlFile, SqlFragment sqlFragment) { + String sqlPath = sqlFile.getAbsolutePath(); + SqlId result = parseSqlId(sqlPath, sqlFragment.getId(), sqlFragment.getInnerSupports(), + sqlFragment.getCommonSupports()); // 如 fileId=user.manage, fragmentId=user.resource.query, supports=mysql // fragmentId和supports都有可能为空 // sqlId=fileId:fragmentId @@ -132,79 +137,145 @@ class SqlFragmentParser { String fileId = result.getFileId(); String fragmentId = result.getFragmentId(); String supports = result.getSupports() != null ? result.getSupports() : "*"; - String sqlRelativePath = getSqlRelativePath(sqlPath); + String sqlRelativePath = sqlFile.getRelativePath(); if (fragmentId == null) { // 只有文件名, 没有SqlId - // 注册fileId(supports) String sqlId = fileId; - String sqlKey = sqlId + '(' + supports + ')'; String sqlLocation = sqlRelativePath; IReader reader = new SimpleReader(sqlLocation, sqlFragment.getContent()); - if (checkCachedSqlFragment(sqlId, supports, sqlLocation)) { - cacheBox.register(sqlKey, new TmplData(sqlId, null, supports, reader)); - } + String cacheKey = RandomTools.generateUuid(); + cacheBox.register(cacheKey, new TmplData(sqlId, null, supports, sqlFile, reader)); } else { - // 注册fileId:fragmentId(supports) String sqlId = fileId + ':' + fragmentId; - String sqlKey = sqlId + '(' + supports + ')'; String sqlLocation = fragmentId + '@' + sqlRelativePath + ':' + sqlFragment.getLine(); IReader reader = new SimpleReader(sqlLocation, sqlFragment.getContent()); - if (checkCachedSqlFragment(sqlId, supports, sqlLocation)) { + String sqlAlias = null; + if (DbTools.getSqlFragmentOptions().isUseFragmentIdQuery()) { + sqlAlias = fragmentId; + } + String cacheKey = RandomTools.generateUuid(); + cacheBox.register(cacheKey, new TmplData(sqlId, sqlAlias, supports, sqlFile, reader)); + } + } + + private void collectXmlParsedFragment(List container, SqlFile sqlFile, IMetaData root) { + // 查找mapper节点 + List mappers = filterTypedNodes(root.getChildren(), MapperTag.class); + for (NodeMetaData mapper : mappers) { + collectXmlParsedFragment(container, sqlFile, root, mapper); + } + } + + private void collectXmlParsedFragment(List container, SqlFile sqlFile, IMetaData root, + NodeMetaData mapper) { + String sqlPath = sqlFile.getAbsolutePath(); + String sqlRelativePath = sqlFile.getRelativePath(); + List children = mapper.getChildren(); + String namespace = mapper.getAttributeValue("namespace", String.class); + // 公共导入类声明 + List commonImports = filterNamedNodes(children, importTagName); + // 公共版本支持声明 + LineContent commonSupports = filterSupportsNode(sqlPath, root.getRow(), children); + for (NodeMetaData node : children) { + if (!SqlBoxTag.class.isAssignableFrom(node.getTagClass())) { + continue; + } + // 当前SQL片断的版本支持声明 + LineContent innerSupports = filterSupportsNode(sqlPath, node.getRow(), node.getChildren()); + String nodeId = node.getAttributeValue("id", String.class); + SqlId result = parseSqlId(sqlPath, nodeId, innerSupports, commonSupports); + // 如 fileId=user.manage, fragmentId=user.resource.query, supports=mysql + // fragmentId和supports都有可能为空 + // sqlId=fileId:fragmentId + // sqlKey=fileId:fragmentId(supports) + String commandType = node.getName(); + String fileId = result.getFileId(); + if (!namespace.endsWith(fileId)) { + // com/xxx/test/mapper/SysUserMapper.xml 3:1, , 属性namespace值与文件名不匹配 + String msg = String.format("%s %s:%s, %s, 属性namespace值与文件名不匹配", // + sqlRelativePath, mapper.getRow(), mapper.getColumn(), mapper.getOpenTag()); + throw new TagException(msg); + } + String fragmentId = result.getFragmentId(); + String supports = result.getSupports() != null ? result.getSupports() : "*"; + Map attrs = new LinkedHashMap<>(); + attrs.put("namespace", namespace); + attrs.put("commandType", commandType); + if (fragmentId == null) { // 只有文件名, 没有SqlId + String sqlId = fileId; + String sqlLocation = sqlRelativePath; + IMetaData data = root.wrapAsRoot(sqlId, sqlLocation, node, commonImports, attrs); + container.add(new ParsedFragment(sqlId, null, supports, sqlFile, data)); + } else { + String sqlId = fileId + ':' + fragmentId; + String sqlLocation = fragmentId + '@' + sqlRelativePath + ':' + node.getRow(); String sqlAlias = null; - if (useFragmentIdQuery && checkCachedSqlFragment(fragmentId, supports, sqlLocation)) { + if (DbTools.getSqlFragmentOptions().isUseFragmentIdQuery()) { sqlAlias = fragmentId; } - cacheBox.register(sqlKey, new TmplData(sqlId, sqlAlias, supports, reader)); + IMetaData data = root.wrapAsRoot(sqlId, sqlLocation, node, commonImports, attrs); + container.add(new ParsedFragment(sqlId, sqlAlias, supports, sqlFile, data)); } } } - private String getSqlRelativePath(String sqlPath) { - // jar:file:/E:/repository/com/gitee/qdbp/xxx-1.0.0.jar!/settings/sqls/xxx.properties - // file:/D:/qdbp/qdbp-jdbc/jdbc-core/target/classes/settings/sqls/xxx.properties - String lowerCase = sqlPath.toLowerCase(); - int jarIndex = lowerCase.indexOf(".jar!/"); - if (jarIndex > 0) { - int startIndex = lowerCase.lastIndexOf('/', jarIndex); - if (startIndex > 0) { - return sqlPath.substring(startIndex + 1); + private List filterNamedNodes(List children, String nodeName) { + List nodes = new ArrayList<>(); + for (NodeMetaData node : children) { + if (nodeName.equals(node.getName())) { + nodes.add(node); } } - int classesIndex = lowerCase.indexOf("/classes/"); - if (classesIndex > 0) { - return sqlPath.substring(classesIndex + "/classes/".length()); + return nodes; + } + + private List filterTypedNodes(List children, Class nodeType) { + List nodes = new ArrayList<>(); + for (NodeMetaData node : children) { + if (nodeType.isAssignableFrom(node.getTagClass())) { + nodes.add(node); + } } - return sqlPath; + return nodes; } - private boolean checkCachedSqlFragment(String sqlId, String supports, String sqlLocation) { - String sqlKeySorted = sqlId + '(' + sortSupports(supports) + ')'; - String sqlKeyOriginal = sqlId + '(' + supports + ')'; - if (!cachedMaps.containsKey(sqlKeySorted)) { - cachedMaps.put(sqlKeySorted, new KeyString(sqlKeyOriginal, sqlLocation)); - return true; - } else { - KeyString original = cachedMaps.get(sqlKeySorted); - KeyString current = new KeyString(sqlKeyOriginal, sqlLocation + " [ignored]"); - if (conflicts.containsKey(sqlKeySorted)) { - conflicts.get(sqlKeySorted).add(current); - } else { - List list = new ArrayList<>(); - list.add(original); - list.add(current); - conflicts.put(sqlKeySorted, list); + private LineContent filterSupportsNode(String sqlPath, int fragmentLine, List children) { + LineContent supports = null; + for (NodeMetaData node : children) { + if (!supportsTagName.equals(node.getName())) { + continue; + } + int line = node.getRow(); + String sqlId = node.getAttributeValue("id", String.class); + if (supports == null) { + String content = getChildrenText(node); + if (content != null && content.trim().length() > 0) { + supports = new LineContent(line, content.trim()); + } + } else if (sqlId == null) { // 出现在SqlId之前的公共版本支持声明 + String msg = "<{}> line {} ignored, conflict with line {}, in SqlTemplate {}"; + log.warn(msg, supportsTagName, line, supports.getLine(), sqlPath); + } else { // 出现在SQL语句中的版本支持声明 + String msg = "<{}> line {} ignored, conflict with line {}, under the SqlFragment[{}] {}(L{})"; + log.warn(msg, supportsTagName, line, supports.getLine(), sqlId, sqlPath, fragmentLine); } - return false; } + return supports; } - private String sortSupports(String supports) { - if (supports == null || "*".equals(supports)) { - return supports; + private String getChildrenText(NodeMetaData node) { + if (node.getChildren() == null) { + return null; } - supports = StringTools.removeLeftRight(supports.trim(), ','); - String[] supportArray = StringTools.split(supports, ','); - Arrays.sort(supportArray); - return ConvertTools.joinToString(supportArray, ','); + StringBuilder buffer = new StringBuilder(); + for (NodeMetaData child : node.getChildren()) { + if (TextTag.class.isAssignableFrom(child.getTagClass())) { + String content = child.getAttributeValue("value", String.class); + if (content != null) { + buffer.append(content); + } + } + } + return buffer.toString(); } protected static class SqlId { @@ -232,8 +303,7 @@ class SqlFragmentParser { } } - private SqlId parseSqlId(String sqlPath, SqlFragment sqlFragment) { - String fragmentId = sqlFragment.getId(); + private SqlId parseSqlId(String sqlPath, String fragmentId, LineContent innerSupports, LineContent commonSupports) { // 如 fileName=user.manage.sql, fileId=user.manage, fileDbType=null // 如 fileName=user.manage.mysql.sql, fileId=user.manage, fileDbType=mysql String fileName = PathTools.getFileName(sqlPath); @@ -248,7 +318,8 @@ class SqlFragmentParser { } } if (fragmentId == null) { - return new SqlId(fileId, null, resolveSqlFragmentSupports(fileDbType, null, sqlFragment)); + String supports = resolveSqlFragmentSupports(fileDbType, null, innerSupports, commonSupports); + return new SqlId(fileId, null, supports); } // 如 user.resource.query 或 user.resource.query:* @@ -263,17 +334,19 @@ class SqlFragmentParser { } // fileId=user.manage, fragmentId=user.resource.query, fragmentDbType=mysql - return new SqlId(fileId, fragmentId, resolveSqlFragmentSupports(fileDbType, fragmentDbType, sqlFragment)); + String supports = resolveSqlFragmentSupports(fileDbType, fragmentDbType, innerSupports, commonSupports); + return new SqlId(fileId, fragmentId, supports); } // 取值顺序: innerSupports/fragmentDbType/commonSupports/fileDbType - private String resolveSqlFragmentSupports(String fileDbType, String fragmentDbType, SqlFragment sqlFragment) { - if (sqlFragment.getInnerSupports() != null) { - return sqlFragment.getInnerSupports().getContent(); + private String resolveSqlFragmentSupports(String fileDbType, String fragmentDbType, LineContent innerSupports, + LineContent commonSupports) { + if (innerSupports != null) { + return innerSupports.getContent(); } else if (fragmentDbType != null) { return fragmentDbType; - } else if (sqlFragment.getCommonSupports() != null) { - return sqlFragment.getCommonSupports().getContent(); + } else if (commonSupports != null) { + return commonSupports.getContent(); } else if (fileDbType != null) { return fileDbType; } else { @@ -285,7 +358,7 @@ class SqlFragmentParser { * 按SqlId将一个文本内容拆分为SQL片断
* 为避免影响源码位置, 第2个片断会从第1个片断结束的位置开始, 前面填充换行符 * - * @param sqlPath 文件路径 + * @param sqlPath 文件路径 (用于记录日志) * @param content 文本内容 * @return SQL片断列表 */ @@ -639,16 +712,22 @@ class SqlFragmentParser { private final String sqlId; private final String sqlAlias; private final String supports; + private final SqlFile sqlFile; private final IReader reader; - public TmplData(String sqlId, String sqlAlias, String supports, IReader reader) { + public TmplData(String sqlId, String sqlAlias, String supports, SqlFile sqlFile, IReader reader) { super(); this.sqlId = sqlId; this.sqlAlias = sqlAlias; this.supports = supports; + this.sqlFile = sqlFile; this.reader = reader; } + public SqlFile getSqlFile() { + return this.sqlFile; + } + public String getSqlId() { return sqlId; } @@ -667,36 +746,36 @@ class SqlFragmentParser { } - private static class CacheBox implements IReaderCreator { + private static class SqlCacheBox implements IReaderCreator { - // key=sqlKey=fileId:fragmentId(supports) - private Map cache = new HashMap<>(); + // key=uuid + private Map cache = new LinkedHashMap<>(); - // sqlKey=fileId:fragmentId(supports) - public void register(String sqlKey, TmplData data) { - cache.put(sqlKey, data); + // cacheKey=uuid + public void register(String cacheKey, TmplData data) { + cache.put(cacheKey, data); } - public List getSqlKeys() { + public List getCacheKeys() { return new ArrayList<>(cache.keySet()); } - public TmplData getSqlFragment(String sqlKey) { - return cache.get(sqlKey); + public TmplData getSqlFragment(String cacheKey) { + return cache.get(cacheKey); } @Override - public IReader create(String sqlKey) throws ResourceNotFoundException { - TmplData data = cache.get(sqlKey); + public IReader create(String cacheKey) throws ResourceNotFoundException { + TmplData data = cache.get(cacheKey); if (data != null) { return data.getReader(); } else { - throw new ResourceNotFoundException(sqlKey + " not found"); + throw new ResourceNotFoundException(cacheKey + " not found"); } } @Override - public String getRelativePath(String sqlKey, String newKey) { + public String getRelativePath(String cacheKey, String newKey) { return newKey; } @@ -705,4 +784,5 @@ class SqlFragmentParser { return null; } } + } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlStringParser.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlStringParser.java deleted file mode 100644 index 728a6f8e11279c9d7d892b7c0cbdebe86b4fd5b2..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/sql/parse/SqlStringParser.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.gitee.qdbp.jdbc.sql.parse; - -import java.io.IOException; -import java.util.Date; -import com.gitee.qdbp.able.exception.ResourceNotFoundException; -import com.gitee.qdbp.able.exception.ServiceException; -import com.gitee.qdbp.jdbc.exception.DbErrorCode; -import com.gitee.qdbp.staticize.common.IMetaData; -import com.gitee.qdbp.staticize.common.IReader; -import com.gitee.qdbp.staticize.io.IReaderCreator; -import com.gitee.qdbp.staticize.io.SimpleReader; -import com.gitee.qdbp.staticize.parse.TagParser; - -/** - * 解析SQL字符串 - * - * @author zhaohuihua - * @version 20200830 - * @since 3.2.0 - */ -public class SqlStringParser { - - /** - * 解析SQL字符串 - * - * @param sqlString SQL字符串 - * @return 解析后的标签元数据 - */ - public static IMetaData parseSqlString(String sqlString) { - TemporaryReaderCreator readerCreator = new TemporaryReaderCreator(sqlString); - TagParser parser = new TagParser(readerCreator); - try { - IMetaData metadata = parser.parse(temporarySqlId); - return metadata; - } catch (Exception e) { - throw new ServiceException(DbErrorCode.DB_SQL_FRAGMENT_PARSE_ERROR, e); - } - } - - private static final String temporarySqlId = "{TemporarySqlString}"; - - private static class TemporaryReaderCreator implements IReaderCreator { - - private IReader reader; - - public TemporaryReaderCreator(String sqlString) { - this.reader = new SimpleReader(temporarySqlId, sqlString); - } - - @Override - public IReader create(String sqlId) throws IOException, ResourceNotFoundException { - if (sqlId.equals(temporarySqlId)) { - return reader; - } else { - throw new ResourceNotFoundException(sqlId + " not found"); - } - } - - @Override - public String getRelativePath(String sqlId, String newId) { - return newId; - } - - @Override - public Date getUpdateTime(String path) throws IOException, ResourceNotFoundException { - return null; - } - } -} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterByIdStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterByIdStream.java new file mode 100644 index 0000000000000000000000000000000000000000..2d4bdef91d4898a04544ed97fd675d8993c71e99 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterByIdStream.java @@ -0,0 +1,99 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * WhereById查询后续操作
+ * 后续操作有: find / logicalDelete / physicalDelete + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnAfterByIdStream { + + private CrudDao dao; + private String id; + + CrudOnAfterByIdStream(CrudDao dao, String id) { + this.dao = dao; + this.id = id; + } + + /** + * 根据主键编号获取对象
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereById("U0000001")
+        .findById();
+     * 
+ * + * @return 实体对象 + */ + public T find() { + return dao.findById(id); + } + + /** + * 根据主键编号删除实体对象(逻辑删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereById("U0000001")
+        .logicalDelete();
+     * 
+ * + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int logicalDelete() throws ServiceException { + return dao.logicalDeleteById(id); + } + + /** + * 根据主键编号删除实体对象(逻辑删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereById("U0000001")
+        .logicalDelete(true, false);
+     * 
+ * + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int logicalDelete(boolean fillUpdateParams, boolean errorOnUnaffected) throws ServiceException { + return dao.logicalDeleteById(id, fillUpdateParams, errorOnUnaffected); + } + + /** + * 根据主键编号删除实体对象(物理删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereById("U0000001")
+        .physicalDelete();
+     * 
+ * + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int physicalDelete() throws ServiceException { + return dao.physicalDeleteById(id); + } + + /** + * 根据主键编号删除实体对象(物理删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereById("U0000001")
+        .physicalDelete(true); // 受影响行数为0时抛异常
+     * 
+ * + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int physicalDelete(boolean errorOnUnaffected) throws ServiceException { + return dao.physicalDeleteById(id, errorOnUnaffected); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterEntitiesStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterEntitiesStream.java new file mode 100644 index 0000000000000000000000000000000000000000..3d5c8ec6601da614b34cbc5f5cc0ba1c3eaab372 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterEntitiesStream.java @@ -0,0 +1,117 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 实体数组后续操作: insert / update + * + * @author zhaohuihua + * @version 20210530 + */ +public class CrudOnAfterEntitiesStream { + + protected CrudDao dao; + private List entities; + + CrudOnAfterEntitiesStream(CrudDao dao, List entities) { + this.dao = dao; + this.entities = entities; + } + + /** + * 批量保存实体对象
+ * 注意: 如果主键编号为空将会自动生成
+ * 注意: 默认创建参数由entityFieldFillExecutor添加, 如dataState=DataState.NORMAL
+ * 注意: 根据实现类的不同, 有以下注意事项, 请详查具体实现类的机制:
+ * -- 大部分的实现类要求实体列表字段对齐
+ * ---- 例如第1个实体有abcd四个字段,第2个实体只有abc三个字段, 则第2个实体的d字段将被设置为NULL
+ * ---- 这将会导致数据库设置的默认值不会生效, 因此需要手动设置d字段, 或在d字段上配置默认值
+ * entities 实体对象列表(只能是entity或map或IdEntity列表, 其他参数将会报错)
+ *
+    List<String> ids = qdbcBoot.crudStream(SysUser.class)
+        .entities(user)
+        .insert();
+     * 
+ * + * @return 返回主键编号 + * @throws ServiceException 操作失败 + */ + public List insert() throws ServiceException { + return dao.inserts(entities); + } + + /** + * 批量保存实体对象
+ * 注意: 如果主键编号为空将会自动生成
+ * 注意: 默认创建参数由entityFieldFillExecutor添加, 如dataState=DataState.NORMAL
+ * 注意: 根据实现类的不同, 有以下注意事项, 请详查具体实现类的机制:
+ * -- 大部分的实现类要求实体列表字段对齐
+ * ---- 例如第1个实体有abcd四个字段,第2个实体只有abc三个字段, 则第2个实体的d字段将被设置为NULL
+ * ---- 这将会导致数据库设置的默认值不会生效, 因此需要手动设置d字段, 或在d字段上配置默认值
+ * entities 实体对象列表(只能是entity或map或IdEntity列表, 其他参数将会报错)
+ *
+    List<String> ids = qdbcBoot.crudStream(SysUser.class)
+        .entities(user)
+        .insert();
+     * 
+ * + * @param fillCreateParams 是否自动填充创建参数(创建人/创建时间等) + * @return 返回主键编号 + * @throws ServiceException 操作失败 + */ + public List insert(boolean fillCreateParams) throws ServiceException { + return dao.inserts(entities, fillCreateParams); + } + + /** + * 根据主键编号批量更新实体对象
+ * 注意: 如果主键编号为空将会报错
+ * 注意: 这里的限制条件只有主键编号, 而不会追加数据状态作为限制条件
+ * 注意: entity将会自动由entityFieldFillExecutor填充更新参数(修改人/修改时间等)
+ * 注意: 根据实现类的不同, 有以下注意事项, 请详查具体实现类的机制:
+ * -- 1.某些实现类可能无法获取到准确的受影响行数
+ * -- 2.大部分的实现类要求实体列表字段对齐
+ * ---- 例如第1个实体有abcd四个字段,第2个实体只有abc三个字段, 则第2个实体的d字段将被更新为NULL
+ * ---- 因此需要手动设置d字段(一般来自数据库查询的记录或用户输入的信息)
+ * entities 实体对象列表, 只能是entity或map或IdEntity列表, 其他参数将会报错
+ * 如果实体对象是map, map下不能有where, 否则将会报错
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entities(users)
+        .update();
+     * 
+ * + * @return 受影响行数(某些实现类可能无法获取到准确的受影响行数) + * @throws ServiceException 操作失败 + */ + public int update() throws ServiceException { + return dao.updates(entities); + } + + /** + * 根据主键编号批量更新实体对象
+ * 注意: 如果主键编号为空将会报错
+ * 注意: 这里的限制条件只有主键编号, 而不会追加数据状态作为限制条件
+ * 注意: entity将会自动由entityFieldFillExecutor填充更新参数(修改人/修改时间等)
+ * 注意: 根据实现类的不同, 有以下注意事项, 请详查具体实现类的机制:
+ * -- 1.某些实现类可能无法获取到准确的受影响行数
+ * -- 2.大部分的实现类要求实体列表字段对齐
+ * ---- 例如第1个实体有abcd四个字段,第2个实体只有abc三个字段, 则第2个实体的d字段将被更新为NULL
+ * ---- 因此需要手动设置d字段(一般来自数据库查询的记录或用户输入的信息)
+ * entities 实体对象列表, 只能是entity或map或IdEntity列表, 其他参数将会报错
+ * 如果实体对象是map, map下不能有where, 否则将会报错
+     * 
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entities(users)
+        .update(true); // 自动填充更新参数
+     * 
+ * + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update(boolean fillUpdateParams) throws ServiceException { + return dao.updates(entities, fillUpdateParams); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterEntityStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterEntityStream.java new file mode 100644 index 0000000000000000000000000000000000000000..8264bc833c8525d4040681f27204c1b8b7cd5e05 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterEntityStream.java @@ -0,0 +1,402 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; +import com.gitee.qdbp.jdbc.stream.CrudStream.DbWhereBuilder; +import com.gitee.qdbp.jdbc.utils.ParseTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 实体对象后续操作: insert / update / where + * + * @author zhaohuihua + * @version 20210530 + */ +public class CrudOnAfterEntityStream { + + protected CrudDao dao; + private T entityObject; + private Map entityMap; + + CrudOnAfterEntityStream(CrudDao dao, T entityObject) { + this.dao = dao; + this.entityObject = entityObject; + } + + CrudOnAfterEntityStream(CrudDao dao, Map entityMap) { + this.dao = dao; + this.entityMap = entityMap; + } + + /** + * 保存实体对象
+ *
+    String id = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .insert();
+     * 
+ * + * @return 返回主键编号 + * @throws ServiceException 操作失败 + */ + public String insert() throws ServiceException { + if (entityObject != null) { + return dao.insert(entityObject); + } else { + return dao.insert(entityMap); + } + } + + /** + * 保存实体对象
+ *
+    String id = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .insert();
+     * 
+ * + * @param fillCreateParams 是否自动填充创建参数(创建人/创建时间等) + * @return 返回主键编号 + * @throws ServiceException 操作失败 + */ + public String insert(boolean fillCreateParams) throws ServiceException { + if (entityObject != null) { + return dao.insert(entityObject, fillCreateParams); + } else { + return dao.insert(entityMap, fillCreateParams); + } + } + + /** + * 根据条件更新实体对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .update();
+     * 
+ * + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update() throws ServiceException { + if (entityObject != null) { + return dao.update(entityObject); + } else { + return dao.update(entityMap); + } + } + + /** + * 根据条件更新实体对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update(true, true); // 自动填充更新参数, 受影响行数为0时抛异常
+     * 
+ * + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update(boolean fillUpdateParams, boolean errorOnUnaffected) throws ServiceException { + if (entityObject != null) { + return dao.update(entityObject, fillUpdateParams, errorOnUnaffected); + } else { + return dao.update(entityMap, fillUpdateParams, errorOnUnaffected); + } + } + + /** + * 设置Where条件 + * + * @param where Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereByStream whereBy(DbWhere where) { + if (entityObject != null) { + return new CrudOnEntityWhereByStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereByStream(dao, entityMap, where); + } + } + + /** + * 从实体类构建Where对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .whereBy(user)
+        .update();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnEntityWhereByStream whereBy(T entity) { + DbWhere where = ParseTools.parseBeanToDbWhere(entity); + if (entityObject != null) { + return new CrudOnEntityWhereByStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereByStream(dao, entityMap, where); + } + } + + /** + * 从Map构建Where对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .whereBy(map)
+        .update();
+     * 

+ * 只会包含clazz注解中通过@Column指定的字段名
+ * 应注意, 此时参数由前端传入, 条件不可控, 也有可能条件为空, 需要仔细检查条件内容, 防止越权操作
+     * 转换规则:
+        fieldName$Equals(=), fieldName$NotEquals(!=), 
+        fieldName$LessThen(<), fieldName$LessEqualsThen(<=), 
+        fieldName$GreaterThen(>), fieldName$GreaterEqualsThen(>=), 
+        fieldName$IsNull, fieldName$IsNotNull, 
+        fieldName$Like, fieldName$NotLike, fieldName$Starts, fieldName$Ends, 
+        fieldName$In, fieldName$NotIn, fieldName$Between
+     * 
+ * + * @param map Map参数 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnEntityWhereByStream whereBy(Map map) { + DbWhere where = ParseTools.parseBeanToDbWhere(map); + if (entityObject != null) { + return new CrudOnEntityWhereByStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereByStream(dao, entityMap, where); + } + } + + /** + * 生成Where条件
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update();
+     * 
+ * + * @param builder Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereByStream whereBy(DbWhereBuilder builder) { + DbWhere where = new DbWhere(); + builder.build(where); + if (entityObject != null) { + return new CrudOnEntityWhereByStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereByStream(dao, entityMap, where); + } + } + + /** + * Where条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * 字段名可以带表别名, 如where.on("u.id", "=", entityObject.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream whereOn(String fieldName, String operate, Object... fieldValues) { + DbWhere where = new DbWhere().on(fieldName, operate, fieldValues); + if (entityObject != null) { + return new CrudOnEntityWhereOnStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereOnStream(dao, entityMap, where); + } + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream whereEquals(String fieldName, Object fieldValue) { + DbWhere where = new DbWhere().andEquals(fieldName, fieldValue); + if (entityObject != null) { + return new CrudOnEntityWhereOnStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereOnStream(dao, entityMap, where); + } + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream whereLike(String fieldName, String fieldValue) { + DbWhere where = new DbWhere().andLike(fieldName, fieldValue); + if (entityObject != null) { + return new CrudOnEntityWhereOnStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereOnStream(dao, entityMap, where); + } + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream whereIn(String fieldName, Object... fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + if (entityObject != null) { + return new CrudOnEntityWhereOnStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereOnStream(dao, entityMap, where); + } + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream whereIn(String fieldName, Collection fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + if (entityObject != null) { + return new CrudOnEntityWhereOnStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereOnStream(dao, entityMap, where); + } + } + + /** + * 以ID作为条件更新
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .whereById("U0000001")
+        .update();
+     * 
+ * + * @param id 实体主键 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereByStream whereById(String id) { + VerifyTools.requireNotBlank(id, "id"); + SimpleFieldColumn pk = dao.getSqlBuilder().helper().getPrimaryKey(); + if (pk == null) { // 没有找到主键字段 + String details = "UnsupportedWhereById, class=" + dao.getBeanClass().getName(); + throw new ServiceException(DbErrorCode.DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, details); + } + String primaryField = pk.getFieldName(); + DbWhere where = new DbWhere(); + where.andEquals(primaryField, id); + if (entityObject != null) { + return new CrudOnEntityWhereByStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereByStream(dao, entityMap, where); + } + } + + /** + * 按ID列表查询
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .whereByIds("U0000001", "U0000002", ...)
+        .update();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereByStream whereByIds(String... ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(Arrays.asList(ids)); + } + + /** + * 以ID列表作为条件更新
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        .whereByIds("U0000001", "U0000002", ...)
+        .update();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereByStream whereByIds(List ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(ids); + } + + protected CrudOnEntityWhereByStream doWhereByIds(List ids) { + SimpleFieldColumn pk = dao.getSqlBuilder().helper().getPrimaryKey(); + if (pk == null) { // 没有找到主键字段 + String details = "UnsupportedWhereByIds, class=" + dao.getBeanClass().getName(); + throw new ServiceException(DbErrorCode.DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, details); + } + String primaryField = pk.getFieldName(); + DbWhere where = new DbWhere(); + where.andIn(primaryField, ids); + if (entityObject != null) { + return new CrudOnEntityWhereByStream(dao, entityObject, where); + } else { + return new CrudOnEntityWhereByStream(dao, entityMap, where); + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterOrderByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterOrderByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..25b9e644ba5bbfa11154dfa83dc1291bc81c7160 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterOrderByStream.java @@ -0,0 +1,163 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 排序查询后续操作
+ * orderBy后续操作有 paging / list|listFieldValues + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnAfterOrderByStream { + + protected CrudDao dao; + protected DbWhere where; + protected Orderings orderings; + + CrudOnAfterOrderByStream(CrudDao dao, DbWhere where, Orderings orderings) { + this.dao = dao; + this.where = where; + this.orderings = orderings; + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10) // 查第1页,每页10行
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(int pageIndex, int pageSize) { + return pageBy(new Paging(pageIndex, pageSize)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10, 88) // 查第1页,每页10行, 88=提前查询的总行数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param total 总行数(用于提前查询总行数的情况) + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(int pageIndex, int pageSize, int total) { + return pageBy(new Paging(pageIndex, pageSize, total)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10, false) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param needCount 是否统计总数 + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(int pageIndex, int pageSize, boolean needCount) { + return pageBy(new Paging(pageIndex, pageSize, needCount)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(new Paging(1,10,false)) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param paging 分页参数 + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(Paging paging) { + return new CrudOnAfterPagingStream<>(dao, where, orderings, paging); + } + + /** + * 根据条件查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @return 列表数据 + */ + public List list() { + return dao.list(where, orderings); + } + + /** + * 根据条件分页查询某个字段的值列表
+ *
+    List<String> deptCodes = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .listFieldValues("deptCode", true, String.class); // 查询部门编号, true=去重
+     * 
+ * + * @param fieldName 指定字段名 + * @param distinct 是否去重 + * @param valueClazz 字段值类型 + * @return 字段的值列表 + */ + public List listFieldValues(String fieldName, boolean distinct, Class valueClazz) + throws ServiceException { + return dao.listFieldValues(fieldName, distinct, where, orderings, valueClazz); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterPagingStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterPagingStream.java new file mode 100644 index 0000000000000000000000000000000000000000..85d0d19c8d74d5c59aaa7d9a561413a226a07a7f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterPagingStream.java @@ -0,0 +1,86 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 分页查询后续操作
+ * paging后续操作有 list|listFieldValues + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnAfterPagingStream { + + protected CrudDao dao; + protected DbWhere where; + protected Orderings orderings; + protected Paging paging; + + CrudOnAfterPagingStream(CrudDao dao, DbWhere where, Orderings orderings, Paging paging) { + this.dao = dao; + this.where = where; + this.orderings = orderings; + this.paging = paging; + } + + /** + * 根据条件查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10) // 分页查询
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @return 列表数据 + */ + public PageList list() { + OrderPaging odpg = OrderPaging.of(paging, orderings); + return dao.list(where, odpg); + } + + /** + * 根据条件分页查询某个字段的值列表
+ *
+    List<String> deptCodes = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10) // 分页查询
+        .listFieldValues("deptCode", true, String.class) // 查询部门编号, true=去重
+        .asPartList();
+     * 
+ * + * @param fieldName 指定字段名 + * @param distinct 是否去重 + * @param valueClazz 字段值类型 + * @return 字段的值列表 + */ + public PageList listFieldValues(String fieldName, boolean distinct, Class valueClazz) + throws ServiceException { + OrderPaging odpg = OrderPaging.of(paging, orderings); + return dao.listFieldValues(fieldName, distinct, where, odpg, valueClazz); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterSelectStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterSelectStream.java new file mode 100644 index 0000000000000000000000000000000000000000..795849edcc069436c10a64b3d7ab573947699b9e --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterSelectStream.java @@ -0,0 +1,265 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; +import com.gitee.qdbp.jdbc.stream.CrudStream.DbWhereBuilder; +import com.gitee.qdbp.jdbc.utils.ParseTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * Select查询后续操作
+ * 后续操作有: where / orderBy / paging / list ( 还有find在where后续 ) + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnAfterSelectStream extends CrudOnSelectWhereByStream { + + CrudOnAfterSelectStream(CrudDao dao, Fields fields) { + super(dao, fields, DbWhere.NONE); + } + + /** + * 设置Where条件 + * + * @param where Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereByStream whereBy(DbWhere where) { + return new CrudOnSelectWhereByStream(dao, fields, where); + } + + /** + * 从实体类构建Where对象
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy(user)
+        .list();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnSelectWhereByStream whereBy(T entity) { + DbWhere where = ParseTools.parseBeanToDbWhere(entity); + return new CrudOnSelectWhereByStream(dao, fields, where); + } + + /** + * 从Map构建Where对象
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy(map)
+        .list();
+     * 

+ * 只会包含clazz注解中通过@Column指定的字段名
+ * 应注意, 此时参数由前端传入, 条件不可控, 也有可能条件为空, 需要仔细检查条件内容, 防止越权操作
+     * 转换规则:
+        fieldName$Equals(=), fieldName$NotEquals(!=), 
+        fieldName$LessThen(<), fieldName$LessEqualsThen(<=), 
+        fieldName$GreaterThen(>), fieldName$GreaterEqualsThen(>=), 
+        fieldName$IsNull, fieldName$IsNotNull, 
+        fieldName$Like, fieldName$NotLike, fieldName$Starts, fieldName$Ends, 
+        fieldName$In, fieldName$NotIn, fieldName$Between
+     * 
+ * + * @param map Map参数 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnSelectWhereByStream whereBy(Map map) { + DbWhere where = ParseTools.parseBeanToDbWhere(map); + return new CrudOnSelectWhereByStream(dao, fields, where); + } + + /** + * 生成Where条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param builder Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereByStream whereBy(DbWhereBuilder builder) { + DbWhere where = new DbWhere(); + builder.build(where); + return new CrudOnSelectWhereByStream(dao, fields, where); + } + + /** + * Where条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * 字段名可以带表别名, 如where.on("u.id", "=", entity.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream whereOn(String fieldName, String operate, Object... fieldValues) { + DbWhere where = new DbWhere().on(fieldName, operate, fieldValues); + return new CrudOnSelectWhereOnStream(dao, fields, where); + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream whereEquals(String fieldName, Object fieldValue) { + DbWhere where = new DbWhere().andEquals(fieldName, fieldValue); + return new CrudOnSelectWhereOnStream(dao, fields, where); + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream whereLike(String fieldName, String fieldValue) { + DbWhere where = new DbWhere().andLike(fieldName, fieldValue); + return new CrudOnSelectWhereOnStream(dao, fields, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream whereIn(String fieldName, Object... fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new CrudOnSelectWhereOnStream(dao, fields, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream whereIn(String fieldName, Collection fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new CrudOnSelectWhereOnStream(dao, fields, where); + } + + /** + * 以ID列表作为查询条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereByIds("U0000001", "U0000002", ...)
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param ids ID列表 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereByStream whereByIds(String... ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(Arrays.asList(ids)); + } + + /** + * 以ID列表作为查询条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereByIds("U0000001", "U0000002", ...)
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param ids ID列表 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereByStream whereByIds(List ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(ids); + } + + protected CrudOnSelectWhereByStream doWhereByIds(List ids) { + SimpleFieldColumn pk = dao.getSqlBuilder().helper().getPrimaryKey(); + if (pk == null) { // 没有找到主键字段 + String details = "UnsupportedWhereByIds, class=" + dao.getBeanClass().getName(); + throw new ServiceException(DbErrorCode.DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, details); + } + String primaryField = pk.getFieldName(); + DbWhere where = new DbWhere(); + where.andIn(primaryField, ids); + return new CrudOnSelectWhereByStream(dao, fields, where); + } + + /** + * 根据主键编号获取对象
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .findById("U0000001");
+     * 
+ * + * @param id 主键编号 + * @return 实体对象 + */ + public T findById(String id) { + VerifyTools.requireNotBlank(id, "id"); + return dao.findById(fields, id); + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterSetByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterSetByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..f765d2d77ecb541eec426e52eb60c9f807ed7935 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterSetByStream.java @@ -0,0 +1,290 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbUpdate; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; +import com.gitee.qdbp.jdbc.stream.CrudStream.DbWhereBuilder; +import com.gitee.qdbp.jdbc.utils.ParseTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * UdateSet后续操作
+ * where / update + * + * @author zhaohuihua + * @version 20210530 + */ +public class CrudOnAfterSetByStream { + + protected CrudDao dao; + protected DbUpdate ud; + + CrudOnAfterSetByStream(CrudDao dao, DbUpdate ud) { + this.dao = dao; + this.ud = ud; + } + + /** + * 设置Where条件 + * + * @param where Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereByStream whereBy(DbWhere where) { + return new CrudOnUpdateWhereByStream(dao, ud, where); + } + + /** + * 从实体类构建Where对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        .whereBy(user)
+        .update();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnUpdateWhereByStream whereBy(T entity) { + DbWhere where = ParseTools.parseBeanToDbWhere(entity); + return new CrudOnUpdateWhereByStream(dao, ud, where); + } + + /** + * 从Map构建Where对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        .whereBy(map)
+        .update();
+     * 

+ * 只会包含clazz注解中通过@Column指定的字段名
+ * 应注意, 此时参数由前端传入, 条件不可控, 也有可能条件为空, 需要仔细检查条件内容, 防止越权操作
+     * 转换规则:
+        fieldName$Equals(=), fieldName$NotEquals(!=), 
+        fieldName$LessThen(<), fieldName$LessEqualsThen(<=), 
+        fieldName$GreaterThen(>), fieldName$GreaterEqualsThen(>=), 
+        fieldName$IsNull, fieldName$IsNotNull, 
+        fieldName$Like, fieldName$NotLike, fieldName$Starts, fieldName$Ends, 
+        fieldName$In, fieldName$NotIn, fieldName$Between
+     * 
+ * + * @param map Map参数 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnUpdateWhereByStream whereBy(Map map) { + DbWhere where = ParseTools.parseBeanToDbWhere(map); + return new CrudOnUpdateWhereByStream(dao, ud, where); + } + + /** + * 生成Where条件
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update();
+     * 
+ * + * @param builder Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereByStream whereBy(DbWhereBuilder builder) { + DbWhere where = new DbWhere(); + builder.build(where); + return new CrudOnUpdateWhereByStream(dao, ud, where); + } + + /** + * Where条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * 字段名可以带表别名, 如where.on("u.id", "=", entity.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream whereOn(String fieldName, String operate, Object... fieldValues) { + DbWhere where = new DbWhere().on(fieldName, operate, fieldValues); + return new CrudOnUpdateWhereOnStream(dao, ud, where); + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream whereEquals(String fieldName, Object fieldValue) { + DbWhere where = new DbWhere().andEquals(fieldName, fieldValue); + return new CrudOnUpdateWhereOnStream(dao, ud, where); + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream whereLike(String fieldName, String fieldValue) { + DbWhere where = new DbWhere().andLike(fieldName, fieldValue); + return new CrudOnUpdateWhereOnStream(dao, ud, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream whereIn(String fieldName, Object... fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new CrudOnUpdateWhereOnStream(dao, ud, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream whereIn(String fieldName, Collection fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new CrudOnUpdateWhereOnStream(dao, ud, where); + } + + /** + * 以ID作为条件更新
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .whereById("U0000001")
+        .update();
+     * 
+ * + * @param id 实体主键 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereByStream whereById(String id) { + VerifyTools.requireNotBlank(id, "id"); + SimpleFieldColumn pk = dao.getSqlBuilder().helper().getPrimaryKey(); + if (pk == null) { // 没有找到主键字段 + String details = "UnsupportedWhereById, class=" + dao.getBeanClass().getName(); + throw new ServiceException(DbErrorCode.DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, details); + } + String primaryField = pk.getFieldName(); + DbWhere where = new DbWhere(); + where.andEquals(primaryField, id); + return new CrudOnUpdateWhereByStream(dao, ud, where); + } + + /** + * 按ID列表查询
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        .whereByIds("U0000001", "U0000002", ...)
+        .update();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereByStream whereByIds(String... ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(Arrays.asList(ids)); + } + + /** + * 以ID列表作为条件更新
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        .whereByIds("U0000001", "U0000002", ...)
+        .update();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereByStream whereByIds(List ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(ids); + } + + protected CrudOnUpdateWhereByStream doWhereByIds(List ids) { + SimpleFieldColumn pk = dao.getSqlBuilder().helper().getPrimaryKey(); + if (pk == null) { // 没有找到主键字段 + String details = "UnsupportedWhereByIds, class=" + dao.getBeanClass().getName(); + throw new ServiceException(DbErrorCode.DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, details); + } + String primaryField = pk.getFieldName(); + DbWhere where = new DbWhere(); + where.andIn(primaryField, ids); + return new CrudOnUpdateWhereByStream(dao, ud, where); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterWhereByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterWhereByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..8345c5049bb4ac122f9179fb7527796d4f9d80b7 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterWhereByStream.java @@ -0,0 +1,209 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Map; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * Where查询后续操作
+ * 后续操作有: delete / find|findFieldValue / count|groupCount / orderBy / paging / list|listFieldValues + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnAfterWhereByStream extends CrudOnAfterOrderByStream { + + CrudOnAfterWhereByStream(CrudDao dao, DbWhere where) { + super(dao, where, Orderings.NONE); + } + + /** + * 根据查询条件获取对象
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereEquals("id", "U00001")
+        .andIn("userState", UserState.NORMAL, UserState.LOCKED)
+        .find();
+     * 
+ * + * @return 实体对象 + */ + public T find() { + return dao.find(where); + } + + /** + * 根据条件查询某个字段的值
+ *
+    String deptCode = qdbcBoot.crudStream(SysUser.class)
+        .whereEquals("id", "U00001")
+        .andIn("userState", UserState.NORMAL, UserState.LOCKED)
+        .findFieldValue("deptCode", String.class);
+     * 
+ * + * @param fieldName 指定字段名 + * @param valueClazz 字段值类型 + * @return 字段的值列表 + */ + public V findFieldValue(String fieldName, Class valueClazz) throws ServiceException { + return dao.findFieldValue(fieldName, where, valueClazz); + } + + /** + * 根据条件统计实体数量
+ *
+    int total = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .count();
+     * 
+ * + * @return 数据数量 + */ + public int count() throws ServiceException { + return dao.count(where); + } + + /** + * 根据条件分组统计实体数量
+ *
+    Map<String, Integer> result = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            where.andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .groupCount("userType");
+    
+    SELECT USER_TYPE, COUNT(*) FROM SYS_USER
+        WHERE CREATE_TIME BETWEEN :1 AND :2
+        AND (USER_CODE LIKE '%test%' OR REAL_NAME LIKE '%test%')
+        AND DATA_STATE='E'
+        GROUP BY USER_TYPE
+     * 
+ * + * @param groupBy 分组条件 + * @return 分组统计结果 + */ + public Map groupCount(String groupBy) throws ServiceException { + return dao.groupCount(groupBy, where); + } + + /** + * 设置OrderBy条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterOrderByStream orderBy(String orderings) { + return orderBy(Orderings.of(orderings)); + } + + /** + * 设置OrderBy条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterOrderByStream orderBy(Orderings orderings) { + return new CrudOnAfterOrderByStream<>(dao, where, orderings); + } + + /** + * 根据主键编号删除实体对象(逻辑删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .logicalDelete();
+     * 
+ * + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int logicalDelete() throws ServiceException { + return dao.logicalDelete(where); + } + + /** + * 根据主键编号删除实体对象(逻辑删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .logicalDelete(true, false);
+     * 
+ * + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int logicalDelete(boolean fillUpdateParams, boolean errorOnUnaffected) throws ServiceException { + return dao.logicalDelete(where, fillUpdateParams, errorOnUnaffected); + } + + /** + * 根据主键编号删除实体对象(物理删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .physicalDelete();
+     * 
+ * + * @param id 待删除的主键编号 + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int physicalDelete() throws ServiceException { + return dao.physicalDelete(where); + } + + /** + * 根据主键编号删除实体对象(物理删除)
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            ...
+        })
+        .physicalDelete(true); // 受影响行数为0时抛异常
+     * 
+ * + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 删除行数 + * @throws ServiceException 删除失败 + */ + public int physicalDelete(boolean errorOnUnaffected) throws ServiceException { + return dao.physicalDelete(where, errorOnUnaffected); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterWhereOnStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterWhereOnStream.java new file mode 100644 index 0000000000000000000000000000000000000000..74011da8d588159da6811629010de4533a000c0d --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnAfterWhereOnStream.java @@ -0,0 +1,100 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 简单条件后续操作
+ * 后续操作有: delete / find|findFieldValue / count|groupCount / orderBy / paging / list|listFieldValues
+ * 以及简单条件: andOn|andEquals|andLike|andIn + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnAfterWhereOnStream extends CrudOnAfterWhereByStream { + + CrudOnAfterWhereOnStream(CrudDao dao, DbWhere where) { + super(dao, where); + } + + /** + * Where条件
+ * 字段名可以带表别名, 如.andOn("u.id", "=", entity.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream andOn(String fieldName, String operate, Object... fieldValues) { + where.on(fieldName, operate, fieldValues); + return this; + } + + /** + * 等于条件
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream andEquals(String fieldName, Object fieldValue) { + where.andEquals(fieldName, fieldValue); + return this; + } + + /** + * LIKE条件
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream andLike(String fieldName, String fieldValue) { + where.andLike(fieldName, fieldValue); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream andIn(String fieldName, Object... fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream andIn(String fieldName, Collection fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnEntityWhereByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnEntityWhereByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..f184332b2453f791ac0c7479e128bdeb6d0a2aad --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnEntityWhereByStream.java @@ -0,0 +1,84 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Map; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 实体+Where后续操作, 只有update! + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnEntityWhereByStream { + + protected CrudDao dao; + protected DbWhere where; + private T entityObject; + private Map entityMap; + + CrudOnEntityWhereByStream(CrudDao dao, T entityObject, DbWhere where) { + this.dao = dao; + this.entityObject = entityObject; + this.where = where; + } + + CrudOnEntityWhereByStream(CrudDao dao, Map entityMap, DbWhere where) { + this.dao = dao; + this.entityMap = entityMap; + this.where = where; + } + + /** + * 根据条件更新实体对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update();
+     * 
+ * + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update() throws ServiceException { + if (entityObject != null) { + return dao.update(entityObject, where); + } else { + entityMap.put("where", where); + return dao.update(entityMap); + } + } + + /** + * 根据条件更新实体对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update(true, true); // 自动填充更新参数, 受影响行数为0时抛异常
+     * 
+ * + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update(boolean fillUpdateParams, boolean errorOnUnaffected) throws ServiceException { + if (entityObject != null) { + return dao.update(entityObject, where, fillUpdateParams, errorOnUnaffected); + } else { + entityMap.put("where", where); + return dao.update(entityMap, fillUpdateParams, errorOnUnaffected); + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnEntityWhereOnStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnEntityWhereOnStream.java new file mode 100644 index 0000000000000000000000000000000000000000..6b288736442121eaf6e298dd82b02ee4098f6c90 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnEntityWhereOnStream.java @@ -0,0 +1,105 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import java.util.Map; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 实体+简单条件后续操作
+ * 后续操作有: update
+ * 以及简单条件: andOn|andEquals|andLike|andIn + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnEntityWhereOnStream extends CrudOnEntityWhereByStream { + + CrudOnEntityWhereOnStream(CrudDao dao, T entityObject, DbWhere where) { + super(dao, entityObject, where); + } + + CrudOnEntityWhereOnStream(CrudDao dao, Map entityMap, DbWhere where) { + super(dao, entityMap, where); + } + + /** + * Where条件
+ * 字段名可以带表别名, 如where.on("u.id", "=", entityObject.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream andOn(String fieldName, String operate, Object... fieldValues) { + where.on(fieldName, operate, fieldValues); + return this; + } + + /** + * 等于条件
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream andEquals(String fieldName, Object fieldValue) { + where.andEquals(fieldName, fieldValue); + return this; + } + + /** + * LIKE条件
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream andLike(String fieldName, String fieldValue) { + where.andLike(fieldName, fieldValue); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream andIn(String fieldName, Object... fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnEntityWhereOnStream andIn(String fieldName, Collection fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectOrderByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectOrderByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..83fcd2594d3dba915dc1547f4244202206294cf9 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectOrderByStream.java @@ -0,0 +1,143 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * Select排序后续操作
+ * orderBy后续操作有 paging / list + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnSelectOrderByStream { + + protected CrudDao dao; + protected Fields fields; + protected DbWhere where; + protected Orderings orderings; + + CrudOnSelectOrderByStream(CrudDao dao, Fields fields, DbWhere where, Orderings orderings) { + this.dao = dao; + this.fields = fields; + this.where = where; + this.orderings = orderings; + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10) // 查第1页,每页10行
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @return 列表数据 + */ + public CrudOnSelectPagingStream pageBy(int pageIndex, int pageSize) { + return pageBy(new Paging(pageIndex, pageSize)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10, 88) // 查第1页,每页10行, 88=提前查询的总行数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param total 总行数(用于提前查询总行数的情况) + * @return 列表数据 + */ + public CrudOnSelectPagingStream pageBy(int pageIndex, int pageSize, int total) { + return pageBy(new Paging(pageIndex, pageSize, total)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10, false) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param needCount 是否统计总数 + * @return 列表数据 + */ + public CrudOnSelectPagingStream pageBy(int pageIndex, int pageSize, boolean needCount) { + return pageBy(new Paging(pageIndex, pageSize, needCount)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .pageBy(new Paging(1,10,false)) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param paging 分页参数 + * @return 列表数据 + */ + public CrudOnSelectPagingStream pageBy(Paging paging) { + return new CrudOnSelectPagingStream<>(dao, fields, where, orderings, paging); + } + + /** + * 根据条件查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @return 列表数据 + */ + public List list() { + return dao.list(fields, where, orderings); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectPagingStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectPagingStream.java new file mode 100644 index 0000000000000000000000000000000000000000..db6ac4c8365a3ca6faca8ebe04aaf4544992ac9f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectPagingStream.java @@ -0,0 +1,59 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * Select分页后续操作
+ * paging后续操作有 list + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnSelectPagingStream { + + protected CrudDao dao; + protected Fields fields; + protected DbWhere where; + protected Orderings orderings; + protected Paging paging; + + CrudOnSelectPagingStream(CrudDao dao, Fields fields, DbWhere where, Orderings orderings, Paging paging) { + this.dao = dao; + this.fields = fields; + this.where = where; + this.orderings = orderings; + this.paging = paging; + } + + /** + * 根据条件查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .pageBy(1, 10) // 分页查询
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @return 列表数据 + */ + public PageList list() { + OrderPaging odpg = OrderPaging.of(paging, orderings); + return dao.list(fields, where, odpg); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectWhereByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectWhereByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..5a3b2e287bba3841fa1bb3265499187df841f971 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectWhereByStream.java @@ -0,0 +1,77 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * Select+Where后续操作
+ * 后续操作有: find / orderBy / paging / list + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnSelectWhereByStream extends CrudOnSelectOrderByStream { + + CrudOnSelectWhereByStream(CrudDao dao, Fields fields, DbWhere where) { + super(dao, fields, where, Orderings.NONE); + } + + /** + * 根据查询条件获取对象
+ *
+    SysUser user = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .andEquals("id", "U00001")
+        .andIn("userState", UserState.NORMAL, UserState.LOCKED)
+        .find();
+     * 
+ * + * @return 实体对象 + */ + public T find() { + return dao.find(fields, where); + } + + /** + * 设置OrderBy条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectOrderByStream orderBy(String orderings) { + return orderBy(Orderings.of(orderings)); + } + + /** + * 设置OrderBy条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .whereBy((where) -> {
+            ...
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectOrderByStream orderBy(Orderings orderings) { + return new CrudOnSelectOrderByStream<>(dao, fields, where, orderings); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectWhereOnStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectWhereOnStream.java new file mode 100644 index 0000000000000000000000000000000000000000..a9ce18acb332a1cb87c4f2ebee49b6924efd11fb --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnSelectWhereOnStream.java @@ -0,0 +1,101 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * Select简单条件后续操作
+ * 后续操作有: find / orderBy / paging / list
+ * 以及简单条件: andOn|andEquals|andLike|andIn + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnSelectWhereOnStream extends CrudOnSelectWhereByStream { + + CrudOnSelectWhereOnStream(CrudDao dao, Fields fields, DbWhere where) { + super(dao, fields, where); + } + + /** + * Where条件
+ * 字段名可以带表别名, 如where.on("u.id", "=", entity.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream andOn(String fieldName, String operate, Object... fieldValues) { + where.on(fieldName, operate, fieldValues); + return this; + } + + /** + * 等于条件
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream andEquals(String fieldName, Object fieldValue) { + where.andEquals(fieldName, fieldValue); + return this; + } + + /** + * LIKE条件
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream andLike(String fieldName, String fieldValue) { + where.andLike(fieldName, fieldValue); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream andIn(String fieldName, Object... fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnSelectWhereOnStream andIn(String fieldName, Collection fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnUpdateWhereByStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnUpdateWhereByStream.java new file mode 100644 index 0000000000000000000000000000000000000000..371762b28b963045ac6d7987a071e6c628d7e31f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnUpdateWhereByStream.java @@ -0,0 +1,77 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbUpdate; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * UpdateSet+Where后续操作, 只有update! + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnUpdateWhereByStream { + + protected CrudDao dao; + protected DbUpdate ud; + protected DbWhere where; + + CrudOnUpdateWhereByStream(CrudDao dao, DbUpdate ud, DbWhere where) { + this.dao = dao; + this.ud = ud; + this.where = where; + } + + /** + * 根据条件批量更新实体对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update();
+     * 
+ * + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update() throws ServiceException { + return dao.update(ud, where); + } + + /** + * 根据条件批量更新实体对象
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update(true, true); // 自动填充更新参数, 受影响行数为0时抛异常
+     * 
+ * + * @param fillUpdateParams 是否自动填充更新参数(修改人/修改时间等) + * @param errorOnUnaffected 受影响行数为0时是否抛异常 + * @return 受影响行数 + * @throws ServiceException 操作失败 + */ + public int update(boolean fillUpdateParams, boolean errorOnUnaffected) throws ServiceException { + return dao.update(ud, where, fillUpdateParams, errorOnUnaffected); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnUpdateWhereOnStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnUpdateWhereOnStream.java new file mode 100644 index 0000000000000000000000000000000000000000..4130ebcdbb95bf41a99833d6691246dad5b1c831 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudOnUpdateWhereOnStream.java @@ -0,0 +1,101 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import com.gitee.qdbp.able.jdbc.condition.DbUpdate; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * Update简单条件后续操作
+ * 后续操作有: update
+ * 以及简单条件: andOn|andEquals|andLike|andIn + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudOnUpdateWhereOnStream extends CrudOnUpdateWhereByStream { + + CrudOnUpdateWhereOnStream(CrudDao dao, DbUpdate ud, DbWhere where) { + super(dao, ud, where); + } + + /** + * Where条件
+ * 字段名可以带表别名, 如where.on("u.id", "=", entity.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream andOn(String fieldName, String operate, Object... fieldValues) { + where.on(fieldName, operate, fieldValues); + return this; + } + + /** + * 等于条件
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream andEquals(String fieldName, Object fieldValue) { + where.andEquals(fieldName, fieldValue); + return this; + } + + /** + * LIKE条件
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream andLike(String fieldName, String fieldValue) { + where.andLike(fieldName, fieldValue); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream andIn(String fieldName, Object... fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnUpdateWhereOnStream andIn(String fieldName, Collection fieldValues) { + where.andIn(fieldName, fieldValues); + return this; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudStream.java new file mode 100644 index 0000000000000000000000000000000000000000..88713a04d7c565197f3003c38f5abd5d97a9427e --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/CrudStream.java @@ -0,0 +1,629 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.able.jdbc.condition.DbUpdate; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.fields.ExcludeFields; +import com.gitee.qdbp.able.jdbc.fields.Fields; +import com.gitee.qdbp.able.jdbc.fields.IncludeFields; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.model.SimpleFieldColumn; +import com.gitee.qdbp.jdbc.utils.ParseTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 单表查询流式操作 + * + * @author zhaohuihua + * @version 20210529 + */ +public class CrudStream implements Serializable { + + /** serialVersionUID **/ + private static final long serialVersionUID = 1L; + + protected CrudDao dao; + + public CrudStream(CrudDao dao) { + this.dao = dao; + } + + /** + * 查询指定字段
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .list();
+     * 
+ * + * @param fields 字段列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSelectStream select(String... fields) { + return new CrudOnAfterSelectStream<>(dao, new IncludeFields(fields)); + } + + /** + * 查询指定字段
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select("id,userCode,realName") // 只查某些字段
+        // .selectExclude("password") // 排除掉密码字段
+        .list();
+     * 
+ * + * @param fields 字段列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSelectStream select(List fields) { + return new CrudOnAfterSelectStream<>(dao, new IncludeFields(fields)); + } + + /** + * 查询指定字段
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .select(new IncludeFields("id,userCode,realName")) // 只查某些字段
+        // .select(new IncludeFields("password")) // 排除掉密码字段
+        .list();
+     * 
+ * + * @param fields 字段列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSelectStream select(Fields fields) { + return new CrudOnAfterSelectStream<>(dao, fields); + } + + /** + * 排除指定字段, 只查询不在此列表中的字段
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        // .select("id,userCode,realName") // 只查某些字段
+        .selectExclude("password") // 排除掉密码字段
+        .list();
+     * 
+ * + * @param fields 字段列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSelectStream selectExclude(String... fields) { + return new CrudOnAfterSelectStream<>(dao, new ExcludeFields(fields)); + } + + /** + * 排除指定字段, 只查询不在此列表中的字段
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        // .select("id,userCode,realName") // 只查某些字段
+        .selectExclude("password") // 排除掉密码字段
+        .list();
+     * 
+ * + * @param fields 字段列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSelectStream selectExclude(List fields) { + return new CrudOnAfterSelectStream<>(dao, new ExcludeFields(fields)); + } + + /** + * 设置Where条件 + * + * @param where Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereByStream whereBy(DbWhere where) { + return new CrudOnAfterWhereByStream(dao, where); + } + + /** + * 从实体类构建Where对象
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy(user)
+        .list();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnAfterWhereByStream whereBy(T entity) { + DbWhere where = ParseTools.parseBeanToDbWhere(entity); + return new CrudOnAfterWhereByStream(dao, where); + } + + /** + * 从Map构建Where对象
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy(map)
+        .list();
+     * 

+ * 只会包含clazz注解中通过@Column指定的字段名
+ * 应注意, 此时参数由前端传入, 条件不可控, 也有可能条件为空, 需要仔细检查条件内容, 防止越权操作
+     * 转换规则:
+        fieldName$Equals(=), fieldName$NotEquals(!=), 
+        fieldName$LessThen(<), fieldName$LessEqualsThen(<=), 
+        fieldName$GreaterThen(>), fieldName$GreaterEqualsThen(>=), 
+        fieldName$IsNull, fieldName$IsNotNull, 
+        fieldName$Like, fieldName$NotLike, fieldName$Starts, fieldName$Ends, 
+        fieldName$In, fieldName$NotIn, fieldName$Between
+     * 
+ * + * @param map Map参数 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public CrudOnAfterWhereByStream whereBy(Map map) { + DbWhere where = ParseTools.parseBeanToDbWhere(map); + return new CrudOnAfterWhereByStream(dao, where); + } + + /** + * 生成Where条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereBy((where) -> {
+            where.andEquals("userType", 1)
+                .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1))
+                .andSubCondition((w) -> {
+                    w.orLike("userCode", "test")
+                    .orLike("realName", "test");
+                });
+        })
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param builder Where条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereByStream whereBy(DbWhereBuilder builder) { + DbWhere where = new DbWhere(); + builder.build(where); + return new CrudOnAfterWhereByStream(dao, where); + } + + /** + * Where条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * 字段名可以带表别名, 如.whereOn("u.id", "=", entity.getId());
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream whereOn(String fieldName, String operate, Object... fieldValues) { + DbWhere where = new DbWhere().on(fieldName, operate, fieldValues); + return new CrudOnAfterWhereOnStream(dao, where); + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName = fieldValue + * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream whereEquals(String fieldName, Object fieldValue) { + DbWhere where = new DbWhere().andEquals(fieldName, fieldValue); + return new CrudOnAfterWhereOnStream(dao, where); + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [ORACLE/DB2] AND fieldName LIKE('%'||fieldValue||'%')
+ * [MYSQL] AND fieldName LIKE CONCAT('%',fieldValue,'%')
+ * 如果fieldValue中包含%等特殊字符, 只会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL
+ * 如果希望自己处理ESCAPE逻辑, 可使用{@link #andLike(String, LikeValue)}方法
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream whereLike(String fieldName, String fieldValue) { + DbWhere where = new DbWhere().andLike(fieldName, fieldValue); + return new CrudOnAfterWhereOnStream(dao, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream whereIn(String fieldName, Object... fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new CrudOnAfterWhereOnStream(dao, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.whereBy((where) -> {})方法
+ * [SQL] AND fieldName IN (fieldValue1, fieldValue2, ...) + * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereOnStream whereIn(String fieldName, Collection fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new CrudOnAfterWhereOnStream(dao, where); + } + + /** + * 以ID作为查询条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereById("U0000001")
+        .find();
+        // logicalDelete();
+        // physicalDelete();
+     * 
+ * + * @param id 实体主键 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterByIdStream whereById(String id) { + VerifyTools.requireNotBlank(id, "id"); + return new CrudOnAfterByIdStream(dao, id); + } + + /** + * 以ID列表作为查询条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereByIds("U0000001", "U0000002", ...)
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param ids ID列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereByStream whereByIds(String... ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(Arrays.asList(ids)); + } + + /** + * 以ID列表作为查询条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .whereByIds("U0000001", "U0000002", ...)
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param ids ID列表 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterWhereByStream whereByIds(List ids) { + VerifyTools.requireNotBlank(ids, "ids"); + return doWhereByIds(ids); + } + + protected CrudOnAfterWhereByStream doWhereByIds(List ids) { + SimpleFieldColumn pk = dao.getSqlBuilder().helper().getPrimaryKey(); + if (pk == null) { // 没有找到主键字段 + String details = "UnsupportedWhereByIds, class=" + dao.getBeanClass().getName(); + throw new ServiceException(DbErrorCode.DB_PRIMARY_KEY_FIELD_IS_UNRESOLVED, details); + } + String primaryField = pk.getFieldName(); + DbWhere where = new DbWhere(); + where.andIn(primaryField, ids); + return new CrudOnAfterWhereByStream(dao, where); + } + + /** + * 设置OrderBy条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterOrderByStream orderBy(String orderings) { + return orderBy(Orderings.of(orderings)); + } + + /** + * 设置OrderBy条件
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .orderBy("createTime desc")
+        .list();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterOrderByStream orderBy(Orderings orderings) { + return new CrudOnAfterOrderByStream<>(dao, DbWhere.NONE, orderings); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .pageBy(1, 10) // 查第1页,每页10行
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(int pageIndex, int pageSize) { + return pageBy(new Paging(pageIndex, pageSize)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .pageBy(1, 10, 88) // 查第1页,每页10行, 88=提前查询的总行数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param total 总行数(用于提前查询总行数的情况) + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(int pageIndex, int pageSize, int total) { + return pageBy(new Paging(pageIndex, pageSize, total)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .pageBy(1, 10, false) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param needCount 是否统计总数 + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(int pageIndex, int pageSize, boolean needCount) { + return pageBy(new Paging(pageIndex, pageSize, needCount)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .pageBy(new Paging(1,10,false)) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+        // .listFieldValues("deptCode", true, String.class).asPartList(); // 查询部门编号, true=去重
+     * 
+ * + * @param paging 分页参数 + * @return 列表数据 + */ + public CrudOnAfterPagingStream pageBy(Paging paging) { + return new CrudOnAfterPagingStream<>(dao, DbWhere.NONE, Orderings.NONE, paging); + } + + /** + * 根据条件查询实体列表
+ *
+    List<SysUser> users = qdbcBoot.crudStream(SysUser.class)
+        .list();
+     * 
+ * + * @return 列表数据 + */ + public List list() { + return dao.list(Fields.ALL, DbWhere.NONE, Orderings.NONE); + } + + /** + * 根据条件查询某个字段的值列表
+ *
+    List<String> deptCodes = qdbcBoot.crudStream(SysUser.class)
+        .listFieldValues("deptCode", true, String.class); // 查询部门编号, true=去重
+     * 
+ * + * @param fieldName 指定字段名 + * @param distinct 是否去重 + * @param valueClazz 字段值类型 + * @return 字段的值列表 + */ + public List listFieldValues(String fieldName, boolean distinct, Class valueClazz) + throws ServiceException { + return dao.listFieldValues(fieldName, distinct, DbWhere.NONE, Orderings.NONE, valueClazz); + } + + /** + * 根据条件统计实体数量
+ *
+    int total = qdbcBoot.crudStream(SysUser.class)
+        .count();
+     * 
+ * + * @return 数据数量 + */ + public int count() throws ServiceException { + return dao.count(DbWhere.NONE); + } + + /** + * 根据条件分组统计实体数量
+ *
+    Map<String, Integer> result = qdbcBoot.crudStream(SysUser.class)
+        .groupCount("userType");
+    
+    SELECT USER_TYPE, COUNT(*) FROM SYS_USER
+        GROUP BY USER_TYPE
+     * 
+ * + * @param groupBy 分组条件 + * @return 分组统计结果 + */ + public Map groupCount(String groupBy) throws ServiceException { + return dao.groupCount(groupBy, DbWhere.NONE); + } + + /** + * 开始递归查询
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param codeField 编号字段名 + * @param parentField 上级编号字段名 + * @return 返回后续流式操作对象 + */ + public RecursiveQueryStream recursiveBy(String codeField, String parentField) { + return new RecursiveQueryStream(dao, codeField, parentField); + } + + /** + * 实体类操作
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .insert();
+        // .update();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterEntityStream entity(T entity) { + VerifyTools.requireNonNull(entity, "entity"); + return new CrudOnAfterEntityStream(dao, entity); + } + + /** + * 实体类操作
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .entity(user)
+        .insert();
+        // .update();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterEntityStream entity(Map entity) { + VerifyTools.requireNotBlank(entity, "entity"); + return new CrudOnAfterEntityStream(dao, entity); + } + + /** + * 实体批量操作
+ *
+    List<String> ids = qdbcBoot.crudStream(SysUser.class)
+        .entities(user)
+        .insert();
+        // .update();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterEntitiesStream entities(List entities) { + return new CrudOnAfterEntitiesStream(dao, entities); + } + + /** + * 设置更新条件 + * + * @param ud 更新条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSetByStream setBy(DbUpdate ud) { + return new CrudOnAfterSetByStream(dao, ud); + } + + /** + * 更新操作
+ *
+    int rows = qdbcBoot.crudStream(SysUser.class)
+        .setBy((ud) -> {
+            ud.set("userName", "zhaohuihua"); // 用户名修改为指定值
+            // ud.add("memberScore", +100); // 会员积分增加100
+            // ud.add("memberScore", -100); // 会员积分减少100
+            // ud.toNull("userState"); // 用户状态修改为空
+        })
+        // .whereEquals("userId", "U0000001");
+        .whereBy((where) -> {
+            where.andEquals("userId", "U0000001")
+                .andIn("userState", UserState.NORMAL, UserState.LOCKED);
+        })
+        .update();
+     * 
+ * + * @param builder DbUpdate条件 + * @return 返回后续流式操作对象 + */ + public CrudOnAfterSetByStream setBy(DbUpdateBuilder builder) { + DbUpdate ud = new DbUpdate(); + builder.build(ud); + return new CrudOnAfterSetByStream(dao, ud); + } + + public static interface DbWhereBuilder { + + void build(DbWhere where); + } + + public static interface DbUpdateBuilder { + + void build(DbUpdate where); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveExecuteStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveExecuteStream.java new file mode 100644 index 0000000000000000000000000000000000000000..280afd5caceb8f7c0d1797701cde6793b4e39a88 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveExecuteStream.java @@ -0,0 +1,156 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 递归执行对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveExecuteStream { + + protected CrudDao dao; + protected String codeField; + protected String parentField; + protected List startCodes; + protected DbWhere filterWhere; + protected DbWhere searchWhere; + protected Orderings orderings; + + RecursiveExecuteStream(CrudDao dao, String codeField, String parentField, List startCodes, + DbWhere filterWhere, DbWhere searchWhere, Orderings orderings) { + this.dao = dao; + this.codeField = codeField; + this.parentField = parentField; + this.startCodes = startCodes; + this.filterWhere = filterWhere; + this.searchWhere = searchWhere; + this.orderings = orderings; + } + + /** + * 递归查询所有子节点
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02")
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .orderBy("sortIndex asc")
+        .listChildren();
+     * 

+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @return 子节点列表 + */ + public List listChildren() { + return dao.listChildren(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + /** + * 递归查询所有父节点
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01000001", "D02000008")
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01000001", "D02000008"); // 查询所有父节点, 但不要包含起始编号自身
+        })
+        .listParents();
+     * 

+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @return 父节点列表 + */ + public List listParents() { + return dao.listParents(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + /** + * 递归查询所有子节点编号
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02")
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .orderBy("sortIndex asc")
+        .listChildrenCodes();
+     * 

+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @return 子节点编号 + */ + public List listChildrenCodes() { + return dao.listChildrenCodes(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } + + /** + * 递归查询所有子节点编号
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<String> parentIds = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01000001", "D02000008")
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotEquals("id", "D01000001", "D02000008"); // 查询所有父节点, 但不要包含起始编号自身
+        })
+        .listParentCodes();
+     * 

+ * ORACLE: START WITH {codeField} IN( {startCode} ) CONNECT BY PRIOR {codeField} = {parentField}
+ * DB2/SqlServer: 使用WITH递归
+ * MYSQL 8.0+/PostgreSQL: 使用WITH RECURSIVE递归
+ * MYSQL 8.0-: 使用存储过程RECURSIVE_LIST_CHILDREN_QUERY + * + * @return 父节点编号 + */ + public List listParentCodes() { + return dao.listParentCodes(startCodes, codeField, parentField, filterWhere, searchWhere, orderings); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveFilterOnStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveFilterOnStream.java new file mode 100644 index 0000000000000000000000000000000000000000..9c1800a17e95eb2c94970e221d7c499a0a9bf0e3 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveFilterOnStream.java @@ -0,0 +1,150 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import java.util.List; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 递归查询, 设置filterOn之后的流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveFilterOnStream extends RecursiveFilteredStream { + + RecursiveFilterOnStream(CrudDao dao, String codeField, String parentField, List startCodes, + DbWhere filterWhere) { + super(dao, codeField, parentField, startCodes, filterWhere); + } + + /** + * 设置数据过滤条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ * 字段名可以带表别名, 如.whereOn("u.id", "=", entity.getId());
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnEquals("tenantCode", "T001") // 租户编号
+            .andOn(...)
+        .searchOn("id", "not in", "D01", "D02") // 查询子节点, 但不要包含起始编号自身
+            .andOn(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream andOn(String fieldName, String operate, Object... fieldValues) { + this.searchWhere.on(fieldName, operate, fieldValues); + return this; + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnEquals("tenantCode", "T001") // 租户编号
+            .andEquals(...)
+        .searchOnEquals("xxxField", "xxxValue")
+            .andEquals(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream andEquals(String fieldName, Object fieldValue) { + this.searchWhere.andEquals(fieldName, fieldValue); + return this; + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnEquals("tenantCode", "T001") // 租户编号
+            .andLike(...)
+        .searchOnLike("xxxField", "xxxValue")
+            .andLike(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream andLike(String fieldName, String fieldValue) { + this.searchWhere.andLike(fieldName, fieldValue); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnEquals("tenantCode", "T001") // 租户编号
+            .andIn(...)
+        .searchOnIn("xxxField", "xxxValue1", "xxxValue2")
+            .andIn(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream andIn(String fieldName, Object... fieldValues) { + this.searchWhere.andIn(fieldName, fieldValues); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnEquals("tenantCode", "T001") // 租户编号
+            .andIn(...)
+        .searchOnIn("xxxField", fieldValues)
+            .andIn(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream andIn(String fieldName, Collection fieldValues) { + this.searchWhere.andIn(fieldName, fieldValues); + return this; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveFilteredStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveFilteredStream.java new file mode 100644 index 0000000000000000000000000000000000000000..7aed92b931898b06537b930a180d9efdb01b6c9d --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveFilteredStream.java @@ -0,0 +1,265 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.stream.CrudStream.DbWhereBuilder; +import com.gitee.qdbp.jdbc.utils.ParseTools; + +/** + * 递归查询, 设置startCodes之后的流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveFilteredStream extends RecursiveSearchedStream { + + RecursiveFilteredStream(CrudDao dao, String codeField, String parentField, List startCodes, + DbWhere filterWhere) { + super(dao, codeField, parentField, startCodes, filterWhere, DbWhere.NONE); + } + + /** + * 设置结果搜索条件
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy(where)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param searchWhere 结果搜索条件 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream searchBy(DbWhere searchWhere) { + return new RecursiveSearchedStream(dao, codeField, parentField, startCodes, filterWhere, searchWhere); + } + + /** + * 设置结果搜索条件 (从实体类构建Where对象)
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy(deptEntity)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public RecursiveSearchedStream searchBy(T entity) { + DbWhere where = ParseTools.parseBeanToDbWhere(entity); + return searchBy(where); + } + + /** + * 设置结果搜索条件 (从Map构建Where对象)
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy(map)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param map Map参数 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public RecursiveSearchedStream searchBy(Map map) { + DbWhere where = ParseTools.parseBeanToDbWhere(map); + return searchBy(where); + } + + /** + * 设置结果搜索条件
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param builder Where条件 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream searchBy(DbWhereBuilder builder) { + DbWhere where = new DbWhere(); + builder.build(where); + return searchBy(where); + } + + /** + * 设置结果搜索条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ * 字段名可以带表别名, 如.whereOn("u.id", "=", entity.getId());
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOn("id", "not in", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchOnStream searchOn(String fieldName, String operate, Object... fieldValues) { + DbWhere where = new DbWhere().on(fieldName, operate, fieldValues); + return new RecursiveSearchOnStream(dao, codeField, parentField, startCodes, filterWhere, where); + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnEquals("xxxField", "xxxValue")
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchOnStream searchOnEquals(String fieldName, Object fieldValue) { + DbWhere where = new DbWhere().andEquals(fieldName, fieldValue); + return new RecursiveSearchOnStream(dao, codeField, parentField, startCodes, filterWhere, where); + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnLike("xxxField", "xxxValue")
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchOnStream searchOnLike(String fieldName, String fieldValue) { + DbWhere where = new DbWhere().andLike(fieldName, fieldValue); + return new RecursiveSearchOnStream(dao, codeField, parentField, startCodes, filterWhere, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnIn("xxxField", "xxxValue1", "xxxValue2")
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchOnStream searchOnIn(String fieldName, Object... fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new RecursiveSearchOnStream(dao, codeField, parentField, startCodes, filterWhere, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnIn("xxxField", fieldValues)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchOnStream searchOnIn(String fieldName, Collection fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new RecursiveSearchOnStream(dao, codeField, parentField, startCodes, filterWhere, where); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveQueryStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveQueryStream.java new file mode 100644 index 0000000000000000000000000000000000000000..716456117ff1f19d81a1cff276cdcf9957bd377f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveQueryStream.java @@ -0,0 +1,83 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Arrays; +import java.util.List; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 递归查询流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveQueryStream { + + protected CrudDao dao; + private String codeField; + private String parentField; + + RecursiveQueryStream(CrudDao dao, String codeField, String parentField) { + this.dao = dao; + this.codeField = codeField; + this.parentField = parentField; + } + + /** + * 设置递归查询起始编号
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param startCodes 起始编号 + * @return 返回后续流式操作对象 + */ + public RecursiveStartedStream startBy(String... startCodes) { + VerifyTools.requireNotBlank(startCodes, "startCodes"); + return new RecursiveStartedStream(dao, codeField, parentField, Arrays.asList(startCodes)); + } + + /** + * 设置递归查询起始编号
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param startCodes 起始编号 + * @return 返回后续流式操作对象 + */ + public RecursiveStartedStream startBy(List startCodes) { + VerifyTools.requireNotBlank(startCodes, "startCodes"); + return new RecursiveStartedStream(dao, codeField, parentField, startCodes); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveSearchOnStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveSearchOnStream.java new file mode 100644 index 0000000000000000000000000000000000000000..b1a4ec30ae50fa3aa8e06183b65361fce9f18b80 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveSearchOnStream.java @@ -0,0 +1,155 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import java.util.List; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 递归查询, 设置searchOn之后的流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveSearchOnStream extends RecursiveSearchedStream { + + RecursiveSearchOnStream(CrudDao dao, String codeField, String parentField, List startCodes, + DbWhere filterWhere, DbWhere searchWhere) { + super(dao, codeField, parentField, startCodes, filterWhere, searchWhere); + } + + /** + * 设置结果搜索条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ * 字段名可以带表别名, 如.whereOn("u.id", "=", entity.getId());
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOn("id", "not in", "D01", "D02") // 查询子节点, 但不要包含起始编号自身
+            .andOn(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream andOn(String fieldName, String operate, Object... fieldValues) { + this.searchWhere.on(fieldName, operate, fieldValues); + return this; + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnEquals("xxxField", "xxxValue")
+            .andEquals(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream andEquals(String fieldName, Object fieldValue) { + this.searchWhere.andEquals(fieldName, fieldValue); + return this; + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnLike("xxxField", "xxxValue")
+            .andLike(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream andLike(String fieldName, String fieldValue) { + this.searchWhere.andLike(fieldName, fieldValue); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnIn("xxxField", "xxxValue1", "xxxValue2")
+            .andIn(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream andIn(String fieldName, Object... fieldValues) { + this.searchWhere.andIn(fieldName, fieldValues); + return this; + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchOnIn("xxxField", fieldValues)
+            .andIn(...)
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveSearchedStream andIn(String fieldName, Collection fieldValues) { + this.searchWhere.andIn(fieldName, fieldValues); + return this; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveSearchedStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveSearchedStream.java new file mode 100644 index 0000000000000000000000000000000000000000..67f2143774f1af4433a4c26b81eaa23d35cd0da9 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveSearchedStream.java @@ -0,0 +1,79 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.api.CrudDao; + +/** + * 递归查询, 设置searchBy之后的流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveSearchedStream extends RecursiveExecuteStream { + + RecursiveSearchedStream(CrudDao dao, String codeField, String parentField, List startCodes, + DbWhere filterWhere, DbWhere searchWhere) { + super(dao, codeField, parentField, startCodes, filterWhere, searchWhere, Orderings.NONE); + } + + /** + * 设置OrderBy条件
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02")
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .orderBy("sortIndex asc")
+        .listChildren();
+        // listParents();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public RecursiveExecuteStream orderBy(String orderings) { + return orderBy(Orderings.of(orderings)); + } + + /** + * 设置OrderBy条件
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在filterWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02")
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .orderBy("sortIndex asc")
+        .listChildren();
+        // listParents();
+     * 
+ * + * @param orderings OrderBy条件 + * @return 返回后续流式操作对象 + */ + public RecursiveExecuteStream orderBy(Orderings orderings) { + return new RecursiveExecuteStream<>(dao, codeField, parentField, startCodes, filterWhere, searchWhere, + orderings); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveStartedStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveStartedStream.java new file mode 100644 index 0000000000000000000000000000000000000000..55acb69ef75b44086ccb7f576a5a392b7a55dd78 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/RecursiveStartedStream.java @@ -0,0 +1,260 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.stream.CrudStream.DbWhereBuilder; +import com.gitee.qdbp.jdbc.utils.ParseTools; + +/** + * 递归查询, 设置startCodes之后的流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class RecursiveStartedStream extends RecursiveFilteredStream { + + RecursiveStartedStream(CrudDao dao, String codeField, String parentField, List startCodes) { + super(dao, codeField, parentField, startCodes, DbWhere.NONE); + } + + /** + * 设置数据过滤条件
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy(where)
+        .searchOnNotIn("id", "D01", "D02") // 查询子节点, 但不要包含起始编号自身
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param filterWhere 数据过滤条件 + * @return 返回后续流式操作对象 + */ + public RecursiveFilteredStream filterBy(DbWhere filterWhere) { + return new RecursiveFilteredStream(dao, codeField, parentField, startCodes, filterWhere); + } + + /** + * 设置数据过滤条件 (从实体类构建Where对象)
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy(deptEntity)
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param entity 实体类 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public RecursiveFilteredStream filterBy(T entity) { + DbWhere where = ParseTools.parseBeanToDbWhere(entity); + return filterBy(where); + } + + /** + * 设置数据过滤条件 (从Map构建Where对象)
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy(map)
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param map Map参数 + * @return 返回后续流式操作对象 + * @see ParseTools#parseBeanToDbWhere(Object) + */ + public RecursiveFilteredStream filterBy(Map map) { + DbWhere where = ParseTools.parseBeanToDbWhere(map); + return filterBy(where); + } + + /** + * 设置数据过滤条件
+ * recursiveBy(codeField, parentField) 按什么字段进行递归查询
+ * startBy 起始编号
+ * filterBy 数据过滤条件, 过滤哪些数据参与递归 (如数据状态,租户编号等条件)
+ * searchBy 结果搜索条件 (如用户输入的查询条件) 这些条件如果放在searchWhere中将无法生成完整的树
+ * 注意: 查询结果包括startCode节点的自身记录, 如果不需要应在searchWhere条件排除
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterBy((where) -> {
+            where.andEquals("tenantCode", "T001"); // 租户编号
+        })
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param builder Where条件 + * @return 返回后续流式操作对象 + */ + public RecursiveFilteredStream filterBy(DbWhereBuilder builder) { + DbWhere where = new DbWhere(); + builder.build(where); + return filterBy(where); + } + + /** + * 设置数据过滤条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ * 字段名可以带表别名, 如.whereOn("u.id", "=", entity.getId());
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOn("tenantCode", "=", "T001") // 租户编号
+        .searchOn("id", "not in", "D01", "D02") // 查询子节点, 但不要包含起始编号自身
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名称 + * @param operate 目前支持如下操作:
+ * =, !=, <, <=, >, >=,
+ * Equals(equals), NotEquals(not equals),
+ * LessThen(less then), LessEqualsThen(less equals then),
+ * GreaterThen(greater then), GreaterEqualsThen(greater equals then),
+ * IsNull(is null), IsNotNull(is not null),
+ * Like(like), NotLike(not like),
+ * Starts(starts), NotStarts(not starts), Ends(ends), NotEnds(not ends),
+ * In(in), NotIn(not in), Between(between), NotBetween(not between) + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream filterOn(String fieldName, String operate, Object... fieldValues) { + DbWhere where = new DbWhere().on(fieldName, operate, fieldValues); + return new RecursiveFilterOnStream(dao, codeField, parentField, startCodes, where); + } + + /** + * 等于条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnEquals("tenantCode", "T001") // 租户编号
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream filterOnEquals(String fieldName, Object fieldValue) { + DbWhere where = new DbWhere().andEquals(fieldName, fieldValue); + return new RecursiveFilterOnStream(dao, codeField, parentField, startCodes, where); + } + + /** + * LIKE条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnLike("xxxField", "xxxValue")
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream filterOnLike(String fieldName, String fieldValue) { + DbWhere where = new DbWhere().andLike(fieldName, fieldValue); + return new RecursiveFilterOnStream(dao, codeField, parentField, startCodes, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnIn("xxxField", fieldValues)
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream filterOnIn(String fieldName, Object... fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new RecursiveFilterOnStream(dao, codeField, parentField, startCodes, where); + } + + /** + * IN查询条件
+ * 注意: 该方法后续只支持简单条件, 如果有复杂条件请使用.searchBy((where) -> {})方法
+ *
+    List<SysDept> depts = qdbcBoot.crudStream(SysDept.class)
+        .recursiveBy("id", "parentId") // 按id/parentId进行递归查询
+        .startBy("D01", "D02") // 起始编号
+        .filterOnIn("xxxField", fieldValues)
+        .searchBy((where) -> {
+            where.andNotIn("id", "D01", "D02"); // 查询子节点, 但不要包含起始编号自身
+        })
+        .listChildren();
+        // .listParents();
+     * 
+ * + * @param fieldName 字段名 + * @param fieldValues 字段值 + * @return 返回后续流式操作对象 + */ + public RecursiveFilterOnStream filterOnIn(String fieldName, Collection fieldValues) { + DbWhere where = new DbWhere().andIn(fieldName, fieldValues); + return new RecursiveFilterOnStream(dao, codeField, parentField, startCodes, where); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterIdPairStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterIdPairStream.java new file mode 100644 index 0000000000000000000000000000000000000000..55df4c2e057a47f7a1cfeb2c4a8e7f556712e2f7 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterIdPairStream.java @@ -0,0 +1,20 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 同时设置queryId和countId的后续操作 (只能是分页查询) + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnAfterIdPairStream extends SqlOnIdPairParamsStream { + + SqlOnAfterIdPairStream(SqlDao dao, String queryId, String countId) { + super(dao, queryId, countId, null); + } + + public SqlOnIdPairParamsStream params(Object params) { + return new SqlOnIdPairParamsStream(dao, queryId, countId, params); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterIdStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterIdStream.java new file mode 100644 index 0000000000000000000000000000000000000000..decd8536889b66a19e3f44893432925443fbf95e --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterIdStream.java @@ -0,0 +1,24 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 设置SqlId的后续操作: exists / params + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnAfterIdStream extends SqlOnAfterParamsStream { + + SqlOnAfterIdStream(SqlDao dao, String sqlId) { + super(dao, sqlId, null); + } + + public boolean exists() { + return this.dao.existSqlTemplate(sqlId); + } + + public SqlOnAfterParamsStream params(Object params) { + return new SqlOnAfterParamsStream(dao, sqlId, params); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterPagingStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterPagingStream.java new file mode 100644 index 0000000000000000000000000000000000000000..8cf693e6dd6889a2eb34bd49d6d1ae83ab08df7f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterPagingStream.java @@ -0,0 +1,74 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Map; +import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 分页的后续操作: resultAs / list + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnAfterPagingStream { + + protected SqlDao dao; + protected String sqlId; + protected Object params; + protected Paging paging; + + SqlOnAfterPagingStream(SqlDao dao, String sqlId, Object params, Paging paging) { + this.dao = dao; + this.sqlId = sqlId; + this.params = params; + this.paging = paging; + } + + /** + * 查询列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .pageBy(1,10)
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @return 查询结果列表 + */ + public PageList> list() { + return this.dao.pageForMaps(sqlId, params, paging); + } + + /** + * 设置结果实体类型
+ *
+    List<SysUser> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .pageBy(1,10)
+        .resultAs(SysUser.class)
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param 实体类型 + * @param resultType 实体类 + * @return 返回后续流式操作对象 + */ + public SqlOnPagingResultStream resultAs(Class resultType) { + return new SqlOnPagingResultStream(dao, sqlId, params, paging, resultType); + } + + /** + * 设置结果实体类型 + * + * @param 实体类型 + * @param rowMapper 结果转换类 + * @return 返回后续流式操作对象 + */ + public SqlOnPagingResultStream resultAs(RowMapper rowMapper) { + return new SqlOnPagingResultStream(dao, sqlId, params, paging, rowMapper); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterParamsStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterParamsStream.java new file mode 100644 index 0000000000000000000000000000000000000000..9a6e3f87b0115c8fbcd8466cc447a954bc3583f2 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnAfterParamsStream.java @@ -0,0 +1,190 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import java.util.Map; +import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.SqlDao; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; + +/** + * 设置参数的后续操作: find|list|insert|update|delete / pageBy / resultAs
+ * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnAfterParamsStream { + + protected SqlDao dao; + protected String sqlId; + protected Object params; + + SqlOnAfterParamsStream(SqlDao dao, String sqlId, Object params) { + this.dao = dao; + this.sqlId = sqlId; + this.params = params; + } + + public SqlBuffer getContent() { + return this.dao.getSqlContent(sqlId, params); + } + + /** + * 查询数据, 结果为Map结构
+ *
+    Map<String, Object> user = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .find();
+     * 
+ * + * @return Map结果 + */ + public Map find() { + return this.dao.findForMap(sqlId, params); + } + + /** + * 查询数据列表, 结果为Map列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .list();
+     * 
+ * + * @return Map列表 + */ + public List> list() { + return this.dao.listForMaps(sqlId, params); + } + + /** + * 执行插入语句 + * + * @return 影响行数 + */ + public int insert() { + return this.dao.insert(sqlId, params); + } + + /** + * 执行更新语句 + * + * @return 影响行数 + */ + public int update() { + return this.dao.update(sqlId, params); + } + + /** + * 执行删除语句 + * + * @return 影响行数 + */ + public int delete() { + return this.dao.delete(sqlId, params); + } + + /** + * 设置结果实体类型
+ *
+    List<SysUser> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .resultAs(SysUser.class)
+        .list();
+     * 
+ * + * @param 实体类型 + * @param resultType 实体类 + * @return 返回后续流式操作对象 + */ + public SqlOnResultTypeStream resultAs(Class resultType) { + return new SqlOnResultTypeStream(dao, sqlId, params, resultType); + } + + /** + * 设置结果实体类型 + * + * @param 实体类型 + * @param rowMapper 结果转换类 + * @return 返回后续流式操作对象 + */ + public SqlOnResultTypeStream resultAs(RowMapper rowMapper) { + return new SqlOnResultTypeStream(dao, sqlId, params, rowMapper); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .pageBy(1, 10) // 查第1页,每页10行
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @return 返回后续流式操作对象 + */ + public SqlOnAfterPagingStream pageBy(int pageIndex, int pageSize) { + return pageBy(new Paging(pageIndex, pageSize)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .pageBy(1, 10, 88) // 查第1页,每页10行, 88=提前查询的总行数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param total 总行数(用于提前查询总行数的情况) + * @return 返回后续流式操作对象 + */ + public SqlOnAfterPagingStream pageBy(int pageIndex, int pageSize, int total) { + return pageBy(new Paging(pageIndex, pageSize, total)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .pageBy(1, 10, false) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param needCount 是否统计总数 + * @return 返回后续流式操作对象 + */ + public SqlOnAfterPagingStream pageBy(int pageIndex, int pageSize, boolean needCount) { + return pageBy(new Paging(pageIndex, pageSize, needCount)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .pageBy(new Paging(1,10,false)) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param paging 分页参数 + * @return 返回后续流式操作对象 + */ + public SqlOnAfterPagingStream pageBy(Paging paging) { + return new SqlOnAfterPagingStream(dao, sqlId, params, paging); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairPagingResultStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairPagingResultStream.java new file mode 100644 index 0000000000000000000000000000000000000000..7ab68f80b388373b5e3381951acd5c2c244a599b --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairPagingResultStream.java @@ -0,0 +1,66 @@ +package com.gitee.qdbp.jdbc.stream; + +import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 同时设置queryId/countId, 分页+结果转换后续操作, 只有list() + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnIdPairPagingResultStream { + + protected SqlDao dao; + protected String queryId; + protected String countId; + protected Object params; + protected Paging paging; + + protected Class resultType; + protected RowMapper rowMapper; + + SqlOnIdPairPagingResultStream(SqlDao dao, String queryId, String countId, Object params, Paging paging, + Class resultType) { + this.dao = dao; + this.queryId = queryId; + this.countId = countId; + this.params = params; + this.paging = paging; + this.resultType = resultType; + } + + SqlOnIdPairPagingResultStream(SqlDao dao, String queryId, String countId, Object params, Paging paging, + RowMapper rowMapper) { + this.dao = dao; + this.queryId = queryId; + this.countId = countId; + this.params = params; + this.paging = paging; + this.rowMapper = rowMapper; + } + + /** + * 查询列表
+ *
+    List<SysUser> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(1,10)
+        .resultAs(SysUser.class)
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @return 查询结果列表 + */ + public PageList list() { + if (resultType != null) { + return this.dao.pageForObjects(queryId, countId, params, paging, resultType); + } else { + return this.dao.pageForObjects(queryId, countId, params, paging, rowMapper); + } + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairPagingStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairPagingStream.java new file mode 100644 index 0000000000000000000000000000000000000000..cff0c8e75230a78ce2afe3d9b6b6dad35874f508 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairPagingStream.java @@ -0,0 +1,76 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.Map; +import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 同时设置queryId/countId, 分页后续操作: list / resultAs + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnIdPairPagingStream { + + protected SqlDao dao; + protected String queryId; + protected String countId; + protected Object params; + protected Paging paging; + + SqlOnIdPairPagingStream(SqlDao dao, String queryId, String countId, Object params, Paging paging) { + this.dao = dao; + this.queryId = queryId; + this.countId = countId; + this.params = params; + this.paging = paging; + } + + /** + * 查询列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(1,10)
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @return 查询结果列表 + */ + public PageList> list() { + return this.dao.pageForMaps(queryId, countId, params, paging); + } + + /** + * 设置结果实体类型
+ *
+    List<SysUser> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(1,10)
+        .resultAs(SysUser.class)
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param 实体类型 + * @param resultType 实体类 + * @return 返回后续流式操作对象 + */ + public SqlOnIdPairPagingResultStream resultAs(Class resultType) { + return new SqlOnIdPairPagingResultStream(dao, queryId, countId, params, paging, resultType); + } + + /** + * 设置结果实体类型 + * + * @param 实体类型 + * @param rowMapper 结果转换类 + * @return 返回后续流式操作对象 + */ + public SqlOnIdPairPagingResultStream resultAs(RowMapper rowMapper) { + return new SqlOnIdPairPagingResultStream(dao, queryId, countId, params, paging, rowMapper); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairParamsStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairParamsStream.java new file mode 100644 index 0000000000000000000000000000000000000000..fd8164a2f534577caf8bfe59d45133555993112b --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnIdPairParamsStream.java @@ -0,0 +1,98 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 同时设置queryId/countId, 设置参数后续操作: pageBy + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnIdPairParamsStream { + + protected SqlDao dao; + protected String queryId; + protected String countId; + protected Object params; + + SqlOnIdPairParamsStream(SqlDao dao, String queryId, String countId, Object params) { + this.dao = dao; + this.queryId = queryId; + this.countId = countId; + this.params = params; + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(1, 10) // 查第1页,每页10行
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @return 返回后续流式操作对象 + */ + public SqlOnIdPairPagingStream pageBy(int pageIndex, int pageSize) { + return pageBy(new Paging(pageIndex, pageSize)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(1, 10, 88) // 查第1页,每页10行, 88=提前查询的总行数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param total 总行数(用于提前查询总行数的情况) + * @return 返回后续流式操作对象 + */ + public SqlOnIdPairPagingStream pageBy(int pageIndex, int pageSize, int total) { + return pageBy(new Paging(pageIndex, pageSize, total)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(1, 10, false) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param pageIndex 第几页 + * @param pageSize 每页行数 + * @param needCount 是否统计总数 + * @return 返回后续流式操作对象 + */ + public SqlOnIdPairPagingStream pageBy(int pageIndex, int pageSize, boolean needCount) { + return pageBy(new Paging(pageIndex, pageSize, needCount)); + } + + /** + * 根据条件分页查询实体列表
+ *
+    List<Map<String, Object>> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers", "SysUserMapper:countRoleUsers")
+        .params(map)
+        .pageBy(new Paging(1,10,false)) // 查第1页,每页10行, false=无需统计总数
+        .list().asPartList(); // 分页后返回的是PageList, 需要转换为普通List
+     * 
+ * + * @param paging 分页参数 + * @return 返回后续流式操作对象 + */ + public SqlOnIdPairPagingStream pageBy(Paging paging) { + return new SqlOnIdPairPagingStream(dao, queryId, countId, params, paging); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnPagingResultStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnPagingResultStream.java new file mode 100644 index 0000000000000000000000000000000000000000..87469b29c4701f95d2c05e135ca48f06a6354f22 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnPagingResultStream.java @@ -0,0 +1,53 @@ +package com.gitee.qdbp.jdbc.stream; + +import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 分页+结果转换后续操作: 只有list() + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnPagingResultStream { + + protected SqlDao dao; + protected String sqlId; + protected Object params; + protected Paging paging; + + protected Class resultType; + protected RowMapper rowMapper; + + SqlOnPagingResultStream(SqlDao dao, String sqlId, Object params, Paging paging, Class resultType) { + this.dao = dao; + this.sqlId = sqlId; + this.params = params; + this.paging = paging; + this.resultType = resultType; + } + + SqlOnPagingResultStream(SqlDao dao, String sqlId, Object params, Paging paging, RowMapper rowMapper) { + this.dao = dao; + this.sqlId = sqlId; + this.params = params; + this.paging = paging; + this.rowMapper = rowMapper; + } + + /** + * 查询列表 + * + * @return 查询结果列表 + */ + public PageList list() { + if (resultType != null) { + return this.dao.pageForObjects(sqlId, params, paging, resultType); + } else { + return this.dao.pageForObjects(sqlId, params, paging, rowMapper); + } + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnResultTypeStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnResultTypeStream.java new file mode 100644 index 0000000000000000000000000000000000000000..f7dbe6e14535f6849571a6ba03f5d3815b03a483 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlOnResultTypeStream.java @@ -0,0 +1,75 @@ +package com.gitee.qdbp.jdbc.stream; + +import java.util.List; +import org.springframework.jdbc.core.RowMapper; +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * 结果转换后续操作: find() / list() + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlOnResultTypeStream { + + protected SqlDao dao; + protected String sqlId; + protected Object params; + + protected Class resultType; + protected RowMapper rowMapper; + + SqlOnResultTypeStream(SqlDao dao, String sqlId, Object params, Class resultType) { + this.dao = dao; + this.sqlId = sqlId; + this.params = params; + this.resultType = resultType; + } + + SqlOnResultTypeStream(SqlDao dao, String sqlId, Object params, RowMapper rowMapper) { + this.dao = dao; + this.sqlId = sqlId; + this.params = params; + this.rowMapper = rowMapper; + } + + /** + * 查询对象
+ *
+    SysUser user = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:findUser")
+        .params(map)
+        .resultAs(SysUser.class)
+        .find();
+     * 
+ * + * @return 查询结果对象 + */ + public T find() { + if (resultType != null) { + return this.dao.findForObject(sqlId, params, resultType); + } else { + return this.dao.findForObject(sqlId, params, rowMapper); + } + } + + /** + * 查询列表
+ *
+    List<SysUser> users = qdbcBoot.sqlStream()
+        .sqlId("SysUserMapper:queryRoleUsers")
+        .params(map)
+        .resultAs(SysUser.class)
+        .list();
+     * 
+ * + * @return 查询结果列表 + */ + public List list() { + if (resultType != null) { + return this.dao.listForObjects(sqlId, params, resultType); + } else { + return this.dao.listForObjects(sqlId, params, rowMapper); + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlStream.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlStream.java new file mode 100644 index 0000000000000000000000000000000000000000..e2e3fe8a81b6f872b61031f10d631900f35fa64b --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/stream/SqlStream.java @@ -0,0 +1,39 @@ +package com.gitee.qdbp.jdbc.stream; + +import com.gitee.qdbp.jdbc.api.SqlDao; + +/** + * SQL模板的流式操作对象 + * + * @author zhaohuihua + * @version 20210530 + */ +public class SqlStream { + + protected SqlDao dao; + + public SqlStream(SqlDao dao) { + this.dao = dao; + } + + /** + * 设置SqlId + * + * @param sqlId 查询数据的SqlId + * @return 返回后续流式操作对象 + */ + public SqlOnAfterIdStream sqlId(String sqlId) { + return new SqlOnAfterIdStream(dao, sqlId); + } + + /** + * 设置SqlId + * + * @param queryId 查询数据的SqlId + * @param countId 统计总数的SqlId + * @return 返回后续流式操作对象 + */ + public SqlOnAfterIdPairStream sqlId(String queryId, String countId) { + return new SqlOnAfterIdPairStream(dao, queryId, countId); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoDataSourceTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoDataSourceTools.java new file mode 100644 index 0000000000000000000000000000000000000000..37e94d7959fbf55ffa113123cbfb0412b54da4a8 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoDataSourceTools.java @@ -0,0 +1,253 @@ +package com.gitee.qdbp.jdbc.support; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.gitee.qdbp.able.exception.ResourceNotFoundException; +import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.tools.crypto.GlobalCipherTools; +import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.PropertyTools; +import com.gitee.qdbp.tools.utils.StringTools; + +class AutoDataSourceTools { + + private static Logger log = LoggerFactory.getLogger(AutoDataSourceTools.class); + + // jdbc:oracle:thin:@//192.168.1.218:1521/DbName + // jdbc:oracle:thin:192.168.1.218:1521:DbSid + // jdbc:oracle:thin:@TnsName + // jdbc:db2://192.168.1.218:60000/DbName + // jdbc:db2://192.168.1.218:60000/DbName:currentSchema=SchemaName; + // jdbc:h2:~/DbName // 嵌入式 + // jdbc:h2:mem:DbName // 内存数据库 + // jdbc:h2:tcp://192.168.1.218:8084/~/DbName + // jdbc:sqlserver://192.168.1.218:1433;DatabaseName=DbName + // jdbc:sqlite::memory: + // jdbc:sqlite://F:/sqlite/main.db + // jdbc:sqlite://home/sqlite/main.db + // jdbc:sqlite::resource:settings/sqlite/main.db + + // dbname可以是一个文件路径 + // 如 jdbc.xxx=sqlite.file@~/E:/sqlite/main.db + // 如 jdbc.xxx=sqlite.file@~/home/sqlite/main.db + /** 解析数据库配置的正则表达式 **/ + private static final Pattern CONFIG = Pattern.compile( + // dbtype.subtype : username : password @ address / dbname : schema ?EncryptedPassword + "(\\w+?(?:\\.\\w+)?)(?:\\:([\\-\\.\\w]+))?(?:\\:(.+?))?@([^@/?]+)/((?:[a-zA-Z]\\:/)?[/\\-\\.\\w]+)(?:\\:(.+?))?(?:\\?(.+))?"); + + public static DataSourceConfig parseConfigString(Properties properties, String configString) { + // 解析配置参数 + Matcher matcher = CONFIG.matcher(configString.trim()); + if (!matcher.matches()) { + String one = "dbtype:username:password@address/dbname"; + String two = "dbtype:username@address/dbname?EncryptedPassword"; + String msg = "Config string format error --> " + configString + "\nformat <1> " + one + " <2> " + two; + throw new IllegalArgumentException(msg); + } + + // dbtype:username:password@address/dbname:schema?EncryptedPassword + int i = 1; + String dbtype = matcher.group(i++); + String username = matcher.group(i++); + String password = matcher.group(i++); + String dbaddress = matcher.group(i++); + String dbname = matcher.group(i++); + String dbschema = matcher.group(i++); + String encrypted = matcher.group(i++); + if (password != null && encrypted != null) { + String desc = "password and EncryptedPassword coexistence is not allowed."; + throw new IllegalArgumentException("Config string format error. " + desc); + } + + DataSourceConfig result = new DataSourceConfig(properties); + result.setDbType(dbtype); + result.setDbAddress(dbaddress); + result.setDbName(dbname); + result.setDbSchema(dbschema); + result.setUserName(username); + if (encrypted != null) { + result.setPassword(GlobalCipherTools.decrypt("db", encrypted.getBytes())); + } else if (password != null) { + result.setPassword(password.getBytes()); + } + return result; + } + + public static Properties loadDefaultProperties() { + // 查找默认配置文件 + // 1. {classpath}/settings/qdbc/jdbc.auto.properties
+ // 2. qdbc-jdbc-core.jar!/settings/qdbc/jdbc.auto.properties
+ try { // 新版本移了位置, 从qdbc-jdbc-spring.jar移到qdbc-jdbc-core.jar + URL path = PathTools.findResource("settings/qdbc/jdbc.auto.properties", DbTools.class); + return PropertyTools.load(path); + } catch (ResourceNotFoundException e) { // 兼容旧版本 + URL path = PathTools.findResource("settings/jdbc/jdbc.auto.properties", AutoDruidDataSource.class); + return PropertyTools.load(path); + } + } + + /** 推断特殊的Driver(druid.1.1.22以上版本不需要了,已贡献到主版本) **/ + // https://github.com/alibaba/druid/pull/3698 + public static String resolveSpecialDriver(String jdbcUrl) { + if (jdbcUrl == null) { + return null; + } + if (jdbcUrl.startsWith("jdbc:db2")) { + return resolveDb2Driver(jdbcUrl); + } + return null; + } + + private static String resolveDb2Driver(String jdbcUrl) { + // 优化DB2 Driver的推断逻辑, 参考自 https://www.cnblogs.com/lidg/articles/3588566.html + // Resolve the DB2 driver from JDBC URL + // Type2 COM.ibm.db2.jdbc.app.DB2Driver, url = jdbc:db2:databasename + // Type3 COM.ibm.db2.jdbc.net.DB2Driver, url = jdbc:db2:ServerIP:6789:databasename + // Type4 8.1+ com.ibm.db2.jcc.DB2Driver, url = jdbc:db2://ServerIP:50000/databasename + String prefix = "jdbc:db2:"; + if (jdbcUrl.startsWith(prefix + "//")) { // Type4 + return "com.ibm.db2.jcc.DB2Driver"; + } else { + String suffix = jdbcUrl.substring(prefix.length()); + if (suffix.indexOf(':') > 0) { // Type3 + return "COM.ibm.db2.jdbc.net.DB2Driver"; + } else { // Type2 + return "COM.ibm.db2.jdbc.app.DB2Driver"; + } + } + } + + public static class DataSourceConfig { + + private Properties properties; + private String dbType; + private String dbAddress; + private String dbName; + private String dbSchema; + private String userName; + private byte[] password; + + public DataSourceConfig(Properties properties) { + this.properties = properties; + } + + /** + * 根据dbtype从配置项中查找配置内容
+ * 如key = jdbc.url, suffixes=x.y.z
+ * 取值顺序为: jdbc.url.x.y.z - jdbc.url.x.y - jdbc.url.x - jdbc.url + * + * @param key KEY前缀 + * @param replacePlaceholder 是否替换占位符 + * @param throwOnNotFound 未找到配置时是否报错 + * @return 配置内容 + */ + protected String findPropertyUseSuffix(String key, boolean replacePlaceholder, boolean throwOnNotFound) { + String suffixes = this.dbType; + char dot = '.'; + List keys = new ArrayList<>(); + keys.add(key + dot + suffixes); + int index = suffixes.length(); + while (true) { + index = suffixes.lastIndexOf(dot, index - 1); + if (index <= 0) { + break; + } + keys.add(key + dot + suffixes.substring(0, index)); + } + keys.add(key); + + String realKey = null; + String realValue = null; + for (String k : keys) { + String value = PropertyTools.getString(properties, k, false); + if (value == null) { + continue; + } else { + realKey = k; + realValue = value; + break; + } + } + if (realValue != null && realValue.length() > 0) { + String result = replacePlaceholder ? replacePlaceholder(realValue) : realValue; + if (log.isDebugEnabled()) { + log.debug("Resolved property value: " + realKey + " = " + result); + } + return result; + } else { + if (!throwOnNotFound) { + return null; + } + String msg; + if (realValue == null) { + msg = "Property value not found: " + ConvertTools.joinToString(keys, " or "); + } else { + msg = "Property value is blank, key=" + realKey; + } + throw new IllegalArgumentException(msg); + } + } + + private String replacePlaceholder(String s) { + if (s == null || s.indexOf('{') < 0 || s.indexOf('}') < 0) { + return s; + } + return StringTools.format(s, "address", dbAddress, "dbname", dbName, "schema", dbSchema); + } + + public String getDbType() { + return dbType; + } + + protected void setDbType(String dbType) { + this.dbType = dbType; + } + + public String getDbAddress() { + return dbAddress; + } + + protected void setDbAddress(String dbAddress) { + this.dbAddress = dbAddress; + } + + public String getDbName() { + return dbName; + } + + protected void setDbName(String dbName) { + this.dbName = dbName; + } + + public String getDbSchema() { + return dbSchema; + } + + protected void setDbSchema(String dbSchema) { + this.dbSchema = dbSchema; + } + + public String getUserName() { + return userName; + } + + protected void setUserName(String userName) { + this.userName = userName; + } + + public byte[] getPassword() { + return password; + } + + protected void setPassword(byte[] password) { + this.password = password; + } + } +} diff --git a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/AutoDruidDataSource.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoDruidDataSource.java similarity index 56% rename from jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/AutoDruidDataSource.java rename to jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoDruidDataSource.java index 5f675129a05739662a17a09c451396179231bbab..c4e0556300936992c835eb9b0b34fe20678df1ed 100644 --- a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/AutoDruidDataSource.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoDruidDataSource.java @@ -1,460 +1,307 @@ -package com.gitee.qdbp.jdbc.support; - -import java.net.URL; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import com.alibaba.druid.pool.DruidDataSource; -import com.alibaba.druid.support.logging.Log; -import com.alibaba.druid.support.logging.LogFactory; -import com.gitee.qdbp.able.exception.ServiceException; -import com.gitee.qdbp.jdbc.exception.DbErrorCode; -import com.gitee.qdbp.tools.crypto.GlobalCipherTools; -import com.gitee.qdbp.tools.files.PathTools; -import com.gitee.qdbp.tools.utils.ConvertTools; -import com.gitee.qdbp.tools.utils.PropertyTools; -import com.gitee.qdbp.tools.utils.StringTools; - -/** - * 简化配置的DruidDataSource, 切换数据库只需要修改一行配置(jdbc.xxx)
- * 配置文件应分为两部分, 一是需要开发评估才能决定修改的, 二是运维人员即可修改的
- * 关于数据库的配置, 用户名/密码/访问地址/数据库名是运维的事, 其他的协议类型/各种参数是开发的事
- * 运维人员的这部分配置文件应该尽量集中到一个文件中
- *
- * 密码支持两种配置方式, 第1种是明文密码, 第2种是加密密码
- * dbtype:username:password@address/dbname:schema // 明文密码
- * dbtype:username@address/dbname:schema?EncryptedPassword // 加密密码
- * 数据库密码加密: java -cp qdbp-able.jar com.gitee.qdbp.tools.crypto.GlobalCipherTools db password
- * config第1段是dbtype.subtype, subtype可以没有. 例如mysql, mysql.8, oracle, oracle.sid
- * 通过dbtype.subtype从properties之中自动查找jdbc.url/jdbc.params/jdbc.driver/jdbc.testquery
- * 查找时优先查找jdbc.url.dbtype.subtype, 如果没有再查找jdbc.url.dbtype, 最后查找jdbc.url
- *
-## MySQL
-jdbc.xxx = mysql:username:password@address/dbname
-## MySQL(8.0的参数不同)
-jdbc.xxx = mysql.8:username:password@address/dbname
-## MySQL(加密的密码放在最后)
-jdbc.xxx = mysql:username@address/dbname?EncryptedPassword
-## MariaDB
-# jdbc.xxx = mariadb:username:password@127.0.0.1:3306/dbname
-## Oracle-DbName
-# jdbc.xxx = oracle:username:password@127.0.0.1:1521/dbname
-## Oracle-SID
-# jdbc.xxx = oracle.sid@127.0.0.1:1521/DbSid
-## Oracle-TNS
-# jdbc.xxx = oracle.tns@~/TnsName
-## DB2 需要schema
-# jdbc.xxx = db2:username:password@127.0.0.1:50000/dbname:schema
-## H2 嵌入式(不需要ip:port)
-# jdbc.xxx = h2.embed:username:password@~/dbname
-## H2 TCP模式
-# jdbc.xxx = h2.tcp:username:password@127.0.0.1:8082/dbname
-## H2 内存模式(不需要username/password/ip:port)
-# jdbc.xxx = h2.mem@~/dbname
-## SqlServer
-# jdbc.xxx = sqlserver:username:password@127.0.0.1:1433/dbname
-## SqlServer 老版本
-# jdbc.xxx = sqlserver.2000:username:password@127.0.0.1:1433/dbname
-## PostgreSQL
-# jdbc.xxx = postgresql:username:password@127.0.0.1:5432/dbname:schema
-## SQLite 内存模式
-# jdbc.xxx = sqlite.mem@~/dbname
-## SQLite 文件模式(windows)
-# jdbc.xxx = sqlite.file@~/F:/sqlite/main.db
-## SQLite 文件模式(linux)
-# jdbc.xxx = sqlite.file@~/home/sqlite/main.db
-## SQLite 类路径中的文件
-# jdbc.xxx = sqlite.res@~/settings/sqlite/main.db
-
-<bean id="setting" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
-    <property  name="fileEncoding" value="UTF-8" />
-    <property name="locations">
-        <list>
-            <value>classpath:settings/jdbc/datasource.properties</value>
-            <value>classpath:settings/jdbc/qdbc.propertiess</value>
-            <value>classpath:setting.properties</value>
-        </list>
-    </property>
-</bean>
-<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
-    <property name="properties" ref="setting" />
-</bean>
-<bean class="com.gitee.qdbp.base.settings.druid.AutoDruidDataSource" init-method="init" destroy-method="close">
-    <property name="properties" ref="setting" />
-    <property name="config" value="${jdbc.xxx}" />
-    <property name="url" value="auto" />
-    <property name="driverClassName" value="auto" />
-    <property name="validationQuery" value="auto" />
-</bean>
- * 
- * - * @author zhaohuihua - * @version 20200328 - */ -public class AutoDruidDataSource extends DruidDataSource { - - /** 版本序列号 **/ - private static final long serialVersionUID = 1L; - // 这里使用DruidDataSource的日志路径吧 - private final static Log log = LogFactory.getLog(DruidDataSource.class); - - // jdbc:oracle:thin:@//192.168.1.218:1521/DbName - // jdbc:oracle:thin:192.168.1.218:1521:DbSid - // jdbc:oracle:thin:@TnsName - // jdbc:db2://192.168.1.218:60000/DbName - // jdbc:db2://192.168.1.218:60000/DbName:currentSchema=SchemaName; - // jdbc:h2:~/DbName // 嵌入式 - // jdbc:h2:mem:DbName // 内存数据库 - // jdbc:h2:tcp://192.168.1.218:8084/~/DbName - // jdbc:sqlserver://192.168.1.218:1433;DatabaseName=DbName - // jdbc:sqlite::memory: - // jdbc:sqlite://F:/sqlite/main.db - // jdbc:sqlite://home/sqlite/main.db - // jdbc:sqlite::resource:settings/sqlite/main.db - - // dbname可以是一个文件路径 - // 如 jdbc.xxx=sqlite.file@~/E:/sqlite/main.db - // 如 jdbc.xxx=sqlite.file@~/home/sqlite/main.db - /** 解析数据库配置的正则表达式 **/ - protected static final Pattern CONFIG = Pattern.compile( - // dbtype.subtype : username : password @ address / dbname : schema ?EncryptedPassword - "(\\w+?(?:\\.\\w+)?)(?:\\:([\\-\\.\\w]+))?(?:\\:(.+?))?@([^@/?]+)/((?:[a-zA-Z]\\:/)?[/\\-\\.\\w]+)(?:\\:(.+?))?(?:\\?(.+))?"); - - protected Properties properties; - protected String dbconfig; - protected String urlKey; - protected String dbtype; - protected String dbname; - protected String dbschema; - protected String dbaddress; - - /** - * 设置配置参数
- * 这个方法要在最前面调用, 因为配置信息中的参数优先级应低于直接通过setter方法设置的
- * 在最前面调用, 后面通过setter方法设置的参数就能覆盖配置信息中的参数
- * 这里面有2部分配置:
- * 1. DruidDataSource连接池配置信息, 这部分在setProperties()时加载;
- * 2. AutoDruidDataSource新增的, 与数据库类型相关的配置信息, 这部分在init()方法中通过findPropertyUseSuffix()调用
- * - * @param properties 配置信息 - */ - public void setProperties(Properties properties) { - // 查找默认配置文件 - Properties defaults = loadDefaultProperties(); - if (properties != null) { - defaults.putAll(properties); - } - this.properties = defaults; - - // 加载配置信息中的连接池信息 - Properties druidProperties = PropertyTools.filter(properties, false, "druid"); - String config = druidProperties.getProperty("druid.config"); - if (config != null) { - this.setConfig(config); - } - druidProperties.remove("druid.config"); - druidProperties.remove("druid.url"); - druidProperties.remove("druid.jdbcUrl"); - druidProperties.remove("druid.driverClassName"); - druidProperties.remove("druid.validationQuery"); - druidProperties.remove("druid.username"); - druidProperties.remove("druid.password"); - this.configFromPropety(druidProperties); - } - - protected Properties loadDefaultProperties() { - // 查找默认配置文件 - // 1. {classpath}/settings/jdbc/druid.auto.properties
- // 2. qdbc-jdbc-spring.jar!/settings/jdbc/druid.auto.properties
- URL path = PathTools.findResource("settings/jdbc/druid.auto.properties", AutoDruidDataSource.class); - return PropertyTools.load(path); - } - - /** 获取URL在Properties中的key **/ - public String getUrlKey(String urlKey) { - return this.urlKey; - } - - /** - * 设置URL在Properties中的key - * - * @param urlKey URL在Properties中的key
- * 如 properties中配置了jdbc.sys=mysql:username:password@127.0.0.1:3306/dbname
- * 则 urlKey应为jdbc.sys - * @since 3.1.1 - */ - public void setUrlKey(String urlKey) { - if (inited) { - throw new UnsupportedOperationException("Initialization is complete"); - } - this.urlKey = urlKey; - } - - /** - * 配置JDBC用户名密码连接地址数据库名称
- * 密码支持两种配置方式, 第1种是明文密码, 第2种是加密密码
- * dbtype:username:password@address/dbname:schema // 明文密码
- * dbtype:username@address/dbname:schema?EncryptedPassword // 加密密码
- * - * @param config - */ - public void setConfig(String config) { - if (inited) { - throw new UnsupportedOperationException("Initialization is complete"); - } - this.dbconfig = config; - - // 解析配置参数 - Matcher matcher = CONFIG.matcher(config.trim()); - if (!matcher.matches()) { - String one = "dbtype:username:password@address/dbname"; - String two = "dbtype:username@address/dbname?EncryptedPassword"; - String msg = "Config string format error --> " + config + "\nformat <1> " + one + " <2> " + two; - throw new IllegalArgumentException(msg); - } - - // dbtype:username:password@address/dbname:schema?EncryptedPassword - int i = 1; - dbtype = matcher.group(i++); - String username = matcher.group(i++); - String password = matcher.group(i++); - dbaddress = matcher.group(i++); - dbname = matcher.group(i++); - dbschema = matcher.group(i++); - String encrypted = matcher.group(i++); - if (password != null && encrypted != null) { - String desc = "password and EncryptedPassword coexistence is not allowed."; - throw new IllegalArgumentException("Config string format error. " + desc); - } - - super.setUsername(username); - if (encrypted != null) { - super.setPassword(GlobalCipherTools.decrypt("db", encrypted)); - } else { - super.setPassword(password); - } - } - - @Override - public void setUrl(String url) { - if (!"auto".equalsIgnoreCase(url)) { - log.warn("Method[setUrl] not supported, we will auto find content from the properties"); - } - } - - @Override - public void setUsername(String username) { - if (!"auto".equalsIgnoreCase(username)) { - log.warn("Method[setUsername] not supported, we will auto parse from the field of 'config'"); - } - } - - @Override - public void setPassword(String password) { - if (!"auto".equalsIgnoreCase(password)) { - log.warn("Method[setPassword] not supported, we will auto parse from the field of 'config'"); - } - } - - @Override - public void setDriverClassName(String driverClassName) { - if (!"auto".equalsIgnoreCase(driverClassName)) { - log.warn("Method[setDriverClassName] not supported, we will auto find content from the properties"); - } - } - - @Override - public void setValidationQuery(String validationQuery) { - if (!"auto".equalsIgnoreCase(validationQuery)) { - log.warn("Method[setValidationQuery] not supported, we will auto find content from the properties"); - } - } - - // 初始化 - public void init() throws SQLException { - if (inited) { - return; - } - this.initProperty(); - super.init(); - - String msg = "{dataSource-" + this.getID(); - if (this.name != null && !this.name.isEmpty()) { - msg += "," + this.name; - } - msg += "} " + this.getUrl(); - log.info(msg); - } - - protected void initProperty() { - if (properties == null) { - properties = loadDefaultProperties(); - } - if (dbconfig == null || dbconfig.trim().length() == 0) { - if (urlKey != null) { - String value = properties.getProperty(urlKey); - this.setConfig(value); - } - } - if (dbconfig == null || dbconfig.trim().length() == 0) { - throw new IllegalArgumentException("Missing argument for dbconfig"); - } - // 根据配置自动推断url/params/driver - String url = findPropertyUseSuffix("jdbc.url", true, true); - String params = findPropertyUseSuffix("jdbc.params", true, false); - String driver = findPropertyUseSuffix("jdbc.driver", false, false); - if (params != null && params.length() > 0) { - super.setUrl(url + params); - } else { - super.setUrl(url); - } - if (driver != null && driver.length() > 0) { - super.setDriverClassName(driver); - } - - // 根据配置自动推断ValidationQuery - String validationQuery = findPropertyUseSuffix("jdbc.testquery", false, false); - super.setValidationQuery(validationQuery); - - // 处理特殊的Driver - resolveSpecialDriver(); - } - - /** - * 根据dbtype从配置项中查找配置内容
- * 如key = jdbc.url, suffixes=x.y.z
- * 取值顺序为: jdbc.url.x.y.z - jdbc.url.x.y - jdbc.url.x - jdbc.url - * - * @param key KEY前缀 - * @param replacePlaceholder 是否替换占位符 - * @param throwOnNotFound 未找到配置时是否报错 - * @return 配置内容 - */ - protected String findPropertyUseSuffix(String key, boolean replacePlaceholder, boolean throwOnNotFound) { - String suffixes = this.dbtype; - char dot = '.'; - List keys = new ArrayList<>(); - keys.add(key + dot + suffixes); - int index = suffixes.length(); - while (true) { - index = suffixes.lastIndexOf(dot, index - 1); - if (index <= 0) { - break; - } - keys.add(key + dot + suffixes.substring(0, index)); - } - keys.add(key); - - String realKey = null; - String realValue = null; - for (String k : keys) { - String value = PropertyTools.getString(properties, k, false); - if (value == null) { - continue; - } else { - realKey = k; - realValue = value; - break; - } - } - if (realValue != null && realValue.length() > 0) { - String result = replacePlaceholder ? replacePlaceholder(realValue) : realValue; - if (log.isDebugEnabled()) { - log.debug("Resolved property value: " + realKey + " = " + result); - } - return result; - } else { - if (!throwOnNotFound) { - return null; - } - String msg; - if (realValue == null) { - msg = "Property value not found: " + ConvertTools.joinToString(keys, " or "); - } else { - msg = "Property value is blank, key=" + realKey; - } - throw new IllegalArgumentException(msg); - } - } - - private String replacePlaceholder(String s) { - if (s == null || s.indexOf('{') < 0 || s.indexOf('}') < 0) { - return s; - } - return StringTools.format(s, "address", dbaddress, "dbname", dbname, "schema", dbschema); - } - - /** 推断Driver(1.1.22以上版本不需要了,已贡献到主版本) **/ - // https://github.com/alibaba/druid/pull/3698 - protected void resolveSpecialDriver() { - String driverClass = this.getDriverClassName(); - String jdbcUrl = this.getUrl(); - if ((driverClass == null || driverClass.length() == 0) && jdbcUrl != null && jdbcUrl.startsWith("jdbc:db2")) { - // 优化DB2 Driver的推断逻辑, 参考自 https://www.cnblogs.com/lidg/articles/3588566.html - // Resolve the DB2 driver from JDBC URL - // Type2 COM.ibm.db2.jdbc.app.DB2Driver, url = jdbc:db2:databasename - // Type3 COM.ibm.db2.jdbc.net.DB2Driver, url = jdbc:db2:ServerIP:6789:databasename - // Type4 8.1+ com.ibm.db2.jcc.DB2Driver, url = jdbc:db2://ServerIP:50000/databasename - String prefix = "jdbc:db2:"; - if (jdbcUrl.startsWith(prefix + "//")) { // Type4 - super.setDriverClassName("com.ibm.db2.jcc.DB2Driver"); - } else { - String suffix = jdbcUrl.substring(prefix.length()); - if (suffix.indexOf(':') > 0) { // Type3 - super.setDriverClassName("COM.ibm.db2.jdbc.net.DB2Driver"); - } else { // Type2 - super.setDriverClassName("COM.ibm.db2.jdbc.app.DB2Driver"); - } - } - } - } - - /** - * 根据单行url参数构造默认的数据源
- * 只能自定义username,password,address, 其他参数都使用默认值 - * - * @param jdbcUrl 数据库连接地址单行url参数
- * 如 mysql:username:password@127.0.0.1:3306/dbname
- * 如 oracle:username:password@127.0.0.1:1521/orcl
- * 如 db2:username:password@127.0.0.1:50000/dbname:schema
- * @return 数据源 - * @since 3.1.1 - */ - public static AutoDruidDataSource buildWith(String jdbcUrl) { - AutoDruidDataSource datasource = new AutoDruidDataSource(); - datasource.setConfig(jdbcUrl); - try { - datasource.init(); - return datasource; - } catch (SQLException e) { - datasource.close(); - throw new ServiceException(DbErrorCode.DB_DATA_SOURCE_INIT_ERROR, e); - } - } - - /** - * 根据配置信息构造数据源 - * - * @param properties 配置信息 - * @param urlKey URL在Properties中的key
- * 如 properties中配置了jdbc.sys=mysql:username:password@127.0.0.1:3306/dbname
- * 则 urlKey应为jdbc.sys - * @return 数据源 - * @since 3.1.1 - */ - public static AutoDruidDataSource buildWith(Properties properties, String urlKey) { - AutoDruidDataSource datasource = new AutoDruidDataSource(); - datasource.setProperties(properties); - datasource.setUrlKey(urlKey); - try { - datasource.init(); - return datasource; - } catch (SQLException e) { - datasource.close(); - throw new ServiceException(DbErrorCode.DB_DATA_SOURCE_INIT_ERROR, e); - } - } -} +package com.gitee.qdbp.jdbc.support; + +import java.sql.SQLException; +import java.util.Properties; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.support.AutoDataSourceTools.DataSourceConfig; +import com.gitee.qdbp.tools.utils.PropertyTools; + +/** + * 简化配置的DruidDataSource, 切换数据库只需要修改一行配置(jdbc.xxx)
+ * 配置文件应分为两部分, 一是需要开发评估才能决定修改的, 二是运维人员即可修改的
+ * 关于数据库的配置, 用户名/密码/访问地址/数据库名是运维的事, 其他的协议类型/各种参数是开发的事
+ * 运维人员的这部分配置文件应该尽量集中到一个文件中
+ *
+ * 密码支持两种配置方式, 第1种是明文密码, 第2种是加密密码
+ * dbtype:username:password@address/dbname:schema // 明文密码
+ * dbtype:username@address/dbname:schema?EncryptedPassword // 加密密码
+ * 数据库密码加密: java -cp qdbp-able.jar com.gitee.qdbp.tools.crypto.GlobalCipherTools db password
+ * config第1段是dbtype.subtype, subtype可以没有. 例如mysql, mysql.8, oracle, oracle.sid
+ * 通过dbtype.subtype从properties之中自动查找jdbc.url/jdbc.params/jdbc.driver/jdbc.testquery
+ * 查找时优先查找jdbc.url.dbtype.subtype, 如果没有再查找jdbc.url.dbtype, 最后查找jdbc.url
+ *
+## MySQL
+jdbc.xxx = mysql:username:password@address/dbname
+## MySQL(8.0的参数不同)
+jdbc.xxx = mysql.8:username:password@address/dbname
+## MySQL(加密的密码放在最后)
+jdbc.xxx = mysql:username@address/dbname?EncryptedPassword
+## MariaDB
+# jdbc.xxx = mariadb:username:password@127.0.0.1:3306/dbname
+## Oracle-DbName
+# jdbc.xxx = oracle:username:password@127.0.0.1:1521/dbname
+## Oracle-SID
+# jdbc.xxx = oracle.sid@127.0.0.1:1521/DbSid
+## Oracle-TNS
+# jdbc.xxx = oracle.tns@~/TnsName
+## DB2 需要schema
+# jdbc.xxx = db2:username:password@127.0.0.1:50000/dbname:schema
+## H2 嵌入式(不需要ip:port)
+# jdbc.xxx = h2.embed:username:password@~/dbname
+## H2 TCP模式
+# jdbc.xxx = h2.tcp:username:password@127.0.0.1:8082/dbname
+## H2 内存模式(不需要username/password/ip:port)
+# jdbc.xxx = h2.mem@~/dbname
+## SqlServer
+# jdbc.xxx = sqlserver:username:password@127.0.0.1:1433/dbname
+## SqlServer 老版本
+# jdbc.xxx = sqlserver.2000:username:password@127.0.0.1:1433/dbname
+## PostgreSQL
+# jdbc.xxx = postgresql:username:password@127.0.0.1:5432/dbname:schema
+## SQLite 内存模式
+# jdbc.xxx = sqlite.mem@~/dbname
+## SQLite 文件模式(windows)
+# jdbc.xxx = sqlite.file@~/F:/sqlite/main.db
+## SQLite 文件模式(linux)
+# jdbc.xxx = sqlite.file@~/home/sqlite/main.db
+## SQLite 类路径中的文件
+# jdbc.xxx = sqlite.res@~/settings/sqlite/main.db
+
+<bean id="setting" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+    <property  name="fileEncoding" value="UTF-8" />
+    <property name="locations">
+        <list>
+            <value>classpath:settings/jdbc/datasource.properties</value>
+            <value>classpath:settings/jdbc/qdbc.propertiess</value>
+            <value>classpath:setting.properties</value>
+        </list>
+    </property>
+</bean>
+<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
+    <property name="properties" ref="setting" />
+</bean>
+<bean class="com.gitee.qdbp.base.settings.druid.AutoDruidDataSource" init-method="init" destroy-method="close">
+    <property name="properties" ref="setting" />
+    <property name="config" value="${jdbc.xxx}" />
+    <property name="url" value="auto" />
+    <property name="driverClassName" value="auto" />
+    <property name="validationQuery" value="auto" />
+</bean>
+ * 
+ * + * @author zhaohuihua + * @version 20200328 + */ +public class AutoDruidDataSource extends DruidDataSource { + + /** 版本序列号 **/ + private static final long serialVersionUID = 1L; + // 这里使用DruidDataSource的日志路径吧 + private final static Log log = LogFactory.getLog(DruidDataSource.class); + + protected Properties properties; + protected String dbconfig; + protected String urlKey; + + /** + * 设置配置参数
+ * 这个方法要在最前面调用, 因为配置信息中的参数优先级应低于直接通过setter方法设置的
+ * 在最前面调用, 后面通过setter方法设置的参数就能覆盖配置信息中的参数
+ * 这里面有2部分配置:
+ * 1. DruidDataSource连接池配置信息, 这部分在setProperties()时加载;
+ * 2. AutoDruidDataSource新增的, 与数据库类型相关的配置信息, 这部分在init()方法中通过findPropertyUseSuffix()调用
+ * + * @param properties 配置信息 + */ + public void setProperties(Properties properties) { + // 查找默认配置文件 + Properties defaults = AutoDataSourceTools.loadDefaultProperties(); + if (properties != null) { + defaults.putAll(properties); + } + this.properties = defaults; + + // 加载配置信息中的连接池信息 + Properties druidProperties = PropertyTools.filter(properties, false, "druid"); + String config = druidProperties.getProperty("druid.config"); + if (config != null) { + this.setConfig(config); + } + druidProperties.remove("druid.config"); + druidProperties.remove("druid.url"); + druidProperties.remove("druid.jdbcUrl"); + druidProperties.remove("druid.driverClassName"); + druidProperties.remove("druid.validationQuery"); + druidProperties.remove("druid.username"); + druidProperties.remove("druid.password"); + this.configFromPropety(druidProperties); + } + + /** 获取URL在Properties中的key **/ + public String getUrlKey(String urlKey) { + return this.urlKey; + } + + /** + * 设置URL在Properties中的key + * + * @param urlKey URL在Properties中的key
+ * 如 properties中配置了jdbc.sys=mysql:username:password@127.0.0.1:3306/dbname
+ * 则 urlKey应为jdbc.sys + * @since 3.1.1 + */ + public void setUrlKey(String urlKey) { + if (inited) { + throw new UnsupportedOperationException("Initialization is complete"); + } + this.urlKey = urlKey; + } + + /** + * 配置JDBC用户名密码连接地址数据库名称
+ * 密码支持两种配置方式, 第1种是明文密码, 第2种是加密密码
+ * dbtype:username:password@address/dbname:schema // 明文密码
+ * dbtype:username@address/dbname:schema?EncryptedPassword // 加密密码
+ * + * @param config + */ + public void setConfig(String config) { + if (inited) { + throw new UnsupportedOperationException("Initialization is complete"); + } + this.dbconfig = config; + } + + @Override + public void setUrl(String url) { + if (!"auto".equalsIgnoreCase(url)) { + log.warn("Method[setUrl] not supported, we will auto find content from the properties"); + } + } + + @Override + public void setUsername(String username) { + if (!"auto".equalsIgnoreCase(username)) { + log.warn("Method[setUsername] not supported, we will auto parse from the field of 'config'"); + } + } + + @Override + public void setPassword(String password) { + if (!"auto".equalsIgnoreCase(password)) { + log.warn("Method[setPassword] not supported, we will auto parse from the field of 'config'"); + } + } + + @Override + public void setDriverClassName(String driverClassName) { + if (!"auto".equalsIgnoreCase(driverClassName)) { + log.warn("Method[setDriverClassName] not supported, we will auto find content from the properties"); + } + } + + @Override + public void setValidationQuery(String validationQuery) { + if (!"auto".equalsIgnoreCase(validationQuery)) { + log.warn("Method[setValidationQuery] not supported, we will auto find content from the properties"); + } + } + + // 初始化 + public void init() throws SQLException { + if (inited) { + return; + } + this.autoConfig(); + super.init(); + + String msg = "{dataSource-" + this.getID(); + if (this.name != null && !this.name.isEmpty()) { + msg += "," + this.name; + } + msg += "} " + this.getUrl(); + log.info(msg); + } + + protected void autoConfig() { + Properties properties = this.properties; + if (properties == null) { + properties = AutoDataSourceTools.loadDefaultProperties(); + } + String configString = this.dbconfig; + if (configString == null && this.urlKey != null) { + configString = properties.getProperty(this.urlKey); + } + if (configString == null) { + throw new IllegalArgumentException("Missing argument for configString"); + } + // 解析configString + DataSourceConfig config = AutoDataSourceTools.parseConfigString(properties, configString); + super.setUsername(config.getUserName()); + if (config.getPassword() != null) { + super.setPassword(new String(config.getPassword())); + } + // 根据配置自动推断url/params/driver + String jdbcUrl = config.findPropertyUseSuffix("jdbc.url", true, true); + String params = config.findPropertyUseSuffix("jdbc.params", true, false); + String driver = config.findPropertyUseSuffix("jdbc.driver", false, false); + if (params != null && params.length() > 0) { + super.setUrl(jdbcUrl + params); + } else { + super.setUrl(jdbcUrl); + } + if (driver != null && driver.length() > 0) { + super.setDriverClassName(driver); + } else { + // 处理特殊的Driver + String newDriver = AutoDataSourceTools.resolveSpecialDriver(jdbcUrl); + if (newDriver != null && newDriver.length() > 0) { + super.setDriverClassName(newDriver); + } + } + + // 根据配置自动推断ValidationQuery + String validationQuery = config.findPropertyUseSuffix("jdbc.testquery", false, false); + super.setValidationQuery(validationQuery); + } + + /** + * 根据单行url参数构造默认的数据源
+ * 只能自定义username,password,address, 其他参数都使用默认值 + * + * @param jdbcUrl 数据库连接地址单行url参数
+ * 如 mysql:username:password@127.0.0.1:3306/dbname
+ * 如 oracle:username:password@127.0.0.1:1521/orcl
+ * 如 db2:username:password@127.0.0.1:50000/dbname:schema
+ * @return 数据源 + * @since 3.1.1 + */ + public static AutoDruidDataSource buildWith(String jdbcUrl) { + AutoDruidDataSource datasource = new AutoDruidDataSource(); + datasource.setConfig(jdbcUrl); + try { + datasource.init(); + return datasource; + } catch (SQLException e) { + datasource.close(); + throw new ServiceException(DbErrorCode.DB_DATA_SOURCE_INIT_ERROR, e); + } + } + + /** + * 根据配置信息构造数据源 + * + * @param properties 配置信息 + * @param urlKey URL在Properties中的key
+ * 如 properties中配置了jdbc.sys=mysql:username:password@127.0.0.1:3306/dbname
+ * 则 urlKey应为jdbc.sys + * @return 数据源 + * @since 3.1.1 + */ + public static AutoDruidDataSource buildWith(Properties properties, String urlKey) { + AutoDruidDataSource datasource = new AutoDruidDataSource(); + datasource.setProperties(properties); + datasource.setUrlKey(urlKey); + try { + datasource.init(); + return datasource; + } catch (SQLException e) { + datasource.close(); + throw new ServiceException(DbErrorCode.DB_DATA_SOURCE_INIT_ERROR, e); + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoHikariDataSource.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoHikariDataSource.java new file mode 100644 index 0000000000000000000000000000000000000000..b089f8ecfe1fff313c8b98a75073ef71a72a8aca --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/AutoHikariDataSource.java @@ -0,0 +1,272 @@ +package com.gitee.qdbp.jdbc.support; + +import java.util.Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.exception.DbErrorCode; +import com.gitee.qdbp.jdbc.support.AutoDataSourceTools.DataSourceConfig; +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +/** + * 简化配置的DruidDataSource, 切换数据库只需要修改一行配置(jdbc.xxx)
+ * 配置文件应分为两部分, 一是需要开发评估才能决定修改的, 二是运维人员即可修改的
+ * 关于数据库的配置, 用户名/密码/访问地址/数据库名是运维的事, 其他的协议类型/各种参数是开发的事
+ * 运维人员的这部分配置文件应该尽量集中到一个文件中
+ *
+ * 密码支持两种配置方式, 第1种是明文密码, 第2种是加密密码
+ * dbtype:username:password@address/dbname:schema // 明文密码
+ * dbtype:username@address/dbname:schema?EncryptedPassword // 加密密码
+ * 数据库密码加密: java -cp qdbp-able.jar com.gitee.qdbp.tools.crypto.GlobalCipherTools db password
+ * config第1段是dbtype.subtype, subtype可以没有. 例如mysql, mysql.8, oracle, oracle.sid
+ * 通过dbtype.subtype从properties之中自动查找jdbc.url/jdbc.params/jdbc.driver/jdbc.testquery
+ * 查找时优先查找jdbc.url.dbtype.subtype, 如果没有再查找jdbc.url.dbtype, 最后查找jdbc.url
+ *
+## MySQL
+jdbc.xxx = mysql:username:password@address/dbname
+## MySQL(8.0的参数不同)
+jdbc.xxx = mysql.8:username:password@address/dbname
+## MySQL(加密的密码放在最后)
+jdbc.xxx = mysql:username@address/dbname?EncryptedPassword
+## MariaDB
+# jdbc.xxx = mariadb:username:password@127.0.0.1:3306/dbname
+## Oracle-DbName
+# jdbc.xxx = oracle:username:password@127.0.0.1:1521/dbname
+## Oracle-SID
+# jdbc.xxx = oracle.sid@127.0.0.1:1521/DbSid
+## Oracle-TNS
+# jdbc.xxx = oracle.tns@~/TnsName
+## DB2 需要schema
+# jdbc.xxx = db2:username:password@127.0.0.1:50000/dbname:schema
+## H2 嵌入式(不需要ip:port)
+# jdbc.xxx = h2.embed:username:password@~/dbname
+## H2 TCP模式
+# jdbc.xxx = h2.tcp:username:password@127.0.0.1:8082/dbname
+## H2 内存模式(不需要username/password/ip:port)
+# jdbc.xxx = h2.mem@~/dbname
+## SqlServer
+# jdbc.xxx = sqlserver:username:password@127.0.0.1:1433/dbname
+## SqlServer 老版本
+# jdbc.xxx = sqlserver.2000:username:password@127.0.0.1:1433/dbname
+## PostgreSQL
+# jdbc.xxx = postgresql:username:password@127.0.0.1:5432/dbname:schema
+## SQLite 内存模式
+# jdbc.xxx = sqlite.mem@~/dbname
+## SQLite 文件模式(windows)
+# jdbc.xxx = sqlite.file@~/F:/sqlite/main.db
+## SQLite 文件模式(linux)
+# jdbc.xxx = sqlite.file@~/home/sqlite/main.db
+## SQLite 类路径中的文件
+# jdbc.xxx = sqlite.res@~/settings/sqlite/main.db
+
+<bean id="setting" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
+    <property  name="fileEncoding" value="UTF-8" />
+    <property name="locations">
+        <list>
+            <value>classpath:settings/jdbc/datasource.properties</value>
+            <value>classpath:settings/jdbc/qdbc.propertiess</value>
+            <value>classpath:setting.properties</value>
+        </list>
+    </property>
+</bean>
+<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
+    <property name="properties" ref="setting" />
+</bean>
+<bean class="com.gitee.qdbp.jdbc.support.AutoHikariDataSource" destroy-method="close">
+    <property name="properties" ref="setting" />
+    <property name="config" value="${jdbc.xxx}" />
+    <property name="url" value="auto" />
+    <property name="driverClassName" value="auto" />
+    <property name="validationQuery" value="auto" />
+</bean>
+ * 
+ * + * @author zhaohuihua + * @version 20210503 + */ +public class AutoHikariDataSource extends HikariDataSource { + + private static Logger log = LoggerFactory.getLogger(AutoHikariDataSource.class); + + private Properties properties; + private String dbconfig; + private String urlKey; + + public AutoHikariDataSource() { + super(); + } + + public AutoHikariDataSource(HikariConfig configuration) { + super(configuration); + } + + @Override + public void validate() { + this.autoConfig(); + super.validate(); + } + + protected void autoConfig() { + Properties properties = this.properties; + if (properties == null) { + properties = AutoDataSourceTools.loadDefaultProperties(); + } + String configString = this.dbconfig; + if (configString == null && this.urlKey != null) { + configString = properties.getProperty(this.urlKey); + } + if (configString == null) { + throw new IllegalArgumentException("Missing argument for configString"); + } + // 解析configString + DataSourceConfig config = AutoDataSourceTools.parseConfigString(properties, configString); + super.setUsername(config.getUserName()); + if (config.getPassword() != null) { + super.setPassword(new String(config.getPassword())); + } + // 根据配置自动推断jdbcUrl/params/driver + String jdbcUrl = config.findPropertyUseSuffix("jdbc.url", true, true); + String params = config.findPropertyUseSuffix("jdbc.params", true, false); + String driver = config.findPropertyUseSuffix("jdbc.driver", false, false); + if (params != null && params.length() > 0) { + super.setJdbcUrl(jdbcUrl + params); + } else { + super.setJdbcUrl(jdbcUrl); + } + if (driver != null && driver.length() > 0) { + super.setDriverClassName(driver); + } else { + // 处理特殊的Driver + String newDriver = AutoDataSourceTools.resolveSpecialDriver(jdbcUrl); + if (newDriver != null && newDriver.length() > 0) { + super.setDriverClassName(newDriver); + } + } + + // 根据配置自动推断ValidationQuery + String validationQuery = config.findPropertyUseSuffix("jdbc.testquery", false, false); + super.setConnectionTestQuery(validationQuery); + } + + public Properties getProperties() { + return properties; + } + + public void setProperties(Properties properties) { + // 查找默认配置文件 + Properties defaults = AutoDataSourceTools.loadDefaultProperties(); + if (properties != null) { + defaults.putAll(properties); + } + this.properties = defaults; + } + + /** + * 配置JDBC用户名密码连接地址数据库名称
+ * 密码支持两种配置方式, 第1种是明文密码, 第2种是加密密码
+ * dbtype:username:password@address/dbname:schema // 明文密码
+ * dbtype:username@address/dbname:schema?EncryptedPassword // 加密密码
+ * + * @param config JDBC配置字符串 + */ + public void setConfig(String config) { + this.dbconfig = config; + } + + /** JDBC配置字符串 **/ + public String getConfig() { + return dbconfig; + } + + /** JDBC配置字符串在Properties中的key **/ + public String getUrlKey() { + return urlKey; + } + + /** JDBC配置字符串在Properties中的key **/ + public void setUrlKey(String urlKey) { + this.urlKey = urlKey; + } + + @Override + public void setJdbcUrl(String url) { + if (!"auto".equalsIgnoreCase(url)) { + log.warn("Method[setJdbcUrl] not supported, we will auto find content from the properties"); + } + } + + @Override + public void setUsername(String username) { + if (!"auto".equalsIgnoreCase(username)) { + log.warn("Method[setUsername] not supported, we will auto parse from the field of 'config'"); + } + } + + @Override + public void setPassword(String password) { + if (!"auto".equalsIgnoreCase(password)) { + log.warn("Method[setPassword] not supported, we will auto parse from the field of 'config'"); + } + } + + @Override + public void setDriverClassName(String driverClassName) { + if (!"auto".equalsIgnoreCase(driverClassName)) { + log.warn("Method[setDriverClassName] not supported, we will auto find content from the properties"); + } + } + + @Override + public void setConnectionTestQuery(String connectionTestQuery) { + if (!"auto".equalsIgnoreCase(connectionTestQuery)) { + log.warn("Method[setConnectionTestQuery] not supported, we will auto find content from the properties"); + } + } + + /** + * 根据单行url参数构造默认的数据源
+ * 只能自定义username,password,address, 其他参数都使用默认值 + * + * @param jdbcUrl 数据库连接地址单行url参数
+ * 如 mysql:username:password@127.0.0.1:3306/dbname
+ * 如 oracle:username:password@127.0.0.1:1521/orcl
+ * 如 db2:username:password@127.0.0.1:50000/dbname:schema
+ * @return 数据源 + * @since 3.4.0 + */ + public static AutoHikariDataSource buildWith(String jdbcUrl) { + AutoHikariDataSource datasource = new AutoHikariDataSource(); + datasource.setConfig(jdbcUrl); + try { + datasource.validate(); + return datasource; + } catch (Exception e) { + datasource.close(); + throw new ServiceException(DbErrorCode.DB_DATA_SOURCE_INIT_ERROR, e); + } + } + + /** + * 根据配置信息构造数据源 + * + * @param properties 配置信息 + * @param urlKey URL在Properties中的key
+ * 如 properties中配置了jdbc.sys=mysql:username:password@127.0.0.1:3306/dbname
+ * 则 urlKey应为jdbc.sys + * @return 数据源 + * @since 3.4.0 + */ + public static AutoHikariDataSource buildWith(Properties properties, String urlKey) { + AutoHikariDataSource datasource = new AutoHikariDataSource(); + datasource.setProperties(properties); + datasource.setUrlKey(urlKey); + try { + datasource.validate(); + return datasource; + } catch (Exception e) { + datasource.close(); + throw new ServiceException(DbErrorCode.DB_DATA_SOURCE_INIT_ERROR, e); + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/NamedParameterQdbcTemplate.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/NamedParameterQdbcTemplate.java new file mode 100644 index 0000000000000000000000000000000000000000..b01157565ecc6752b31f1bf1684007a8b2e0aa8a --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/NamedParameterQdbcTemplate.java @@ -0,0 +1,363 @@ +package com.gitee.qdbp.jdbc.support; + +import java.util.List; +import java.util.Map; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcOperations; +import org.springframework.jdbc.core.PreparedStatementCallback; +import org.springframework.jdbc.core.ResultSetExtractor; +import org.springframework.jdbc.core.RowCallbackHandler; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; +import org.springframework.jdbc.core.namedparam.NamedParameterTools; +import org.springframework.jdbc.core.namedparam.SqlParameterSource; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.jdbc.support.rowset.SqlRowSet; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.api.SqlDao; +import com.gitee.qdbp.jdbc.biz.SqlBufferJdbcTemplate; +import com.gitee.qdbp.jdbc.model.DbVersion; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.result.RowToMapMapper; +import com.gitee.qdbp.jdbc.result.SimpleRowToMapMapper; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * NamedParameterJdbcTemplate的代理类
+ * 主要用于旧项目改造, 同时使用SqlBufferJdbcOperations和NamedParameterJdbcOperations会导致日志输出两次
+ * SqlBufferJdbcTemplate输出的是可执行SQL, JdbcTemplate输出的是带?的SQL
+ * 因此: 先关闭JdbcTemplate的日志;
+ * 再使用NamedParameterQdbcTemplate替换NamedParameterJdbcTemplate, 将SQL转换为SqlBuffer执行 + * + * @author zhaohuihua + * @version 20201208 + */ +public class NamedParameterQdbcTemplate implements NamedParameterJdbcOperations { + + private SqlBufferJdbcTemplate jdbc; + + public NamedParameterQdbcTemplate(SqlBufferJdbcTemplate jdbc) { + this(jdbc, new SimpleRowToMapMapper()); + } + + public NamedParameterQdbcTemplate(SqlBufferJdbcTemplate jdbc, RowToMapMapper rowToMapMapper) { + VerifyTools.requireNonNull(jdbc, "jdbc"); + VerifyTools.requireNonNull(rowToMapMapper, "rowToMapMapper"); + this.jdbc = new MapSqlBufferJdbcTemplate(jdbc, rowToMapMapper); + } + + @Override + public JdbcOperations getJdbcOperations() { + return this.jdbc.getJdbcOperations(); + } + + @Override + public T execute(String sql, SqlParameterSource paramSource, PreparedStatementCallback action) + throws DataAccessException { + VerifyTools.requireNotBlank(sql, "sql"); + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + T result = this.jdbc.execute(sb, action); + return requiredSingleResult(result); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public T execute(String sql, Map paramMap, PreparedStatementCallback action) + throws DataAccessException { + return this.execute(sql, new MapSqlParameterSource(paramMap), action); + } + + @Override + public T execute(String sql, PreparedStatementCallback action) throws DataAccessException { + return this.execute(sql, EmptySqlParameterSource.INSTANCE, action); + } + + @Override + public T query(String sql, SqlParameterSource paramSource, ResultSetExtractor rse) + throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + T result = this.jdbc.query(sb, rse); + return requiredSingleResult(result); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public T query(String sql, Map paramMap, ResultSetExtractor rse) throws DataAccessException { + return this.query(sql, new MapSqlParameterSource(paramMap), rse); + } + + @Override + public T query(String sql, ResultSetExtractor rse) throws DataAccessException { + return this.query(sql, EmptySqlParameterSource.INSTANCE, rse); + } + + @Override + public void query(String sql, SqlParameterSource paramSource, RowCallbackHandler rch) throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + this.jdbc.query(sb, rch); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public void query(String sql, Map paramMap, RowCallbackHandler rch) throws DataAccessException { + this.query(sql, new MapSqlParameterSource(paramMap), rch); + } + + @Override + public void query(String sql, RowCallbackHandler rch) throws DataAccessException { + this.query(sql, EmptySqlParameterSource.INSTANCE, rch); + } + + @Override + public List query(String sql, SqlParameterSource paramSource, RowMapper rowMapper) + throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + return this.jdbc.query(sb, rowMapper); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public List query(String sql, Map paramMap, RowMapper rowMapper) throws DataAccessException { + return this.query(sql, new MapSqlParameterSource(paramMap), rowMapper); + } + + @Override + public List query(String sql, RowMapper rowMapper) throws DataAccessException { + return this.query(sql, EmptySqlParameterSource.INSTANCE, rowMapper); + } + + @Override + public T queryForObject(String sql, SqlParameterSource paramSource, RowMapper rowMapper) + throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + T result = this.jdbc.queryForObject(sb, rowMapper); + return requiredSingleResult(result); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public T queryForObject(String sql, Map paramMap, RowMapper rowMapper) + throws DataAccessException { + return this.queryForObject(sql, new MapSqlParameterSource(paramMap), rowMapper); + } + + @Override + public T queryForObject(String sql, SqlParameterSource paramSource, Class requiredType) + throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + T result = this.jdbc.queryForObject(sb, requiredType); + return requiredSingleResult(result); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public T queryForObject(String sql, Map paramMap, Class requiredType) throws DataAccessException { + return this.queryForObject(sql, new MapSqlParameterSource(paramMap), requiredType); + } + + @Override + public Map queryForMap(String sql, SqlParameterSource paramSource) throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + Map result = this.jdbc.queryForMap(sb); + return requiredSingleResult(result); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public Map queryForMap(String sql, Map paramMap) throws DataAccessException { + return this.queryForMap(sql, new MapSqlParameterSource(paramMap)); + } + + @Override + public List queryForList(String sql, SqlParameterSource paramSource, Class elementType) + throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + return this.jdbc.queryForList(sb, elementType); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public List queryForList(String sql, Map paramMap, Class elementType) + throws DataAccessException { + return this.queryForList(sql, new MapSqlParameterSource(paramMap), elementType); + } + + @Override + public List> queryForList(String sql, SqlParameterSource paramSource) + throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + return this.jdbc.queryForList(sb); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public List> queryForList(String sql, Map paramMap) throws DataAccessException { + return this.queryForList(sql, new MapSqlParameterSource(paramMap)); + } + + @Override + public SqlRowSet queryForRowSet(String sql, SqlParameterSource paramSource) throws DataAccessException { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + try { + return this.jdbc.queryForRowSet(sb); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public SqlRowSet queryForRowSet(String sql, Map paramMap) throws DataAccessException { + return this.queryForRowSet(sql, new MapSqlParameterSource(paramMap)); + } + + @Override + public int update(String sql, SqlParameterSource paramSource) throws DataAccessException { + VerifyTools.requireNotBlank(sql, "sql"); + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + String lowerSql = sql.toLowerCase(); + try { + if (lowerSql.contains("insert")) { + return this.jdbc.insert(sb); + } else if (lowerSql.contains("delete")) { + return this.jdbc.delete(sb); + } else { + return this.jdbc.update(sb); + } + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public int update(String sql, Map paramMap) throws DataAccessException { + return this.update(sql, new MapSqlParameterSource(paramMap)); + } + + @Override + public int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder) + throws DataAccessException { + VerifyTools.requireNotBlank(sql, "sql"); + String lowerSql = sql.toLowerCase(); + try { + if (lowerSql.contains("insert")) { + SqlBuffer sb = NamedParameterTools.buildSqlBuffer(sql, paramSource); + return this.jdbc.insert(sb, generatedKeyHolder); + } else { + NamedParameterJdbcOperations npjo = this.jdbc.getNamedParameterJdbcOperations(); + return npjo.update(sql, paramSource, generatedKeyHolder); + } + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public int update(String sql, SqlParameterSource paramSource, KeyHolder generatedKeyHolder, String[] keyColumnNames) + throws DataAccessException { + NamedParameterJdbcOperations npjdbc = this.jdbc.getNamedParameterJdbcOperations(); + try { + return npjdbc.update(sql, paramSource, generatedKeyHolder, keyColumnNames); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public int[] batchUpdate(String sql, Map[] batchValues) { + NamedParameterJdbcOperations npjdbc = this.jdbc.getNamedParameterJdbcOperations(); + try { + return npjdbc.batchUpdate(sql, batchValues); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + @Override + public int[] batchUpdate(String sql, SqlParameterSource[] batchArgs) { + NamedParameterJdbcOperations npjdbc = this.jdbc.getNamedParameterJdbcOperations(); + try { + return npjdbc.batchUpdate(sql, batchArgs); + } catch (ServiceException e) { + throw toDataAccessException(e); + } + } + + private T requiredSingleResult(T object) { + if (object == null) { + throw new EmptyResultDataAccessException(1); + } + return object; + } + + private RuntimeException toDataAccessException(ServiceException e) { + if (e.getCause() instanceof DataAccessException) { + return (DataAccessException) e.getCause(); + } else { + return e; + } + } + + /** 自定义RowToMapMapper **/ + private static class MapSqlBufferJdbcTemplate extends SqlBufferJdbcTemplate { + + private SqlBufferJdbcTemplate parent; + private RowToMapMapper rowToMapMapper; + + public MapSqlBufferJdbcTemplate(SqlBufferJdbcTemplate parent, RowToMapMapper rowToMapMapper) { + super(parent); + this.parent = parent; + this.rowToMapMapper = rowToMapMapper; + } + + @Override + public DbVersion getDbVersion() { + return this.parent.getDbVersion(); + } + + @Override + public SqlDialect getSqlDialect() { + return this.parent.getSqlDialect(); + } + + @Override + public SqlDao getSqlDao() { + throw new UnsupportedOperationException(); + } + + @Override + protected RowMapper> getRowToMapConverter() { + return rowToMapMapper; + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/fastjson/UseSpringConversionDeserializer.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/fastjson/UseSpringConversionDeserializer.java index f603498e159f9133a87bc085c03a67aad16f96e5..323673d8d7830f85df6009c649ef527a26b84c30 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/fastjson/UseSpringConversionDeserializer.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/support/fastjson/UseSpringConversionDeserializer.java @@ -85,15 +85,15 @@ public class UseSpringConversionDeserializer implements ObjectDeserializer, Conv } else if (isIntegral && conversionService.canConvert(BigInteger.class, targetClass)) { return conversionService.convert(integer, targetClass); } else if (conversionService.canConvert(Double.class, targetClass)) { - return conversionService.convert(new Double(number.doubleValue()), targetClass); + return conversionService.convert(Double.valueOf(number.doubleValue()), targetClass); } else if (conversionService.canConvert(Float.class, targetClass)) { - return conversionService.convert(new Float(number.doubleValue()), targetClass); + return conversionService.convert(Float.valueOf(number.floatValue()), targetClass); } else if (isIntegral && conversionService.canConvert(Long.class, targetClass)) { - return conversionService.convert(new Long(TypeUtils.longValue(number)), targetClass); + return conversionService.convert(Long.valueOf(TypeUtils.longValue(number)), targetClass); } else if (isIntegral && conversionService.canConvert(Integer.class, targetClass)) { - return conversionService.convert(new Integer(TypeUtils.intValue(number)), targetClass); + return conversionService.convert(Integer.valueOf(TypeUtils.intValue(number)), targetClass); } else if (isIntegral && conversionService.canConvert(Short.class, targetClass)) { - return conversionService.convert(new Short(TypeUtils.shortValue(number)), targetClass); + return conversionService.convert(Short.valueOf(TypeUtils.shortValue(number)), targetClass); } else if (conversionService.canConvert(String.class, targetClass)) { String string = isIntegral ? integer.toString() : number.toPlainString(); return conversionService.convert(string, targetClass); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/AppendTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/AppendTag.java index c9fcd8acd4c9e016a19fd74786cbe07c77f3658b..a1c4baa066775fe0302d2cfc90d1d1028bfb7033 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/AppendTag.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/AppendTag.java @@ -1,5 +1,7 @@ package com.gitee.qdbp.jdbc.tags; +import com.gitee.qdbp.able.enums.WrapMode; + /** * 如果内容不为空, 则输出prefix+内容+suffix
* <append prefix="AND">#{whereCondition}</append> @@ -30,4 +32,13 @@ public class AppendTag extends TrimBase { super.setSuffix(suffix); } + @Override + public WrapMode getBrackets() { + return super.getBrackets(); + } + + @Override + public void setBrackets(String brackets) { + super.setBrackets(brackets); + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/ColumnsTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/ColumnsTag.java new file mode 100644 index 0000000000000000000000000000000000000000..be6994bd90d1bda3b16ba99c14f9a5ead3755244 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/ColumnsTag.java @@ -0,0 +1,162 @@ +package com.gitee.qdbp.jdbc.tags; + +import java.io.IOException; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.staticize.common.NodeMetaData; +import com.gitee.qdbp.staticize.exception.TagException; +import com.gitee.qdbp.staticize.tags.base.BaseTag; +import com.gitee.qdbp.staticize.tags.base.NextStep; +import com.gitee.qdbp.staticize.utils.TagUtils; +import com.gitee.qdbp.tools.parse.StringAccess; +import com.gitee.qdbp.tools.utils.StringTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 输出表的列名
+ * 完整写法 <columns alias="u" class="@SysUserEntity" excludes="password,userRemark" />
+ * 简略写法 <columns value="@SysUserEntity u -password,userRemark"/> + * + * @author zhaohuihua + * @version 20210504 + * @since 3.4.0 + */ +public class ColumnsTag extends BaseTag { + + /** 简略写法: value="@SysUserEntity u" **/ + private String value; + /** 目标类名 **/ + private String className; + /** 目标类 **/ + private Class classType; + /** 表别名 (可为空) **/ + private String alias; + /** 排除字段 **/ + private String excludes; + + /** 简略写法: value="@SysUserEntity u -password,userRemark" **/ + public void setValue(String value) { + this.value = value; + } + + /** 简略写法: value="@SysUserEntity u -password,userRemark" **/ + public String getValue() { + return value; + } + + /** 目标类名 **/ + public void setClass(String className) { + this.className = className; + } + + /** 目标类名 **/ + public String getClassName() { + return className; + } + + /** 目标类名 **/ + public void setClassName(String clazz) { + this.className = clazz; + } + + /** 表别名 (可为空) **/ + public void setAlias(String alias) { + this.alias = alias; + } + + /** 排除字段 **/ + public void setExcludes(String excludes) { + this.excludes = excludes; + } + + /** 排除字段 **/ + public String getExcludes() { + return excludes; + } + + @Override + public NextStep doHandle() throws TagException, IOException { + SqlDialect dialect = this.getStackValue("db.dialect", SqlDialect.class); + this.print(SqlTools.buildSelectFieldsSql(classType, alias, excludes, dialect)); + return NextStep.SKIP_BODY; + } + + protected Class parseClass(String clazz, String fromField) { + String className = clazz.trim(); + if (className.charAt(0) == '@') { + if (!this.containsImportClass(className)) { + throw new TagException("属性" + fromField + "错误, 类名" + className + "未找到相关的import类"); + } else { + className = this.getImportClass(className); + } + } + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + throw new TagException("属性" + fromField + "错误, 类名" + className + "未找到类"); + } + } + + protected void parseValue(String value) { + StringAccess sa = new StringAccess(value); + String className = sa.readUniterm(' ', '\t', '\r', '\n'); + if (className != null) { + this.className = className; + } else { + String msg = "属性value格式错误, 正确格式为\"类名 别名 -排除字段\", 示例: value=\"@SysUserEntity t -password,userRemark\""; + throw new TagException(msg); + } + if (!sa.skipWhitespace().isReadable()) { + return; + } + String alias = sa.readUniterm(' ', '\t', '\r', '\n'); + if (alias != null) { + if (StringTools.isWordString(alias)) { + this.alias = alias; + } else { + throw new TagException("属性value格式错误, class后面的alias[" + alias + "]必须是一个单词"); + } + } + if (!sa.skipWhitespace().isReadable()) { + return; + } + if (sa.peekChar() != '-') { + throw new TagException("属性value格式错误, 排除字段后面必须为-号"); + } + // 读取排除字段 + String excludes = sa.skipChar().skipWhitespace().readToEnd(); + if (excludes != null) { + this.excludes = excludes.trim(); + } + } + + @Override + public void doValidate(NodeMetaData metadata) throws TagException { + if (VerifyTools.isBlank(value)) { + if (VerifyTools.isBlank(className)) { + throw new TagException("属性class和value不能同时为空"); + } else { + // 解析目标类型 + this.classType = parseClass(className, "class"); + } + } else { + if (VerifyTools.isNotBlank(className) || VerifyTools.isNotBlank(alias) + || VerifyTools.isNotBlank(excludes)) { + throw new TagException("属性value与class,alias,excludes不能同时设置"); + } + // 解析value字符串 + this.parseValue(this.value); + // 解析目标类型 + this.classType = parseClass(className, "value"); + } + for (NodeMetaData item : metadata.getChildren()) { + if (!TagUtils.isTextTag(item.getTagClass())) { + throw new TagException("标签体中不能存在内容"); + } + String text = item.getAttributeValue("value", String.class); + if (text != null && !StringTools.isAsciiWhitespace(text)) { + throw new TagException("标签体中不能存在内容"); + } + } + } +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..9094b3d834da95b29af29c9f28a90134ab286749 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/OrderByTag.java @@ -0,0 +1,106 @@ +package com.gitee.qdbp.jdbc.tags; + +import java.io.IOException; +import java.util.Collection; +import com.gitee.qdbp.able.jdbc.ordering.Ordering; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.plugins.impl.ColumnNamingConverter; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.staticize.common.NodeMetaData; +import com.gitee.qdbp.staticize.exception.TagException; +import com.gitee.qdbp.staticize.tags.base.BaseTag; +import com.gitee.qdbp.staticize.tags.base.NextStep; +import com.gitee.qdbp.staticize.utils.TagUtils; +import com.gitee.qdbp.tools.utils.StringTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 如果内容不为空, 则输出ORDER BY ${orderByCondition}
+ * <orderby value="orderByCondition" /> + * + * @author zhaohuihua + * @version 20210504 + * @since 3.4.0 + */ +public class OrderByTag extends BaseTag { + + private Object value; + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + @Override + public NextStep doHandle() throws TagException, IOException { + if (VerifyTools.isBlank(value)) { + return NextStep.SKIP_BODY; + } + SqlDialect dialect = this.getStackValue("db.dialect", SqlDialect.class); + SqlBuffer sql = null; + if (value instanceof String) { + ColumnNameResolver resolver = ColumnNamingConverter.defaults(); + sql = SqlTools.buildOrderBySql(Orderings.of((String) value), resolver, dialect); + } else if (value instanceof SqlBuffer) { + sql = new SqlBuilder().ad("ORDER BY").ad((SqlBuffer) value).out(); + } else if (value instanceof SqlBuilder) { + sql = new SqlBuilder().ad("ORDER BY").ad((SqlBuilder) value).out(); + } else if (value instanceof Orderings) { + ColumnNameResolver resolver = ColumnNamingConverter.defaults(); + sql = SqlTools.buildOrderBySql((Orderings) value, resolver, dialect); + } else if (value instanceof Collection) { + Collection list = (Collection) value; + Orderings orderings = new Orderings(); + for (Object item : list) { + if (item instanceof Ordering) { + orderings.add((Ordering) item); + } else { + throw new TagException("属性value只能传入String或Orderings或SqlBuffer对象"); + } + } + ColumnNameResolver resolver = ColumnNamingConverter.defaults(); + sql = SqlTools.buildOrderBySql(orderings, resolver, dialect); + } else { + throw new TagException("属性value只能传入String或Orderings或SqlBuffer对象"); + } + if (sql != null && !sql.isBlank()) { + sql.prepend("ORDER BY", ' '); + this.print(sql.getExecutableSqlString(dialect)); + } + return NextStep.SKIP_BODY; + } + + @Override + public void doValidate(NodeMetaData metadata) throws TagException { + if (value != null) { + if (!(value instanceof String) && !(value instanceof SqlBuffer) && !(value instanceof SqlBuilder) + && !(value instanceof Orderings) && !(value instanceof Collection)) { + throw new TagException("属性value只能传入String或Orderings或SqlBuffer对象"); + } + if (value instanceof Collection) { + Collection list = (Collection) value; + for (Object item : list) { + if (!(item instanceof Ordering)) { + throw new TagException("属性value只能传入String或Orderings或SqlBuffer对象"); + } + } + } + } + for (NodeMetaData item : metadata.getChildren()) { + if (!TagUtils.isTextTag(item.getTagClass())) { + throw new TagException("标签体中不能存在内容"); + } + String text = item.getAttributeValue("value", String.class); + if (text != null && !StringTools.isAsciiWhitespace(text)) { + throw new TagException("标签体中不能存在内容"); + } + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingEachTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingEachTag.java new file mode 100644 index 0000000000000000000000000000000000000000..6d72eb55265b5274ca5ad869b0bd69806b9ea2fa --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingEachTag.java @@ -0,0 +1,41 @@ +package com.gitee.qdbp.jdbc.tags; + +import java.io.IOException; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.parse.SqlCachingWriter; +import com.gitee.qdbp.staticize.common.IWriter; +import com.gitee.qdbp.staticize.exception.TagException; +import com.gitee.qdbp.staticize.tags.base.BaseEachTag; + +/** + * 输出SQL的Each标签 + * + * @author zhaohuihua + * @version 20210427 + */ +public class SqlCachingEachTag extends BaseEachTag { + + public SqlCachingEachTag() { + super(new SqlCachingWriter()); + } + + @Override + protected final void doEnded(SqlCachingWriter caching, IWriter origin) throws TagException, IOException { + doEnded(caching.getContent(), origin); + caching.clear(); + } + + protected void doEnded(SqlBuffer buffer, IWriter writer) throws TagException, IOException { + if (buffer.isBlank()) { + return; + } + + // 循环标签不需要输出前置空白, 因为迭代内容已经有换行符了 + + if (this.isInline() && !this.es.isFirst()) { + writer.write(buffer.trimLeft()); + } else { + writer.write(buffer); + } + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingTag.java index fca422dd4b506114df2e74f6f5db93e7bd0809f3..c968348c27ffb08df1464737c014fc33450307ff 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingTag.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlCachingTag.java @@ -30,10 +30,10 @@ public abstract class SqlCachingTag extends CachingTag { /** * 标签结束的处理, 一般是将缓冲区内容写入原IWriter * - * @param caching 缓冲区内容 + * @param content 缓冲区内容 * @param writer 原Writer * @throws TagException 标签处理异常 * @throws IOException 输出处理异常 */ - protected abstract void doEnded(SqlBuffer caching, IWriter writer) throws TagException, IOException; + protected abstract void doEnded(SqlBuffer content, IWriter writer) throws TagException, IOException; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlForEachTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlForEachTag.java new file mode 100644 index 0000000000000000000000000000000000000000..dd9b295b7be9692a471b69ea6cf314f6aa415d19 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlForEachTag.java @@ -0,0 +1,100 @@ +package com.gitee.qdbp.jdbc.tags; + +/** + * MyBatis的foreach标签兼容处理类 + * + * @author zhaohuihua + * @version 20210425 + */ +public class SqlForEachTag extends SqlCachingEachTag { + + /** 前置字符 **/ + public void setOpen(String open) { + super.setPrefix(open); + } + + /** 后置字符 **/ + public void setClose(String close) { + super.setSuffix(close); + } + + /** 前置字符 **/ + public void setPrefix(String prefix) { + super.setPrefix(prefix); + } + + /** 后置字符 **/ + public void setSuffix(String suffix) { + super.setSuffix(suffix); + } + + /** 每次迭代后的分隔符 **/ + public void setSeparator(String separator) { + super.setSeparator(separator); + } + + /** 设置迭代对象的变量名 **/ + public void setVar(String var) { + super.setVar(var); + } + + /** 设置迭代对象的变量名 **/ + public void setItem(String var) { + super.setVar(var); + } + + /** 设置保存迭代状态的变量名 **/ + public void setStatus(String status) { + super.setStatus(status); + } + + /** 设置保存迭代状态的变量名(兼容c:forEach) **/ + public void setVarStatus(String status) { + super.setStatus(status); + } + + /** 设置保存迭代序号的变量名 **/ + public void setIndex(String index) { + super.setIndex(index); + } + + /** 设置开始行数(从0开始) **/ + public void setBegin(Integer begin) { + super.setBegin(begin); + } + + /** 设置结束行数(begin+rows-1=end) **/ + public void setEnd(Integer end) { + super.setEnd(end); // 可以为空, 可以小于0 + } + + /** 设置限制行数(可为空, 默认为不限制) **/ + public void setRows(Integer rows) { + super.setRows(rows); + } + + /** 设置迭代步长 **/ + public void setStep(Integer step) { + super.setStep(step); // 可以为空, 可以小于0 + } + + /** 设置迭代次数限制 **/ + public void setLimit(Integer limit) { + super.setLimit(limit); + } + + /** 设置是否输出在同一行(非首次迭代时删除第一个文本节点前面的换行) **/ + public void setInline(Boolean inline) { + super.setInline(inline); + } + + /** 要进行迭代的集合 **/ + public void setItems(Object items) { + super.setItems(items); + } + + /** 要进行迭代的集合 **/ + public void setCollection(Object items) { + super.setItems(items); + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlLikeTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlLikeTag.java index 6883706c07d7b9503a3b001c713a865b2b848e3b..40cb84fe5dd02a89e737a8a635558c56ff599790 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlLikeTag.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/SqlLikeTag.java @@ -1,8 +1,10 @@ package com.gitee.qdbp.jdbc.tags; import java.io.IOException; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.staticize.annotation.AttrRequired; import com.gitee.qdbp.staticize.exception.TagException; import com.gitee.qdbp.staticize.tags.base.BaseTag; import com.gitee.qdbp.staticize.tags.base.NextStep; @@ -44,6 +46,7 @@ public class SqlLikeTag extends BaseTag { return column; } + @AttrRequired public void setColumn(String column) { this.column = column; } @@ -86,17 +89,31 @@ public class SqlLikeTag extends BaseTag { if (VerifyTools.isNotBlank(prefix)) { sql.ad(prefix); } + sql.ad(column); if (not) { sql.ad("NOT"); } - if (type == null) { - sql.ad(dialect.buildLikeSql(value)); - } else if ("starts".equalsIgnoreCase(type) || "StartsWith".equalsIgnoreCase(type)) { - sql.ad(dialect.buildStartsWithSql(value)); - } else if ("ends".equalsIgnoreCase(type) || "EndsWith".equalsIgnoreCase(type)) { - sql.ad(dialect.buildEndsWithSql(value)); + if (value instanceof LikeValue) { + if (type == null) { + sql.ad(dialect.buildLikeSql((LikeValue) value)); + } else if ("starts".equalsIgnoreCase(type) || "StartsWith".equalsIgnoreCase(type)) { + throw new IllegalArgumentException("StartsWith not support 'LikeValue'."); + } else if ("ends".equalsIgnoreCase(type) || "EndsWith".equalsIgnoreCase(type)) { + throw new IllegalArgumentException("EndsWith not support 'LikeValue'."); + } else { + sql.ad(dialect.buildLikeSql((LikeValue) value)); + } } else { - sql.ad(dialect.buildLikeSql(value)); + String stringValue = value == null ? null : value.toString(); + if (type == null) { + sql.ad(dialect.buildLikeSql(stringValue)); + } else if ("starts".equalsIgnoreCase(type) || "StartsWith".equalsIgnoreCase(type)) { + sql.ad(dialect.buildStartsWithSql(stringValue)); + } else if ("ends".equalsIgnoreCase(type) || "EndsWith".equalsIgnoreCase(type)) { + sql.ad(dialect.buildEndsWithSql(stringValue)); + } else { + sql.ad(dialect.buildLikeSql(stringValue)); + } } if (VerifyTools.isNotBlank(suffix)) { sql.ad(suffix); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimBase.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimBase.java index 2dd1152b9a13d295c4efd0d4ad7a46a9fa5fcb24..dc17dfda68eb46710bc1585e74239f08d085a1cf 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimBase.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimBase.java @@ -1,11 +1,12 @@ package com.gitee.qdbp.jdbc.tags; import java.io.IOException; +import com.gitee.qdbp.able.enums.WrapMode; import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlTools; import com.gitee.qdbp.staticize.common.IWriter; import com.gitee.qdbp.staticize.exception.TagException; import com.gitee.qdbp.staticize.tags.base.NextStep; -import com.gitee.qdbp.tools.utils.VerifyTools; /** * 去掉第1个prefixOverrides, 去掉最后一个suffixOverrides
@@ -21,6 +22,8 @@ public abstract class TrimBase extends SqlCachingTag { private String suffix; private String prefixOverrides; private String suffixOverrides; + /** 是否添加括号 **/ + private WrapMode brackets; protected String getPrefix() { return prefix; @@ -54,6 +57,14 @@ public abstract class TrimBase extends SqlCachingTag { this.suffixOverrides = suffixOverrides; } + protected WrapMode getBrackets() { + return brackets; + } + + protected void setBrackets(String brackets) { + this.brackets = WrapMode.of(brackets, "brackets value"); + } + @Override public NextStep doHandle() throws TagException, IOException { return NextStep.EVAL_BODY; @@ -64,16 +75,13 @@ public abstract class TrimBase extends SqlCachingTag { if (buffer.isBlank()) { return; } - if (VerifyTools.isNotBlank(prefix) || VerifyTools.isNotBlank(prefixOverrides)) { - buffer.insertPrefix(prefix, prefixOverrides); - } - String leadingBlank = getLeadingBlank(); + + SqlTools.wrap(buffer, brackets, prefix, prefixOverrides, suffix, suffixOverrides); + String leadingBlank = clearLeadingBlank(); if (leadingBlank != null) { - buffer.prepend(leadingBlank); - } - if (VerifyTools.isNotBlank(suffix) || VerifyTools.isNotBlank(suffixOverrides)) { - buffer.insertSuffix(suffix, suffixOverrides); + buffer.shortcut().pd(leadingBlank); } + writer.write(buffer); } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimTag.java index e4ba53265239b55a07c1089f37de2c5c2579537f..3aa64e605c25411878c999db527267ba9dde363f 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimTag.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/TrimTag.java @@ -1,5 +1,7 @@ package com.gitee.qdbp.jdbc.tags; +import com.gitee.qdbp.able.enums.WrapMode; + /** * 去掉第1个prefixOverrides, 去掉最后一个suffixOverrides
* 在前面增加prefix, 在后面增加suffix
@@ -29,4 +31,14 @@ public class TrimTag extends TrimBase { public void setSuffixOverrides(String suffixOverrides) { super.setSuffixOverrides(suffixOverrides); } + + @Override + public WrapMode getBrackets() { + return super.getBrackets(); + } + + @Override + public void setBrackets(String brackets) { + super.setBrackets(brackets); + } } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/DeleteTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/DeleteTag.java new file mode 100644 index 0000000000000000000000000000000000000000..bd805739b2e87d20aa16a917ec3bd5859a6be887 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/DeleteTag.java @@ -0,0 +1,25 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +import com.gitee.qdbp.staticize.annotation.AttrRequired; +import com.gitee.qdbp.staticize.tags.core.BlockTag; + +/** + * delete标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public class DeleteTag extends BlockTag implements SqlBoxTag { + + private String id; + + @AttrRequired + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/InsertTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/InsertTag.java new file mode 100644 index 0000000000000000000000000000000000000000..7ad8a7de276d3532b680df4fd9ff8786e66ebd23 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/InsertTag.java @@ -0,0 +1,25 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +import com.gitee.qdbp.staticize.annotation.AttrRequired; +import com.gitee.qdbp.staticize.tags.core.BlockTag; + +/** + * insert标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public class InsertTag extends BlockTag implements SqlBoxTag { + + private String id; + + @AttrRequired + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/MapperTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/MapperTag.java new file mode 100644 index 0000000000000000000000000000000000000000..d351928b4fb69a36252f76729f935d1055a9852f --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/MapperTag.java @@ -0,0 +1,23 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +import com.gitee.qdbp.staticize.tags.core.BlockTag; + +/** + * mapper标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public class MapperTag extends BlockTag { + + private String namespace; + + public String getNamespace() { + return namespace; + } + + public void setNamespace(String namespace) { + this.namespace = namespace; + } + +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SelectTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SelectTag.java new file mode 100644 index 0000000000000000000000000000000000000000..fdcf50ac5bed32cc27434700f2768f382f809145 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SelectTag.java @@ -0,0 +1,25 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +import com.gitee.qdbp.staticize.annotation.AttrRequired; +import com.gitee.qdbp.staticize.tags.core.BlockTag; + +/** + * select 标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public class SelectTag extends BlockTag implements SqlBoxTag { + + private String id; + + @AttrRequired + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SqlBoxTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SqlBoxTag.java new file mode 100644 index 0000000000000000000000000000000000000000..2ef5239e5cdf86e587ef009c541cadf405727a2d --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SqlBoxTag.java @@ -0,0 +1,12 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +/** + * SQL容器标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public interface SqlBoxTag { + + String getId(); +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SqlTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SqlTag.java new file mode 100644 index 0000000000000000000000000000000000000000..b481cbe75010511db869d0b9d20fc161d49babac --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/SqlTag.java @@ -0,0 +1,25 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +import com.gitee.qdbp.staticize.annotation.AttrRequired; +import com.gitee.qdbp.staticize.tags.core.BlockTag; + +/** + * sql标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public class SqlTag extends BlockTag implements SqlBoxTag { + + private String id; + + @AttrRequired + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/UpdateTag.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/UpdateTag.java new file mode 100644 index 0000000000000000000000000000000000000000..7a4fd7c5ec182cdf411a6e51eb2bd27e8f1be6d6 --- /dev/null +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/tags/mapper/UpdateTag.java @@ -0,0 +1,25 @@ +package com.gitee.qdbp.jdbc.tags.mapper; + +import com.gitee.qdbp.staticize.annotation.AttrRequired; +import com.gitee.qdbp.staticize.tags.core.BlockTag; + +/** + * update标签 + * + * @author zhaohuihua + * @version 20210504 + */ +public class UpdateTag extends BlockTag implements SqlBoxTag { + + private String id; + + @AttrRequired + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } +} diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/CountSqlParser.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/CountSqlParser.java index bfc5c34af8fbec2b9a5e325395f74051b81ba644..d56421c40fbcee37100e1a640f9ccecf17fd2620 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/CountSqlParser.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/CountSqlParser.java @@ -131,7 +131,7 @@ public class CountSqlParser { stringBuilder.append("SELECT").append(' '); stringBuilder.append("COUNT").append('(').append(name).append(')').append(' '); stringBuilder.append("FROM").append(' ').append('(').append('\n'); - stringBuilder.append(IndentTools.indentAll(sql, 1)); + stringBuilder.append(IndentTools.space.indentAll(sql, 1)); stringBuilder.append('\n').append(')').append(' ').append("TMP_COUNT"); return stringBuilder.toString(); } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbConfig.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbConfig.java index 43b118127537df60d8de0f8df7566735a1c17c23..578c553ab1eabe3613a023f1bda4968e94eb0bdf 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbConfig.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbConfig.java @@ -2,7 +2,7 @@ package com.gitee.qdbp.jdbc.utils; import java.io.Serializable; import com.gitee.qdbp.jdbc.model.DbVersion; -import com.gitee.qdbp.tools.utils.Config; +import com.gitee.qdbp.tools.property.PropertyContainer; /** * 数据库配置容器类 @@ -15,15 +15,15 @@ public class DbConfig implements Serializable { /** serialVersionUID **/ private static final long serialVersionUID = 1L; - private final Config config; + private final PropertyContainer config; private final DbVersion version; - public DbConfig(Config config, DbVersion version) { + public DbConfig(PropertyContainer config, DbVersion version) { this.config = config; this.version = version; } - public Config content() { + public PropertyContainer content() { return this.config; } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTools.java index 0ba9605e0aca9cbfbcb7942ae1287e97e0c5b317..497a18013c81b2b8bbbb1f1218a70a22669aaea8 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTools.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTools.java @@ -10,11 +10,16 @@ import java.util.List; import java.util.Map; import javax.sql.DataSource; import org.springframework.jdbc.core.SqlParameterValue; +import com.gitee.qdbp.able.convert.BeanToMapConverter; +import com.gitee.qdbp.able.convert.MapToBeanConverter; +import com.gitee.qdbp.able.convert.ObjectTypeConverter; +import com.gitee.qdbp.able.jdbc.base.OrderByCondition; import com.gitee.qdbp.able.jdbc.base.UpdateCondition; import com.gitee.qdbp.able.jdbc.base.WhereCondition; import com.gitee.qdbp.able.jdbc.condition.TableJoin; -import com.gitee.qdbp.able.jdbc.condition.TableJoin.JoinItem; -import com.gitee.qdbp.able.jdbc.condition.TableJoin.TableItem; +import com.gitee.qdbp.able.jdbc.model.DbFieldName; +import com.gitee.qdbp.able.jdbc.model.DbFieldValue; +import com.gitee.qdbp.able.jdbc.model.DbRawValue; import com.gitee.qdbp.jdbc.model.AllFieldColumn; import com.gitee.qdbp.jdbc.model.DbType; import com.gitee.qdbp.jdbc.model.DbVersion; @@ -25,24 +30,31 @@ import com.gitee.qdbp.jdbc.model.TypedDbVariable; import com.gitee.qdbp.jdbc.operator.DbBaseOperator; import com.gitee.qdbp.jdbc.plugins.BatchInsertExecutor; import com.gitee.qdbp.jdbc.plugins.BatchUpdateExecutor; -import com.gitee.qdbp.jdbc.plugins.BeanToMapConverter; import com.gitee.qdbp.jdbc.plugins.DbConditionConverter; import com.gitee.qdbp.jdbc.plugins.DbOperatorContainer; import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; import com.gitee.qdbp.jdbc.plugins.DbVersionFinder; import com.gitee.qdbp.jdbc.plugins.EntityDataStateFillStrategy; import com.gitee.qdbp.jdbc.plugins.EntityFieldFillStrategy; -import com.gitee.qdbp.jdbc.plugins.MapToBeanConverter; +import com.gitee.qdbp.jdbc.plugins.JdbcDataTypeResolver; +import com.gitee.qdbp.jdbc.plugins.JdbcNamingConverter; +import com.gitee.qdbp.jdbc.plugins.OrderBySqlBuilder; import com.gitee.qdbp.jdbc.plugins.RawValueConverter; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.plugins.SqlFileScanner; import com.gitee.qdbp.jdbc.plugins.SqlFormatter; +import com.gitee.qdbp.jdbc.plugins.SqlFragmentOptions; import com.gitee.qdbp.jdbc.plugins.TableInfoScans; +import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser; import com.gitee.qdbp.jdbc.plugins.UpdateSqlBuilder; import com.gitee.qdbp.jdbc.plugins.VariableToDbValueConverter; import com.gitee.qdbp.jdbc.plugins.WhereSqlBuilder; +import com.gitee.qdbp.jdbc.result.RowToBeanMapper; +import com.gitee.qdbp.jdbc.result.RowToBeanMapper.FactoryOfTable; +import com.gitee.qdbp.jdbc.result.RowToBeanMapper.FactoryOfTables; +import com.gitee.qdbp.jdbc.result.RowToMapMapper; import com.gitee.qdbp.staticize.tags.base.Taglib; -import com.gitee.qdbp.tools.utils.Config; +import com.gitee.qdbp.tools.property.PropertyContainer; import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -52,7 +64,11 @@ import com.gitee.qdbp.tools.utils.VerifyTools; * @author 赵卉华 * @version 190601 */ -public abstract class DbTools { +public class DbTools { + + /** 静态工具类私有构造方法 **/ + private DbTools() { + } /** * 将变量转换为数据库字段值
@@ -78,13 +94,18 @@ public abstract class DbTools { } return result; } else { + if (variable instanceof SqlParameterValue) { + return variable; + } VariableToDbValueConverter converter = DbPluginContainer.defaults().getToDbValueConverter(); Object result = converter.convert(variable); - if (result instanceof TypedDbVariable) { + if (result instanceof TypedDbVariable) { // 转换结果指定了类型 TypedDbVariable temp = (TypedDbVariable) result; - return new SqlParameterValue(temp.getSqlType(), temp.getValue()); - } else if (result instanceof Character) { - // Character类型不能自动识别 + return new SqlParameterValue(temp.getJdbcType(), temp.getValue()); + } else if (variable instanceof TypedDbVariable) { // 入参指定了类型 + TypedDbVariable typed = (TypedDbVariable) variable; + return new SqlParameterValue(typed.getJdbcType(), result); + } else if (result instanceof Character) { // Character类型不能自动识别 // @see org.springframework.jdbc.core.StatementCreatorUtils.setValue // 调用的是PreparedStatement.setObject(int index, Object x) // 而不是PreparedStatement.setObject(int index, Object x, int Types.xxx) @@ -148,6 +169,58 @@ public abstract class DbTools { } } + public static Object unwrapDbVariable(Object value) { + if (value instanceof TypedDbVariable) { + return ((TypedDbVariable) value).getValue(); + } else if (value instanceof SqlParameterValue) { + return ((SqlParameterValue) value).getValue(); + } else if (value instanceof DbFieldName) { + return ((DbFieldName) value).getFieldName(); + } else if (value instanceof DbFieldValue) { + return ((DbFieldValue) value).getFieldValue(); + } else if (value instanceof DbRawValue) { + return ((DbRawValue) value).toString(); + } else { + return value; + } + } + + public static Object wrapDbVariable(SimpleFieldColumn column, Object value) { + if (value instanceof DbFieldName || value instanceof DbFieldValue || value instanceof DbRawValue) { + return value; + } else if (value instanceof TypedDbVariable || value instanceof SqlParameterValue) { + return value; + } else if (column.getJdbcType() != null) { + return new TypedDbVariable(column.getJdbcType(), value); + } else { + return value; + } + } + + /** + * 将代码中写的数据类型转换为java.sql.Types中的数据类型 + * + * @param dataType 代码中的数据类型
+ * 1.来自SQL模板中, 如#{createTime,jdbcType=TIMESTAMP}
+ * 2.来自实体类上的注解, 如@Column(columnDefinition="TIMESTAMP NOT NULL") + * @return java.sql.Types中的数据类型 + * @see java.sql.Types + * @see JdbcDataTypeResolver + */ + public static Integer parseJdbcDataType(String dataType) { + JdbcDataTypeResolver resolver = DbPluginContainer.defaults().getJdbcDataTypeResolver(); + return resolver.parseDataType(dataType); + } + + /** + * 获取Jdbc数据类型转换类 + * + * @return JdbcDataTypeResolver + */ + public static JdbcDataTypeResolver getJdbcDataTypeResolver() { + return DbPluginContainer.defaults().getJdbcDataTypeResolver(); + } + /** * 可用的数据库类型
* 除MainDbType以外, 可通过DbPluginContainer.defaults().addDbType()注册其他数据库类型 @@ -160,7 +233,7 @@ public abstract class DbTools { /** * SQL标签库
- * 默认的标签库为classpath:settings/dbtags/taglib.txt
+ * 默认的标签库为classpath:settings/qdbc/qdbc.taglib.txt
* 可通过DbPluginContainer.defaults().setSqlTaglibPath()修改 * * @return SQL标签库 @@ -182,6 +255,28 @@ public abstract class DbTools { return converter.convert(rawValue, dialect); } + /** 获取对象类型转换处理类 **/ + public static ObjectTypeConverter getObjectTypeConverter() { + return DbPluginContainer.defaults().getObjectTypeConverter(); + } + + /** 获取Row到Map的转换处理类 **/ + public static RowToMapMapper getRowToMapConverter() { + return DbPluginContainer.defaults().getRowToMapConverter(); + } + + /** 获取Row到Bean的转换处理类 (单表) **/ + public static RowToBeanMapper getRowToBeanMapper(Class clazz) { + FactoryOfTable factory = DbPluginContainer.defaults().getTableRowToBeanFactory(); + return factory.getRowToBeanMapper(clazz); + } + + /** 获取Row到Bean的转换处理类 (多表关联) **/ + public static RowToBeanMapper getRowToBeanMapper(TableJoin tables, Class clazz) { + FactoryOfTables factory = DbPluginContainer.defaults().getTablesRowToBeanFactory(); + return factory.getRowToBeanMapper(tables, clazz); + } + /** 获取Map到JavaBean的转换处理类 **/ public static MapToBeanConverter getMapToBeanConverter() { return DbPluginContainer.defaults().getMapToBeanConverter(); @@ -242,6 +337,18 @@ public abstract class DbTools { return DbPluginContainer.defaults().getUpdateSqlBuilder(type); } + /** 获取自定义UpdateSqlBuilder **/ + public static > B getOrderBySqlBuilder(Class type) { + return DbPluginContainer.defaults().getOrderBySqlBuilder(type); + } + + /** 获取自定义UpdateSqlBuilder **/ + public static > B getOrderBySqlBuilder(T condition) { + @SuppressWarnings("unchecked") + Class type = (Class) condition.getClass(); + return DbPluginContainer.defaults().getOrderBySqlBuilder(type); + } + /** * 根据运算符获取WhereOperator处理类 * @@ -292,11 +399,21 @@ public abstract class DbTools { return DbPluginContainer.defaults().getSqlFileScanner(); } + /** 获取SQL片断的选项配置类 **/ + public static SqlFragmentOptions getSqlFragmentOptions() { + return DbPluginContainer.defaults().getSqlFragmentOptions(); + } + /** 获取数据库配置选项 **/ - public static Config getDbConfig() { + public static PropertyContainer getDbConfig() { return DbPluginContainer.defaults().getDbConfig(true); } + /** 获取命名转换接口 **/ + public static JdbcNamingConverter getNamingConverter() { + return DbPluginContainer.defaults().getNamingConverter(); + } + /** 获取省略策略配置 **/ public static OmitStrategy getOmitSizeConfig(String key, String defvalue) { String string = getDbConfig().getStringUseDefValue(key, defvalue); @@ -305,30 +422,12 @@ public abstract class DbTools { /** 根据数据库类型获取批量新增处理类 **/ public static BatchInsertExecutor getBatchInsertExecutor(DbVersion version) { - DbPluginContainer plugins = DbPluginContainer.defaults(); - List batchOperateExecutors = plugins.getBatchInsertExecutors(); - if (batchOperateExecutors != null && !batchOperateExecutors.isEmpty()) { - for (BatchInsertExecutor item : batchOperateExecutors) { - if (item.supports(version)) { - return item; - } - } - } - return plugins.getDefaultBatchInsertExecutor(); + return DbPluginContainer.defaults().getBatchInsertExecutor(version); } /** 根据数据库类型获取批量更新处理类 **/ public static BatchUpdateExecutor getBatchUpdateExecutor(DbVersion version) { - DbPluginContainer plugins = DbPluginContainer.defaults(); - List batchOperateExecutors = plugins.getBatchUpdateExecutors(); - if (batchOperateExecutors != null && !batchOperateExecutors.isEmpty()) { - for (BatchUpdateExecutor item : batchOperateExecutors) { - if (item.supports(version)) { - return item; - } - } - } - return plugins.getDefaultBatchUpdateExecutor(); + return DbPluginContainer.defaults().getBatchUpdateExecutor(version); } /** Entity的表名缓存 **/ @@ -376,21 +475,6 @@ public abstract class DbTools { /** TableJoin的列名缓存 **/ private static Map> joinColumnsCache = new HashMap<>(); - private static List scanColumnList(TableItem table) { - TableInfoScans scans = DbPluginContainer.defaults().getTableInfoScans(); - List fields = scans.scanColumnList(table.getTableType()); - String tableAlias = table.getTableAlias(); - String resultField = table.getResultField(); - List result = new ArrayList<>(fields.size()); - for (SimpleFieldColumn item : fields) { - TablesFieldColumn copied = item.to(TablesFieldColumn.class); - copied.setTableAlias(tableAlias); - copied.setResultField(resultField); - result.add(copied); - } - return result; - } - /** * 扫描获取字段名和数据库列名的映射表(有缓存) * @@ -403,41 +487,8 @@ public abstract class DbTools { if (joinColumnsCache.containsKey(cacheKey)) { return joinColumnsCache.get(cacheKey); } - TableItem major = tables.getMajor(); - List joins = tables.getJoins(); - List all = new ArrayList<>(); - { // 添加主表的字段 - List fields = scanColumnList(major); - all.addAll(fields); - } - if (VerifyTools.isNotBlank(joins)) { - // 添加关联表的字段 - for (JoinItem item : joins) { - List fields = scanColumnList(item); - all.addAll(fields); - } - } - // 处理重名字段: 设置columnAlias - // 1.先统计字段出现次数 - Map countMaps = new HashMap<>(); - for (SimpleFieldColumn field : all) { - String fieldName = field.getFieldName(); - if (countMaps.containsKey(fieldName)) { - countMaps.put(fieldName, countMaps.get(fieldName) + 1); - } else { - countMaps.put(fieldName, 1); - } - } - // 2.如果出现多次则设置columnAlias=tableAlias_columnName - for (TablesFieldColumn field : all) { - String fieldName = field.getFieldName(); - if (countMaps.get(fieldName) > 1) { - String tableAlias = field.getTableAlias().toUpperCase(); - String columnName = field.getColumnName(); - field.setColumnAlias(tableAlias + "__" + columnName); - field.setAmbiguous(true); - } - } + TablesFieldColumnParser parser = DbPluginContainer.defaults().getTablesFieldColumnParser(); + List all = parser.parseFieldColumns(tables); AllFieldColumn fieldColumns = new AllFieldColumn<>(all); joinColumnsCache.put(cacheKey, fieldColumns); return fieldColumns; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTypes.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTypes.java index abb1d5724d4bb34752f488898445b4d95573c29d..94186b8f1cf3a0554a7e08c05aa83e39a9ac6e43 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTypes.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/DbTypes.java @@ -13,7 +13,11 @@ import com.gitee.qdbp.tools.utils.VerifyTools; * @version 20200902 * @since 3.2.0 */ -public abstract class DbTypes { +public class DbTypes { + + /** 静态工具类私有构造方法 **/ + private DbTypes() { + } public static boolean equals(DbType source, DbType target) { VerifyTools.requireNonNull(source, "source"); diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/InnerTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/InnerTools.java index 41ffaf0f25b7c6e95682b7357292f62321cb0fb6..95613eb652b8e544e43bb926d2645cb58fe4bd00 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/InnerTools.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/InnerTools.java @@ -9,6 +9,10 @@ import com.gitee.qdbp.able.matches.WrapStringMatcher; public class InnerTools { + /** 静态工具类私有构造方法 **/ + private InnerTools() { + } + /** 列表型配置值的分隔符 **/ private static final String LIST_VALUE_DELIMITERS = ",; \t\n"; @@ -57,4 +61,5 @@ public class InnerTools { } return result; } + } diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/PagingQuery.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/PagingQuery.java index d1547590c9b242f435bc01de2234b4b8dec55d08..44de56b6bbc03da168e5dd892868e04e447d8720 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/PagingQuery.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/PagingQuery.java @@ -77,7 +77,6 @@ public class PagingQuery { Integer total = paging.getTotal(); if (paging.isNeedCount()) { total = jdbc.queryForObject(csb, Integer.class); - paging.setTotal(total); } // 再查询数据列表 List list; diff --git a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/ParseTools.java b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/ParseTools.java index 4cb2956e70d169834827a15bea05f71e8c45ff82..eef9bbb7fe2ce4d352807878cd014b56bf21019c 100644 --- a/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/ParseTools.java +++ b/jdbc-core/src/main/java/com/gitee/qdbp/jdbc/utils/ParseTools.java @@ -14,6 +14,10 @@ import com.gitee.qdbp.tools.utils.VerifyTools; */ public class ParseTools { + /** 静态工具类私有构造方法 **/ + private ParseTools() { + } + /** * 将JavaBean解析为DbWhere对象 * diff --git a/jdbc-core/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterTools.java b/jdbc-core/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterTools.java new file mode 100644 index 0000000000000000000000000000000000000000..6391587ccd904b3cc7304348272a3065c34a4e42 --- /dev/null +++ b/jdbc-core/src/main/java/org/springframework/jdbc/core/namedparam/NamedParameterTools.java @@ -0,0 +1,43 @@ +package org.springframework.jdbc.core.namedparam; + +import java.util.List; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 解析名称参数, 生成SqlBuffer
+ * ParsedSql中的getOriginalSql,getParameterNames不是public方法, 只好写在这个package中 + * + * @author zhaohuihua + * @version 20201208 + */ +public class NamedParameterTools { + + public static SqlBuffer buildSqlBuffer(String sql, SqlParameterSource paramSource) { + VerifyTools.requireNotBlank(sql, "sql"); + ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(sql); + String originalSql = parsedSql.getOriginalSql(); + List paramNames = parsedSql.getParameterNames(); + if (paramNames.isEmpty()) { + return new SqlBuffer(originalSql); + } + SqlBuffer actualSql = new SqlBuffer(); + int lastIndex = 0; + for (int i = 0; i < paramNames.size(); i++) { + String paramName = paramNames.get(i); + int[] indexes = parsedSql.getParameterIndexes(i); + int startIndex = indexes[0]; + int endIndex = indexes[1]; + actualSql.append(originalSql.substring(lastIndex, startIndex)); + if (paramSource != null && paramSource.hasValue(paramName)) { + Object value = paramSource.getValue(paramName); + actualSql.addVariable(value); + } else { + actualSql.addVariable(null); + } + lastIndex = endIndex; + } + actualSql.append(originalSql.substring(lastIndex)); + return actualSql; + } +} diff --git a/jdbc-core/src/main/resources/settings/dbtags/taglib.txt b/jdbc-core/src/main/resources/settings/dbtags/taglib.txt deleted file mode 100644 index 41c8bc8b68cd09493e1f50113046a06805a1c8b0..0000000000000000000000000000000000000000 --- a/jdbc-core/src/main/resources/settings/dbtags/taglib.txt +++ /dev/null @@ -1,26 +0,0 @@ - -import = com.gitee.qdbp.staticize.tags.base.ImportTag -block = com.gitee.qdbp.staticize.tags.core.BlockTag -if = com.gitee.qdbp.staticize.tags.core.IfTag -elseif = com.gitee.qdbp.staticize.tags.core.ElseIfTag -else = com.gitee.qdbp.staticize.tags.core.ElseTag -each = com.gitee.qdbp.staticize.tags.core.EachTag -comment = com.gitee.qdbp.staticize.tags.core.CommentTag -set = com.gitee.qdbp.staticize.tags.core.SetVariableTag - -sql:include = com.gitee.qdbp.jdbc.tags.IncludeTag -append = com.gitee.qdbp.jdbc.tags.AppendTag -sql:append = com.gitee.qdbp.jdbc.tags.AppendTag -where = com.gitee.qdbp.jdbc.tags.WhereTag -sql:where = com.gitee.qdbp.jdbc.tags.WhereTag -update:set = com.gitee.qdbp.jdbc.tags.UpdateSetTag -sql:trim = com.gitee.qdbp.jdbc.tags.TrimTag -sql:in = com.gitee.qdbp.jdbc.tags.SqlInTag -sql:like = com.gitee.qdbp.jdbc.tags.SqlLikeTag -supports = com.gitee.qdbp.staticize.tags.base.IgnoreContentTag - -fmt:date = com.gitee.qdbp.staticize.tags.core.DateFormatTag - -@DateTools = com.gitee.qdbp.tools.utils.DateTools -@StringTools = com.gitee.qdbp.tools.utils.StringTools -@VerifyTools = com.gitee.qdbp.tools.utils.VerifyTools diff --git a/jdbc-core/src/main/resources/settings/dbinit/RECURSIVE_LIST_CHILDREN_QUERY.mysql.sql b/jdbc-core/src/main/resources/settings/qdbc/RECURSIVE_LIST_CHILDREN_QUERY.mysql.sql similarity index 97% rename from jdbc-core/src/main/resources/settings/dbinit/RECURSIVE_LIST_CHILDREN_QUERY.mysql.sql rename to jdbc-core/src/main/resources/settings/qdbc/RECURSIVE_LIST_CHILDREN_QUERY.mysql.sql index e4933311b9c4a7fc0ecac0665cab0b56f2ed3c7b..bf045006deebdf491419e279f4df5d230c731ec1 100644 --- a/jdbc-core/src/main/resources/settings/dbinit/RECURSIVE_LIST_CHILDREN_QUERY.mysql.sql +++ b/jdbc-core/src/main/resources/settings/qdbc/RECURSIVE_LIST_CHILDREN_QUERY.mysql.sql @@ -24,8 +24,8 @@ BEGIN "area_code", -- 区域编号 "parent_code", -- 上级编号 "area_code,area_name,parent_code,type", -- 结果集字段 - "scene_type='default'", -- 场景类型=default - "area_code NOT IN ('340000','350000')", -- 只要子集不要本级 + "scene_type='default'", -- 过滤条件: 场景类型=default + "area_code NOT IN ('340000','350000')", -- 搜索条件: 只要子集不要本级 "type,sort_index" -- 按类型和序号排序 ); */ diff --git a/jdbc-spring/src/main/resources/settings/jdbc/druid.auto.properties b/jdbc-core/src/main/resources/settings/qdbc/jdbc.auto.properties similarity index 96% rename from jdbc-spring/src/main/resources/settings/jdbc/druid.auto.properties rename to jdbc-core/src/main/resources/settings/qdbc/jdbc.auto.properties index 88b3c3dec74bbc8ea315a48351b44858f230619b..7a1a12dd8d15f092a88db3147a92692f514ea597 100644 --- a/jdbc-spring/src/main/resources/settings/jdbc/druid.auto.properties +++ b/jdbc-core/src/main/resources/settings/qdbc/jdbc.auto.properties @@ -1,4 +1,4 @@ -## 简化配置的DruidDataSource, 切换数据库只需要修改一行配置(jdbc.xxx) +## 简化配置的DataSource, 切换数据库只需要修改一行配置(jdbc.xxx) # 配置文件应分为两部分, 一是需要开发评估才能决定修改的, 二是运维人员即可修改的 # 关于数据库的配置, 用户名/密码/访问地址/数据库名是运维的事, 其他的协议类型/各种参数是开发的事 # 运维人员的这部分配置文件应该尽量集中到一个文件中 @@ -63,7 +63,7 @@ jdbc.url.oracle.sid = jdbc:oracle:thin:{address}:{dbname} ## jdbc:oracle:thin:@TnsName jdbc.url.oracle.tns = jdbc:oracle:thin:@{dbname} ## DB2 -## jdbc:db2://192.168.1.218:60000/DbName:currentSchema=SchemaName; +## jdbc:db2://127.0.0.1:50000/DbName:currentSchema=SchemaName; jdbc.url.db2 = jdbc:db2://{address}/{dbname} ## H2 Embedded jdbc.url.h2.embed = jdbc:h2:~/{dbname} diff --git a/jdbc-core/src/main/resources/settings/qdbc/qdbc.datatype.txt b/jdbc-core/src/main/resources/settings/qdbc/qdbc.datatype.txt new file mode 100644 index 0000000000000000000000000000000000000000..5316c5e65c21b0638437e8f60036da63ee26e83f --- /dev/null +++ b/jdbc-core/src/main/resources/settings/qdbc/qdbc.datatype.txt @@ -0,0 +1,131 @@ +# 数据类型转换 +# 左侧是代码中写的数据类型 +# 1.来自SQL模板中, 如#{createTime,jdbcType=TIMESTAMP} +# 2.来自实体类上的注解, 如@Column(columnDefinition="TIMESTAMP(3)") +# 右侧是java.sql.Types中字段名 +# 如果不是, 将会报警告, 其配置内容将被忽略 +# Qdbc读取到左侧的类型, 通过本配置转换为java.sql.Types中的字段值 +# 由TypedDbVariable传给SqlParameterValue +# 再由StatementCreatorUtils设置给PreparedStatement + +# 支持定义精度的数据类型 +supportDefinePrecision.jdbcTypes = BIT|TINYINT|SMALLINT|INTEGER|BIGINT +# 支持定义小数位的数据类型 +supportDefineScale.jdbcTypes = FLOAT|DOUBLE|REAL|NUMERIC|DECIMAL +# 支持定义长度的数据类型 +supportDefineLength.jdbcTypes = CHAR|VARCHAR|LONGVARCHAR|NCHAR|NVARCHAR + +# 来自Oracle +VARCHAR2 = VARCHAR +NVARCHAR = NVARCHAR +NCHAR = NCHAR +NUMBER = DECIMAL +# 长字符串, 最长2G +LONG = LONGVARCHAR + +# 来自MySQL的com.mysql.cj.MysqlType +DECIMAL = DECIMAL +DECIMAL_UNSIGNED = DECIMAL +TINYINT = TINYINT +TINYINT_UNSIGNED = TINYINT +BOOLEAN = BOOLEAN +SMALLINT = SMALLINT +SMALLINT_UNSIGNED = SMALLINT +INT = INTEGER +INT_UNSIGNED = INTEGER +FLOAT = REAL +FLOAT_UNSIGNED = REAL +DOUBLE = DOUBLE +DOUBLE_UNSIGNED = DOUBLE +NULL = NULL +TIMESTAMP = TIMESTAMP +BIGINT = BIGINT +BIGINT_UNSIGNED = BIGINT +MEDIUMINT = INTEGER +MEDIUMINT_UNSIGNED = INTEGER +DATE = DATE +TIME = TIME +DATETIME = TIMESTAMP +YEAR = DATE +VARCHAR = VARCHAR +VARBINARY = VARBINARY +BIT = BIT +JSON = LONGVARCHAR +ENUM = CHAR +SET = CHAR +TINYBLOB = VARBINARY +TINYTEXT = VARCHAR +MEDIUMBLOB = LONGVARBINARY +MEDIUMTEXT = LONGVARCHAR +LONGBLOB = LONGVARBINARY +LONGTEXT = LONGVARCHAR +BLOB = LONGVARBINARY +TEXT = LONGVARCHAR +CHAR = CHAR +BINARY = BINARY +GEOMETRY = BINARY +UNKNOWN = OTHER + +# 来自SqlServer的com.microsoft.sqlserver.jdbc.JDBCType +ARRAY = ARRAY +# BIGINT = BIGINT +# BINARY = BINARY +# BIT = BIT +# BLOB = BLOB +# BOOLEAN = BOOLEAN +# CHAR = CHAR +CLOB = CLOB +DATALINK = DATALINK +# DATE = DATE +# DECIMAL = DECIMAL +DISTINCT = DISTINCT +# DOUBLE = DOUBLE +# FLOAT = FLOAT +INTEGER = INTEGER +JAVA_OBJECT = JAVA_OBJECT +LONGVARBINARY = LONGVARBINARY +LONGVARCHAR = LONGVARCHAR +# NULL = NULL +NUMERIC = NUMERIC +# NVARCHAR = NVARCHAR +OTHER = OTHER +REAL = REAL +REF = REF +# SMALLINT = SMALLINT +STRUCT = STRUCT +# TIME = TIME +# TIMESTAMP = TIMESTAMP +# TINYINT = TINYINT +# VARBINARY = VARBINARY +# VARCHAR = VARCHAR +LOCALDATETIME = TIMESTAMP + +# 来自MariaDB的org.mariadb.jdbc.internal.ColumnType +OLDDECIMAL = DECIMAL +# TINYINT = SMALLINT +# SMALLINT = SMALLINT +# INTEGER = INTEGER +# FLOAT = REAL +# DOUBLE = DOUBLE +# NULL = NULL +# TIMESTAMP = TIMESTAMP +# BIGINT = BIGINT +# MEDIUMINT = INTEGER +# DATE = DATE +# TIME = TIME +# DATETIME = TIMESTAMP +# YEAR = SMALLINT +NEWDATE = DATE +# VARCHAR = VARCHAR +# BIT = BIT +# JSON = VARCHAR +# DECIMAL = DECIMAL +# ENUM = VARCHAR +# SET = VARCHAR +# TINYBLOB = VARBINARY +# MEDIUMBLOB = VARBINARY +# LONGBLOB = LONGVARBINARY +# BLOB = LONGVARBINARY +VARSTRING = VARCHAR +STRING = VARCHAR +# GEOMETRY = VARBINARY diff --git a/jdbc-core/src/main/resources/settings/qdbc/qdbc.taglib.txt b/jdbc-core/src/main/resources/settings/qdbc/qdbc.taglib.txt new file mode 100644 index 0000000000000000000000000000000000000000..1ea3f37a9bc8ce93e080c44a8b2405e67e01e6f2 --- /dev/null +++ b/jdbc-core/src/main/resources/settings/qdbc/qdbc.taglib.txt @@ -0,0 +1,48 @@ + +import = com.gitee.qdbp.staticize.tags.base.ImportTag +block = com.gitee.qdbp.staticize.tags.core.BlockTag +if = com.gitee.qdbp.staticize.tags.core.IfTag +elseif = com.gitee.qdbp.staticize.tags.core.ElseIfTag +else = com.gitee.qdbp.staticize.tags.core.ElseTag +each = com.gitee.qdbp.staticize.tags.core.EachTag +choose = com.gitee.qdbp.staticize.tags.core.ChooseTag +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 + +foreach = com.gitee.qdbp.jdbc.tags.SqlForEachTag +sql:include = com.gitee.qdbp.jdbc.tags.IncludeTag +append = com.gitee.qdbp.jdbc.tags.AppendTag +sql:append = com.gitee.qdbp.jdbc.tags.AppendTag +where = com.gitee.qdbp.jdbc.tags.WhereTag +sql:where = com.gitee.qdbp.jdbc.tags.WhereTag +set = com.gitee.qdbp.jdbc.tags.UpdateSetTag +update:set = com.gitee.qdbp.jdbc.tags.UpdateSetTag +trim = com.gitee.qdbp.jdbc.tags.TrimTag +sql:trim = com.gitee.qdbp.jdbc.tags.TrimTag +sql-in = com.gitee.qdbp.jdbc.tags.SqlInTag +sql:in = com.gitee.qdbp.jdbc.tags.SqlInTag +sql-like = com.gitee.qdbp.jdbc.tags.SqlLikeTag +sql:like = com.gitee.qdbp.jdbc.tags.SqlLikeTag +columns = com.gitee.qdbp.jdbc.tags.ColumnsTag +orderby = com.gitee.qdbp.jdbc.tags.OrderByTag +supports = com.gitee.qdbp.staticize.tags.base.IgnoreContentTag + +mapper = com.gitee.qdbp.jdbc.tags.mapper.MapperTag +sql = com.gitee.qdbp.jdbc.tags.mapper.SqlTag +select = com.gitee.qdbp.jdbc.tags.mapper.SelectTag +insert = com.gitee.qdbp.jdbc.tags.mapper.InsertTag +update = com.gitee.qdbp.jdbc.tags.mapper.UpdateTag +delete = com.gitee.qdbp.jdbc.tags.mapper.DeleteTag + + +fmt:date = com.gitee.qdbp.staticize.tags.core.DateFormatTag + +# 指定值栈包装类, 用于实现在业务侧自定义值栈自身的函数 +@StackWrapper = com.gitee.qdbp.staticize.common.StackWrapper + +# 全局静态类, 最常用的在这里指定, 省去 +@DateTools = com.gitee.qdbp.tools.utils.DateTools +@StringTools = com.gitee.qdbp.tools.utils.StringTools +@VerifyTools = com.gitee.qdbp.tools.utils.VerifyTools diff --git a/jdbc-core/src/main/resources/settings/sqls/global.recursive.sql b/jdbc-core/src/main/resources/settings/sqls/global.recursive.sql index 532d8151694ead53bafd0d082688b534b04cb95f..091b73c0c7f6bf6e7c4e1b06b127b36d940eca9c 100644 --- a/jdbc-core/src/main/resources/settings/sqls/global.recursive.sql +++ b/jdbc-core/src/main/resources/settings/sqls/global.recursive.sql @@ -6,14 +6,14 @@ -- WITH AS中不支持INNER JOIN, 于是改成 A, B WHERE A.PARENT=B.temp_parent -- <> 递归查询所有子节点, 标准递归语法 --- mysql.8,mariadb.10.2.2,postgresql,db2,sqlserver,sqlite.3.8.3 +-- mysql.8,mariadb.10.2.2,postgresql,db2,h2,sqlserver,sqlite.3.8.3 ${db.config.get('qdbc.recursive.keyword')} recursive_temp_table(temp_parent) AS ( SELECT ${codeColumn} temp_parent FROM ${tableName} WHERE #{filterWhere} UNION ALL SELECT ${codeColumn} FROM ${tableName} A, recursive_temp_table B - WHERE A.${parentColumn} = B.temp_parent + WHERE A.${parentColumn}=B.temp_parent #{filterWhere} ) SELECT ${selectColumns} @@ -45,3 +45,45 @@ CALL RECURSIVE_LIST_CHILDREN_QUERY ( "${searchWhere}", "${orderBy}" ); + + +-- <> 递归查询所有父节点, 标准递归语法 +-- mysql.8,mariadb.10.2.2,postgresql,db2,h2,sqlserver,sqlite.3.8.3 +${db.config.get('qdbc.recursive.keyword')} recursive_temp_table(temp_parent) AS ( + SELECT ${codeColumn} temp_parent FROM ${tableName} + WHERE + #{filterWhere} + UNION ALL + SELECT ${parentColumn} FROM ${tableName} A, recursive_temp_table B + WHERE B.temp_parent=A.${codeColumn} + #{filterWhere} +) +SELECT ${selectColumns} + FROM ${tableName} +WHERE ${codeColumn} IN ( SELECT temp_parent FROM recursive_temp_table ) + #{searchWhere} +#{orderBy} + + +-- <> 递归查询所有父节点, oracle专用递归语法 +SELECT ${selectColumns} FROM ( + SELECT * FROM ${tableName} + START WITH + CONNECT BY PRIOR ${parentColumn}=${codeColumn} + #{filterWhere} +) +#{searchWhere} +#{orderBy} + + +-- <> 递归查询所有父节点, 使用存储过程实现的递归查询 +CALL RECURSIVE_LIST_PARENTS_QUERY ( + "${tableName}", + "${startCodes}", + "${codeColumn}", + "${parentColumn}", + "${selectColumns}", + "${filterWhere}", + "${searchWhere}", + "${orderBy}" +); diff --git a/jdbc-spring/pom.xml b/jdbc-spring/pom.xml index 5a270f5e775a6c25a67cce8da999de5b730311b3..f0406118a0659697f820943315833621f30341aa 100644 --- a/jdbc-spring/pom.xml +++ b/jdbc-spring/pom.xml @@ -5,11 +5,11 @@ com.gitee.qdbp qdbp-parent - 5.0.0 + 5.0.1 qdbp-jdbc-spring - 3.2.9 + 3.4.6 jar ${project.artifactId} https://gitee.com/qdbp/qdbp-jdbc/ @@ -21,19 +21,13 @@ + com.gitee.qdbp qdbp-jdbc-core ${project.version} - - com.alibaba - druid - 1.1.23 - true - - org.springframework spring-context diff --git a/jdbc-spring/src/main/java/com/gitee/qdbp/filling/support/SpringEntityDataFillService.java b/jdbc-spring/src/main/java/com/gitee/qdbp/filling/support/SpringEntityDataFillService.java new file mode 100644 index 0000000000000000000000000000000000000000..5e576c4544c3a62984469d3078828af0b2b9beec --- /dev/null +++ b/jdbc-spring/src/main/java/com/gitee/qdbp/filling/support/SpringEntityDataFillService.java @@ -0,0 +1,48 @@ +package com.gitee.qdbp.filling.support; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import com.gitee.qdbp.filling.api.EntityDataTypedFillService; +import com.gitee.qdbp.filling.api.EntityFillType; +import com.gitee.qdbp.filling.biz.BaseEntityDataFillService; + +/** + * 实体数据处理类的Spring实现类 + * + * @author zhaohuihua + * @version 20210113 + */ +public class SpringEntityDataFillService extends BaseEntityDataFillService + implements InitializingBean, ApplicationContextAware { + + @Override + protected EntityDataTypedFillService findService(String type) { + return this.services.get(type); + } + + @Override + public void afterPropertiesSet() { + Map maps = context.getBeansOfType(EntityDataTypedFillService.class); + for (EntityDataTypedFillService bean : maps.values()) { + EntityFillType annotation = bean.getClass().getAnnotation(EntityFillType.class); + if (annotation != null && annotation.value().length > 0) { + for (String type : annotation.value()) { + this.services.put(type, bean); + } + } + } + } + + private ApplicationContext context; + private Map services = new ConcurrentHashMap<>(); + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.context = context; + } + +} diff --git a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootBaseFactory.java b/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootBaseFactory.java deleted file mode 100644 index c67f8a43ca20d8547d82821437e8a3c888486ae1..0000000000000000000000000000000000000000 --- a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootBaseFactory.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.gitee.qdbp.jdbc.support; - -import org.springframework.core.convert.ConversionService; -import org.springframework.core.convert.support.DefaultConversionService; -import com.gitee.qdbp.jdbc.api.QdbcBoot; -import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations; -import com.gitee.qdbp.jdbc.biz.QdbcBootImpl; -import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; -import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; - -/** - * 初始化QdbcBoot, 同时负责数据库插件初始化 - * - * @author zhaohuihua - * @version 20200128 - */ -public class QdbcBootBaseFactory { - - private boolean singleton = true; - private boolean initialized = false; - private QdbcBootImpl singletonInstance; - - /** Spring的类型转换处理类 **/ - private ConversionService conversionService; - /** 数据库插件容器类 **/ - private DbPluginContainer pluginContainer; - /** SqlBuffer数据库操作类 **/ - private SqlBufferJdbcOperations sqlBufferJdbcOperations; - /** - * 是否在系统启动时扫描SQL文件
- * 如果启动时不扫描, 则首次获取SQL模板时会扫描(很慢)
- * 所以, 一般情况下都应该配置为true, 除非系统中未使用SQL模板 - */ - private boolean sqlTemplateScanOnStartup = true; - - public void afterPropertiesSet() { - if (singletonInstance != null) { - singletonInstance.setSqlBufferJdbcOperations(sqlBufferJdbcOperations); - } - if (conversionService == null) { - conversionService = DefaultConversionService.getSharedInstance(); - } - if (pluginContainer == null) { - pluginContainer = new DbPluginContainer(); - } - pluginContainer.setConversionService(conversionService); - DbPluginContainer.init(pluginContainer); - - if (sqlTemplateScanOnStartup) { - SqlFragmentContainer.defaults().scanSqlFiles(); - } - } - - /** 获取实例 **/ - public QdbcBoot getObject() { - if (isSingleton()) { - if (!this.initialized) { - this.singletonInstance = createInstance(); - this.initialized = true; - } - return this.singletonInstance; - } else { - return createInstance(); - } - } - - protected QdbcBootImpl createInstance() { - QdbcBootImpl boot = new QdbcBootImpl(); - boot.setSqlBufferJdbcOperations(sqlBufferJdbcOperations); - return boot; - } - - /** 判断是否使用单例模式 **/ - public boolean isSingleton() { - return singleton; - } - - /** 设置是否使用单例模式 **/ - public void setSingleton(boolean singleton) { - this.singleton = singleton; - } - - /** Spring的类型转换处理类 **/ - public ConversionService getConversionService() { - return conversionService; - } - - /** Spring的类型转换处理类 **/ - public void setConversionService(ConversionService conversionService) { - this.conversionService = conversionService; - } - - /** SqlBuffer数据库操作类 **/ - public SqlBufferJdbcOperations getSqlBufferJdbcOperations() { - return sqlBufferJdbcOperations; - } - - /** SqlBuffer数据库操作类 **/ - public void setSqlBufferJdbcOperations(SqlBufferJdbcOperations sqlBufferJdbcOperations) { - this.sqlBufferJdbcOperations = sqlBufferJdbcOperations; - } - - /** 数据库插件容器类 **/ - public DbPluginContainer getPluginContainer() { - return pluginContainer; - } - - /** 数据库插件容器类 **/ - public void setPluginContainer(DbPluginContainer pluginContainer) { - this.pluginContainer = pluginContainer; - } - - /** 是否在系统启动时扫描SQL文件 **/ - public boolean isSqlTemplateScanOnStartup() { - return sqlTemplateScanOnStartup; - } - - /** - * 设置是否在系统启动时扫描SQL文件
- * 如果启动时不扫描, 则首次获取SQL模板时会扫描(很慢)
- * 所以, 一般情况下都应该配置为true, 除非系统中未使用SQL模板 - * - * @param sqlTemplateScanOnStartup 是否在系统启动时扫描SQL文件 - */ - public void setSqlTemplateScanOnStartup(boolean sqlTemplateScanOnStartup) { - this.sqlTemplateScanOnStartup = sqlTemplateScanOnStartup; - } - -} diff --git a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootFactoryBean.java b/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootFactoryBean.java index 9b71a260388398ecbb975e9f67178abb1f4bbe5a..ec5b0bc7167fe517fec8a2f6808afb4b1c841a62 100644 --- a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootFactoryBean.java +++ b/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootFactoryBean.java @@ -4,35 +4,96 @@ import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.core.convert.ConversionService; import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.api.SqlBufferJdbcOperations; +import com.gitee.qdbp.jdbc.biz.QdbcBootImpl; import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; +import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlFragmentOptions; /** * QdbcBootFactoryBean
- * 同时负责数据库插件初始化处理 + * 同时负责数据库插件初始化处理
+ * 在3.2.10版本改为QdbcBootSpringInitializer初始化
+ * -- 原因是QdbcBoot有可能存在多个实例, 就会导致DbPluginContainer重复初始化
+ * -- 但是如果在@PostConstruct方法中需要执行数据库操作, 将会导致在QdbcBootSpringInitializer前调用DbPluginContainer
+ * 在3.2.11版本中将QdbcBootSpringInitializer增加BeanPostProcessor接口
+ * -- 通过将QdbcBootSpringInitializer提前实例化解决上述问题
+ * -- 但是如果其他BeanPostProcessor子类(如ShiroFilterFactoryBean)导致某service(如UserService)提前实例化
+ * -- 而UserService的@PostConstruct方法中需要执行数据库操作, 则仍然存在问题
+ * 于是只能在3.2.12版本中恢复之前的初始化方式, 仍由QdbcBootFactoryBean初始化DbPluginContainer
+ * -- 在DbPluginContainer.init中增加判断, 如果已经通过同一个全局实例初始化了, 则不再执行, 避免重复初始化
+ *
+ * 为什么QdbcBootFactoryBean正常, 而QdbcBootSpringInitializer会存在这个问题呢
+ * -- 因为QdbcBootFactoryBean是其他service需要依赖的bean, 执行顺序为:
+ * -- XxxService - @Autowired:qdbcBoot - QdbcBootFactoryBean.afterPropertiesSet() - DbPluginContainer.init()
+ * -- 而QdbcBootSpringInitializer与其他bean没有依赖关系, 执行顺序为:
+ * -- 1. QdbcBootSpringInitializer.afterPropertiesSet() - DbPluginContainer.init()
+ * -- 2. XxxService - @Autowired:qdbcBoot - XxxService:@PostConstruct - DbPluginContainer.defaults()
+ * -- 正常情况下这是没有问题的, 因为BeanPostProcessor会使QdbcBootSpringInitializer优先初始化
+ * -- 但是ShiroFilterFactoryBean也是BeanPostProcessor, 就有可能导致UserService提前, 执行顺序为:
+ * -- 1. ShiroFilter - SessionManager - SessionDAO - authShiroRealm - UserService
+ * -- - @Autowired:qdbcBoot - XxxService:@PostConstruct - DbPluginContainer.defaults()
+ * -- 2. QdbcBootSpringInitializer.afterPropertiesSet() - DbPluginContainer.init()
+ * -- 此时DbPluginContainer.defaults()就在DbPluginContainer.init()之前被调用了
+ * -- 为什么DbPluginContainer.defaults()要做成全局静态方法?
+ * -- 因为SqlBuffer也要用到插件, 这里显然不能用注入的方式获取插件 * * @author zhaohuihua * @version 20200128 + * @see QdbcBootSpringInitializer */ -public class QdbcBootFactoryBean extends QdbcBootBaseFactory - implements FactoryBean, InitializingBean, ApplicationContextAware { +public class QdbcBootFactoryBean implements FactoryBean, InitializingBean, ApplicationContextAware { + + private boolean singleton = true; + private boolean initialized = false; + private QdbcBootImpl singletonInstance; private ApplicationContext context; + /** Spring的类型转换处理类 **/ + private ConversionService conversionService; + /** 数据库插件容器类 **/ + private DbPluginContainer pluginContainer; + /** SqlBuffer数据库操作类 **/ + private SqlBufferJdbcOperations sqlBufferJdbcOperations; + /** 是否在系统启动时扫描SQL文件 **/ + @Deprecated // 移至SimpleSqlFragmentOptions + private Boolean sqlTemplateScanOnStartup; + @Override public void afterPropertiesSet() { - this.scanAndRegisterPlugin(); - super.afterPropertiesSet(); - } - - protected void scanAndRegisterPlugin() { - DbPluginContainer pluginContainer = getPluginContainer(); - if (pluginContainer != null && context != null) { - PluginInstanceScanTools.scanAndRegisterWhereSqlBuilder(pluginContainer, context); - PluginInstanceScanTools.scanAndRegisterUpdateSqlBuilder(pluginContainer, context); - PluginInstanceScanTools.scanAndRegisterOrderBySqlBuilder(pluginContainer, context); - PluginInstanceScanTools.scanAndRegisterBatchInsertExecutor(pluginContainer, context); - PluginInstanceScanTools.scanAndRegisterBatchUpdateExecutor(pluginContainer, context); + if (singletonInstance != null) { + singletonInstance.setSqlBufferJdbcOperations(sqlBufferJdbcOperations); + } + if (pluginContainer == null) { + pluginContainer = new DbPluginContainer(); + } + if (conversionService != null && pluginContainer.getConversionService() == null) { + pluginContainer.setConversionService(conversionService); + } + if (sqlTemplateScanOnStartup != null && pluginContainer.getSqlFragmentOptions() == null) { + SimpleSqlFragmentOptions sqlFragmentOptions = new SimpleSqlFragmentOptions(); + sqlFragmentOptions.setSqlTemplateScanOnStartup(sqlTemplateScanOnStartup); + pluginContainer.setSqlFragmentOptions(sqlFragmentOptions); + } + QdbcBootSpringInitializer initializer = new QdbcBootSpringInitializer(); + initializer.setApplicationContext(context); + initializer.setPluginContainer(pluginContainer); + initializer.afterPropertiesSet(); + } + + /** 获取实例 **/ + @Override + public QdbcBoot getObject() { + if (isSingleton()) { + if (!this.initialized) { + this.singletonInstance = createInstance(); + this.initialized = true; + } + return this.singletonInstance; + } else { + return createInstance(); } } @@ -41,6 +102,23 @@ public class QdbcBootFactoryBean extends QdbcBootBaseFactory return QdbcBoot.class; } + protected QdbcBootImpl createInstance() { + QdbcBootImpl boot = new QdbcBootImpl(); + boot.setSqlBufferJdbcOperations(sqlBufferJdbcOperations); + return boot; + } + + /** 判断是否使用单例模式 **/ + @Override + public boolean isSingleton() { + return singleton; + } + + /** 设置是否使用单例模式 **/ + public void setSingleton(boolean singleton) { + this.singleton = singleton; + } + @Override public void setApplicationContext(ApplicationContext context) { this.context = context; @@ -50,4 +128,44 @@ public class QdbcBootFactoryBean extends QdbcBootBaseFactory return context; } + /** Spring的类型转换处理类 **/ + public ConversionService getConversionService() { + return conversionService; + } + + /** Spring的类型转换处理类 **/ + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } + + /** SqlBuffer数据库操作类 **/ + public SqlBufferJdbcOperations getSqlBufferJdbcOperations() { + return sqlBufferJdbcOperations; + } + + /** SqlBuffer数据库操作类 **/ + public void setSqlBufferJdbcOperations(SqlBufferJdbcOperations sqlBufferJdbcOperations) { + this.sqlBufferJdbcOperations = sqlBufferJdbcOperations; + } + + /** 是否在系统启动时扫描SQL文件 **/ + public Boolean isSqlTemplateScanOnStartup() { + return sqlTemplateScanOnStartup; + } + + /** 是否在系统启动时扫描SQL文件 **/ + public void setSqlTemplateScanOnStartup(Boolean sqlTemplateScanOnStartup) { + this.sqlTemplateScanOnStartup = sqlTemplateScanOnStartup; + } + + /** 数据库插件容器类 **/ + public DbPluginContainer getPluginContainer() { + return pluginContainer; + } + + /** 数据库插件容器类 **/ + public void setPluginContainer(DbPluginContainer pluginContainer) { + this.pluginContainer = pluginContainer; + } + } diff --git a/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootSpringInitializer.java b/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootSpringInitializer.java new file mode 100644 index 0000000000000000000000000000000000000000..0f827102f18209d10d170b4cff970e28b7ec0162 --- /dev/null +++ b/jdbc-spring/src/main/java/com/gitee/qdbp/jdbc/support/QdbcBootSpringInitializer.java @@ -0,0 +1,77 @@ +package com.gitee.qdbp.jdbc.support; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; + +/** + * QdbcBoot的Spring初始化处理类
+ * 从ApplicationContext中扫描并注册所有的WhereSqlBuilder/UpdateSqlBuilder子类 + * + * @author zhaohuihua + * @version 20201124 + * @since 3.2.10 + */ +// 只要继承了BeanPostProcessor, Bean的加载顺序就会提到最前面 +// 否则, 执行其他Bean的afterPropertiesSet方法时, DbPluginContainer.init()还没有执行 +// 就会导致用到DbPluginContainer里面的InnerInstance而不是spring注入的插件 +public class QdbcBootSpringInitializer implements BeanPostProcessor, InitializingBean, ApplicationContextAware { + + private ApplicationContext context; + /** 数据库插件容器类 **/ + private DbPluginContainer pluginContainer; + + @Override + public void afterPropertiesSet() { + if (pluginContainer == null) { + pluginContainer = new DbPluginContainer(); + } + this.scanAndRegisterPlugin(pluginContainer, context); + this.customizePlugins(pluginContainer); + DbPluginContainer.init(pluginContainer); + } + + protected void scanAndRegisterPlugin(DbPluginContainer pluginContainer, ApplicationContext context) { + PluginInstanceScanTools.scanAndRegisterWhereSqlBuilder(pluginContainer, context); + PluginInstanceScanTools.scanAndRegisterUpdateSqlBuilder(pluginContainer, context); + PluginInstanceScanTools.scanAndRegisterOrderBySqlBuilder(pluginContainer, context); + PluginInstanceScanTools.scanAndRegisterBatchInsertExecutor(pluginContainer, context); + PluginInstanceScanTools.scanAndRegisterBatchUpdateExecutor(pluginContainer, context); + } + + protected void customizePlugins(DbPluginContainer pluginContainer) { + } + + @Override + public void setApplicationContext(ApplicationContext context) { + this.context = context; + } + + public ApplicationContext getApplicationContext() { + return context; + } + + /** 数据库插件容器类 **/ + public DbPluginContainer getPluginContainer() { + return pluginContainer; + } + + /** 数据库插件容器类 **/ + public void setPluginContainer(DbPluginContainer pluginContainer) { + this.pluginContainer = pluginContainer; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + +} diff --git a/jdbc-test/pom.xml b/jdbc-test/pom.xml index dfd12ba3476d5be40525019d098d7106487a57d3..57ebcacf012dde6557d81950b0596a2b927a72d3 100644 --- a/jdbc-test/pom.xml +++ b/jdbc-test/pom.xml @@ -5,11 +5,11 @@ com.gitee.qdbp qdbp-parent - 5.0.0 + 5.0.1 qdbp-jdbc-test - 3.2.9 + 3.4.6 jar ${project.artifactId} https://gitee.com/qdbp/qdbp-jdbc/ @@ -25,10 +25,14 @@ + + h2 + h2 + true + mysql mysql - true oracle @@ -50,7 +54,7 @@ com.gitee.qdbp qdbp-tools - 5.2.5 + 5.2.25 @@ -64,6 +68,11 @@ druid 1.1.23 + + com.zaxxer + HikariCP + 3.4.5 + org.javassist @@ -81,11 +90,21 @@ aspectjrt 1.6.12 + + org.apache.poi + poi + 3.17 + + + org.apache.poi + poi-ooxml + 3.17 + mysql mysql-connector-java - 8.0.21 + 8.0.23 com.ibm.db2.jcc @@ -167,6 +186,13 @@ test + + com.gitee.qdbp + sqltest + 0.0.1 + system + ${project.basedir}/src/test/resources/libs/sqltest-0.0.1.jar +
@@ -183,6 +209,17 @@ jdbc.${jdbc.active}.properties + + src/main/java + + **/*.properties + **/*.json + **/*.tpl + **/*.txt + **/*.xml + **/*.sql + + src/test/java diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..a94f5883f583e1f7b45743703810ee323d47f22c --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/support/convert/LocalDateTimeToDateConverter.java @@ -0,0 +1,24 @@ +package com.gitee.qdbp.jdbc.support.convert; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.springframework.core.convert.converter.Converter; + +/** + * LocalDateTime转换为Date + * + * @author zhaohuihua + * @version 20210302 + */ +public class LocalDateTimeToDateConverter implements Converter { + + @Override + public Date convert(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()); + } +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/condition/DeptIsolationBuilder.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/condition/DeptIsolationBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..97862664026219efb95b7741750a53f94da30585 --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/condition/DeptIsolationBuilder.java @@ -0,0 +1,59 @@ +package com.gitee.qdbp.jdbc.test.condition; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.exception.UnsupportedFieldException; +import com.gitee.qdbp.jdbc.plugins.ColumnNameResolver; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.plugins.WhereSqlBuilder; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.test.service.EntityTools; + +/** + * 部门数据权限隔离SqlBuilder
+ * 如果不存在部门隔离字段, 那么就查询经办人=当前用户的记录
+ * 如果存在部门隔离字段, 那么就查询经办人=当前用户OR部门=指定部门的记录
+ * + * @author zhaohuihua + * @version 190308 + */ +@Service +public class DeptIsolationBuilder implements WhereSqlBuilder { + + @Autowired + private QdbcBoot qdbcBoot; + + @Override + public Class supported() { + return DeptIsolationWhere.class; + } + + @Override + public SqlBuffer buildSql(DeptIsolationWhere condition, ColumnNameResolver columnResolver, SqlDialect dialect) + throws UnsupportedFieldException { + if (condition.isEmpty()) { + return new SqlBuffer(); // 没有指定任何隔离字段, 没法加限制条件 + } + String deptColumn = columnResolver.getColumnName(condition.getDeptField()); + String userColumn = columnResolver.getColumnName(condition.getUserField()); + String agentColumn = columnResolver.getColumnName(condition.getAgentField()); + + Map params = new HashMap<>(); + params.put("userColumn", userColumn); + params.put("agentColumn", agentColumn); + params.put("deptColumn", deptColumn); + String userId = EntityTools.getCurrUserIdFromSession(); + List deptIds = EntityTools.getCurrDeptIdsFromSession(); + params.put("specUserIds", Arrays.asList(userId)); + params.put("specDeptIds", deptIds); + + String sqlId = "GlobalIsolation:deptIsolation"; + return qdbcBoot.getSqlDao().getSqlContent(sqlId, params); + } + +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/condition/DeptIsolationWhere.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/condition/DeptIsolationWhere.java new file mode 100644 index 0000000000000000000000000000000000000000..50fd2d4be14f6ca1e3d8211a2b3ab88ce2de8305 --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/condition/DeptIsolationWhere.java @@ -0,0 +1,78 @@ +package com.gitee.qdbp.jdbc.test.condition; + +import java.io.Serializable; +import com.gitee.qdbp.able.jdbc.base.WhereCondition; +import com.gitee.qdbp.tools.utils.VerifyTools; + +/** + * 部门隔离WHERE条件 + * + * @author zhaohuihua + * @version 20201113 + */ +public class DeptIsolationWhere implements WhereCondition, Serializable { + + /** serialVersionUID **/ + private static final long serialVersionUID = 1L; + + /** 用户隔离字段 **/ + private String userField; + /** 代办人隔离字段 **/ + private String agentField; + /** 部门隔离字段 **/ + private String deptField; + + public DeptIsolationWhere() { + } + + public DeptIsolationWhere(String deptField) { + this.deptField = deptField; + } + + public DeptIsolationWhere(String userField, String deptField) { + this.userField = userField; + this.deptField = deptField; + } + + public DeptIsolationWhere(String userField, String agentField, String deptField) { + this.userField = userField; + this.agentField = agentField; + this.deptField = deptField; + } + + /** 用户隔离字段 **/ + public String getUserField() { + return userField; + } + + /** 用户隔离字段 **/ + public void setUserField(String userField) { + this.userField = userField; + } + + /** 代办人隔离字段 **/ + public String getAgentField() { + return agentField; + } + + /** 代办人隔离字段 **/ + public void setAgentField(String agentField) { + this.agentField = agentField; + } + + /** 部门隔离字段 **/ + public String getDeptField() { + return deptField; + } + + /** 部门隔离字段 **/ + public void setDeptField(String deptField) { + this.deptField = deptField; + } + + @Override + public boolean isEmpty() { + return VerifyTools.isAllBlank(deptField, userField, agentField); + } + +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/mapper/SysDeptMapper.xml b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/mapper/SysDeptMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..c44c4d803d1c7c5a2fdd2c07634e225c468773b7 --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/mapper/SysDeptMapper.xml @@ -0,0 +1,48 @@ + + + + + com.gitee.qdbp.jdbc.test.model.SysDeptEntity + + + + mysql,mariadb,db2,h2,sqlserver.2008 + INSERT INTO TEST_DEPARTMENT_CORE_INFO (ID,TENANT_CODE,DEPT_CODE,DEPT_NAME,PARENT_CODE,SORT_INDEX,CREATOR_ID,CREATE_TIME,DATA_STATE) + VALUES + + (#{i.id},#{i.tenantCode},#{i.deptCode},#{i.deptName},#{i.parentCode},#{i.sortIndex},#{i.creatorId},#{i.createTime},#{i.dataState}) + + + + + + INSERT ALL + + INTO TEST_DEPARTMENT_CORE_INFO (ID,TENANT_CODE,DEPT_CODE,DEPT_NAME,PARENT_CODE,SORT_INDEX,CREATOR_ID,CREATE_TIME,DATA_STATE) + VALUES (#{i.id},#{i.tenantCode},#{i.deptCode},#{i.deptName},#{i.parentCode},#{i.sortIndex},#{i.creatorId},#{i.createTime},#{i.dataState}) + + SELECT * FROM DUAL + + + + + + diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/mapper/SysUserMapper.xml b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/mapper/SysUserMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..c183c6e6a47d97b7f16e6e4af141729082cfb11a --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/mapper/SysUserMapper.xml @@ -0,0 +1,89 @@ + + + + + com.gitee.qdbp.jdbc.test.model.SysUserEntity + com.gitee.qdbp.jdbc.test.model.SysRoleEntity + com.gitee.qdbp.jdbc.test.model.SysDeptEntity + + + + + + + + + + + + + + + + diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysLoggerEntity.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysLoggerEntity.java index b76ed4eb0ba4aa2fc62c3cead2243c179964e775..71ac8fcfe4d3e4b32c85ec3e4b90ff9ff5ea6161 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysLoggerEntity.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysLoggerEntity.java @@ -17,14 +17,27 @@ public class SysLoggerEntity extends CommEntity { /** 版本序列号 **/ private static final long serialVersionUID = 1L; + /** 租户编号 **/ @Column - private String name; + private String tenantCode; @Column + private String name; + @Column(columnDefinition = "CLOB") private String content; @Column @ColumnDefault("1") private Integer sortIndex; + /** 获取租户编号 **/ + public String getTenantCode() { + return tenantCode; + } + + /** 设置租户编号 **/ + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } + public String getName() { return name; } diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysSettingEntity.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysSettingEntity.java index 87176ff7cd5bc06930c5b5658d56647ab14123aa..78181a7c8e0095f2fa8ed247f399d0fcb70b30bf 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysSettingEntity.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysSettingEntity.java @@ -18,6 +18,9 @@ public class SysSettingEntity extends CommEntity { /** 版本序列号 **/ private static final long serialVersionUID = 1L; + /** 租户编号 **/ + @Column + private String tenantCode; @Column private String name; @Column @@ -34,6 +37,16 @@ public class SysSettingEntity extends CommEntity { @Column private Date updateTime; + /** 获取租户编号 **/ + public String getTenantCode() { + return tenantCode; + } + + /** 设置租户编号 **/ + public void setTenantCode(String tenantCode) { + this.tenantCode = tenantCode; + } + public String getName() { return name; } diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysUserEntity.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysUserEntity.java index ff9df25f4237cf6005885ebcbf5de58f0fb088e4..67c6ff2a11a740360625376ddac759870a4c3f43 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysUserEntity.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/SysUserEntity.java @@ -1,6 +1,7 @@ package com.gitee.qdbp.jdbc.test.model; import java.util.ArrayList; +import java.util.Date; import java.util.List; import javax.persistence.Column; import javax.persistence.Table; @@ -29,9 +30,9 @@ public class SysUserEntity extends CommEntity { /** 用户类型 **/ @Column private AccountType userType; - /** 部门编号 **/ + /** 部门ID **/ @Column - private String deptCode; + private String deptId; /** 账号/工号 **/ @Column private String userCode; @@ -56,6 +57,9 @@ public class SysUserEntity extends CommEntity { /** 头像 **/ @Column private String photo; + /** 生日 **/ + @Column + private Date birthday; /** 城市 **/ @Column private String city; @@ -98,14 +102,14 @@ public class SysUserEntity extends CommEntity { this.userType = userType; } - /** 获取部门编号 **/ - public String getDeptCode() { - return deptCode; + /** 获取部门ID **/ + public String getDeptId() { + return deptId; } - /** 设置部门编号 **/ - public void setDeptCode(String deptCode) { - this.deptCode = deptCode; + /** 设置部门ID **/ + public void setDeptId(String deptId) { + this.deptId = deptId; } /** 获取账号/工号 **/ @@ -187,6 +191,16 @@ public class SysUserEntity extends CommEntity { public void setPhoto(String photo) { this.photo = photo; } + + /** 获取生日 **/ + public Date getBirthday() { + return birthday; + } + + /** 设置生日 **/ + public void setBirthday(Date birthday) { + this.birthday = birthday; + } /** 获取城市 **/ public String getCity() { @@ -308,7 +322,7 @@ public class SysUserEntity extends CommEntity { instance.setId(this.getId()); // 用户ID instance.setTenantCode(this.getTenantCode()); // 租户编号 instance.setUserType(this.getUserType()); // 用户类型 - instance.setDeptCode(this.getDeptCode()); // 部门编号 + instance.setDeptId(this.getDeptId()); // 部门ID instance.setUserCode(this.getUserCode()); // 账号/工号 instance.setUserName(this.getUserName()); // 登录用户名 instance.setNickName(this.getNickName()); // 昵称 diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/UserAndDeptResult.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/UserAndDeptResult.java new file mode 100644 index 0000000000000000000000000000000000000000..a14637a1dcc2719400780abb4b4dcc68128353a4 --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/model/UserAndDeptResult.java @@ -0,0 +1,31 @@ +package com.gitee.qdbp.jdbc.test.model; + +public class UserAndDeptResult extends SysUserEntity { + + /** serialVersionUID **/ + private static final long serialVersionUID = 1L; + /** 部门编号 **/ + private String deptCode; + /** 部门名称 **/ + private String deptName; + + /** 获取部门编号 **/ + public String getDeptCode() { + return deptCode; + } + + /** 设置部门编号 **/ + public void setDeptCode(String deptCode) { + this.deptCode = deptCode; + } + + /** 获取部门名称 **/ + public String getDeptName() { + return deptName; + } + + /** 设置部门名称 **/ + public void setDeptName(String deptName) { + this.deptName = deptName; + } +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/EntityTools.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/EntityTools.java index 7839da553a77e4b329674f0daab09fd67753fc7d..aff16fc4a1c950996e4fa148d0f7cbfd53e44d5b 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/EntityTools.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/EntityTools.java @@ -3,8 +3,12 @@ package com.gitee.qdbp.jdbc.test.service; import java.util.Arrays; import java.util.List; import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.plugins.impl.ColumnNamingConverter; import com.gitee.qdbp.jdbc.sql.SqlBuffer; -import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.jdbc.test.condition.DeptIsolationBuilder; +import com.gitee.qdbp.jdbc.test.condition.DeptIsolationWhere; +import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.tools.utils.VerifyTools; /** * 实体类工具 @@ -13,15 +17,38 @@ import com.gitee.qdbp.jdbc.sql.SqlTools; * @version 20201007 */ public class EntityTools { - - private static List getCurrDeptIdsFromSession() { + + public static String getCurrUserIdFromSession() { + // 模拟从SESSION中获取当前登录用户ID + return "U0000001"; + } + + public static List getCurrDeptIdsFromSession() { // 模拟从SESSION中获取当前登录用户有权限的部门ID return Arrays.asList("D0000001", "D0000005", "D0000012"); } /** 生成部门数据权限查询条件 **/ public static SqlBuffer buildDeptDataPermission(String deptColumn, SqlDialect dialect) { - List deptIds = getCurrDeptIdsFromSession(); - return SqlTools.buildInSql(deptColumn, deptIds, dialect); + return buildDeptDataPermission(null, null, deptColumn, dialect); + } + + /** 生成部门数据权限查询条件 **/ + public static SqlBuffer buildDeptDataPermission(String userColumn, String deptColumn, SqlDialect dialect) { + return buildDeptDataPermission(userColumn, null, deptColumn, dialect); + } + + /** 生成部门数据权限查询条件 **/ + public static SqlBuffer buildDeptDataPermission(String userColumn, String agentColumn, String deptColumn, + SqlDialect dialect) { + if (VerifyTools.isAllBlank(userColumn, agentColumn, deptColumn)) { + return new SqlBuffer(); + } + DeptIsolationWhere condition = new DeptIsolationWhere(); + condition.setDeptField(deptColumn); + condition.setUserField(userColumn); + condition.setAgentField(agentColumn); + DeptIsolationBuilder builder = DbTools.getWhereSqlBuilder(DeptIsolationWhere.class); + return builder.buildSql(condition, ColumnNamingConverter.defaults(), dialect); } } diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysSettingService.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysSettingService.java index 52a301c490b939674722c81d744b4ed2cb155285..3d78faeb7e50ed1173b80ac1a3ad8dcfaca26b3d 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysSettingService.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysSettingService.java @@ -20,6 +20,7 @@ import com.gitee.qdbp.jdbc.api.QdbcBoot; import com.gitee.qdbp.jdbc.test.enums.SettingState; import com.gitee.qdbp.jdbc.test.model.SysLoggerEntity; import com.gitee.qdbp.jdbc.test.model.SysSettingEntity; +import com.gitee.qdbp.tools.utils.VerifyTools; @Service public class SysSettingService { @@ -78,14 +79,14 @@ public class SysSettingService { } @Transactional(propagation = Propagation.REQUIRED) - public void clearAllRecord() { + public void clearAllRecord(String tenantCode) { { CrudDao dao = qdbcBoot.buildCrudDao(SysSettingEntity.class); - dao.physicalDelete(DbWhere.NONE); + dao.physicalDelete(new DbWhere().andEquals("tenantCode", tenantCode)); } { CrudDao dao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); - dao.physicalDelete(DbWhere.NONE); + dao.physicalDelete(new DbWhere().andEquals("tenantCode", tenantCode)); } } @@ -106,6 +107,8 @@ public class SysSettingService { @Transactional(propagation = Propagation.REQUIRED) public String createSetting(SysSettingEntity entity, long sleepMills, TestModel testModel) { + VerifyTools.requireNotBlank(entity.getTenantCode(), "tenantCode"); + String tenantCode = entity.getTenantCode(); // 新增数据 log.debug("Before insert setting"); entity.setState(SettingState.DISABLED); @@ -113,7 +116,7 @@ public class SysSettingService { String id = dao.insert(entity, true); log.debug("Success to insert setting, id={}", id); if (testModel != TestModel.skipLogging) { - recordLogger("SysSetting", "Success to create " + entity.getName(), true); + recordLogger(tenantCode, "SysSetting", "Success to create " + entity.getName(), true); } if (sleepMills > 0) { @@ -141,24 +144,26 @@ public class SysSettingService { dao.update(ud, where, true, true); if (testModel != TestModel.skipLogging) { log.debug("Success to update setting, id={}", id); - recordLogger("SysSetting", "Success to update setting for " + entity.getName(), logSuccess); + recordLogger(tenantCode, "SysSetting", "Success to update setting for " + entity.getName(), logSuccess); } return id; } catch (Throwable e) { log.debug("Failed to update setting, id={}, {}", id, e.toString()); if (testModel != TestModel.skipLogging) { - recordLogger("SysSetting", "Failed to update setting for " + entity.getName(), logSuccess); + recordLogger(entity.getTenantCode(), "SysSetting", "Failed to update setting for " + entity.getName(), + logSuccess); } throw e; } } - private void recordLogger(String name, String content, boolean success) { + private void recordLogger(String tenantCode, String name, String content, boolean success) { // 非事务执行, 如果已在事务中则挂起存在的事务 int transLevel = TransactionDefinition.PROPAGATION_NOT_SUPPORTED; TransactionStatus ts = transactionManager.getTransaction(new DefaultTransactionDefinition(transLevel)); CrudDao dao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); SysLoggerEntity entity = new SysLoggerEntity(); + entity.setTenantCode(tenantCode); entity.setName(name); if (success) { // 如果模拟成功就设置必填字段, 否则利用必填字段为空制造异常 diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserService.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserService.java index f7d517639652defc7ae3da29846302c1831682f1..9ba075652c51597240c05533e2eef76f76f6dd5c 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserService.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserService.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.gitee.qdbp.able.jdbc.condition.DbWhere; @@ -14,15 +15,18 @@ import com.gitee.qdbp.able.jdbc.ordering.Orderings; import com.gitee.qdbp.able.jdbc.paging.PageList; import com.gitee.qdbp.jdbc.api.CrudDao; import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.api.SqlDao; import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; 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.RoleIdUserResult; +import com.gitee.qdbp.jdbc.test.model.SysDeptEntity; import com.gitee.qdbp.jdbc.test.model.SysRoleEntity; import com.gitee.qdbp.jdbc.test.model.SysUserEntity; import com.gitee.qdbp.jdbc.test.model.SysUserRoleEntity; +import com.gitee.qdbp.jdbc.test.model.UserAndDeptResult; import com.gitee.qdbp.jdbc.test.model.UserIdRoleResult; import com.gitee.qdbp.tools.utils.StringTools; import com.gitee.qdbp.tools.utils.VerifyTools; @@ -32,9 +36,51 @@ public class SysUserService { @Autowired private QdbcBoot qdbcBoot; + private SqlDao sqlDao; + private CrudDao userDao; + private CrudDao roleDao; + private CrudDao deptDao; + + @PostConstruct + private void init() { + sqlDao = qdbcBoot.getSqlDao(); + userDao = qdbcBoot.buildCrudDao(SysUserEntity.class); + roleDao = qdbcBoot.buildCrudDao(SysRoleEntity.class); + deptDao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + } + + /** 查询用户信息 (来自sql模板) **/ + public PageList getUserInfosForSql(Map where, OrderPaging odpg) { + String sqlId = "user.info.query"; + return doQueryUserInfos(sqlId, where, odpg); + } + + /** 查询用户信息 (来自xml模板) **/ + public PageList getUserInfosForXml(Map where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryUserInfos"; + return doQueryUserInfos(sqlId, where, odpg); + } + + private PageList doQueryUserInfos(String sqlId, Map where, OrderPaging odpg) { + Map params = new HashMap<>(); + params.put("where", where); + params.put("orderings", odpg.getOrderings()); + return sqlDao.pageForObjects(sqlId, params, odpg, UserAndDeptResult.class); + } /** 查询指定用户所分配的角色信息 **/ - public List getUserRoles(String userId, DbWhere where, Orderings orderings) { + public List getUserRolesForSql(String userId, DbWhere where, Orderings orderings) { + String sqlId = "user.roles.query"; + return doQueryUserRoles(sqlId, userId, where, orderings); + } + + /** 查询指定用户所分配的角色信息 **/ + public List getUserRolesForXml(String userId, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userId, where, orderings); + } + + private List doQueryUserRoles(String sqlId, String userId, DbWhere where, Orderings orderings) { VerifyTools.requireNonNull(userId, "userId"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); @@ -50,12 +96,23 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); } + return sqlDao.listForObjects(sqlId, params, SysRoleEntity.class); + } + + /** 批量查询用户所分配的角色信息 **/ + public List getUserRolesForSql(List userIds, DbWhere where, Orderings orderings) { String sqlId = "user.roles.query"; - return qdbcBoot.getSqlDao().listForObjects(sqlId, params, SysRoleEntity.class); + return doQueryUserRoles(sqlId, userIds, where, orderings); } /** 批量查询用户所分配的角色信息 **/ - public List getUserRoles(List userIds, DbWhere where, Orderings orderings) { + public List getUserRolesForXml(List userIds, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userIds, where, orderings); + } + + private List doQueryUserRoles(String sqlId, List userIds, DbWhere where, + Orderings orderings) { VerifyTools.requireNonNull(userIds, "userIds"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); @@ -71,12 +128,22 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); } + return sqlDao.listForObjects(sqlId, params, UserIdRoleResult.class); + } + + /** 查询指定用户所分配的角色信息 **/ + public PageList getUserRolesForSql(String userId, DbWhere where, OrderPaging odpg) { String sqlId = "user.roles.query"; - return qdbcBoot.getSqlDao().listForObjects(sqlId, params, UserIdRoleResult.class); + return doQueryUserRoles(sqlId, userId, where, odpg); } /** 查询指定用户所分配的角色信息 **/ - public PageList getUserRoles(String userId, DbWhere where, OrderPaging odpg) { + public PageList getUserRolesForXml(String userId, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userId, where, odpg); + } + + private PageList doQueryUserRoles(String sqlId, String userId, DbWhere where, OrderPaging odpg) { VerifyTools.requireNonNull(userId, "userId"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); @@ -92,12 +159,23 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); } + return sqlDao.pageForObjects(sqlId, params, odpg, SysRoleEntity.class); + } + + /** 批量查询用户所分配的角色信息 **/ + public PageList getUserRolesForSql(List userIds, DbWhere where, OrderPaging odpg) { String sqlId = "user.roles.query"; - return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, SysRoleEntity.class); + return doQueryUserRoles(sqlId, userIds, where, odpg); } /** 批量查询用户所分配的角色信息 **/ - public PageList getUserRoles(List userIds, DbWhere where, OrderPaging odpg) { + public PageList getUserRolesForXml(List userIds, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userIds, where, odpg); + } + + private PageList doQueryUserRoles(String sqlId, List userIds, DbWhere where, + OrderPaging odpg) { VerifyTools.requireNonNull(userIds, "userIds"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); @@ -113,12 +191,22 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); } - String sqlId = "user.roles.query"; - return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, UserIdRoleResult.class); + return sqlDao.pageForObjects(sqlId, params, odpg, UserIdRoleResult.class); } - /** 查询指定角色下的用户信息 **/ - public List getRoleUsers(String roleId, DbWhere where, Orderings orderings) { + /** 查询指定角色下的用户信息 (来自sql模板) **/ + public List getRoleUsersForSql(String roleId, DbWhere where, Orderings orderings) { + String sqlId = "role.users.query"; + return doQueryRoleUsers(sqlId, roleId, where, orderings); + } + + /** 查询指定角色下的用户信息 (来自xml模板) **/ + public List getRoleUsersForXml(String roleId, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleId, where, orderings); + } + + private List doQueryRoleUsers(String sqlId, String roleId, DbWhere where, Orderings orderings) { VerifyTools.requireNonNull(roleId, "roleId"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); @@ -134,12 +222,23 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); } + return sqlDao.listForObjects(sqlId, params, SysUserEntity.class); + } + + /** 批量查询指定角色下的用户信息 (来自sql模板) **/ + public List getRoleUsersForSql(List roleIds, DbWhere where, Orderings orderings) { String sqlId = "role.users.query"; - return qdbcBoot.getSqlDao().listForObjects(sqlId, params, SysUserEntity.class); + return doQueryRoleUsers(sqlId, roleIds, where, orderings); } - /** 批量查询指定角色下的用户信息 **/ - public List getRoleUsers(List roleIds, DbWhere where, Orderings orderings) { + /** 批量查询指定角色下的用户信息 (来自xml模板) **/ + public List getRoleUsersForXml(List roleIds, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleIds, where, orderings); + } + + private List doQueryRoleUsers(String sqlId, List roleIds, DbWhere where, + Orderings orderings) { VerifyTools.requireNonNull(roleIds, "roleIds"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); @@ -155,12 +254,22 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); } + return sqlDao.listForObjects(sqlId, params, RoleIdUserResult.class); + } + + /** 查询指定角色下的用户信息 (来自sql模板) **/ + public PageList getRoleUsersForSql(String roleId, DbWhere where, OrderPaging odpg) { String sqlId = "role.users.query"; - return qdbcBoot.getSqlDao().listForObjects(sqlId, params, RoleIdUserResult.class); + return doQueryRoleUsers(sqlId, roleId, where, odpg); } - /** 查询指定角色下的用户信息 **/ - public PageList getRoleUsers(String roleId, DbWhere where, OrderPaging odpg) { + /** 查询指定角色下的用户信息 (来自xml模板) **/ + public PageList getRoleUsersForXml(String roleId, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleId, where, odpg); + } + + private PageList doQueryRoleUsers(String sqlId, String roleId, DbWhere where, OrderPaging odpg) { VerifyTools.requireNonNull(roleId, "roleId"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); @@ -176,12 +285,23 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); } + return sqlDao.pageForObjects(sqlId, params, odpg, SysUserEntity.class); + } + + /** 批量查询指定角色下的用户信息 (来自sql模板) **/ + public PageList getRoleUsersForSql(List roleIds, DbWhere where, OrderPaging odpg) { String sqlId = "role.users.query"; - return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, SysUserEntity.class); + return doQueryRoleUsers(sqlId, roleIds, where, odpg); } - /** 批量查询指定角色下的用户信息 **/ - public PageList getRoleUsers(List roleIds, DbWhere where, OrderPaging odpg) { + /** 批量查询指定角色下的用户信息 (来自xml模板) **/ + public PageList getRoleUsersForXml(List roleIds, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleIds, where, odpg); + } + + private PageList doQueryRoleUsers(String sqlId, List roleIds, DbWhere where, + OrderPaging odpg) { VerifyTools.requireNonNull(roleIds, "roleIds"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); @@ -197,8 +317,7 @@ public class SysUserService { params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); } - String sqlId = "role.users.query"; - return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, RoleIdUserResult.class); + return sqlDao.pageForObjects(sqlId, params, odpg, RoleIdUserResult.class); } /** 清除用户角色关联表数据 **/ @@ -206,49 +325,97 @@ public class SysUserService { String sqlId = "user.role.ref.clear.by.tenant"; Map params = new HashMap<>(); params.put("tenantCode", tenantCode); - return qdbcBoot.getSqlDao().delete(sqlId, params); + return sqlDao.delete(sqlId, params); + } + + /** 初始化用户部门数据 **/ + public void initUserDepts(String tenantCode, boolean clear) { + initUserDepts(tenantCode, tenantCode, clear); + } + + /** 初始化用户部门数据 **/ + public void initUserDepts(String tenantCode, String prefix, boolean clear) { + List deptIds = initDepts(tenantCode, prefix, clear); + initDeptUsers(tenantCode, prefix, deptIds, clear); } /** 初始化用户角色数据 **/ - public void initUserRoles(char prefix, String tenantCode) { - List userIds = initUsers(prefix, tenantCode); - List roleIds = initRoles(prefix, tenantCode); - initUserRoles(tenantCode, userIds, roleIds); + public void initUserRoles(String tenantCode, boolean clear) { + initUserRoles(tenantCode, tenantCode, clear); } - private List initUsers(char prefix, String tenantCode) { - CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); - { // 清理用户数据 + /** 初始化用户角色数据 **/ + public void initUserRoles(String tenantCode, String prefix, boolean clear) { + List userIds = initUsers(tenantCode, prefix, clear); + List roleIds = initRoles(tenantCode, prefix, clear); + initUserRoles(tenantCode, userIds, roleIds, clear); + } + + private List initDeptUsers(String tenantCode, String prefix, List deptIds, boolean clear) { + if (clear) { // 清理用户数据 DbWhere where = new DbWhere(); where.on("tenantCode", "=", tenantCode); - dao.physicalDelete(where); + userDao.physicalDelete(where); } { // 新增用户数据 SysUserEntity entity = new SysUserEntity(); entity.setTenantCode(tenantCode); entity.setSuperman(false); - entity.setDeptCode("0"); + entity.setGender(Gender.FEMALE); + entity.setUserType(UserType.USER); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + List entities = new ArrayList<>(); + int index = 0; + for (int i = 0; i < deptIds.size(); i++) { + String deptId = deptIds.get(i); + entity.setDeptId(deptId); + // 第1个部门2个用户, 第2个部门4个, 以此类推 + for (int j = 0; j < (i + 1) * 2; j++) { + index++; + entity.setId(prefix + "U" + "0000" + StringTools.pad(index, 3)); + entity.setUserCode(prefix + "U" + StringTools.pad(index, 3)); + entity.setNickName(prefix + "U" + StringTools.pad(index, 3)); + entities.add(entity.to(SysUserEntity.class)); + } + } + // 批量新增用户 + return userDao.inserts(entities); + } + } + + private List initUsers(String tenantCode, String prefix, boolean clear) { + if (clear) { // 清理用户数据 + DbWhere where = new DbWhere(); + where.on("tenantCode", "=", tenantCode); + userDao.physicalDelete(where); + } + { // 新增用户数据 + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(tenantCode); + entity.setSuperman(false); + entity.setDeptId("0"); entity.setGender(Gender.FEMALE); entity.setUserType(UserType.USER); entity.setUserState(UserState.NORMAL); entity.setUserSource(UserSource.INPUT); List entities = new ArrayList<>(); for (int i = 1; i <= 10; i++) { - entity.setId("U" + prefix + "0000" + StringTools.pad(i, 3)); - entity.setUserCode("U" + StringTools.pad(i, 3)); + entity.setId(prefix + "U" + "0000" + StringTools.pad(i, 3)); + entity.setUserCode(prefix + "U" + StringTools.pad(i, 3)); + entity.setNickName(prefix + "U" + StringTools.pad(i, 3)); entities.add(entity.to(SysUserEntity.class)); } // 批量新增用户 - return dao.inserts(entities); + return userDao.inserts(entities); } } - private List initRoles(char prefix, String tenantCode) { - CrudDao dao = qdbcBoot.buildCrudDao(SysRoleEntity.class); - { // 清理角色数据 + private List initRoles(String tenantCode, String prefix, boolean clear) { + if (clear) { // 清理角色数据 DbWhere where = new DbWhere(); where.on("tenantCode", "=", tenantCode); - dao.physicalDelete(where); + roleDao.physicalDelete(where); } { // 新增角色数据 SysRoleEntity entity = new SysRoleEntity(); @@ -258,20 +425,43 @@ public class SysUserService { List entities = new ArrayList<>(); for (int i = 1; i <= 20; i++) { entity.setSortIndex(i); - entity.setId("R" + prefix + "0000" + StringTools.pad(i, 3)); - entity.setRoleName("R" + StringTools.pad(i, 3)); + entity.setId(prefix + "R" + "0000" + StringTools.pad(i, 3)); + entity.setRoleName(prefix + "R" + StringTools.pad(i, 3)); entities.add(entity.to(SysRoleEntity.class)); } // 批量新增角色 - return dao.inserts(entities); + return roleDao.inserts(entities); + } + } + + private List initDepts(String tenantCode, String prefix, boolean clear) { + if (clear) { // 清理部门数据 + DbWhere where = new DbWhere(); + where.on("tenantCode", "=", tenantCode); + deptDao.physicalDelete(where); + } + { // 新增部门数据 + List entities = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + SysDeptEntity entity = new SysDeptEntity(); + entity.setTenantCode(tenantCode); + entity.setParentCode("0"); // 上级部门编号 + entity.setSortIndex(i); // 排序号(越小越靠前) + entity.setId(prefix + "D" + "0000" + StringTools.pad(i, 3)); + entity.setDeptCode(prefix + "D" + StringTools.pad(i, 3)); + entity.setDeptName(prefix + "D" + StringTools.pad(i, 3)); + entities.add(entity); + } + // 批量新增部门 + return deptDao.inserts(entities); } } - private List initUserRoles(String tenantCode, List userIds, List roleIds) { - clearUserRoleRefByTenant(tenantCode); + private List initUserRoles(String tenantCode, List userIds, List roleIds, boolean clear) { + if (clear) { + clearUserRoleRefByTenant(tenantCode); + } - CrudDao dao = qdbcBoot.buildCrudDao(SysUserRoleEntity.class); - dao.physicalDelete(DbWhere.NONE); List entities = new ArrayList<>(); for (int i = 0; i < userIds.size(); i++) { String userId = userIds.get(i); @@ -283,6 +473,7 @@ public class SysUserService { entities.add(entity); } } + CrudDao dao = qdbcBoot.buildCrudDao(SysUserRoleEntity.class); // 批量新增用户角色关系 return dao.inserts(entities); } diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserStream.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserStream.java new file mode 100644 index 0000000000000000000000000000000000000000..142f6e98bfaba57d01251126ffee81516f53f90d --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/service/SysUserStream.java @@ -0,0 +1,563 @@ +package com.gitee.qdbp.jdbc.test.service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.PartList; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; +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.RoleIdUserResult; +import com.gitee.qdbp.jdbc.test.model.SysDeptEntity; +import com.gitee.qdbp.jdbc.test.model.SysRoleEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserRoleEntity; +import com.gitee.qdbp.jdbc.test.model.UserAndDeptResult; +import com.gitee.qdbp.jdbc.test.model.UserIdRoleResult; +import com.gitee.qdbp.tools.utils.StringTools; +import com.gitee.qdbp.tools.utils.VerifyTools; + +@Service +public class SysUserStream { + + @Autowired + private QdbcBoot qdbcBoot; + + /** 查询用户信息 (来自sql模板) **/ + public PartList getUserInfosForSql(Map where, OrderPaging odpg) { + String sqlId = "user.info.query"; + return doQueryUserInfos(sqlId, where, odpg); + } + + /** 查询用户信息 (来自xml模板) **/ + public PartList getUserInfosForXml(Map where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryUserInfos"; + return doQueryUserInfos(sqlId, where, odpg); + } + + private PartList doQueryUserInfos(String sqlId, Map where, OrderPaging odpg) { + Map params = new HashMap<>(); + params.put("where", where); + params.put("orderings", odpg.getOrderings()); + // sqlDao.pageForObjects(sqlId, params, odpg, UserAndDeptResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .resultAs(UserAndDeptResult.class) + .list().asPartList(); + // @formatter:on + } + + /** 查询指定用户所分配的角色信息 **/ + public List getUserRolesForSql(String userId, DbWhere where, Orderings orderings) { + String sqlId = "user.roles.query"; + return doQueryUserRoles(sqlId, userId, where, orderings); + } + + /** 查询指定用户所分配的角色信息 **/ + public List getUserRolesForXml(String userId, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userId, where, orderings); + } + + private List doQueryUserRoles(String sqlId, String userId, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(userId, "userId"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*")); + params.put("userIds", Arrays.asList(userId)); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + // return sqlDao.listForObjects(sqlId, params, SysRoleEntity.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .resultAs(SysRoleEntity.class) + .list(); + // @formatter:on + } + + /** 批量查询用户所分配的角色信息 **/ + public List getUserRolesForSql(List userIds, DbWhere where, Orderings orderings) { + String sqlId = "user.roles.query"; + return doQueryUserRoles(sqlId, userIds, where, orderings); + } + + /** 批量查询用户所分配的角色信息 **/ + public List getUserRolesForXml(List userIds, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userIds, where, orderings); + } + + private List doQueryUserRoles(String sqlId, List userIds, DbWhere where, + Orderings orderings) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.userId,r.*")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + // return sqlDao.listForObjects(sqlId, params, UserIdRoleResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .resultAs(UserIdRoleResult.class) + .list(); + // @formatter:on + } + + /** 查询指定用户所分配的角色信息 **/ + public PartList getUserRolesForSql(String userId, DbWhere where, OrderPaging odpg) { + String sqlId = "user.roles.query"; + return doQueryUserRoles(sqlId, userId, where, odpg); + } + + /** 查询指定用户所分配的角色信息 **/ + public PartList getUserRolesForXml(String userId, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userId, where, odpg); + } + + private PartList doQueryUserRoles(String sqlId, String userId, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(userId, "userId"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*")); + params.put("userIds", Arrays.asList(userId)); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + // return sqlDao.pageForObjects(sqlId, params, odpg, SysRoleEntity.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .resultAs(SysRoleEntity.class) + .list().asPartList(); + // @formatter:on + } + + /** 批量查询用户所分配的角色信息 **/ + public PartList getUserRolesForSql(List userIds, DbWhere where, OrderPaging odpg) { + String sqlId = "user.roles.query"; + return doQueryUserRoles(sqlId, userIds, where, odpg); + } + + /** 批量查询用户所分配的角色信息 **/ + public PartList getUserRolesForXml(List userIds, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryUserRoles"; + return doQueryUserRoles(sqlId, userIds, where, odpg); + } + + private PartList doQueryUserRoles(String sqlId, List userIds, DbWhere where, + OrderPaging odpg) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.userId,r.*")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + // return sqlDao.pageForObjects(sqlId, params, odpg, UserIdRoleResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .resultAs(UserIdRoleResult.class) + .list().asPartList(); + // @formatter:on + } + + /** 查询指定角色下的用户信息 (来自sql模板) **/ + public List getRoleUsersForSql(String roleId, DbWhere where, Orderings orderings) { + String sqlId = "role.users.query"; + return doQueryRoleUsers(sqlId, roleId, where, orderings); + } + + /** 查询指定角色下的用户信息 (来自xml模板) **/ + public List getRoleUsersForXml(String roleId, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleId, where, orderings); + } + + private List doQueryRoleUsers(String sqlId, String roleId, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(roleId, "roleId"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("u.*")); + params.put("roleIds", Arrays.asList(roleId)); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + // return sqlDao.listForObjects(sqlId, params, SysUserEntity.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .resultAs(SysUserEntity.class) + .list(); + // @formatter:on + } + + /** 批量查询指定角色下的用户信息 (来自sql模板) **/ + public List getRoleUsersForSql(List roleIds, DbWhere where, Orderings orderings) { + String sqlId = "role.users.query"; + return doQueryRoleUsers(sqlId, roleIds, where, orderings); + } + + /** 批量查询指定角色下的用户信息 (来自xml模板) **/ + public List getRoleUsersForXml(List roleIds, DbWhere where, Orderings orderings) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleIds, where, orderings); + } + + private List doQueryRoleUsers(String sqlId, List roleIds, DbWhere where, + Orderings orderings) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + // return sqlDao.listForObjects(sqlId, params, .class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .resultAs(RoleIdUserResult.class) + .list(); + // @formatter:on + } + + /** 查询指定角色下的用户信息 (来自sql模板) **/ + public PartList getRoleUsersForSql(String roleId, DbWhere where, OrderPaging odpg) { + String sqlId = "role.users.query"; + return doQueryRoleUsers(sqlId, roleId, where, odpg); + } + + /** 查询指定角色下的用户信息 (来自xml模板) **/ + public PartList getRoleUsersForXml(String roleId, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleId, where, odpg); + } + + private PartList doQueryRoleUsers(String sqlId, String roleId, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleId, "roleId"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("u.*")); + params.put("roleIds", Arrays.asList(roleId)); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + // return sqlDao.pageForObjects(sqlId, params, odpg, SysUserEntity.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .resultAs(SysUserEntity.class) + .list().asPartList(); + // @formatter:on + } + + /** 批量查询指定角色下的用户信息 (来自sql模板) **/ + public PartList getRoleUsersForSql(List roleIds, DbWhere where, OrderPaging odpg) { + String sqlId = "role.users.query"; + return doQueryRoleUsers(sqlId, roleIds, where, odpg); + } + + /** 批量查询指定角色下的用户信息 (来自xml模板) **/ + public PartList getRoleUsersForXml(List roleIds, DbWhere where, OrderPaging odpg) { + String sqlId = "SysUserMapper:queryRoleUsers"; + return doQueryRoleUsers(sqlId, roleIds, where, odpg); + } + + private PartList doQueryRoleUsers(String sqlId, List roleIds, DbWhere where, + OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + // return sqlDao.pageForObjects(sqlId, params, odpg, RoleIdUserResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .resultAs(RoleIdUserResult.class) + .list().asPartList(); + // @formatter:on + } + + /** 清除用户角色关联表数据 **/ + public int clearUserRoleRefByTenant(String tenantCode) { + String sqlId = "user.role.ref.clear.by.tenant"; + Map params = new HashMap<>(); + params.put("tenantCode", tenantCode); + // return sqlDao.delete(sqlId, params); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .delete(); + // @formatter:on + } + + /** 初始化用户部门数据 **/ + public void initUserDepts(String tenantCode, boolean clear) { + initUserDepts(tenantCode, tenantCode, clear); + } + + /** 初始化用户部门数据 **/ + public void initUserDepts(String tenantCode, String prefix, boolean clear) { + List deptIds = initDepts(tenantCode, prefix, clear); + initDeptUsers(tenantCode, prefix, deptIds, clear); + } + + /** 初始化用户角色数据 **/ + public void initUserRoles(String tenantCode, boolean clear) { + initUserRoles(tenantCode, tenantCode, clear); + } + + /** 初始化用户角色数据 **/ + public void initUserRoles(String tenantCode, String prefix, boolean clear) { + List userIds = initUsers(tenantCode, prefix, clear); + List roleIds = initRoles(tenantCode, prefix, clear); + initUserRoles(tenantCode, userIds, roleIds, clear); + } + + private List initDeptUsers(String tenantCode, String prefix, List deptIds, boolean clear) { + if (clear) { // 清理用户数据 + // DbWhere where = new DbWhere(); + // where.on("tenantCode", "=", tenantCode); + // userDao.physicalDelete(where); + // @formatter:off + qdbcBoot.crudStream(SysUserEntity.class) + .whereEquals("tenantCode", tenantCode) + .physicalDelete(); + // @formatter:on + } + { // 新增用户数据 + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(tenantCode); + entity.setSuperman(false); + entity.setGender(Gender.FEMALE); + entity.setUserType(UserType.USER); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + List entities = new ArrayList<>(); + int index = 0; + for (int i = 0; i < deptIds.size(); i++) { + String deptId = deptIds.get(i); + entity.setDeptId(deptId); + // 第1个部门2个用户, 第2个部门4个, 以此类推 + for (int j = 0; j < (i + 1) * 2; j++) { + index++; + entity.setId(prefix + "U" + "0000" + StringTools.pad(index, 3)); + entity.setUserCode(prefix + "U" + StringTools.pad(index, 3)); + entity.setNickName(prefix + "U" + StringTools.pad(index, 3)); + entities.add(entity.to(SysUserEntity.class)); + } + } + // 批量新增用户 + return qdbcBoot.buildCrudDao(SysUserEntity.class).inserts(entities); + } + } + + private List initUsers(String tenantCode, String prefix, boolean clear) { + if (clear) { // 清理用户数据 + // DbWhere where = new DbWhere(); + // where.on("tenantCode", "=", tenantCode); + // userDao.physicalDelete(where); + // @formatter:off + qdbcBoot.crudStream(SysUserEntity.class) + .whereEquals("tenantCode", tenantCode) + .physicalDelete(); + // @formatter:on + } + { // 新增用户数据 + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(tenantCode); + entity.setSuperman(false); + entity.setDeptId("0"); + entity.setGender(Gender.FEMALE); + entity.setUserType(UserType.USER); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + List entities = new ArrayList<>(); + for (int i = 1; i <= 10; i++) { + entity.setId(prefix + "U" + "0000" + StringTools.pad(i, 3)); + entity.setUserCode(prefix + "U" + StringTools.pad(i, 3)); + entity.setNickName(prefix + "U" + StringTools.pad(i, 3)); + entities.add(entity.to(SysUserEntity.class)); + } + // 批量新增用户 + // return userDao.inserts(entities); + return qdbcBoot.buildCrudDao(SysUserEntity.class).inserts(entities); + } + } + + private List initRoles(String tenantCode, String prefix, boolean clear) { + if (clear) { // 清理角色数据 + // DbWhere where = new DbWhere(); + // where.on("tenantCode", "=", tenantCode); + // roleDao.physicalDelete(where); + // @formatter:off + qdbcBoot.crudStream(SysRoleEntity.class) + .whereEquals("tenantCode", tenantCode) + .physicalDelete(); + // @formatter:on + } + { // 新增角色数据 + SysRoleEntity entity = new SysRoleEntity(); + entity.setTenantCode(tenantCode); // 租户编号 + entity.setUserType(UserType.USER); // 用户类型 + entity.setDefaults(false); + List entities = new ArrayList<>(); + for (int i = 1; i <= 20; i++) { + entity.setSortIndex(i); + entity.setId(prefix + "R" + "0000" + StringTools.pad(i, 3)); + entity.setRoleName(prefix + "R" + StringTools.pad(i, 3)); + entities.add(entity.to(SysRoleEntity.class)); + } + // 批量新增角色 + // return roleDao.inserts(entities); + return qdbcBoot.buildCrudDao(SysRoleEntity.class).inserts(entities); + } + } + + private List initDepts(String tenantCode, String prefix, boolean clear) { + if (clear) { // 清理部门数据 + // DbWhere where = new DbWhere(); + // where.on("tenantCode", "=", tenantCode); + // deptDao.physicalDelete(where); + // @formatter:off + qdbcBoot.crudStream(SysDeptEntity.class) + .whereEquals("tenantCode", tenantCode) + .physicalDelete(); + // @formatter:on + } + { // 新增部门数据 + List entities = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + SysDeptEntity entity = new SysDeptEntity(); + entity.setTenantCode(tenantCode); + entity.setParentCode("0"); // 上级部门编号 + entity.setSortIndex(i); // 排序号(越小越靠前) + entity.setId(prefix + "D" + "0000" + StringTools.pad(i, 3)); + entity.setDeptCode(prefix + "D" + StringTools.pad(i, 3)); + entity.setDeptName(prefix + "D" + StringTools.pad(i, 3)); + entities.add(entity); + } + // 批量新增部门 + // return deptDao.inserts(entities); + return qdbcBoot.buildCrudDao(SysDeptEntity.class).inserts(entities); + } + } + + private List initUserRoles(String tenantCode, List userIds, List roleIds, boolean clear) { + if (clear) { + clearUserRoleRefByTenant(tenantCode); + } + + List entities = new ArrayList<>(); + for (int i = 0; i < userIds.size(); i++) { + String userId = userIds.get(i); + for (int j = 0; j < (i + 1) * 2 && j < roleIds.size(); j++) { + String roleId = roleIds.get(j); + SysUserRoleEntity entity = new SysUserRoleEntity(); + entity.setUserId(userId); + entity.setRoleId(roleId); + entities.add(entity); + } + } + CrudDao dao = qdbcBoot.buildCrudDao(SysUserRoleEntity.class); + // 批量新增用户角色关系 + return dao.inserts(entities); + } +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/stream/CrudStreamTest.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/stream/CrudStreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..048878e0fb4585fa63fd5442d48ca296e3159229 --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/stream/CrudStreamTest.java @@ -0,0 +1,33 @@ +package com.gitee.qdbp.jdbc.test.stream; + +import java.util.Date; +import java.util.List; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; +import com.gitee.qdbp.tools.utils.DateTools; + +@SuppressWarnings("unused") +public class CrudStreamTest { + + private QdbcBoot qdbcBoot; + + public void test1() { + Date now = new Date(); + // @formatter:off + List users = qdbcBoot.crudStream(SysUserEntity.class) + .select("id,userCode,realName") // 只查某些字段 + // .selectExclude("password") // 排除掉密码字段 + .whereBy((where) -> { + where.andEquals("userType", 1) + .andBetween("createTime", DateTools.addDay(now, -1), DateTools.addDay(now, +1)) + .andSubCondition((w) -> { + w.orLike("userCode", "test") + .orLike("realName", "test"); + }); + }) + .orderBy("createTime desc") + .pageBy(1, 10) + .list().asPartList(); + // @formatter:on + } +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/stream/SqlStreamTest.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/stream/SqlStreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..695d288bd7da65c50125143cafdec45d6060c125 --- /dev/null +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/stream/SqlStreamTest.java @@ -0,0 +1,64 @@ +package com.gitee.qdbp.jdbc.test.stream; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; + +@SuppressWarnings("unused") +public class SqlStreamTest { + + private QdbcBoot qdbcBoot; + + public void testSqlIdFind() { + // @formatter:off + Map users = qdbcBoot.sqlStream() + .sqlId("SysUserMapper:queryRoleUsers") + .find(); + // @formatter:on + } + + public void testSqlIdList() { + // @formatter:off + List> users = qdbcBoot.sqlStream() + .sqlId("SysUserMapper:queryRoleUsers") + .list(); + // @formatter:on + } + + public void testSqlIdResultPageList() { + Map map = new HashMap<>(); + // @formatter:off + List users = qdbcBoot.sqlStream() + .sqlId("SysUserMapper:queryRoleUsers") + .pageBy(1, 10) + .resultAs(SysUserEntity.class) + .list().asPartList(); + // @formatter:on + } + + public void test2() { + Map map = new HashMap<>(); + // @formatter:off + List users = qdbcBoot.sqlStream() + .sqlId("SysUserMapper:queryRoleUsers") + .params(map) + .pageBy(1, 10) + .resultAs(SysUserEntity.class) + .list().asPartList(); + // @formatter:on + } + + public void test3() { + Map map = new HashMap<>(); + // @formatter:off + List users = qdbcBoot.sqlStream() + .sqlId("SysUserMapper:queryRoleUsers") + .params(map) + .pageBy(1, 10) + .resultAs(SysUserEntity.class) + .list().asPartList(); + // @formatter:on + } +} diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/tags/DeptIsolationTag.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/tags/DeptIsolationTag.java index 7a4478772fbc47a8a3d2d1955e5965df1b55fe31..856989967cd9f20174a89d19ca86674df6210b8e 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/tags/DeptIsolationTag.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/tags/DeptIsolationTag.java @@ -1,13 +1,17 @@ package com.gitee.qdbp.jdbc.test.tags; import java.io.IOException; +import com.gitee.qdbp.able.enums.WrapMode; import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.plugins.impl.ColumnNamingConverter; import com.gitee.qdbp.jdbc.sql.SqlBuffer; -import com.gitee.qdbp.jdbc.test.service.EntityTools; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.jdbc.test.condition.DeptIsolationBuilder; +import com.gitee.qdbp.jdbc.test.condition.DeptIsolationWhere; +import com.gitee.qdbp.jdbc.utils.DbTools; import com.gitee.qdbp.staticize.exception.TagException; import com.gitee.qdbp.staticize.tags.base.BaseTag; import com.gitee.qdbp.staticize.tags.base.NextStep; -import com.gitee.qdbp.tools.utils.VerifyTools; /** * 生成部门数据权限隔离条件的标签 @@ -20,7 +24,42 @@ public class DeptIsolationTag extends BaseTag { private String prefix; private String suffix; - private String column; + /** 是否添加括号 **/ + private WrapMode brackets; + /** 用户隔离字段 **/ + private String userColumn; + /** 代办人隔离字段 **/ + private String agentColumn; + /** 部门隔离字段 **/ + private String deptColumn; + + @Override + public NextStep doHandle() throws TagException, IOException { + SqlDialect dialect = this.getStackValue("db.dialect", SqlDialect.class); + if (dialect == null) { + throw new TagException("Context variable of '${db.dialect}' is null"); + } + + DeptIsolationWhere condition = new DeptIsolationWhere(); + condition.setDeptField(deptColumn); + condition.setUserField(userColumn); + condition.setAgentField(agentColumn); + DeptIsolationBuilder builder = DbTools.getWhereSqlBuilder(DeptIsolationWhere.class); + SqlBuffer sql = builder.buildSql(condition, ColumnNamingConverter.defaults(), dialect); + + if (sql == null || sql.isBlank()) { + return NextStep.SKIP_BODY; + } + + String leadingBlank = clearLeadingBlank(); + if (leadingBlank != null) { + sql.shortcut().pd(leadingBlank); + } + SqlTools.wrap(sql, brackets, prefix, null, suffix, null); + + this.print(sql); + return NextStep.SKIP_BODY; + } public String getPrefix() { return prefix; @@ -38,32 +77,35 @@ public class DeptIsolationTag extends BaseTag { this.suffix = suffix; } - public String getColumn() { - return column; + public WrapMode getBrackets() { + return brackets; } - public void setColumn(String column) { - this.column = column; + public void setBrackets(String brackets) { + this.brackets = WrapMode.of(brackets, "brackets value"); } - @Override - public NextStep doHandle() throws TagException, IOException { - SqlDialect dialect = this.getStackValue("db.dialect", SqlDialect.class); - if (dialect == null) { - throw new TagException("Context variable of '${db.dialect}' is null"); - } - SqlBuffer sql = EntityTools.buildDeptDataPermission(column, dialect); - if (sql == null || sql.isBlank()) { - return NextStep.SKIP_BODY; - } + public String getUserColumn() { + return userColumn; + } - if (VerifyTools.isNotBlank(prefix)) { - sql.shortcut().pd(prefix); - } - if (VerifyTools.isNotBlank(suffix)) { - sql.shortcut().ad(suffix); - } - this.print(sql); - return NextStep.SKIP_BODY; + public void setUserColumn(String userColumn) { + this.userColumn = userColumn; + } + + public String getDeptColumn() { + return deptColumn; + } + + public void setDeptColumn(String deptColumn) { + this.deptColumn = deptColumn; + } + + public String getAgentColumn() { + return agentColumn; + } + + public void setAgentColumn(String agentColumn) { + this.agentColumn = agentColumn; } } diff --git a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/utils/QdbcBootManualFactory.java b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/utils/QdbcBootManualInitializer.java similarity index 85% rename from jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/utils/QdbcBootManualFactory.java rename to jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/utils/QdbcBootManualInitializer.java index 793ce16b6c9de463f7ae6cd4d80f990f76b0c942..90b8a38e489f5df2b651d47ba7d1c1084fdc4762 100644 --- a/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/utils/QdbcBootManualFactory.java +++ b/jdbc-test/src/main/java/com/gitee/qdbp/jdbc/test/utils/QdbcBootManualInitializer.java @@ -2,6 +2,9 @@ package com.gitee.qdbp.jdbc.test.utils; import java.awt.Image; import java.io.File; +import java.util.Properties; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.support.DefaultConversionService; import com.gitee.qdbp.able.matches.EqualsStringMatcher; import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; import com.gitee.qdbp.jdbc.plugins.EntityFillBizResolver; @@ -11,30 +14,38 @@ import com.gitee.qdbp.jdbc.plugins.impl.RandomNumberEntityDataStateFillStrategy; import com.gitee.qdbp.jdbc.plugins.impl.SimpleCommonFieldResolver; import com.gitee.qdbp.jdbc.plugins.impl.SimpleDbOperatorContainer; import com.gitee.qdbp.jdbc.plugins.impl.SimpleEntityFieldFillStrategy; -import com.gitee.qdbp.jdbc.plugins.impl.SimpleNameConverter; import com.gitee.qdbp.jdbc.plugins.impl.SimpleSqlFormatter; import com.gitee.qdbp.jdbc.plugins.impl.SimpleTableInfoScans; import com.gitee.qdbp.jdbc.plugins.impl.SimpleTableNameScans; import com.gitee.qdbp.jdbc.plugins.impl.SpringMapToBeanConverter; import com.gitee.qdbp.jdbc.plugins.impl.SpringVarToDbValueConverter; import com.gitee.qdbp.jdbc.plugins.impl.StaticFieldTableNameScans; -import com.gitee.qdbp.jdbc.support.QdbcBootBaseFactory; import com.gitee.qdbp.jdbc.test.enums.DataState; /** - * QdbcBoot工厂类, 手动设置DbPluginContainer + * QdbcBoot初始化类, 手动设置DbPluginContainer * * @author zhaohuihua * @version 20200129 */ -public class QdbcBootManualFactory extends QdbcBootBaseFactory { +public class QdbcBootManualInitializer { /** 与业务强相关的数据提供者 **/ private EntityFillBizResolver entityFillBizResolver; + /** Spring的类型转换处理类 **/ + private ConversionService conversionService; + /** 数据库配置选项 **/ + private Properties dbConfig; - @Override public void afterPropertiesSet() { + if (this.conversionService == null) { + this.conversionService = DefaultConversionService.getSharedInstance(); + } DbPluginContainer plugins = new DbPluginContainer(); + plugins.setConversionService(this.conversionService); + if (this.dbConfig != null) { + plugins.setDbConfig(this.dbConfig); + } registerTableInfoScans(plugins, true); registerToDbValueConverter(plugins); registerMapToBeanConverter(plugins); @@ -47,8 +58,9 @@ public class QdbcBootManualFactory extends QdbcBootBaseFactory { plugins.setOperatorContainer(new SimpleDbOperatorContainer()); // 设置SQL格式化处理类 plugins.setSqlFormatter(new SimpleSqlFormatter()); - this.setPluginContainer(plugins); - super.afterPropertiesSet(); + + // 初始化DbPluginContainer全局实例 + DbPluginContainer.init(plugins); } private void registerEntityFieldFillStrategy(DbPluginContainer plugins) { @@ -124,8 +136,6 @@ public class QdbcBootManualFactory extends QdbcBootBaseFactory { // 主键查找方式: 查找字段名为id的字段 // 仅在useMissAnnotationField=true时使用 // scans.setPrimaryKeyMatcher(new EqualsStringMatcher("id")); - // 表名/字段名的转换器: java的驼峰命名与数据库的下划线命名之间的转换 - scans.setNameConverter(new SimpleNameConverter()); // SimpleTableNameScans: 扫描java类的@Table注解, 如果没有注解则将java类名转换为下划线形式的表名 scans.setTableNameScans(new SimpleTableNameScans()); // 判断是否公共字段的处理器(为了将公共字段排在最后) @@ -143,8 +153,6 @@ public class QdbcBootManualFactory extends QdbcBootBaseFactory { SimpleTableInfoScans scans = new SimpleTableInfoScans(); // 主键查找方式: 查找字段名为id的字段 scans.setPrimaryKeyMatcher(new EqualsStringMatcher("id")); - // 表名/字段名的转换器: java的驼峰命名与数据库的下划线命名之间的转换 - scans.setNameConverter(new SimpleNameConverter()); // 表名扫描类: 如果从java类信息中找到表名, 已提供两个实现类: // SimpleTableNameScans: 扫描java类的@Table注解, 如果没有注解则将java类名转换为下划线形式的表名 // StaticFieldTableNameScans: 从静态字段中获取表名 @@ -169,4 +177,27 @@ public class QdbcBootManualFactory extends QdbcBootBaseFactory { this.entityFillBizResolver = entityFillBizResolver; } + /** Spring的类型转换处理类 **/ + public ConversionService getConversionService() { + return conversionService; + } + + /** Spring的类型转换处理类 **/ + public void setConversionService(ConversionService conversionService) { + this.conversionService = conversionService; + } + + /** 设置数据库配置选项 **/ + public void setDbConfig(Properties config) { + this.dbConfig = config; + } + + /** 设置数据库配置选项 **/ + public void addDbConfig(String key, String value) { + if (dbConfig == null) { + dbConfig = new Properties(); + } + this.dbConfig.put(key, value); + } + } diff --git a/jdbc-test/src/main/resources/settings/dbtags/taglib.txt b/jdbc-test/src/main/resources/settings/dbtags/taglib.txt deleted file mode 100644 index a1a3a12522f7c1406197b6b7176ea0b1e33e6fb9..0000000000000000000000000000000000000000 --- a/jdbc-test/src/main/resources/settings/dbtags/taglib.txt +++ /dev/null @@ -1,28 +0,0 @@ - -import = com.gitee.qdbp.staticize.tags.base.ImportTag -block = com.gitee.qdbp.staticize.tags.core.BlockTag -if = com.gitee.qdbp.staticize.tags.core.IfTag -elseif = com.gitee.qdbp.staticize.tags.core.ElseIfTag -else = com.gitee.qdbp.staticize.tags.core.ElseTag -each = com.gitee.qdbp.staticize.tags.core.EachTag -comment = com.gitee.qdbp.staticize.tags.core.CommentTag -set = com.gitee.qdbp.staticize.tags.core.SetVariableTag - -sql:include = com.gitee.qdbp.jdbc.tags.IncludeTag -append = com.gitee.qdbp.jdbc.tags.AppendTag -sql:append = com.gitee.qdbp.jdbc.tags.AppendTag -where = com.gitee.qdbp.jdbc.tags.WhereTag -sql:where = com.gitee.qdbp.jdbc.tags.WhereTag -update:set = com.gitee.qdbp.jdbc.tags.UpdateSetTag -sql:trim = com.gitee.qdbp.jdbc.tags.TrimTag -sql:in = com.gitee.qdbp.jdbc.tags.SqlInTag -sql:like = com.gitee.qdbp.jdbc.tags.SqlLikeTag -supports = com.gitee.qdbp.staticize.tags.base.IgnoreContentTag - -dept:isolation = com.gitee.qdbp.jdbc.test.tags.DeptIsolationTag - -fmt:date = com.gitee.qdbp.staticize.tags.core.DateFormatTag - -@DateTools = com.gitee.qdbp.tools.utils.DateTools -@StringTools = com.gitee.qdbp.tools.utils.StringTools -@VerifyTools = com.gitee.qdbp.tools.utils.VerifyTools diff --git a/jdbc-test/src/main/resources/settings/qdbc/qdbc.mapper.dtd b/jdbc-test/src/main/resources/settings/qdbc/qdbc.mapper.dtd new file mode 100644 index 0000000000000000000000000000000000000000..28dced5780c9cc6db3dc3aff0bebbe7b4b7d3304 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/qdbc/qdbc.mapper.dtd @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt b/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt new file mode 100644 index 0000000000000000000000000000000000000000..3f60f84a95a89908c162bc002055c5357fcaec91 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/qdbc/qdbc.taglib.txt @@ -0,0 +1,50 @@ + +import = com.gitee.qdbp.staticize.tags.base.ImportTag +block = com.gitee.qdbp.staticize.tags.core.BlockTag +if = com.gitee.qdbp.staticize.tags.core.IfTag +elseif = com.gitee.qdbp.staticize.tags.core.ElseIfTag +else = com.gitee.qdbp.staticize.tags.core.ElseTag +each = com.gitee.qdbp.staticize.tags.core.EachTag +choose = com.gitee.qdbp.staticize.tags.core.ChooseTag +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 + +foreach = com.gitee.qdbp.jdbc.tags.SqlForEachTag +sql:include = com.gitee.qdbp.jdbc.tags.IncludeTag +append = com.gitee.qdbp.jdbc.tags.AppendTag +sql:append = com.gitee.qdbp.jdbc.tags.AppendTag +where = com.gitee.qdbp.jdbc.tags.WhereTag +sql:where = com.gitee.qdbp.jdbc.tags.WhereTag +set = com.gitee.qdbp.jdbc.tags.UpdateSetTag +update:set = com.gitee.qdbp.jdbc.tags.UpdateSetTag +trim = com.gitee.qdbp.jdbc.tags.TrimTag +sql:trim = com.gitee.qdbp.jdbc.tags.TrimTag +sql-in = com.gitee.qdbp.jdbc.tags.SqlInTag +sql:in = com.gitee.qdbp.jdbc.tags.SqlInTag +sql-like = com.gitee.qdbp.jdbc.tags.SqlLikeTag +sql:like = com.gitee.qdbp.jdbc.tags.SqlLikeTag +columns = com.gitee.qdbp.jdbc.tags.ColumnsTag +orderby = com.gitee.qdbp.jdbc.tags.OrderByTag +supports = com.gitee.qdbp.staticize.tags.base.IgnoreContentTag + +mapper = com.gitee.qdbp.jdbc.tags.mapper.MapperTag +sql = com.gitee.qdbp.jdbc.tags.mapper.SqlTag +select = com.gitee.qdbp.jdbc.tags.mapper.SelectTag +insert = com.gitee.qdbp.jdbc.tags.mapper.InsertTag +update = com.gitee.qdbp.jdbc.tags.mapper.UpdateTag +delete = com.gitee.qdbp.jdbc.tags.mapper.DeleteTag + +dept-isolation = com.gitee.qdbp.jdbc.test.tags.DeptIsolationTag +dept:isolation = com.gitee.qdbp.jdbc.test.tags.DeptIsolationTag + +fmt:date = com.gitee.qdbp.staticize.tags.core.DateFormatTag + +# 指定值栈包装类, 用于实现在业务侧自定义值栈自身的函数 +@StackWrapper = com.gitee.qdbp.staticize.common.StackWrapper + +# 全局静态类, 最常用的在这里指定, 省去 +@DateTools = com.gitee.qdbp.tools.utils.DateTools +@StringTools = com.gitee.qdbp.tools.utils.StringTools +@VerifyTools = com.gitee.qdbp.tools.utils.VerifyTools diff --git a/jdbc-test/src/main/resources/settings/sqls/BizBacklog.xml b/jdbc-test/src/main/resources/settings/sqls/BizBacklog.xml new file mode 100644 index 0000000000000000000000000000000000000000..e5725bf2687881d1aec1373d831fd6017bcdc495 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/BizBacklog.xml @@ -0,0 +1,97 @@ + + + + + com.gitee.qdbp.jdbc.sql.SqlTools + com.gitee.qdbp.jdbc.test.service.EntityTools + + + + + + + + + + + diff --git a/jdbc-test/src/main/resources/settings/sqls/BizBacklog2.xml b/jdbc-test/src/main/resources/settings/sqls/BizBacklog2.xml new file mode 100644 index 0000000000000000000000000000000000000000..885506507817b6293eb9bd7cf37d0d5cab1e2726 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/BizBacklog2.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + diff --git a/jdbc-test/src/main/resources/settings/sqls/GlobalIsolation.xml b/jdbc-test/src/main/resources/settings/sqls/GlobalIsolation.xml new file mode 100644 index 0000000000000000000000000000000000000000..378902b2d6cbba119a702abb71960749dc387c3a --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/GlobalIsolation.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + OR + + + + OR + + + + OR ${deptColumn} IS NULL OR + + + + + diff --git a/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive.xml b/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive.xml new file mode 100644 index 0000000000000000000000000000000000000000..c4c91d19b45167d73e9e40c6aac6ba42b2e59a00 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/GlobalRecursive.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jdbc-test/src/main/resources/settings/sqls/biz.backlog.sql b/jdbc-test/src/main/resources/settings/sqls/biz.backlog.sql index 9c8724d4626950a7e20d778737096fb7638f17dd..a5d9634e7b425ff14d8eb32f8ca17950d2d4af28 100644 --- a/jdbc-test/src/main/resources/settings/sqls/biz.backlog.sql +++ b/jdbc-test/src/main/resources/settings/sqls/biz.backlog.sql @@ -3,71 +3,86 @@ -- << backlog.todo.query >> 待办数据查询 SELECT * FROM ( - SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, - T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_=#{userName} - OR ( - #{@SqlTools.buildInSql('T.ASSIGNEE_',roleIds,db.dialect)} - #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} - ) - ) - #{whereCondition} + SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, + T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_=#{userName} + OR ( + #{@SqlTools.buildInSql('T.ASSIGNEE_',roleIds,db.dialect)} + /*-- brackets="AUTO", 自动添加括号 --*/ + #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} + ) + ) + #{whereCondition} UNION ALL - SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, - S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE=#{userName} - OR ( - #{@SqlTools.buildInSql('S.ASSIGNEE',roleIds,db.dialect)} - #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} - ) - ) - #{whereCondition} + SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, + S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE=#{userName} + OR ( + #{@SqlTools.buildInSql('S.ASSIGNEE',roleIds,db.dialect)} + /*-- brackets="AUTO", 自动添加括号 --*/ + #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} + ) + ) + #{whereCondition} ) R ORDER BY R.RECEIVE_TIME DESC,R.CREATE_TIME DESC,R.BUSINESS_ID -- << backlog.todo.count >> 待办数据统计 SELECT SUM(CNT) FROM ( - SELECT COUNT(*) AS CNT - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_=#{userName} - OR ( - #{@SqlTools.buildInSql('T.ASSIGNEE_',roleIds,db.dialect)} - #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} - ) - ) - #{whereCondition} + SELECT COUNT(*) AS CNT + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_=#{userName} + OR ( + #{@SqlTools.buildInSql('T.ASSIGNEE_',roleIds,db.dialect)} + /*-- brackets="AUTO", 自动添加括号 --*/ + #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} + ) + ) + #{whereCondition} UNION ALL - SELECT COUNT(*) AS CNT - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE=#{userName} - OR ( - #{@SqlTools.buildInSql('S.ASSIGNEE',roleIds,db.dialect)} - #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} - ) - ) - #{whereCondition} + SELECT COUNT(*) AS CNT + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE=#{userName} + OR ( + #{@SqlTools.buildInSql('S.ASSIGNEE',roleIds,db.dialect)} + /*-- brackets="AUTO", 自动添加括号 --*/ + #{@EntityTools.buildDeptDataPermission('S.DEPT_ID',db.dialect)} + ) + ) + #{whereCondition} ) R + +-- << backlog.mine.query >> 我的流程数据查询 +SELECT * FROM FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + + #{whereCondition} + /*-- brackets="AUTO", 自动添加括号 --*/ + #{@EntityTools.buildDeptDataPermission('S.USER_ID',null,db.dialect)} + +ORDER BY S.CREATE_TIME DESC,S.BUSINESS_ID ASC + diff --git a/jdbc-test/src/main/resources/settings/sqls/biz.backlog2.sql b/jdbc-test/src/main/resources/settings/sqls/biz.backlog2.sql index ecb1561392d440e842fe1752119dbae252d7664d..9fa4229ea97892e5bf9a32c34c71650514f7f3ea 100644 --- a/jdbc-test/src/main/resources/settings/sqls/biz.backlog2.sql +++ b/jdbc-test/src/main/resources/settings/sqls/biz.backlog2.sql @@ -4,71 +4,86 @@ -- << backlog.todo.query2 >> 待办数据查询 SELECT * FROM ( - SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, - T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_=#{userName} - OR ( - - - ) - ) - #{whereCondition} + SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, + T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_=#{userName} + OR ( + + /*-- brackets="AUTO", 自动添加括号 --*/ + + ) + ) + #{whereCondition} UNION ALL - SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, - S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE=#{userName} - OR ( - - - ) - ) - #{whereCondition} + SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, + S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE=#{userName} + OR ( + + /*-- brackets="AUTO", 自动添加括号 --*/ + + ) + ) + #{whereCondition} ) R ORDER BY R.RECEIVE_TIME DESC,R.CREATE_TIME DESC,R.BUSINESS_ID -- << backlog.todo.count2 >> 待办数据统计 SELECT SUM(CNT) FROM ( - SELECT COUNT(*) AS CNT - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_=#{userName} - OR ( - - - ) - ) - #{whereCondition} + SELECT COUNT(*) AS CNT + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_=#{userName} + OR ( + + /*-- brackets="AUTO", 自动添加括号 --*/ + + ) + ) + #{whereCondition} UNION ALL - SELECT COUNT(*) AS CNT - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE=#{userName} - OR ( - - - ) - ) - #{whereCondition} + SELECT COUNT(*) AS CNT + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE=#{userName} + OR ( + + /*-- brackets="AUTO", 自动添加括号 --*/ + + ) + ) + #{whereCondition} ) R + +-- << backlog.mine.query2 >> 我的流程数据查询 +SELECT * FROM FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + + #{whereCondition} + /*-- brackets="AUTO", 自动添加括号 --*/ + + +ORDER BY S.CREATE_TIME DESC,S.BUSINESS_ID ASC + 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 54373eedd88540b49965991db1ddeadc74ccfc10..0afd1bfc5f6d97a92df06a5d49744ef7ba82ea8f 100644 --- a/jdbc-test/src/main/resources/settings/sqls/select.001.sql +++ b/jdbc-test/src/main/resources/settings/sqls/select.001.sql @@ -1,35 +1,18 @@ - --- << user.roles.query >> 查询用户有哪些角色 -SELECT ${selectColumns} - FROM TEST_USER_ROLE_REF ur - INNER JOIN TEST_ROLE_CORE_INFO r - ON ur.ROLE_ID=r.ID - AND r.DATA_STATE=1 +-- << user.roles.query1 >> 查询用户有哪些角色 +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} ) - #{whereCondition} -${orderByCondition} - +AND ur.USER_ID IN ( #{userIds} ) +AND r.TENANT_CODE='000000' --- << role.users.query >> 查询角色下有哪些用户 -SELECT ${selectColumns} - FROM TEST_USER_ROLE_REF ur - INNER JOIN TEST_USER_CORE_INFO u - ON ur.USER_ID=u.ID - AND u.DATA_STATE=1 +-- << user.roles.query2 >> 查询用户有哪些角色 +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.ROLE_ID IN ( #{roleIds} ) - #{whereCondition} -${orderByCondition} - +AND ur.USER_ID IN ( #{userIds} ) --- << user.role.ref.clear.by.tenant >> 清除用户角色表中的数据 -DELETE FROM TEST_USER_ROLE_REF - WHERE ROLE_ID IN ( - SELECT ID FROM TEST_ROLE_CORE_INFO WHERE TENANT_CODE = #{tenantCode} - ) - OR USER_ID IN ( - SELECT ID FROM TEST_USER_CORE_INFO WHERE TENANT_CODE = #{tenantCode} - ); - \ No newline at end of file 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 b9516cc10649be24664db01e692408d4dd06f94c..4ef1440824daea89940d64984891948510dc5424 100644 --- a/jdbc-test/src/main/resources/settings/sqls/select.002.sql +++ b/jdbc-test/src/main/resources/settings/sqls/select.002.sql @@ -1,8 +1,17 @@ --- << user.roles.query >> 查询用户有哪些角色 +-- << user.roles.query2 >> 查询用户有哪些角色 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 ur.USER_ID IN ( #{userIds} ) + +-- << user.roles.query5 >> 查询用户有哪些角色 +-- oracle.999,db2.999,mysql.999,h2.999 +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 new file mode 100644 index 0000000000000000000000000000000000000000..7d3ab8728ec10b8b987791d71011a502a58fff7f --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/select.003.sql @@ -0,0 +1,24 @@ + +-- << user.roles.query3 >> 查询用户有哪些角色 +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} ) + +-- << user.roles.query4 >> 查询用户有哪些角色 +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} ) + +-- << user.roles.query5:mysql.988 >> 查询用户有哪些角色 +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/system.dept.sql b/jdbc-test/src/main/resources/settings/sqls/system.dept.sql new file mode 100644 index 0000000000000000000000000000000000000000..8c4d3a28b25fae2264ffc24602be972f9377f319 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/system.dept.sql @@ -0,0 +1,37 @@ + +-- << dept.batch.insert >> 批量插入 (测试foreach方法) +-- mysql,mariadb,db2,h2,sqlserver.2008 +INSERT INTO TEST_DEPARTMENT_CORE_INFO (ID,TENANT_CODE,DEPT_CODE,DEPT_NAME,PARENT_CODE,SORT_INDEX,CREATOR_ID,CREATE_TIME,DATA_STATE) + VALUES + + (#{i.id},#{i.tenantCode},#{i.deptCode},#{i.deptName},#{i.parentCode},#{i.sortIndex},#{i.creatorId},#{i.createTime},#{i.dataState}) + + + +-- << dept.batch.insert:oracle >> 批量插入 (测试foreach方法) +INSERT ALL + + INTO TEST_DEPARTMENT_CORE_INFO (ID,TENANT_CODE,DEPT_CODE,DEPT_NAME,PARENT_CODE,SORT_INDEX,CREATOR_ID,CREATE_TIME,DATA_STATE) + VALUES (#{i.id},#{i.tenantCode},#{i.deptCode},#{i.deptName},#{i.parentCode},#{i.sortIndex},#{i.creatorId},#{i.createTime},#{i.dataState}) + +SELECT * FROM DUAL; + + +-- << dept.info.query >> 部门信息查询 +SELECT * FROM TEST_DEPARTMENT_CORE_INFO d + + AND d.TENANT_CODE=#{where.tenantCode} + AND d.ID=#{where.deptId} + + AND d.ID IN + + #{i,jdbcType=VARCHAR} + + + AND d.DEPT_CODE=#{where.deptCode} + AND + =#{where.createTimeMin}]]> + + AND d.DATA_STATE=1 + +${orderByCondition} diff --git a/jdbc-test/src/main/resources/settings/sqls/system.user.sql b/jdbc-test/src/main/resources/settings/sqls/system.user.sql new file mode 100644 index 0000000000000000000000000000000000000000..44b89560cdb902901901ec161bd8a16e8739fed0 --- /dev/null +++ b/jdbc-test/src/main/resources/settings/sqls/system.user.sql @@ -0,0 +1,84 @@ +-- import com.gitee.qdbp.jdbc.test.model.SysUserEntity + +-- << user.roles.query >> 查询用户有哪些角色 +SELECT ${selectColumns} + 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} ) + #{whereCondition} +${orderByCondition} + +-- << user.roles.count >> 统计用户有多少角色 +SELECT COUNT(*) + 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} ) + #{whereCondition} +${orderByCondition} + + +-- << role.users.query >> 查询角色下有哪些用户 +SELECT ${selectColumns} + FROM TEST_USER_ROLE_REF ur + INNER JOIN TEST_USER_CORE_INFO u + ON ur.USER_ID=u.ID + AND u.DATA_STATE=1 +WHERE ur.DATA_STATE=1 + AND ur.ROLE_ID IN ( #{roleIds} ) + #{whereCondition} +${orderByCondition} + + +-- << role.users.count >> 统计角色下有多少用户 +SELECT COUNT(*) + FROM TEST_USER_ROLE_REF ur + INNER JOIN TEST_USER_CORE_INFO u + ON ur.USER_ID=u.ID + AND u.DATA_STATE=1 +WHERE ur.DATA_STATE=1 + AND ur.ROLE_ID IN ( #{roleIds} ) + #{whereCondition} +${orderByCondition} + + +-- << user.role.ref.clear.by.tenant >> 清除用户角色表中的数据 +DELETE FROM TEST_USER_ROLE_REF + WHERE ROLE_ID IN ( + SELECT ID FROM TEST_ROLE_CORE_INFO WHERE TENANT_CODE = #{tenantCode} + ) + OR USER_ID IN ( + SELECT ID FROM TEST_USER_CORE_INFO WHERE TENANT_CODE = #{tenantCode} + ); + + +-- << user.info.query >> 查询用户信息 +SELECT ,d.DEPT_CODE,d.DEPT_NAME + FROM TEST_USER_CORE_INFO u + INNER JOIN TEST_DEPARTMENT_CORE_INFO d + ON u.DEPT_ID=d.ID + AND d.DATA_STATE=1 + + AND u.TENANT_CODE=#{where.tenantCode} + AND u.ID=#{where.userId} + AND + AND + AND + AND + AND + =#{where.createTimeMin}]]> + + AND d.ID=#{where.deptId} + AND d.DEPT_CODE=#{where.deptCode} + AND + + AND ( OR OR OR ) + + AND u.DATA_STATE=1 + + diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/sql/SqlTextIndentSizeTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/sql/SqlTextIndentSizeTest.java index 8357bdbfcfd80fcf1943c6aa47cb19fb60d01b89..300b3e969225c623bebf6eef4837f9f77dfd6664 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/sql/SqlTextIndentSizeTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/sql/SqlTextIndentSizeTest.java @@ -105,7 +105,7 @@ public class SqlTextIndentSizeTest { private static void testFindLastIndentSize(String input, Boolean inserBlank, int expectedIndent) { SqlBuffer sql = buildTestSql(input, inserBlank); SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.MySQL); - int actualIndent = SqlTextTools.findLastIndentSize(sql); + int actualIndent = SqlTools.Indent.findLastIndentSize(sql); if (actualIndent != expectedIndent) { String sqlString = getSqlString(sql, dialect); System.out.printf("Error: expected=%s, expected=%s, sql=%s%n", expectedIndent, actualIndent, sqlString); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/support/AutoDruidRegexpTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/support/AutoDruidRegexpTest.java index a35ba5c04590396b8c00f522776125acbc68a79c..9de91ce8b9672f1d3ab148dc599acd7940ac6777 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/support/AutoDruidRegexpTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/support/AutoDruidRegexpTest.java @@ -13,7 +13,7 @@ public class AutoDruidRegexpTest { private Properties properties; public AutoDruidRegexpTest() { - String path = "settings/jdbc/druid.auto.properties"; + String path = "settings/qdbc/jdbc.auto.properties"; URL url = PathTools.findResource(path, AutoDruidDataSource.class); properties = PropertyTools.load(url); } @@ -68,7 +68,7 @@ public class AutoDruidRegexpTest { AutoDruidDataSource ds = new AutoDruidDataSource(); ds.setProperties(properties); ds.setConfig(config); - ds.initProperty(); + ds.autoConfig(); Assert.assertEquals(ds.getUsername(), username, "username"); Assert.assertEquals(ds.getPassword(), password, "password"); Assert.assertEquals(ds.getUrl(), url, "url"); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/base/DatabaseInitiator.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/base/DatabaseInitiator.java index e8e8efe50b5da53030fee3d87a45ad1fd2b47d29..a753430e544fac52387e74ded29ce81ee4cd504a 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/base/DatabaseInitiator.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/base/DatabaseInitiator.java @@ -17,11 +17,14 @@ public class DatabaseInitiator extends AbstractTestNGSpringContextTests { private QdbcBoot qdbcBoot; @PostConstruct - public void init() { - if (!enabled) { - return; + public synchronized void init() { + // H2内存数据库, 必须初始化, 否则找不到表 + if (enabled || qdbcBoot.getSqlDialect().getDbVersion().matchesWith("h2")) { + initTables(qdbcBoot); } + } + public static void initTables(QdbcBoot qdbcBoot) { DbType dbType = qdbcBoot.getSqlDialect().getDbVersion().getDbType(); String path = "settings/dbinit/create.tables." + dbType.name().toLowerCase() + ".sql"; qdbcBoot.getSqlBufferJdbcOperations().executeSqlScript(path); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BatchInsertUpdateTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BatchInsertUpdateTest.java index 3450daaf92fee6f537f27ed5daa87e92f13b1b3c..2c5e87a4b528d9427c7622a20ccddd51b2fca879 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BatchInsertUpdateTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BatchInsertUpdateTest.java @@ -1,6 +1,7 @@ package com.gitee.qdbp.jdbc.test.biz; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; @@ -14,27 +15,65 @@ import com.gitee.qdbp.able.jdbc.paging.PageList; import com.gitee.qdbp.able.jdbc.paging.Paging; import com.gitee.qdbp.jdbc.api.CrudDao; import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.test.enums.SettingState; import com.gitee.qdbp.jdbc.test.model.SysLoggerEntity; +import com.gitee.qdbp.jdbc.test.model.SysSettingEntity; +import com.gitee.qdbp.tools.utils.RandomTools; import com.gitee.qdbp.tools.utils.StringTools; @Test @ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) public class BatchInsertUpdateTest extends AbstractTestNGSpringContextTests { + private static final String TACD = "BIUT"; @Autowired private QdbcBoot qdbcBoot; - private CrudDao dao; + private CrudDao logdao; + private CrudDao setdao; private int total = 855; private int defaultIndex; private int updateTotal = 25; + protected void setQdbcBoot(QdbcBoot qdbcBoot) { + this.qdbcBoot = qdbcBoot; + } + @BeforeClass public void init() { - this.dao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); + this.logdao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); + this.setdao = qdbcBoot.buildCrudDao(SysSettingEntity.class); DbWhere where = new DbWhere(); - where.on("name", "starts", "BatchTest"); - dao.physicalDelete(where); + where.on("tenantCode", "=", TACD); + this.logdao.physicalDelete(where); + this.setdao.physicalDelete(where); + } + + @Test(priority = 100) + public void testBatchInsertForDate() { + // 构造实体数据 + // ORACLE, 日期列有值和NULL值混合的数据, 批量新增时报错 + // ORA-01790: 表达式必须具有与对应表达式相同的数据类型 + // 通过SqlParameterValue指定Types.TIMESTAMP解决 + List entities = new ArrayList<>(); + for (int i = 1; i <= 3; i++) { + SysSettingEntity entity = new SysSettingEntity(); + String index = StringTools.pad(i, 4); + entity.setTenantCode(TACD); + entity.setName("BatchTest-Date-" + index); + entity.setValue(index); + entity.setVersion(1); + entity.setState(SettingState.ENABLED); + entity.setCreateTime(new Date()); + if (i % 3 == 2) { + entity.setUpdateTime(null); + } else { + entity.setUpdateTime(new Date()); + } + entities.add(entity); + } + // 执行批量新增 + setdao.inserts(entities, false); } @Test(priority = 101) @@ -44,8 +83,13 @@ public class BatchInsertUpdateTest extends AbstractTestNGSpringContextTests { for (int i = 1; i <= total; i++) { SysLoggerEntity entity = new SysLoggerEntity(); String index = StringTools.pad(i, 4); + entity.setTenantCode(TACD); entity.setName("BatchTest-Insert-" + index); - entity.setContent("BatchTest-Content-" + index); + if (i % 100 == 1) { + entity.setContent(generateRandomContent(5000)); + } else { + entity.setContent("BatchTest-Content-" + index); + } if (i % 5 == 0) { defaultIndex++; // 测试默认值 @@ -58,15 +102,24 @@ public class BatchInsertUpdateTest extends AbstractTestNGSpringContextTests { entities.add(entity); } // 执行批量新增 - dao.inserts(entities); + logdao.inserts(entities); + } + + private String generateRandomContent(int size) { + StringBuilder buffer = new StringBuilder(); + for (int i = 0, z = size / 10; i < z; i++) { + buffer.append(RandomTools.generateString(10)); + } + return buffer.toString(); } @Test(priority = 102, dependsOnMethods = "testBatchInsert") public void testAllRecordTotal() { // 检查记录总数 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "starts", "BatchTest-Insert"); - int count = dao.count(where); + int count = logdao.count(where); Assert.assertEquals(count, total, "TotalRecord"); } @@ -74,9 +127,10 @@ public class BatchInsertUpdateTest extends AbstractTestNGSpringContextTests { public void testDefaultIndexTotal() { // 检查序号为默认值的记录数 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "starts", "BatchTest-Insert"); where.on("sortIndex", "=", 1); - int count = dao.count(where); + int count = logdao.count(where); Assert.assertEquals(count, defaultIndex, "DefaultIndexRecord"); } @@ -84,10 +138,11 @@ public class BatchInsertUpdateTest extends AbstractTestNGSpringContextTests { public void testBatchUpdate() { // 查询一些数据的ID, 用来执行批量更新 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "starts", "BatchTest-Insert"); where.on("sortIndex", ">", 1); OrderPaging odpg = OrderPaging.of(new Paging(3, updateTotal, false), "sortIndex"); - PageList ids = dao.listFieldValues("id", false, where, odpg, String.class); + PageList ids = logdao.listFieldValues("id", false, where, odpg, String.class); // 构造批量更新的实体数据 List changed = new ArrayList<>(); for (int i = 0; i < ids.size(); i++) { @@ -96,19 +151,24 @@ public class BatchInsertUpdateTest extends AbstractTestNGSpringContextTests { entity.setId(id); String index = StringTools.pad(i + 1, 4); entity.setName("BatchTest-Update-" + index); - entity.setContent("BatchTest-Content-" + index); + if (i % 20 == 1) { + entity.setContent(generateRandomContent(5000)); + } else { + entity.setContent("BatchTest-Content-" + index); + } changed.add(entity); } // 执行批量更新 - dao.updates(changed); + logdao.updates(changed); } @Test(priority = 202, dependsOnMethods = { "testBatchInsert", "testBatchUpdate" }) public void testUpdateTotal() { // 检查更新成功的记录数 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "starts", "BatchTest-Update"); - int count = dao.count(where); + int count = logdao.count(where); Assert.assertEquals(count, updateTotal, "UpdateRecord"); } } diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BuildLikeSqlTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BuildLikeSqlTest.java new file mode 100644 index 0000000000000000000000000000000000000000..75944f586b4972e87105672b69f2a4ed1e778171 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/BuildLikeSqlTest.java @@ -0,0 +1,54 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.jdbc.model.MainDbType; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.utils.DbTools; + +@Test +public class BuildLikeSqlTest { + + @Test + public void testMysqlBuildLikeSql() { + SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.MySQL); + // 查找包含xxx的数据 + testBuildLikeSql(dialect, "xxx", "LIKE CONCAT('%','xxx'/*$1*/,'%')"); + // 查找包含10%的数据 + testBuildLikeSql(dialect, "10%", "LIKE CONCAT('%','10#%'/*$1*/,'%') ESCAPE '#'/*$2*/"); + // 查找以10%结尾的数据 + testBuildLikeSql(dialect, new LikeValue('#', "%10#%"), "LIKE '%10#%'/*$1*/ ESCAPE '#'/*$2*/"); + // 查找含有HOT和COOL的数据 + testBuildLikeSql(dialect, new LikeValue("%HOT%COOL%"), "LIKE '%HOT%COOL%'/*$1*/"); + } + + @Test + public void testOracleBuildLikeSql() { + SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.Oracle); + // 查找包含xxx的数据 + testBuildLikeSql(dialect, "xxx", "LIKE('%'||'xxx'/*$1*/||'%')"); + // 查找包含10%的数据 + testBuildLikeSql(dialect, "10%", "LIKE('%'||'10#%'/*$1*/||'%') ESCAPE '#'/*$2*/"); + // 查找以10%结尾的数据 + testBuildLikeSql(dialect, new LikeValue('#', "%10#%"), "LIKE '%10#%'/*$1*/ ESCAPE '#'/*$2*/"); + // 查找含有HOT和COOL的数据 + testBuildLikeSql(dialect, new LikeValue("%HOT%COOL%"), "LIKE '%HOT%COOL%'/*$1*/"); + } + + private void testBuildLikeSql(SqlDialect dialect, Object likeValue, String expected) { + SqlBuffer sql; + if (likeValue instanceof LikeValue) { + sql = dialect.buildLikeSql((LikeValue) likeValue); + } else { + sql = dialect.buildLikeSql((String) likeValue); + } + String result = sql.getLoggingSqlString(dialect); + System.out.println("------------------------------"); + System.out.println(likeValue); + System.out.println(result); + System.out.println(expected); + Assert.assertEquals(result, expected, "BuildLikeSql for " + likeValue); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/DataSourceTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/DataSourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4690431dbc2ca145c54325f0501e1ec1711180c5 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/DataSourceTest.java @@ -0,0 +1,168 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.File; +import java.net.URL; +import java.util.Date; +import java.util.List; +import java.util.Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import com.alibaba.druid.pool.DruidDataSource; +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.biz.QdbcBootImpl; +import com.gitee.qdbp.jdbc.support.AutoDruidDataSource; +import com.gitee.qdbp.jdbc.support.AutoHikariDataSource; +import com.gitee.qdbp.jdbc.test.base.DatabaseInitiator; +import com.gitee.qdbp.jdbc.test.enums.SettingState; +import com.gitee.qdbp.jdbc.test.model.SysSettingEntity; +import com.gitee.qdbp.tools.files.FileTools; +import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.JsonTools; +import com.gitee.qdbp.tools.utils.PropertyTools; +import com.gitee.qdbp.tools.utils.RandomTools; +import com.zaxxer.hikari.HikariDataSource; + +/** + * 纯手工连接数据库测试类
+ * 这里使用的是DbPluginContainer的默认插件, 未提供的特性包括:
+ * • TableInfoScans.commonFieldResolver: 不会将公共字段放在查询列表的最后
+ * • EntityFieldFillStrategy.entityFillBizResolver: 不会自动填充创建人/创建时间等业务参数
+ * • EntityDataStateFillStrategy: 不会自动填充数据状态, 不支持逻辑删除
+ * 需要通过DbPluginContainer.defaults().registerXxx针对具体项目进行定制配置。
+ * + * @author zhaohuihua + * @version 20210503 + */ +@Test +public class DataSourceTest { + + private static Logger log = LoggerFactory.getLogger(DataSourceTest.class); + private static final String TACD = "DataSourceTest"; + private File sqliteFile = new File("/home/sqlite/temp-" + RandomTools.generateAlphabet(8) + ".db"); + + @Test + public void testH2DataSource() { + String name = RandomTools.generateAlphabet(8); + testDataSource("h2.mem@~/" + name); + } + + @Test + public void testSqliteDataSource() { + FileTools.mkdirsIfNotExists(sqliteFile); + testDataSource("sqlite.file@~/" + PathTools.formatPath(sqliteFile.getAbsolutePath())); + } + + // 对同一数据库, 不能同时进行测试 + @Test(enabled = false) + public void testMysqlDataSource() { + testManualDataSource("jdbc.mysql.properties"); + } + + @Test(enabled = false) + public void testOracleDataSource() { + testManualDataSource("jdbc.oracle.properties"); + } + + @Test(enabled = false) + public void testDb2DataSource() { + testManualDataSource("jdbc.db2.properties"); + } + + private void testManualDataSource(String file) { + URL url = PathTools.findResource(file); + if (url == null) { + return; + } + + Properties properties = PropertyTools.load(url); + String jdbcUrl = properties.getProperty("jdbc.sys"); + testDataSource(jdbcUrl); + } + + private void testDataSource(String jdbcUrl) { + log.info("jdbcUrl={}", jdbcUrl); + // 1. 获取数据源对象 + try (HikariDataSource datasource = AutoHikariDataSource.buildWith(jdbcUrl);) { + // 2. 获取数据库操作的构造器对象 + QdbcBoot qdbcBoot = QdbcBootImpl.buildWith(datasource); + // 创建数据库表 + DatabaseInitiator.initTables(qdbcBoot); + // 业务测试 + testSettingEntity(qdbcBoot); + } + // 1. 获取数据源对象 + try (DruidDataSource datasource = AutoDruidDataSource.buildWith(jdbcUrl);) { + // 2. 获取数据库操作的构造器对象 + QdbcBoot qdbcBoot = QdbcBootImpl.buildWith(datasource); + // 创建数据库表 + DatabaseInitiator.initTables(qdbcBoot); + // 业务测试 + testSettingEntity(qdbcBoot); + } + } + + private void testSettingEntity(QdbcBoot qdbcBoot) { + // 3. 针对具体的实体, 生成增删改查DAO对象 + CrudDao dao = qdbcBoot.buildCrudDao(SysSettingEntity.class); + { // 测试删除 + DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); + where.on("name", "starts", "HelloQdbc-"); + dao.physicalDelete(where); + } + String id; + { // 测试新增 + SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); + entity.setName("HelloQdbc-1"); + entity.setValue("测试新增 HelloQdbc-1"); + entity.setVersion(1); + entity.setCreateTime(new Date()); + entity.setState(SettingState.ENABLED); + id = dao.insert(entity); + log.info("id = {}", id); + } + { // 测试查询 + SysSettingEntity entity = dao.findById(id); + log.info(JsonTools.toLogString(entity)); + Assert.assertEquals(entity.getName(), "HelloQdbc-1", "SysSettingEntity.name"); + Assert.assertEquals(entity.getValue(), "测试新增 HelloQdbc-1", "SysSettingEntity.value"); + } + { // 测试修改 + SysSettingEntity entity = new SysSettingEntity(); + entity.setId(id); + entity.setTenantCode(TACD); + entity.setValue("测试修改 HelloQdbc-1"); + entity.setVersion(2); + dao.update(entity); + } + { // 测试查询 + SysSettingEntity entity = dao.findById(id); + log.info(JsonTools.toLogString(entity)); + Assert.assertEquals(entity.getName(), "HelloQdbc-1", "SysSettingEntity.name"); + Assert.assertEquals(entity.getValue(), "测试修改 HelloQdbc-1", "SysSettingEntity.value"); + } + } + + @BeforeClass + public void init() { + File folder = sqliteFile.getParentFile(); + List files = FileTools.collect(folder.getAbsolutePath(), "temp-*.db"); + for (File file : files) { + boolean state = FileTools.delete(file); + log.info("Delete sqlite history file return {}: {}", state, file.getAbsolutePath()); + } + } + + @AfterClass + public void destroy() { // 这里总是删不掉 + boolean state = FileTools.delete(sqliteFile); + log.info("Delete sqlite temporary file return {}: {}", state, sqliteFile.getAbsolutePath()); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/EntityDataStateTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/EntityDataStateTest.java index 8e0f1b6ebe1c0585d778a42ef057d776cbb66e28..8eb28133b1f9aa72c22e2a9e63ceb2aac04cfb60 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/EntityDataStateTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/EntityDataStateTest.java @@ -28,6 +28,7 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { private static Logger log = LoggerFactory.getLogger(EntityDataStateTest.class); + private static final String TACD = "EntityDataState"; @Autowired private QdbcBoot qdbcBoot; private CrudDao dao; @@ -40,7 +41,7 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { this.dao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); DbWhere where = new DbWhere(); - where.on("name", "starts", "DsTest"); + where.andEquals("tenantCode", TACD); dao.physicalDelete(where); } @@ -51,6 +52,7 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { for (int i = 1; i <= total; i++) { SysLoggerEntity entity = new SysLoggerEntity(); String index = StringTools.pad(i, 4); + entity.setTenantCode(TACD); entity.setName("DsTest-Insert-" + index); entity.setContent("DsTest-Content-" + index); entity.setSortIndex(i * 10); @@ -64,7 +66,8 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { public void testAllRecordTotal() { // 检查记录总数 DbWhere where = new DbWhere(); - where.on("name", "starts", "DsTest-Insert"); + where.andEquals("tenantCode", TACD); + where.andStarts("name", "DsTest-Insert"); int count = dao.count(where); Assert.assertEquals(count, total, "TotalRecord"); } @@ -73,8 +76,9 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { public void testLogicalDelete() { { // 查询一些数据的ID, 用来执行逻辑删除 DbWhere where = new DbWhere(); - where.on("name", "starts", "DsTest-Insert"); - where.on("sortIndex", ">", 1); + where.andEquals("tenantCode", TACD); + where.andStarts("name", "DsTest-Insert"); + where.andGreaterThen("sortIndex", 1); OrderPaging odpg = OrderPaging.of(new Paging(3, updateTotal, false), "sortIndex"); PageList temp = dao.listFieldValues("id", false, where, odpg, String.class); deletedIds = temp.toList(); @@ -89,7 +93,8 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { public void testDefaultDataStateCount() { // 根据默认数据状态过滤条件检查记录总数 DbWhere where = new DbWhere(); - where.on("name", "starts", "DsTest"); + where.andEquals("tenantCode", TACD); + where.andStarts("name", "DsTest"); int count = dao.count(where); Assert.assertEquals(count, total - updateTotal, "DefaultDataState"); } @@ -103,8 +108,9 @@ public class EntityDataStateTest extends AbstractTestNGSpringContextTests { } { // 测试数据状态=已删除的记录 DbWhere where = new DbWhere(); - where.on("id", "in", deletedIds); - where.on("dataState", "=", DataState.DELETED); + where.andEquals("tenantCode", TACD); + where.andIn("id", deletedIds); + where.andEquals("dataState", DataState.DELETED); List entities = dao.list(where, Orderings.NONE); Assert.assertEquals(entities.size(), deletedIds.size(), "DataState=DELETED"); log.info("Deleted entity data: {}", JsonTools.toLogString(entities.get(0))); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/InConditionTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/InConditionTest.java index 5501298a4ca0ace30b812813a55fa33000a09dcb..b0e61090fe851a58aa6347571a9ee3217b5e43e1 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/InConditionTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/InConditionTest.java @@ -32,7 +32,7 @@ public class InConditionTest extends AbstractTestNGSpringContextTests { values.add(calcCode(i)); } DbWhere where = new DbWhere(); - where.on("deptCode", "in", values); + where.on("deptId", "in", values); CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); int count = dao.count(where); Assert.assertEquals(count, 0, "InQueryResult"); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadInsertTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadInsertTest.java index 82253d8de60ea4e1be34ca50d2cf1ae4b2291470..e7bf3c181d0c6055d057393e04d11eba5995d339 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadInsertTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadInsertTest.java @@ -25,6 +25,7 @@ public class MultiThreadInsertTest extends AbstractTestNGSpringContextTests { private Logger log = LoggerFactory.getLogger(MultiThreadInsertTest.class); + private static final String TACD = "MultiThreadInsert"; @Autowired private QdbcBoot qdbcBoot; @Autowired @@ -35,13 +36,13 @@ public class MultiThreadInsertTest extends AbstractTestNGSpringContextTests { { CrudDao dao = qdbcBoot.buildCrudDao(SysSettingEntity.class); DbWhere where = new DbWhere(); - where.on("name", "starts", "MultiThreadInsert"); + where.on("tenantCode", "=", TACD); dao.physicalDelete(where); } { CrudDao dao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); DbWhere where = new DbWhere(); - where.on("content", "like", "MultiThreadInsert"); + where.on("tenantCode", "=", TACD); dao.physicalDelete(where); } } @@ -49,6 +50,7 @@ public class MultiThreadInsertTest extends AbstractTestNGSpringContextTests { @Test(priority = 2) public void testInsert() { SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName("MultiThreadInsert-" + 0); entity.setValue("测试 MultiThreadInsert-" + 0); sysSettingService.createSetting(entity, 5000L); @@ -63,6 +65,7 @@ public class MultiThreadInsertTest extends AbstractTestNGSpringContextTests { for (int i = 1; i <= size; i++) { for (int j = 1; j <= times; j++) { SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName("MultiThreadInsert-" + i); entity.setValue("测试 MultiThreadInsert-" + i); InsertThread thread = new InsertThread(entity, latch, counter); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadQueryTest.java index 49f54770c4e4d3faa90947706301b31243c668b1..81ebac9de955b17f1ed7d3e06ccac87888be3aad 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadQueryTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/MultiThreadQueryTest.java @@ -27,6 +27,7 @@ public class MultiThreadQueryTest extends AbstractTestNGSpringContextTests { private Logger log = LoggerFactory.getLogger(MultiThreadQueryTest.class); + private static final String TACD = "MultiThreadQuery"; @Autowired private QdbcBoot qdbcBoot; @Autowired @@ -36,13 +37,14 @@ public class MultiThreadQueryTest extends AbstractTestNGSpringContextTests { public void init() { CrudDao dao = qdbcBoot.buildCrudDao(SysSettingEntity.class); DbWhere where = new DbWhere(); - where.on("name", "starts", "MultiThreadQuery"); + where.on("tenantCode", "=", TACD); dao.physicalDelete(where); } @Test(priority = 2) public void testQuery() { DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("state", "=", SettingState.ENABLED); sysSettingService.listSetting(where, OrderPaging.NONE, 5000L); } @@ -56,6 +58,7 @@ public class MultiThreadQueryTest extends AbstractTestNGSpringContextTests { for (int i = 1; i <= size; i++) { int times = 1; DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "=", "MultiThreadQuery-" + i); where.on("state", "=", SettingState.ENABLED); QueryThread thread = new QueryThread(where, latch, counter, 2000L); @@ -69,6 +72,7 @@ public class MultiThreadQueryTest extends AbstractTestNGSpringContextTests { for (int i = 1; i <= size; i++) { int times = 2; DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "=", "MultiThreadQuery-" + i); where.on("state", "=", SettingState.ENABLED); QueryThread thread = new QueryThread(where, latch, counter, 0L); 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 new file mode 100644 index 0000000000000000000000000000000000000000..d92389dbd85a4882e3099137c3d7c36ba8d3d62a --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/RecursiveQueryTest.java @@ -0,0 +1,178 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.test.model.SysDeptEntity; +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.ConvertTools; +import com.gitee.qdbp.tools.utils.PropertyTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class RecursiveQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "DeptRecursive"; + @Autowired + private QdbcBoot qdbcBoot; + private Map> childrenMaps = new HashMap<>(); + + @BeforeClass + public void init() { + // 删除数据 + CrudDao dao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + DbWhere where = new DbWhere().andEquals("tenantCode", TACD); + dao.physicalDelete(where); + } + + @Test(priority = 1) + public void testInsertRecursiveTestData() { + CrudDao dao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + // 新增测试数据 + URL path = PathTools.findResource("settings/data/AreaDivision.txt"); + System.out.println(path); + List metadata = ToJsonProperties.parseMetadata(PropertyTools.load(path)); + String folder = PathTools.getOutputFolder(path, "./"); + Map>> result = ExcelToJson.convert(folder, metadata); + List> areas = result.get("areas"); + + List depts = new ArrayList<>(); + for (Map item : areas) { + String deptCode = (String) item.get("code"); + String deptName = (String) item.get("name"); + String parentCode = (String) item.get("parent"); + if (childrenMaps.containsKey(parentCode)) { + childrenMaps.get(parentCode).add(deptCode); + } else { + List list = ConvertTools.toList(deptCode); + childrenMaps.put(parentCode, list); + } + + SysDeptEntity entity = new SysDeptEntity(); + entity.setTenantCode(TACD); + entity.setId(deptCode); + entity.setDeptCode(deptCode); + entity.setDeptName(deptName); + entity.setParentCode(parentCode); + entity.setSortIndex(((Number) item.get("index")).intValue()); + depts.add(entity); + } + dao.inserts(depts); + } + + @Test(priority = 10) + public void testListChildren100000() { + testListChildren("100000"); + } + + @Test(priority = 11) + public void testListChildren320000() { + testListChildren("320000"); + } + + @Test(priority = 12) + public void testListChildren330000() { + testListChildren("330000"); + } + + @Test(priority = 13) + public void testListChildren340000() { + testListChildren("340000"); + } + + private void testListChildren(String deptCode) { + CrudDao dao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + DbWhere filter = new DbWhere().andEquals("tenantCode", TACD); + DbWhere search = DbWhere.NONE; + Orderings order = Orderings.of("parentCode, sortIndex"); + List children = dao.listChildren(deptCode, "deptCode", "parentCode", filter, search, order); + int actual = children.size(); + int expected = 1 + countChildren(childrenMaps, deptCode); + Assert.assertEquals(actual, expected, "Children size for '" + deptCode + "'"); + } + + @Test(priority = 21) + public void testListParents100000() { + testListParents("100000", 1); + } + + @Test(priority = 22) + public void testListParents320000() { + testListParents("320000", 2); + } + + @Test(priority = 23) + public void testListParents330000() { + testListParents("330000", 2); + } + + @Test(priority = 24) + public void testListParents320100() { + testListParents("320100", 3); + } + + @Test(priority = 25) + public void testListParents340100() { + testListParents("340100", 3); + } + + @Test(priority = 26) + public void testListParents320102() { + testListParents("320102", 4); + } + + @Test(priority = 27) + public void testListParents330110() { + testListParents("330110", 4); + } + + private void testListParents(String deptCode, int expected) { + CrudDao dao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + DbWhere filter = new DbWhere().andEquals("tenantCode", TACD); + DbWhere search = DbWhere.NONE; + Orderings order = Orderings.of("parentCode, sortIndex"); + List children = dao.listParents(deptCode, "deptCode", "parentCode", filter, search, order); + for (SysDeptEntity item : children) { + 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 + "'"); + } + + // 统计子节点数量, 返回结果不包含自身节点 + private int countChildren(Map> maps, String code) { + if (!maps.containsKey(code)) { + return 0; + } else { + List children = maps.get(code); + int count = children.size(); + for (String item : children) { + if (maps.containsKey(code)) { + count += countChildren(maps, item); + } else { + count += 1; + } + } + return count; + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SelectFieldsSqlTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SelectFieldsSqlTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ca6adfbcb019f2e23ea248841094c09fc34301e3 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SelectFieldsSqlTest.java @@ -0,0 +1,49 @@ +package com.gitee.qdbp.jdbc.test.biz; + +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.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.jdbc.test.model.SysSettingEntity; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class SelectFieldsSqlTest extends AbstractTestNGSpringContextTests { + + @Test + public void testBuildAllSelectFieldsSql1() { + testBuildSelectFieldsSql(SysSettingEntity.class, null, null, + "ID,TENANT_CODE,NAME,VALUE,VERSION,REMARK,STATE,CREATE_TIME,UPDATE_TIME,DATA_STATE"); + } + + @Test + public void testBuildAllSelectFieldsSql2() { + testBuildSelectFieldsSql(SysSettingEntity.class, "s", null, + "s.ID,s.TENANT_CODE,s.NAME,s.VALUE,s.VERSION,s.REMARK,s.STATE,s.CREATE_TIME,s.UPDATE_TIME,s.DATA_STATE"); + } + + @Test + public void testBuildExcludesSelectFieldsSql1() { + testBuildSelectFieldsSql(SysSettingEntity.class, null, "remark,tenantCode,dataState", + "ID,NAME,VALUE,VERSION,STATE,CREATE_TIME,UPDATE_TIME"); + } + + @Test + public void testBuildExcludesSelectFieldsSql2() { + testBuildSelectFieldsSql(SysSettingEntity.class, "s", "remark,tenantCode,dataState", + "s.ID,s.NAME,s.VALUE,s.VERSION,s.STATE,s.CREATE_TIME,s.UPDATE_TIME"); + } + + private void testBuildSelectFieldsSql(Class clazz, String alias, String excludes, String expected) { + SqlDialect dialect = null; + SqlBuffer buffer = SqlTools.buildSelectFieldsSql(clazz, alias, excludes, dialect); + String result = buffer.getExecutableSqlString(dialect); + System.out.println("------------------------------------------------------"); + System.out.println(result); + System.out.println(expected); + Assert.assertEquals(result, expected, "buildSelectFieldsSql"); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleCrudDaoTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleCrudDaoTest.java index f244f1c46c3a935aa78ed60e27166e3ee847449c..3be5852243c016182069b6e4d9a2ccc186f646fd 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleCrudDaoTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleCrudDaoTest.java @@ -1,6 +1,7 @@ package com.gitee.qdbp.jdbc.test.biz; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +40,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { private Logger log = LoggerFactory.getLogger(SimpleCrudDaoTest.class); + private static final String TACD = "CrudTest"; @Autowired private QdbcBoot qdbcBoot; @@ -51,25 +53,54 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { @Test(priority = 1001) public void testUserEntityDelete() { - DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); - CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); - dao.physicalDelete(where); + { + DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + dao.physicalDelete(where); + } + { + DbWhere where = new DbWhere(); + where.on("tenantCode", "=", "EntityFieldRefill"); + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + dao.physicalDelete(where); + } } + // 测试实体类自动填充的字段反填到实体类 @Test(priority = 1002) + public void testUserEntityFieldRefill() { + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode("EntityFieldRefill"); + entity.setUserCode("AA000001"); + entity.setSuperman(false); + entity.setDeptId("0"); + entity.setGender(Gender.UNKNOWN); + entity.setBirthday(DateTools.parse("2018-02-03 15:25:35.456")); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + entity.setUserType(UserType.ADMIN); + String id = dao.insert(entity); + Assert.assertEquals(entity.getId(), id, "EntityFieldRefill-id"); + Assert.assertEquals(entity.getDataState(), DataState.NORMAL, "EntityFieldRefill-dataState"); + Assert.assertNotNull(entity.getCreateTime(), "EntityFieldRefill-createTime"); + } + + @Test(priority = 1003) public void testUserEntityCreate() { CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); { // 超级管理员 SysUserEntity entity = new SysUserEntity(); - entity.setTenantCode("test"); + entity.setTenantCode(TACD); entity.setUserCode("super"); entity.setSuperman(true); - entity.setDeptCode("0"); + entity.setDeptId("0"); 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")); + entity.setCreateTime(DateTools.parse("2018-05-15 20:30:40.599")); entity.setId("Y1000001"); entity.setUserType(UserType.ADMIN); @@ -81,9 +112,9 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { } { // 添加普通用户 SysUserEntity entity = new SysUserEntity(); - entity.setTenantCode("test"); + entity.setTenantCode(TACD); entity.setSuperman(false); - entity.setDeptCode("0"); + entity.setDeptId("0"); entity.setGender(Gender.FEMALE); entity.setUserType(UserType.USER); entity.setUserState(UserState.NORMAL); @@ -103,9 +134,9 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { } { // 添加分页查询数据 SysUserEntity entity = new SysUserEntity(); - entity.setTenantCode("test"); + entity.setTenantCode(TACD); entity.setSuperman(false); - entity.setDeptCode("0"); + entity.setDeptId("0"); entity.setGender(Gender.FEMALE); entity.setUserType(UserType.USER); entity.setUserState(UserState.NORMAL); @@ -123,7 +154,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { @Test(priority = 2011) public void testUserEntityQuery() { SysUserEntity entity = new SysUserEntity(); - entity.setTenantCode("test"); + entity.setTenantCode(TACD); entity.setUserCode("super"); entity.setSuperman(true); entity.setUserType(UserType.ADMIN); @@ -135,6 +166,9 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { String desc = "UserEntity[super]"; Assert.assertNotNull(user, desc + "QueryResult"); Assert.assertEquals(user.getSuperman(), Boolean.TRUE, desc + ":BooleanField[superman]"); + String actualCreateTime = DateTools.toNormativeString(user.getCreateTime()); + String expectedCreateTime = "2018-05-15 20:30:40.599"; + Assert.assertEquals(actualCreateTime, expectedCreateTime, desc + ":InterfaceField[createTime]"); Assert.assertEquals(user.getUserType(), UserType.ADMIN, desc + ":InterfaceField[userType]"); Assert.assertEquals(user.getUserState(), UserState.NORMAL, desc + ":EnumField[userState]"); Assert.assertEquals(user.getDataState(), DataState.NORMAL, desc + ":DataStateField[dataState]"); @@ -143,7 +177,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { @Test(priority = 2012) public void testUserWhereQuery() { DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); + where.on("tenantCode", "=", TACD); where.on("userCode", "=", "super"); where.on("superman", "=", true); where.on("userType", "=", UserType.ADMIN); @@ -155,11 +189,12 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { Assert.assertNotNull(user, "UserWhere[super]QueryResult"); } + @SuppressWarnings("deprecation") @Test(priority = 2013) public void testUserOrQuery() { // @formatter:off DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); + where.on("tenantCode", "=", TACD); where.on("userType", "=", UserType.USER); where.on("userState", "in", UserState.NORMAL, UserState.LOCKED); where.on("createTime", ">=", DateTools.parse("2017-01-01")); @@ -173,11 +208,30 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { Assert.assertEquals(users.size(), 2, "UserOrQueryResult"); } + @Test(priority = 2014) + public void testUserOrNewQuery() { + // @formatter:off + DbWhere where = new DbWhere() + .andEquals("tenantCode", TACD) + .andEquals("userType", UserType.USER) + .andIn("userState", UserState.NORMAL, UserState.LOCKED) + .andGreaterEqualsThen("createTime", DateTools.parse("2017-01-01")) + .andSubCondition((w) -> { + w.orEquals("userCode", "kelly") + .orEquals("superman", true); + }); + // @formatter:on + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + PageList users = dao.list(where, OrderPaging.NONE); + log.debug("UserOrQueryResult: {}", JsonTools.toLogString(users)); + Assert.assertEquals(users.size(), 2, "UserOrQueryResult"); + } + @Test(priority = 2020) public void testFindIncludeFields() { Fields fields = new IncludeFields("id,userCode,userName,nickName,realName,phone,email"); DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); + where.on("tenantCode", "=", TACD); where.on("userType", "=", UserType.USER); where.on("userCode", "=", "kelly"); CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); @@ -186,7 +240,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { Assert.assertNotNull(entity, "FindIncludeFieldsQueryResult"); Assert.assertNotNull(entity.getId(), "UserEntity.id"); Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); - Assert.assertNull(entity.getDeptCode(), "UserEntity.deptCode"); + Assert.assertNull(entity.getDeptId(), "UserEntity.deptId"); Assert.assertNull(entity.getUserType(), "UserEntity.userType"); Assert.assertNull(entity.getUserState(), "UserEntity.userState"); Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); @@ -197,7 +251,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { public void testFindExcludeFields() { Fields fields = new ExcludeFields("tenantCode,userState,createTime"); DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); + where.on("tenantCode", "=", TACD); where.on("userType", "=", UserType.USER); where.on("userCode", "=", "kelly"); CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); @@ -206,7 +260,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { Assert.assertNotNull(entity, "FindExcludeFieldsQueryResult"); Assert.assertNotNull(entity.getId(), "UserEntity.id"); Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); - Assert.assertNotNull(entity.getDeptCode(), "UserEntity.deptCode"); + Assert.assertNotNull(entity.getDeptId(), "UserEntity.deptId"); Assert.assertNotNull(entity.getUserType(), "UserEntity.userType"); Assert.assertNull(entity.getUserState(), "UserEntity.userState"); Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); @@ -217,7 +271,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { public void testListIncludeFields() { IncludeFields fields = new IncludeFields("id,userCode,userName,nickName,realName,phone,email"); DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); + where.on("tenantCode", "=", TACD); where.on("userType", "=", UserType.USER); where.on("userCode", "in", "kelly", "evan", "coral"); CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); @@ -226,7 +280,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { for (SysUserEntity entity : entities) { Assert.assertNotNull(entity.getId(), "UserEntity.id"); Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); - Assert.assertNull(entity.getDeptCode(), "UserEntity.deptCode"); + Assert.assertNull(entity.getDeptId(), "UserEntity.deptId"); Assert.assertNull(entity.getUserType(), "UserEntity.userType"); Assert.assertNull(entity.getUserState(), "UserEntity.userState"); Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); @@ -238,7 +292,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { public void testListExcludeFields() { Fields fields = new ExcludeFields("tenantCode,userState,createTime"); DbWhere where = new DbWhere(); - where.on("tenantCode", "=", "test"); + where.on("tenantCode", "=", TACD); where.on("userType", "=", UserType.USER); where.on("userCode", "in", "kelly", "evan", "coral"); CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); @@ -247,7 +301,7 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { for (SysUserEntity entity : entities) { Assert.assertNotNull(entity.getId(), "UserEntity.id"); Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); - Assert.assertNotNull(entity.getDeptCode(), "UserEntity.deptCode"); + Assert.assertNotNull(entity.getDeptId(), "UserEntity.deptId"); Assert.assertNotNull(entity.getUserType(), "UserEntity.userType"); Assert.assertNull(entity.getUserState(), "UserEntity.userState"); Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); @@ -272,6 +326,25 @@ public class SimpleCrudDaoTest extends AbstractTestNGSpringContextTests { } } + @Test(priority = 3020) + public void testFindFieldValue() { + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + DbWhere where = new DbWhere(); + where.andEquals("id", "Y1000001"); + String userCode = dao.findFieldValue("userCode", where, String.class); + log.debug("FindFieldValue: userCode={}", userCode); + String email = dao.findFieldValue("email", where, String.class); + log.debug("FindFieldValue: email={}", email); + Gender gender = dao.findFieldValue("gender", where, Gender.class); + log.debug("FindFieldValue: gender={}", gender); + UserState userState = dao.findFieldValue("userState", where, UserState.class); + log.debug("FindFieldValue: userState={}", userState); + UserSource userSource = dao.findFieldValue("userSource", where, UserSource.class); + log.debug("FindFieldValue: userSource={}", userSource); + Date createTime = dao.findFieldValue("createTime", where, Date.class); + log.debug("FindFieldValue: createTime={}", createTime); + } + private List getUserNames(PageList users) { List userNames = new ArrayList<>(); for (SysUserEntity user : users) { diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleJoinQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleJoinQueryTest.java index 9dc9663707f987bcb7700f7d0236a8f71123ed3c..f50b7e9033628fcae6543cc8451cf89cd330b35c 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleJoinQueryTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleJoinQueryTest.java @@ -14,6 +14,7 @@ import com.gitee.qdbp.able.jdbc.condition.DbWhere; import com.gitee.qdbp.able.jdbc.condition.TableJoin; import com.gitee.qdbp.able.jdbc.model.DbFieldName; import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; import com.gitee.qdbp.able.jdbc.paging.PageList; import com.gitee.qdbp.jdbc.api.JoinQueryer; import com.gitee.qdbp.jdbc.api.QdbcBoot; @@ -30,6 +31,7 @@ public class SimpleJoinQueryTest extends AbstractTestNGSpringContextTests { private Logger log = LoggerFactory.getLogger(SimpleJoinQueryTest.class); + private static final String TACD = "SJQT"; @Autowired private QdbcBoot qdbcBoot; @Autowired @@ -37,7 +39,7 @@ public class SimpleJoinQueryTest extends AbstractTestNGSpringContextTests { @BeforeClass public void init() { - userService.initUserRoles('A', "joinquery"); + userService.initUserRoles(TACD, true); } /** @@ -48,17 +50,17 @@ public class SimpleJoinQueryTest extends AbstractTestNGSpringContextTests { */ @Test(priority = 2) public void testUserBeanQuery() { - List userCodes = Arrays.asList("U001", "U002", "U005"); + List userCodes = Arrays.asList(TACD + "U001", TACD + "U002", TACD + "U005"); // @formatter:off TableJoin tables = new TableJoin(SysUserEntity.class, "u", "user") .innerJoin(SysUserRoleEntity.class, "ur") .on("u.id", "=", new DbFieldName("ur.userId")) .innerJoin(SysRoleEntity.class, "r", "role") .on("ur.roleId", "=", new DbFieldName("r.id")) - .and("r.tenantCode", "=", "joinquery") + .and("r.tenantCode", "=", TACD) .end(); DbWhere where = new DbWhere() - .on("u.tenantCode", "=", "joinquery") + .on("u.tenantCode", "=", TACD) .on("u.userCode", "in", userCodes); // @formatter:on @@ -78,7 +80,7 @@ public class SimpleJoinQueryTest extends AbstractTestNGSpringContextTests { */ @Test(priority = 3) public void testRolesQueryByUser() { - String userCode = "U003"; + String userCode = TACD + "U003"; // @formatter:off TableJoin tables = new TableJoin(SysUserEntity.class, "u") .innerJoin(SysUserRoleEntity.class, "ur") @@ -86,10 +88,10 @@ public class SimpleJoinQueryTest extends AbstractTestNGSpringContextTests { // this表示结果字段放在主对象中 .innerJoin(SysRoleEntity.class, "r", "this") .on("ur.roleId", "=", new DbFieldName("r.id")) - .and("r.tenantCode", "=", "joinquery") + .and("r.tenantCode", "=", TACD) .end(); DbWhere where = new DbWhere() - .on("r.tenantCode", "=", "joinquery") + .on("r.tenantCode", "=", TACD) .on("u.userCode", "=", userCode); // @formatter:on JoinQueryer query = qdbcBoot.buildJoinQuery(tables, SysRoleEntity.class); @@ -99,4 +101,38 @@ public class SimpleJoinQueryTest extends AbstractTestNGSpringContextTests { // 用户1有2个角色, 2有4个, 3=6, 4=8, ... Assert.assertEquals(roles.size(), 6, "JoinQuery.roles"); } + + + + /** + * 查询某用户所配置的角色名称
+ * sys_user关联sys_user_role再关联sys_role
+ * 只取角色名称
+ */ + @Test(priority = 4) + public void testRoleNamesQueryByUser() { + String userCode = TACD + "U003"; + // @formatter:off + TableJoin tables = new TableJoin(SysUserEntity.class, "u") + .innerJoin(SysUserRoleEntity.class, "ur") + .on("u.id", "=", new DbFieldName("ur.userId")) + // this表示结果字段放在主对象中 + .innerJoin(SysRoleEntity.class, "r") + .on("ur.roleId", "=", new DbFieldName("r.id")) + .and("r.tenantCode", "=", TACD) + .end(); + DbWhere where = new DbWhere() + .on("r.tenantCode", "=", TACD) + .on("u.userCode", "=", userCode); + // @formatter:on + JoinQueryer query = qdbcBoot.buildJoinQuery(tables, SysRoleEntity.class); + List roleNames = query.listFieldValues("roleName", true, where, Orderings.NONE, String.class); + log.debug("RolesQueryByUserResult: {}", JsonTools.toLogString(roleNames)); + Assert.assertNotNull(roleNames); + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + Assert.assertEquals(roleNames.size(), 6, "JoinQuery.testRoleNamesQueryByUser"); + for (String roleName : roleNames) { + Assert.assertNotNull(roleName); + } + } } diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleLikeTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleLikeTest.java index 773089cbeb9d140eb3abe0f4cfc01794af934275..cecca1d9be970febd6a45e451b38922d7e8db822 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleLikeTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SimpleLikeTest.java @@ -9,9 +9,9 @@ import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.api.CrudDao; import com.gitee.qdbp.jdbc.api.QdbcBoot; -import com.gitee.qdbp.jdbc.model.LikeValue; import com.gitee.qdbp.jdbc.test.enums.Gender; import com.gitee.qdbp.jdbc.test.enums.UserSource; import com.gitee.qdbp.jdbc.test.enums.UserState; @@ -27,7 +27,7 @@ public class SimpleLikeTest extends AbstractTestNGSpringContextTests { private QdbcBoot qdbcBoot; @BeforeClass - public void testVersionQuery() { + public void initData() { CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); // 清除数据 dao.physicalDelete(new DbWhere().on("tenantCode", "=", "liketest")); @@ -36,7 +36,7 @@ public class SimpleLikeTest extends AbstractTestNGSpringContextTests { SysUserEntity entity = new SysUserEntity(); entity.setTenantCode("liketest"); entity.setSuperman(false); - entity.setDeptCode("0"); + entity.setDeptId("0"); entity.setGender(Gender.FEMALE); entity.setUserType(UserType.USER); entity.setUserState(UserState.NORMAL); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateDeptInfosQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateDeptInfosQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..65907da2937ae67a72789bd515880e3fa72d9159 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateDeptInfosQueryTest.java @@ -0,0 +1,82 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +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.DataState; +import com.gitee.qdbp.jdbc.test.model.SysDeptEntity; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class SqlTemplateDeptInfosQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "STDI"; + @Autowired + private QdbcBoot qdbcBoot; + + @BeforeClass + public void init() { + CrudDao dao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + { // 清除数据 + DbWhere where = new DbWhere().andEquals("tenantCode", TACD); + dao.physicalDelete(where); + } + } + + @Test(priority = 1) + public void testBatchInsert() throws IOException { + Date now = new Date(); + List list = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + SysDeptEntity dept = new SysDeptEntity(); + dept.setTenantCode(TACD); + dept.setId(TACD + "D000" + i); + dept.setDeptCode(dept.getId()); + dept.setDeptName("Test000" + i); + dept.setCreateTime(now); + dept.setParentCode("0"); + dept.setSortIndex(i); + dept.setDataState(DataState.NORMAL); + list.add(dept); + } + Map params = new HashMap<>(); + params.put("list", list); + qdbcBoot.getSqlDao().insert("dept.batch.insert", params); + } + + @Test(priority = 2, dependsOnMethods = "testBatchInsert") + public void testGetDeptInfosQuerySql13() throws IOException { + Map map = new HashMap<>(); + map.put("deptIds", Arrays.asList(TACD + "D0001", TACD + "D0002", TACD + "D0003")); + testGetDeptInfosQuery(map, 3); + } + + private void testGetDeptInfosQuery(Map where, int size) throws IOException { + where.put("tenantCode", TACD); + Map map = new HashMap<>(); + map.put("where", where); + String sqlId = "dept.info.query"; + List result = qdbcBoot.getSqlDao().listForObjects(sqlId, map, SysDeptEntity.class); + + Assert.assertEquals(result.size(), size, "dept.info.query"); + for (SysDeptEntity item : result) { + Assert.assertNotNull(item.getId(), "dept.info.query[result.id]"); + Assert.assertNotNull(item.getDeptCode(), "dept.info.query[result.deptCode]"); + Assert.assertNotNull(item.getDeptName(), "dept.info.query[result.deptName]"); + Assert.assertNotNull(item.getDataState(), "dept.info.query[result.dataState]"); + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.count.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.count.sql deleted file mode 100644 index 987489dc30dd45bf3786a0d5a14ca1942cf00579..0000000000000000000000000000000000000000 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.count.sql +++ /dev/null @@ -1,27 +0,0 @@ -SELECT SUM(CNT) FROM ( - SELECT COUNT(*) AS CNT - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_='U0000001'/*$1*/ - OR ( - T.ASSIGNEE_ IN('R0000001'/*$2*/,'R0000006'/*$3*/) - AND S.DEPT_ID IN('D0000001'/*$4*/,'D0000005'/*$5*/,'D0000012'/*$6*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$7*/ -UNION ALL - SELECT COUNT(*) AS CNT - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE='U0000001'/*$8*/ - OR ( - S.ASSIGNEE IN('R0000001'/*$9*/,'R0000006'/*$10*/) - AND S.DEPT_ID IN('D0000001'/*$11*/,'D0000005'/*$12*/,'D0000012'/*$13*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$14*/ -) R \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.count2.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.count2.sql deleted file mode 100644 index 987489dc30dd45bf3786a0d5a14ca1942cf00579..0000000000000000000000000000000000000000 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.count2.sql +++ /dev/null @@ -1,27 +0,0 @@ -SELECT SUM(CNT) FROM ( - SELECT COUNT(*) AS CNT - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_='U0000001'/*$1*/ - OR ( - T.ASSIGNEE_ IN('R0000001'/*$2*/,'R0000006'/*$3*/) - AND S.DEPT_ID IN('D0000001'/*$4*/,'D0000005'/*$5*/,'D0000012'/*$6*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$7*/ -UNION ALL - SELECT COUNT(*) AS CNT - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE='U0000001'/*$8*/ - OR ( - S.ASSIGNEE IN('R0000001'/*$9*/,'R0000006'/*$10*/) - AND S.DEPT_ID IN('D0000001'/*$11*/,'D0000005'/*$12*/,'D0000012'/*$13*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$14*/ -) R \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.query.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.query.sql deleted file mode 100644 index 3dbd05939f97ed06e2f69f0f50755fcb178ba9ad..0000000000000000000000000000000000000000 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.query.sql +++ /dev/null @@ -1,38 +0,0 @@ -SELECT * FROM ( - SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, - T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_='U0000001'/*$1*/ - OR ( - T.ASSIGNEE_ IN('R0000001'/*$2*/,'R0000006'/*$3*/) - AND S.DEPT_ID IN('D0000001'/*$4*/,'D0000005'/*$5*/,'D0000012'/*$6*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$7*/ -UNION ALL - SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, - S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE='U0000001'/*$8*/ - OR ( - S.ASSIGNEE IN('R0000001'/*$9*/,'R0000006'/*$10*/) - AND S.DEPT_ID IN('D0000001'/*$11*/,'D0000005'/*$12*/,'D0000012'/*$13*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$14*/ -) R -ORDER BY R.RECEIVE_TIME DESC,R.CREATE_TIME DESC,R.BUSINESS_ID \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.query2.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.query2.sql deleted file mode 100644 index 3dbd05939f97ed06e2f69f0f50755fcb178ba9ad..0000000000000000000000000000000000000000 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.backlog.todo.query2.sql +++ /dev/null @@ -1,38 +0,0 @@ -SELECT * FROM ( - SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, - T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER - FROM ACT_RU_TASK T - INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID - INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - T.ASSIGNEE_='U0000001'/*$1*/ - OR ( - T.ASSIGNEE_ IN('R0000001'/*$2*/,'R0000006'/*$3*/) - AND S.DEPT_ID IN('D0000001'/*$4*/,'D0000005'/*$5*/,'D0000012'/*$6*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$7*/ -UNION ALL - SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, - S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, - S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, - S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, - S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, - S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER - FROM COMM_OPERATE_TASK S - LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE - WHERE ( - S.ASSIGNEE='U0000001'/*$8*/ - OR ( - S.ASSIGNEE IN('R0000001'/*$9*/,'R0000006'/*$10*/) - AND S.DEPT_ID IN('D0000001'/*$11*/,'D0000005'/*$12*/,'D0000012'/*$13*/) - ) - ) - AND S.PROJECT_CODE='P0000001'/*$14*/ -) R -ORDER BY R.RECEIVE_TIME DESC,R.CREATE_TIME DESC,R.BUSINESS_ID \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserInfosQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserInfosQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6dadd000fe1700d3dfc3c836600c663091bb48ef --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserInfosQueryTest.java @@ -0,0 +1,127 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.test.model.UserAndDeptResult; +import com.gitee.qdbp.jdbc.test.service.SysUserService; +import com.gitee.qdbp.tools.utils.DateTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class SqlTemplateUserInfosQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "STUI"; + private static final String TACD1 = "STUI1"; + private static final String TACD2 = "STUI%"; + // 部门1有2个用户, 2有4个, 3=6, 4=8, ... + @Autowired + private SysUserService userService; + + @BeforeClass + public void init() { + userService.initUserDepts(TACD, TACD1, true); + userService.initUserDepts(TACD, TACD2, false); + } + + @Test + public void testQueryUserInfosQuerySql12() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", TACD2); + testQueryUserInfosQuery(map, 30); // 2+4+6+8+10 + } + + @Test + public void testQueryUserInfosQuerySql13() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", TACD1); + testQueryUserInfosQuery(map, 30); // 2+4+6+8+10 + } + + @Test + public void testQueryUserInfosQuerySql14() throws IOException { + Map map = new HashMap<>(); + map.put("userId", TACD1 + "U0000001"); + testQueryUserInfosQuery(map, 1); + } + + @Test + public void testQueryUserInfosQuerySql15() throws IOException { + Map map = new HashMap<>(); + map.put("userIds", Arrays.asList(TACD1 + "U0000001", TACD1 + "U0000002", TACD1 + "U0000003")); + testQueryUserInfosQuery(map, 3); + } + + @Test + public void testQueryUserInfosQuerySql21() throws IOException { + Date now = new Date(); + Map map = new HashMap<>(); + map.put("userCode", TACD2); + map.put("createTimeMin", DateTools.toStartTime(now)); + map.put("createTimeMax", DateTools.toEndTime(now)); + testQueryUserInfosQuery(map, 30); + } + + @Test + public void testQueryUserInfosQuerySql22() throws IOException { + Map map = new HashMap<>(); + map.put("keyword", TACD2 + "U00"); + testQueryUserInfosQuery(map, 9); + } + + @Test + public void testQueryUserInfosQuerySql23() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", new LikeValue('#', TACD + "#%U00%")); + testQueryUserInfosQuery(map, 9); + } + + @Test + public void testQueryUserInfosQuerySql24() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", new LikeValue(TACD2 + "U00%")); + testQueryUserInfosQuery(map, 18); + } + + @Test + public void testQueryUserInfosQuerySql31() throws IOException { + Map map = new HashMap<>(); + map.put("deptId", TACD1 + "D0000002"); + testQueryUserInfosQuery(map, 4); + } + + @Test + public void testQueryUserInfosQuerySql32() throws IOException { + Map map = new HashMap<>(); + map.put("deptCode", TACD1 + "D003"); + testQueryUserInfosQuery(map, 6); + } + + private void testQueryUserInfosQuery(Map map, int size) throws IOException { + map.put("tenantCode", TACD); + OrderPaging odpg = OrderPaging.of(Paging.NONE, "id,createTime"); + PageList result = userService.getUserInfosForSql(map, odpg); + + Assert.assertEquals(result.size(), size, "SysUserService.getUserInfos"); + for (UserAndDeptResult item : result) { + Assert.assertNotNull(item.getId(), "SysUserService.getUserInfos[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.getUserInfos[result.userCode]"); + Assert.assertNotNull(item.getNickName(), "SysUserService.getUserInfos[result.nickName]"); + Assert.assertNotNull(item.getDeptCode(), "SysUserService.getUserInfos[result.deptCode]"); + Assert.assertNotNull(item.getDeptName(), "SysUserService.getUserInfos[result.deptName]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.getUserInfos[result.dataState]"); + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserRolesQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserRolesQueryTest.java index b782a734d448a37236c63cc0d70f17fe2adaa709..e38a74214194ce2d11db724a09141c1e4682eab6 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserRolesQueryTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateUserRolesQueryTest.java @@ -31,12 +31,13 @@ import com.gitee.qdbp.tools.utils.VerifyTools; @ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTests { + private static final String TACD = "STUR"; @Autowired private SysUserService userService; @BeforeClass public void init() { - userService.initUserRoles('B', "userrole"); + userService.initUserRoles(TACD, true); } /////////////////////////////////////////////// @@ -45,96 +46,96 @@ public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTe @Test public void testGetUserRolesQuerySql11() throws IOException { - String userId = "UB0000001"; - List list = userService.getUserRoles(userId, DbWhere.NONE, Orderings.NONE); + String userId = TACD + "U0000001"; + List list = userService.getUserRolesForSql(userId, DbWhere.NONE, Orderings.NONE); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 2; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (SysRoleEntity item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); } } @Test public void testGetUserRolesQuerySql12() throws IOException { - List userIds = Arrays.asList("UB0000001", "UB0000002", "UB0000008"); - List list = userService.getUserRoles(userIds, DbWhere.NONE, Orderings.NONE); + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List list = userService.getUserRolesForSql(userIds, DbWhere.NONE, Orderings.NONE); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 22; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (UserIdRoleResult item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.getUserId(), "SysUserService.getUserRoles[result.userId]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); } } @Test public void testGetUserRolesQuerySql21() throws IOException { - String userId = "UB0000001"; + String userId = TACD + "U0000001"; Orderings orderings = Orderings.of("ur.userId, r.id"); - List list = userService.getUserRoles(userId, DbWhere.NONE, orderings); + List list = userService.getUserRolesForSql(userId, DbWhere.NONE, orderings); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 2; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (SysRoleEntity item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); } } @Test public void testGetUserRolesQuerySql22() throws IOException { - List userIds = Arrays.asList("UB0000001", "UB0000002", "UB0000008"); + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); Orderings orderings = Orderings.of("ur.userId, r.id"); - List list = userService.getUserRoles(userIds, DbWhere.NONE, orderings); + List list = userService.getUserRolesForSql(userIds, DbWhere.NONE, orderings); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 22; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (UserIdRoleResult item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.getUserId(), "SysUserService.getUserRoles[result.userId]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); } } @Test public void testGetUserRolesQuerySql31() throws IOException { - String userId = "UB0000001"; + String userId = TACD + "U0000001"; DbWhere where = new DbWhere(); - where.on("r.tenantCode", "=", "userrole"); + where.on("r.tenantCode", "=", TACD); Orderings orderings = Orderings.of("ur.userId, r.id"); - List list = userService.getUserRoles(userId, where, orderings); + List list = userService.getUserRolesForSql(userId, where, orderings); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 2; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (SysRoleEntity item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); } } @Test public void testGetUserRolesQuerySql32() throws IOException { - List userIds = Arrays.asList("UB0000001", "UB0000002", "UB0000008"); + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); DbWhere where = new DbWhere(); - where.on("r.tenantCode", "=", "userrole"); + where.on("r.tenantCode", "=", TACD); Orderings orderings = Orderings.of("ur.userId, r.id"); - List list = userService.getUserRoles(userIds, where, orderings); + List list = userService.getUserRolesForSql(userIds, where, orderings); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 22; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (UserIdRoleResult item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.getUserId(), "SysUserService.getUserRoles[result.userId]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); } } @@ -144,110 +145,110 @@ public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTe @Test public void testGetRoleUsersQuerySql11() throws IOException { - String roleId = "RB0000001"; + String roleId = TACD + "R0000001"; OrderPaging odpg = OrderPaging.of(new Paging(1, 5)); - PageList list = userService.getRoleUsers(roleId, DbWhere.NONE, odpg); + PageList list = userService.getRoleUsersForSql(roleId, DbWhere.NONE, odpg); // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 5; int total = 10; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (SysUserEntity item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @Test public void testGetRoleUsersQuerySql12() throws IOException { - List roleIds = Arrays.asList("RB0000001", "RB0000002", "RB0000008"); + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); - PageList list = userService.getRoleUsers(roleIds, DbWhere.NONE, odpg); + PageList list = userService.getRoleUsersForSql(roleIds, DbWhere.NONE, odpg); // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 7; int total = 27; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (RoleIdUserResult item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.getRoleId(), "SysUserService.getRoleUsers[result.roleId]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @Test public void testGetRoleUsersQuerySql21() throws IOException { - String roleId = "RB0000001"; + String roleId = TACD + "R0000001"; OrderPaging odpg = OrderPaging.of(new Paging(1, 5), "ur.roleId, u.id"); - PageList list = userService.getRoleUsers(roleId, DbWhere.NONE, odpg); + PageList list = userService.getRoleUsersForSql(roleId, DbWhere.NONE, odpg); // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 5; int total = 10; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (SysUserEntity item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @Test public void testGetRoleUsersQuerySql22() throws IOException { - List roleIds = Arrays.asList("RB0000001", "RB0000002", "RB0000008"); + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); OrderPaging odpg = OrderPaging.of(new Paging(3, 10), "ur.roleId, u.id"); - PageList list = userService.getRoleUsers(roleIds, DbWhere.NONE, odpg); + PageList list = userService.getRoleUsersForSql(roleIds, DbWhere.NONE, odpg); // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 7; int total = 27; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (RoleIdUserResult item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.getRoleId(), "SysUserService.getRoleUsers[result.roleId]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @Test public void testGetRoleUsersQuerySql31() throws IOException { - String roleId = "RB0000001"; + String roleId = TACD + "R0000001"; DbWhere where = new DbWhere(); - where.on("u.tenantCode", "=", "userrole"); + where.on("u.tenantCode", "=", TACD); OrderPaging odpg = OrderPaging.of(new Paging(1, 5), "ur.roleId, u.id"); - PageList list = userService.getRoleUsers(roleId, where, odpg); + PageList list = userService.getRoleUsersForSql(roleId, where, odpg); // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 5; int total = 10; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (SysUserEntity item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @Test public void testGetRoleUsersQuerySql32() throws IOException { - List roleIds = Arrays.asList("RB0000001", "RB0000002", "RB0000008"); + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); DbWhere where = new DbWhere(); - where.on("u.tenantCode", "=", "userrole"); + where.on("u.tenantCode", "=", TACD); OrderPaging odpg = OrderPaging.of(new Paging(3, 10), "ur.roleId, u.id"); - PageList list = userService.getRoleUsers(roleIds, where, odpg); + PageList list = userService.getRoleUsersForSql(roleIds, where, odpg); // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 7; int total = 27; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (RoleIdUserResult item : list) { - Assert.assertNotNull(item.getId(), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.getRoleId(), "SysUserService.getRoleUsers[result.roleId]"); - Assert.assertNotNull(item.getDataState(), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @@ -257,34 +258,109 @@ public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTe @Test public void testGetUserRolesMapQuery() throws IOException { - List userIds = Arrays.asList("UB0000001", "UB0000002", "UB0000008"); - List> list = getUserRoles(userIds, DbWhere.NONE, Orderings.NONE); + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List> list = getUserRoleMaps(userIds, DbWhere.NONE, Orderings.NONE); // 用户1有2个角色, 2有4个, 3=6, 4=8, ... int size = 22; - Assert.assertTrue(list.size() == size, "SysUserService.getUserRoles"); + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); for (Map item : list) { - Assert.assertNotNull(item.get("id"), "SysUserService.getUserRoles[result.id]"); - Assert.assertNotNull(item.get("userId"), "SysUserService.getUserRoles[result.userId]"); - Assert.assertNotNull(item.get("dataState"), "SysUserService.getUserRoles[result.dataState]"); + Assert.assertNotNull(item.get("userId"), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.get("id"), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.get("roleName"), "SysUserService.queryUserRoles[result.roleName]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + // 3.2.17修复的BUG, count语句缓存错误 + @Test + public void testGetRoleUsersMapQuery2() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000005"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList> list = getRoleUserMaps(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 8; + int total = 28; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (Map item : list) { + Assert.assertNotNull(item.get("id"), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.get("roleId"), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryRoleUsers[result.dataState]"); } } @Test public void testGetRoleUsersMapQuery() throws IOException { - List roleIds = Arrays.asList("RB0000001", "RB0000002", "RB0000008"); + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); - PageList> list = getRoleUsers(roleIds, DbWhere.NONE, odpg); + PageList> list = getRoleUserMaps(roleIds, DbWhere.NONE, odpg); // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 int size = 7; int total = 27; - Assert.assertTrue(list.size() == size, "SysUserService.getRoleUsers[result.size]"); - Assert.assertTrue(list.getTotal() == total, "SysUserService.getRoleUsers[result.total]"); + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); for (Map item : list) { - Assert.assertNotNull(item.get("id"), "SysUserService.getRoleUsers[result.id]"); - Assert.assertNotNull(item.get("roleId"), "SysUserService.getRoleUsers[result.roleId]"); - Assert.assertNotNull(item.get("dataState"), "SysUserService.getRoleUsers[result.dataState]"); + Assert.assertNotNull(item.get("roleId"), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.get("id"), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.get("userCode"), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetUserRolesObjectQuery() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List list = getUserRoleObjects(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getRoleName(), "SysUserService.queryUserRoles[result.roleName]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersObjectQuery() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList list = getRoleUserObjects(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersObjectQuery2() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList list = getRoleUserObjects2(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); } } @@ -292,14 +368,14 @@ public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTe private QdbcBoot qdbcBoot; /** 批量查询用户所分配的角色信息 **/ - public List> getUserRoles(List userIds, DbWhere where, Orderings orderings) { + private List> getUserRoleMaps(List userIds, DbWhere where, Orderings orderings) { VerifyTools.requireNonNull(userIds, "userIds"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); Map params = new HashMap<>(); - params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.userId,r.*")); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*,ur.userId")); params.put("userIds", userIds); if (VerifyTools.isNotBlank(where)) { params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); @@ -313,7 +389,7 @@ public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTe } /** 批量查询指定角色下的用户信息 **/ - public PageList> getRoleUsers(List roleIds, DbWhere where, OrderPaging odpg) { + private PageList> getRoleUserMaps(List roleIds, DbWhere where, OrderPaging odpg) { VerifyTools.requireNonNull(roleIds, "roleIds"); TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); @@ -332,4 +408,68 @@ public class SqlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTe String sqlId = "role.users.query"; return qdbcBoot.getSqlDao().pageForMaps(sqlId, params, odpg); } + + /** 批量查询用户所分配的角色信息 **/ + private List getUserRoleObjects(List userIds, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*,ur.userId")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + String sqlId = "user.roles.query"; + return qdbcBoot.getSqlDao().listForObjects(sqlId, params, UserIdRoleResult.class); + } + + /** 批量查询指定角色下的用户信息 **/ + private PageList getRoleUserObjects(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String sqlId = "role.users.query"; + return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, RoleIdUserResult.class); + } + + /** 批量查询指定角色下的用户信息 **/ + private PageList getRoleUserObjects2(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String queryId = "role.users.query"; + String countId = "role.users.count"; + return qdbcBoot.getSqlDao().pageForObjects(queryId, countId, params, odpg, RoleIdUserResult.class); + } } diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/StreamCrudDaoTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/StreamCrudDaoTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1f03d54dbc2748955f8bd820375d17f3a9415a68 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/StreamCrudDaoTest.java @@ -0,0 +1,384 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +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.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.paging.PartList; +import com.gitee.qdbp.jdbc.api.CrudDao; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.model.DbVersion; +import com.gitee.qdbp.jdbc.test.enums.DataState; +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.tools.utils.ConvertTools; +import com.gitee.qdbp.tools.utils.DateTools; +import com.gitee.qdbp.tools.utils.JsonTools; +import com.gitee.qdbp.tools.utils.StringTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class StreamCrudDaoTest extends AbstractTestNGSpringContextTests { + + private Logger log = LoggerFactory.getLogger(StreamCrudDaoTest.class); + + private static final String TACD = "STCDT"; + @Autowired + private QdbcBoot qdbcBoot; + + @BeforeClass + public void testVersionQuery() { + DbVersion version = qdbcBoot.getSqlBufferJdbcOperations().getDbVersion(); + log.debug("DbVersion: {}", version); + Assert.assertNotNull(version); + } + + @Test(priority = 1001) + public void testUserEntityDelete() { + { + // DbWhere where = new DbWhere(); + // where.on("tenantCode", "=", TACD); + // CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + // dao.physicalDelete(where); + // @formatter:off + qdbcBoot.crudStream(SysUserEntity.class) + .whereEquals("tenantCode", TACD) + .physicalDelete(); + // @formatter:on + } + { + // DbWhere where = new DbWhere(); + // where.on("tenantCode", "=", "StreamRefill"); + // CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + // dao.physicalDelete(where); + // @formatter:off + qdbcBoot.crudStream(SysUserEntity.class) + .whereEquals("tenantCode", TACD) + .physicalDelete(); + // @formatter:on + } + } + + // 测试实体类自动填充的字段反填到实体类 + @Test(priority = 1002) + public void testUserEntityFieldRefill() { + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode("StreamRefill"); + entity.setUserCode("AA000001"); + entity.setSuperman(false); + entity.setDeptId("0"); + entity.setGender(Gender.UNKNOWN); + entity.setBirthday(DateTools.parse("2018-02-03 15:25:35.456")); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + entity.setUserType(UserType.ADMIN); + // @formatter:off + String id = qdbcBoot.crudStream(SysUserEntity.class) + .entity(entity) + .insert(); + // @formatter:on + Assert.assertEquals(entity.getId(), id, "EntityFieldRefill-id"); + Assert.assertEquals(entity.getDataState(), DataState.NORMAL, "EntityFieldRefill-dataState"); + Assert.assertNotNull(entity.getCreateTime(), "EntityFieldRefill-createTime"); + } + + @Test(priority = 1003) + public void testUserEntityCreate() { + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + { // 超级管理员 + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(TACD); + entity.setUserCode("super"); + entity.setSuperman(true); + entity.setDeptId("0"); + 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("SY1000001"); + entity.setUserType(UserType.ADMIN); + dao.insert(entity); + + entity.setId("SU1000001"); + entity.setUserType(UserType.USER); + dao.insert(entity); + } + { // 添加普通用户 + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(TACD); + entity.setSuperman(false); + entity.setDeptId("0"); + entity.setGender(Gender.FEMALE); + entity.setUserType(UserType.USER); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + + entity.setId(null); + entity.setUserCode("kelly"); + dao.insert(entity); + + entity.setId(null); + entity.setUserCode("evan"); + dao.insert(entity); + + entity.setId(null); + entity.setUserCode("coral"); + dao.insert(entity); + } + { // 添加分页查询数据 + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(TACD); + entity.setSuperman(false); + entity.setDeptId("0"); + entity.setGender(Gender.FEMALE); + entity.setUserType(UserType.USER); + entity.setUserState(UserState.NORMAL); + entity.setUserSource(UserSource.INPUT); + List entities = new ArrayList<>(); + for (int i = 1; i <= 50; i++) { + entity.setUserCode("test-" + StringTools.pad(i, 3)); + entities.add(entity.to(SysUserEntity.class)); + } + // 批量新增 + dao.inserts(entities); + } + } + + @Test(priority = 2011) + public void testUserEntityQuery() { + SysUserEntity entity = new SysUserEntity(); + entity.setTenantCode(TACD); + entity.setUserCode("super"); + entity.setSuperman(true); + entity.setUserType(UserType.ADMIN); + entity.setUserState(UserState.NORMAL); + // @formatter:off + SysUserEntity user = qdbcBoot.crudStream(SysUserEntity.class) + .whereBy(entity) + .find(); + // @formatter:on + log.debug("UserEntityQueryResult: {}", JsonTools.toLogString(user)); + String desc = "UserEntity[super]"; + Assert.assertNotNull(user, desc + "QueryResult"); + Assert.assertEquals(user.getSuperman(), Boolean.TRUE, desc + ":BooleanField[superman]"); + String actualCreateTime = DateTools.toNormativeString(user.getCreateTime()); + String expectedCreateTime = "2018-05-15 20:30:40.599"; + Assert.assertEquals(actualCreateTime, expectedCreateTime, desc + ":InterfaceField[createTime]"); + Assert.assertEquals(user.getUserType(), UserType.ADMIN, desc + ":InterfaceField[userType]"); + Assert.assertEquals(user.getUserState(), UserState.NORMAL, desc + ":EnumField[userState]"); + Assert.assertEquals(user.getDataState(), DataState.NORMAL, desc + ":DataStateField[dataState]"); + } + + @Test(priority = 2012) + public void testUserWhereQuery() { + // @formatter:off + SysUserEntity user = qdbcBoot.crudStream(SysUserEntity.class) + .whereBy((where) -> { + where.on("tenantCode", "=", TACD); + where.on("userCode", "=", "super"); + where.on("superman", "=", true); + where.on("userType", "=", UserType.ADMIN); + where.on("userState", "in", UserState.NORMAL, UserState.LOCKED); + where.on("createTime", ">=", DateTools.parse("2017-01-01")); + }) + .find(); + // @formatter:on + log.debug("UserWhereQueryResult: {}", JsonTools.toLogString(user)); + Assert.assertNotNull(user, "UserWhere[super]QueryResult"); + } + + @SuppressWarnings("deprecation") + @Test(priority = 2013) + public void testUserOrQuery() { + // @formatter:off + List users = qdbcBoot.crudStream(SysUserEntity.class) + .whereBy((where) -> { + where.on("tenantCode", "=", TACD); + where.on("userType", "=", UserType.USER); + where.on("userState", "in", UserState.NORMAL, UserState.LOCKED); + where.on("createTime", ">=", DateTools.parse("2017-01-01")); + where.sub("or") + .on("userCode", "=", "kelly") + .on("superman", "=", true); + }) + .list(); + // @formatter:on + log.debug("UserOrQueryResult: {}", JsonTools.toLogString(users)); + Assert.assertEquals(users.size(), 2, "UserOrQueryResult"); + } + + @Test(priority = 2014) + public void testUserOrNewQuery() { + // @formatter:off + List users = qdbcBoot.crudStream(SysUserEntity.class) + .whereBy((where) -> { + where.andEquals("tenantCode", TACD) + .andEquals("userType", UserType.USER) + .andIn("userState", UserState.NORMAL, UserState.LOCKED) + .andGreaterEqualsThen("createTime", DateTools.parse("2017-01-01")) + .andSubCondition((w) -> { + w.orEquals("userCode", "kelly") + .orEquals("superman", true); + }); + }) + .list(); + // @formatter:on + log.debug("UserOrQueryResult: {}", JsonTools.toLogString(users)); + Assert.assertEquals(users.size(), 2, "UserOrQueryResult"); + } + + @Test(priority = 2020) + public void testFindIncludeFields() { + // @formatter:off + SysUserEntity entity = qdbcBoot.crudStream(SysUserEntity.class) + .select("id,userCode,userName,nickName,realName,phone,email") + .whereBy((where) -> { + where.on("tenantCode", "=", TACD); + where.on("userType", "=", UserType.USER); + where.on("userCode", "=", "kelly"); + }) + .find(); + // @formatter:on + log.debug("FindIncludeFieldsQueryResult: {}", JsonTools.toLogString(entity)); + Assert.assertNotNull(entity, "FindIncludeFieldsQueryResult"); + Assert.assertNotNull(entity.getId(), "UserEntity.id"); + Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); + Assert.assertNull(entity.getDeptId(), "UserEntity.deptId"); + Assert.assertNull(entity.getUserType(), "UserEntity.userType"); + Assert.assertNull(entity.getUserState(), "UserEntity.userState"); + Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); + Assert.assertEquals(entity.getUserCode(), "kelly", "UserEntity.userCode"); + } + + @Test(priority = 2021) + public void testFindExcludeFields() { + // @formatter:off + SysUserEntity entity = qdbcBoot.crudStream(SysUserEntity.class) + .selectExclude("tenantCode,userState,createTime") + .whereBy((where) -> { + where.on("tenantCode", "=", TACD); + where.on("userType", "=", UserType.USER); + where.on("userCode", "=", "kelly"); + }) + .find(); + // @formatter:on + log.debug("FindExcludeFieldsQueryResult: {}", JsonTools.toLogString(entity)); + Assert.assertNotNull(entity, "FindExcludeFieldsQueryResult"); + Assert.assertNotNull(entity.getId(), "UserEntity.id"); + Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); + Assert.assertNotNull(entity.getDeptId(), "UserEntity.deptId"); + Assert.assertNotNull(entity.getUserType(), "UserEntity.userType"); + Assert.assertNull(entity.getUserState(), "UserEntity.userState"); + Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); + Assert.assertEquals(entity.getUserCode(), "kelly", "UserEntity.userCode"); + } + + @Test(priority = 2022) + public void testListIncludeFields() { + // @formatter:off + List entities = qdbcBoot.crudStream(SysUserEntity.class) + .select("id,userCode,userName,nickName,realName,phone,email") + .whereBy((where) -> { + where.on("tenantCode", "=", TACD); + where.on("userType", "=", UserType.USER); + where.on("userCode", "in", "kelly", "evan", "coral"); + }) + .orderBy("createTime") + .list(); + // @formatter:on + Assert.assertEquals(entities.size(), 3, "ListIncludeFieldsQueryResult"); + for (SysUserEntity entity : entities) { + Assert.assertNotNull(entity.getId(), "UserEntity.id"); + Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); + Assert.assertNull(entity.getDeptId(), "UserEntity.deptId"); + Assert.assertNull(entity.getUserType(), "UserEntity.userType"); + Assert.assertNull(entity.getUserState(), "UserEntity.userState"); + Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); + Assert.assertNotNull(entity.getUserCode(), "UserEntity.userCode"); + } + } + + @Test(priority = 2023) + public void testListExcludeFields() { + // @formatter:off + List entities = qdbcBoot.crudStream(SysUserEntity.class) + .selectExclude("tenantCode,userState,createTime") + .whereBy((where) -> { + where.on("tenantCode", "=", TACD); + where.on("userType", "=", UserType.USER); + where.on("userCode", "in", "kelly", "evan", "coral"); + }) + .orderBy("createTime") + .list(); + // @formatter:on + Assert.assertEquals(entities.size(), 3, "ListExcludeFieldsQueryResult"); + for (SysUserEntity entity : entities) { + Assert.assertNotNull(entity.getId(), "UserEntity.id"); + Assert.assertNull(entity.getTenantCode(), "UserEntity.tenantCode"); + Assert.assertNotNull(entity.getDeptId(), "UserEntity.deptId"); + Assert.assertNotNull(entity.getUserType(), "UserEntity.userType"); + Assert.assertNull(entity.getUserState(), "UserEntity.userState"); + Assert.assertNull(entity.getCreateTime(), "UserEntity.createTime"); + Assert.assertNotNull(entity.getUserCode(), "UserEntity.userCode"); + } + } + + @Test(priority = 3010) + public void testUserPaging() { + int total = qdbcBoot.crudStream(SysUserEntity.class).count(); + log.debug("UserRecordCount: {}", total); + int pageSize = 10; + int pageCount = Math.min(5, (total + pageSize - 1) / pageSize); + for (int i = 1; i <= pageCount; i++) { + // @formatter:off + PartList users = + qdbcBoot.crudStream(SysUserEntity.class) + .orderBy("createTime desc") + .pageBy(i, pageSize, false) + .list().asPartList(); + // @formatter:on + log.debug("UserNames: {}", ConvertTools.joinToString(getUserNames(users))); + Assert.assertTrue(users.size() > 0); + } + } + + @Test(priority = 3020) + public void testFindFieldValue() { + CrudDao dao = qdbcBoot.buildCrudDao(SysUserEntity.class); + DbWhere where = new DbWhere(); + where.andEquals("id", "SY1000001"); + String userCode = dao.findFieldValue("userCode", where, String.class); + log.debug("FindFieldValue: userCode={}", userCode); + String email = dao.findFieldValue("email", where, String.class); + log.debug("FindFieldValue: email={}", email); + Gender gender = dao.findFieldValue("gender", where, Gender.class); + log.debug("FindFieldValue: gender={}", gender); + UserState userState = dao.findFieldValue("userState", where, UserState.class); + log.debug("FindFieldValue: userState={}", userState); + UserSource userSource = dao.findFieldValue("userSource", where, UserSource.class); + log.debug("FindFieldValue: userSource={}", userSource); + Date createTime = dao.findFieldValue("createTime", where, Date.class); + log.debug("FindFieldValue: createTime={}", createTime); + } + + private List getUserNames(PartList users) { + List userNames = new ArrayList<>(); + for (SysUserEntity user : users) { + userNames.add(user.toDisplayName()); + } + return userNames; + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/TransactionalTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/TransactionalTest.java index 09fc9950228c52ef6806f9b994e379c23c9c6416..b5b571961f7f54661e6389249dfa84f2c3727f2f 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/TransactionalTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/TransactionalTest.java @@ -1,7 +1,6 @@ package com.gitee.qdbp.jdbc.test.biz; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataAccessException; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; import org.testng.Assert; @@ -26,6 +25,7 @@ import com.gitee.qdbp.jdbc.test.service.SysSettingService.TestModel; @ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) public class TransactionalTest extends AbstractTestNGSpringContextTests { + private static final String TACD = "TransactionalTest2"; @Autowired private QdbcBoot qdbcBoot; @Autowired @@ -36,13 +36,13 @@ public class TransactionalTest extends AbstractTestNGSpringContextTests { { CrudDao dao = qdbcBoot.buildCrudDao(SysSettingEntity.class); DbWhere where = new DbWhere(); - where.on("name", "starts", "Transactional"); + where.on("tenantCode", "=", TACD); dao.physicalDelete(where); } { CrudDao dao = qdbcBoot.buildCrudDao(SysLoggerEntity.class); DbWhere where = new DbWhere(); - where.on("content", "like", "Transactional"); + where.on("tenantCode", "=", TACD); dao.physicalDelete(where); } } @@ -51,17 +51,20 @@ public class TransactionalTest extends AbstractTestNGSpringContextTests { public void testSuccessToInsert() { String key = "Transactional-20"; SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName(key); entity.setValue("测试 " + key); sysSettingService.createSetting(entity, TestModel.skipLogging); { // 主记录成功 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "=", key); int mainCount = sysSettingService.countSetting(where); Assert.assertEquals(mainCount, 1); } { // 不记录日志 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("content", "like", key); int logCount = sysSettingService.countLogging(where); Assert.assertEquals(logCount, 0); // 期望日志数为0 @@ -72,21 +75,24 @@ public class TransactionalTest extends AbstractTestNGSpringContextTests { public void testFailedToInsert() { String key = "Transactional-30"; SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName(key); entity.setValue("测试 " + key); try { sysSettingService.createSetting(entity, TestModel.mainErrorLogSuccess); - } catch (ServiceException e) { - Assert.assertTrue(e.getCause() instanceof DataAccessException); + } catch (Exception e) { + Assert.assertTrue(e instanceof ServiceException); } { // 主记录失败 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "=", key); int mainCount = sysSettingService.countSetting(where); Assert.assertEquals(mainCount, 0); } { // 日志记录成功 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("content", "like", key); int logCount = sysSettingService.countLogging(where); Assert.assertEquals(logCount, 2); // 新增的日志+更新的日志 @@ -97,17 +103,20 @@ public class TransactionalTest extends AbstractTestNGSpringContextTests { public void testFailedToLogging() { String key = "Transactional-40"; SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName(key); entity.setValue("测试 " + key); sysSettingService.createSetting(entity, TestModel.mainSuccessLogError); { // 主记录成功 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("name", "=", key); int mainCount = sysSettingService.countSetting(where); Assert.assertEquals(mainCount, 1); } { // 日志记录失败 DbWhere where = new DbWhere(); + where.on("tenantCode", "=", TACD); where.on("content", "like", key); int logCount = sysSettingService.countLogging(where); Assert.assertEquals(logCount, 1); // 新增的日志保存成功, 更新的日志保存失败 diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlStreamUserRolesQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlStreamUserRolesQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a4740ad91699c8d497a2c6b4c9c79264f6e6cc28 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlStreamUserRolesQueryTest.java @@ -0,0 +1,511 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.PartList; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; +import com.gitee.qdbp.jdbc.test.model.RoleIdUserResult; +import com.gitee.qdbp.jdbc.test.model.SysRoleEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserRoleEntity; +import com.gitee.qdbp.jdbc.test.model.UserIdRoleResult; +import com.gitee.qdbp.jdbc.test.service.SysUserStream; +import com.gitee.qdbp.tools.utils.VerifyTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class XmlStreamUserRolesQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "XSUR"; + @Autowired + private SysUserStream userService; + + @BeforeClass + public void init() { + userService.initUserRoles(TACD, true); + } + + /////////////////////////////////////////////// + // 查询用户所分配的角色 + /////////////////////////////////////////////// + + @Test + public void testGetUserRolesQuerySql11() throws IOException { + String userId = TACD + "U0000001"; + List list = userService.getUserRolesForXml(userId, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 2; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (SysRoleEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql12() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List list = userService.getUserRolesForXml(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql21() throws IOException { + String userId = TACD + "U0000001"; + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userId, DbWhere.NONE, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 2; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (SysRoleEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql22() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userIds, DbWhere.NONE, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql31() throws IOException { + String userId = TACD + "U0000001"; + DbWhere where = new DbWhere(); + where.on("r.tenantCode", "=", TACD); + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userId, where, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 2; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (SysRoleEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql32() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + DbWhere where = new DbWhere(); + where.on("r.tenantCode", "=", TACD); + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userIds, where, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + /////////////////////////////////////////////// + // 查询角色下的用户, 分页查询 + /////////////////////////////////////////////// + + @Test + public void testGetRoleUsersQuerySql11() throws IOException { + String roleId = TACD + "R0000001"; + OrderPaging odpg = OrderPaging.of(new Paging(1, 5)); + PartList list = userService.getRoleUsersForXml(roleId, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 5; + int total = 10; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (SysUserEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql12() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PartList list = userService.getRoleUsersForXml(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql21() throws IOException { + String roleId = TACD + "R0000001"; + OrderPaging odpg = OrderPaging.of(new Paging(1, 5), "ur.roleId, u.id"); + PartList list = userService.getRoleUsersForXml(roleId, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 5; + int total = 10; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (SysUserEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql22() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10), "ur.roleId, u.id"); + PartList list = userService.getRoleUsersForXml(roleIds, DbWhere.NONE, odpg); + + // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql31() throws IOException { + String roleId = TACD + "R0000001"; + DbWhere where = new DbWhere(); + where.on("u.tenantCode", "=", TACD); + OrderPaging odpg = OrderPaging.of(new Paging(1, 5), "ur.roleId, u.id"); + PartList list = userService.getRoleUsersForXml(roleId, where, odpg); + + // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 5; + int total = 10; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (SysUserEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql32() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + DbWhere where = new DbWhere(); + where.on("u.tenantCode", "=", TACD); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10), "ur.roleId, u.id"); + PartList list = userService.getRoleUsersForXml(roleIds, where, odpg); + + // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + /////////////////////////////////////////////// + // 查询用户所分配的角色 + /////////////////////////////////////////////// + + @Test + public void testGetUserRolesMapQuery() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List> list = getUserRoleMaps(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (Map item : list) { + Assert.assertNotNull(item.get("userId"), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.get("id"), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.get("roleName"), "SysUserService.queryUserRoles[result.roleName]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + // 3.2.17修复的BUG, count语句缓存错误 + @Test + public void testGetRoleUsersMapQuery2() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000005"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PartList> list = getRoleUserMaps(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 8; + int total = 28; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (Map item : list) { + Assert.assertNotNull(item.get("id"), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.get("roleId"), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersMapQuery() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PartList> list = getRoleUserMaps(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (Map item : list) { + Assert.assertNotNull(item.get("roleId"), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.get("id"), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.get("userCode"), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetUserRolesObjectQuery() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List list = getUserRoleObjects(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getRoleName(), "SysUserService.queryUserRoles[result.roleName]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersObjectQuery() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PartList list = getRoleUserObjects(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersObjectQuery2() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PartList list = getRoleUserObjects2(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Autowired + private QdbcBoot qdbcBoot; + + /** 批量查询用户所分配的角色信息 **/ + private List> getUserRoleMaps(List userIds, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*,ur.userId")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + String sqlId = "user.roles.query"; + // return qdbcBoot.getSqlDao().listForMaps(sqlId, params); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .list(); + // @formatter:on + } + + /** 批量查询指定角色下的用户信息 **/ + private PartList> getRoleUserMaps(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String sqlId = "SysUserMapper:queryRoleUsers"; + // // return qdbcBoot.getSqlDao().pageForMaps(sqlId, params, odpg); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .list().asPartList(); + // @formatter:on + } + + /** 批量查询用户所分配的角色信息 **/ + private List getUserRoleObjects(List userIds, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*,ur.userId")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + String sqlId = "SysUserMapper:queryUserRoles"; + // return qdbcBoot.getSqlDao().listForObjects(sqlId, params, UserIdRoleResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .resultAs(UserIdRoleResult.class) + .list(); + // @formatter:on + } + + /** 批量查询指定角色下的用户信息 **/ + private PartList getRoleUserObjects(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String sqlId = "SysUserMapper:queryRoleUsers"; + // return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, RoleIdUserResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(sqlId) + .params(params) + .pageBy(odpg) + .resultAs(RoleIdUserResult.class) + .list().asPartList(); + // @formatter:on + } + + /** 批量查询指定角色下的用户信息 **/ + private PartList getRoleUserObjects2(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String queryId = "SysUserMapper:queryRoleUsers"; + String countId = "SysUserMapper:countRoleUsers"; + // return qdbcBoot.getSqlDao().pageForObjects(queryId, countId, params, odpg, RoleIdUserResult.class); + // @formatter:off + return qdbcBoot.sqlStream() + .sqlId(queryId, countId) + .params(params) + .pageBy(odpg) + .resultAs(RoleIdUserResult.class) + .list().asPartList(); + // @formatter:on + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateDeptInfosQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateDeptInfosQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..00fa3f2c21636f15ba39e90ad5b978cc9e60e6f9 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateDeptInfosQueryTest.java @@ -0,0 +1,82 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +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.DataState; +import com.gitee.qdbp.jdbc.test.model.SysDeptEntity; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class XmlTemplateDeptInfosQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "XTDI"; + @Autowired + private QdbcBoot qdbcBoot; + + @BeforeClass + public void init() { + CrudDao dao = qdbcBoot.buildCrudDao(SysDeptEntity.class); + { // 清除数据 + DbWhere where = new DbWhere().andEquals("tenantCode", TACD); + dao.physicalDelete(where); + } + } + + @Test(priority = 1) + public void testBatchInsert() throws IOException { + Date now = new Date(); + List list = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + SysDeptEntity dept = new SysDeptEntity(); + dept.setTenantCode(TACD); + dept.setId(TACD + "D000" + i); + dept.setDeptCode(dept.getId()); + dept.setDeptName("Test000" + i); + dept.setCreateTime(now); + dept.setParentCode("0"); + dept.setSortIndex(i); + dept.setDataState(DataState.NORMAL); + list.add(dept); + } + Map params = new HashMap<>(); + params.put("list", list); + qdbcBoot.getSqlDao().insert("SysDeptMapper:batchInsertDepts", params); + } + + @Test(priority = 2, dependsOnMethods = "testBatchInsert") + public void testGetDeptInfosQuerySql13() throws IOException { + Map map = new HashMap<>(); + map.put("deptIds", Arrays.asList(TACD + "D0001", TACD + "D0002", TACD + "D0003")); + testGetDeptInfosQuery(map, 3); + } + + private void testGetDeptInfosQuery(Map where, int size) throws IOException { + where.put("tenantCode", TACD); + Map map = new HashMap<>(); + map.put("where", where); + String sqlId = "SysDeptMapper:queryDeptInfos"; + List result = qdbcBoot.getSqlDao().listForObjects(sqlId, map, SysDeptEntity.class); + + Assert.assertEquals(result.size(), size, "dept.info.query"); + for (SysDeptEntity item : result) { + Assert.assertNotNull(item.getId(), "dept.info.query[result.id]"); + Assert.assertNotNull(item.getDeptCode(), "dept.info.query[result.deptCode]"); + Assert.assertNotNull(item.getDeptName(), "dept.info.query[result.deptName]"); + Assert.assertNotNull(item.getDataState(), "dept.info.query[result.dataState]"); + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateUserInfosQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateUserInfosQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3b62f722ea8a03d2cf7a199f4af23b9dc19d2be0 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateUserInfosQueryTest.java @@ -0,0 +1,127 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.model.LikeValue; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.test.model.UserAndDeptResult; +import com.gitee.qdbp.jdbc.test.service.SysUserService; +import com.gitee.qdbp.tools.utils.DateTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class XmlTemplateUserInfosQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "XTUI"; + private static final String TACD1 = "XTUI1"; + private static final String TACD2 = "XTUI%"; + // 部门1有2个用户, 2有4个, 3=6, 4=8, ... + @Autowired + private SysUserService userService; + + @BeforeClass + public void init() { + userService.initUserDepts(TACD, TACD1, true); + userService.initUserDepts(TACD, TACD2, false); + } + + @Test + public void testQueryUserInfosQuerySql12() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", TACD2); + testQueryUserInfosQuery(map, 30); // 2+4+6+8+10 + } + + @Test + public void testQueryUserInfosQuerySql13() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", TACD1); + testQueryUserInfosQuery(map, 30); // 2+4+6+8+10 + } + + @Test + public void testQueryUserInfosQuerySql14() throws IOException { + Map map = new HashMap<>(); + map.put("userId", TACD1 + "U0000001"); + testQueryUserInfosQuery(map, 1); + } + + @Test + public void testQueryUserInfosQuerySql15() throws IOException { + Map map = new HashMap<>(); + map.put("userIds", Arrays.asList(TACD1 + "U0000001", TACD1 + "U0000002", TACD1 + "U0000003")); + testQueryUserInfosQuery(map, 3); + } + + @Test + public void testQueryUserInfosQuerySql21() throws IOException { + Date now = new Date(); + Map map = new HashMap<>(); + map.put("userCode", TACD2); + map.put("createTimeMin", DateTools.toStartTime(now)); + map.put("createTimeMax", DateTools.toEndTime(now)); + testQueryUserInfosQuery(map, 30); + } + + @Test + public void testQueryUserInfosQuerySql22() throws IOException { + Map map = new HashMap<>(); + map.put("keyword", TACD2 + "U00"); + testQueryUserInfosQuery(map, 9); + } + + @Test + public void testQueryUserInfosQuerySql23() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", new LikeValue('#', TACD + "#%U00%")); + testQueryUserInfosQuery(map, 9); + } + + @Test + public void testQueryUserInfosQuerySql24() throws IOException { + Map map = new HashMap<>(); + map.put("userCode", new LikeValue(TACD2 + "U00%")); + testQueryUserInfosQuery(map, 18); + } + + @Test + public void testQueryUserInfosQuerySql31() throws IOException { + Map map = new HashMap<>(); + map.put("deptId", TACD1 + "D0000002"); + testQueryUserInfosQuery(map, 4); + } + + @Test + public void testQueryUserInfosQuerySql32() throws IOException { + Map map = new HashMap<>(); + map.put("deptCode", TACD1 + "D003"); + testQueryUserInfosQuery(map, 6); + } + + private void testQueryUserInfosQuery(Map map, int size) throws IOException { + map.put("tenantCode", TACD); + OrderPaging odpg = OrderPaging.of(Paging.NONE, "id,createTime"); + PageList result = userService.getUserInfosForXml(map, odpg); + + Assert.assertEquals(result.size(), size, "SysUserService.queryUserInfos"); + for (UserAndDeptResult item : result) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserInfos[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryUserInfos[result.userCode]"); + Assert.assertNotNull(item.getNickName(), "SysUserService.queryUserInfos[result.nickName]"); + Assert.assertNotNull(item.getDeptCode(), "SysUserService.queryUserInfos[result.deptCode]"); + Assert.assertNotNull(item.getDeptName(), "SysUserService.queryUserInfos[result.deptName]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserInfos[result.dataState]"); + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateUserRolesQueryTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateUserRolesQueryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c07acd4447552eaf5cec27155d43c966c289cd94 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/XmlTemplateUserRolesQueryTest.java @@ -0,0 +1,475 @@ +package com.gitee.qdbp.jdbc.test.biz; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +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.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.able.jdbc.ordering.OrderPaging; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.able.jdbc.paging.PageList; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; +import com.gitee.qdbp.jdbc.test.model.RoleIdUserResult; +import com.gitee.qdbp.jdbc.test.model.SysRoleEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserRoleEntity; +import com.gitee.qdbp.jdbc.test.model.UserIdRoleResult; +import com.gitee.qdbp.jdbc.test.service.SysUserService; +import com.gitee.qdbp.tools.utils.VerifyTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class XmlTemplateUserRolesQueryTest extends AbstractTestNGSpringContextTests { + + private static final String TACD = "XTUR"; + @Autowired + private SysUserService userService; + + @BeforeClass + public void init() { + userService.initUserRoles(TACD, true); + } + + /////////////////////////////////////////////// + // 查询用户所分配的角色 + /////////////////////////////////////////////// + + @Test + public void testGetUserRolesQuerySql11() throws IOException { + String userId = TACD + "U0000001"; + List list = userService.getUserRolesForXml(userId, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 2; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (SysRoleEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql12() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List list = userService.getUserRolesForXml(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql21() throws IOException { + String userId = TACD + "U0000001"; + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userId, DbWhere.NONE, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 2; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (SysRoleEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql22() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userIds, DbWhere.NONE, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql31() throws IOException { + String userId = TACD + "U0000001"; + DbWhere where = new DbWhere(); + where.on("r.tenantCode", "=", TACD); + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userId, where, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 2; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (SysRoleEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetUserRolesQuerySql32() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + DbWhere where = new DbWhere(); + where.on("r.tenantCode", "=", TACD); + Orderings orderings = Orderings.of("ur.userId, r.id"); + List list = userService.getUserRolesForXml(userIds, where, orderings); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + /////////////////////////////////////////////// + // 查询角色下的用户, 分页查询 + /////////////////////////////////////////////// + + @Test + public void testGetRoleUsersQuerySql11() throws IOException { + String roleId = TACD + "R0000001"; + OrderPaging odpg = OrderPaging.of(new Paging(1, 5)); + PageList list = userService.getRoleUsersForXml(roleId, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 5; + int total = 10; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (SysUserEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql12() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList list = userService.getRoleUsersForXml(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql21() throws IOException { + String roleId = TACD + "R0000001"; + OrderPaging odpg = OrderPaging.of(new Paging(1, 5), "ur.roleId, u.id"); + PageList list = userService.getRoleUsersForXml(roleId, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 5; + int total = 10; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (SysUserEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql22() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10), "ur.roleId, u.id"); + PageList list = userService.getRoleUsersForXml(roleIds, DbWhere.NONE, odpg); + + // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql31() throws IOException { + String roleId = TACD + "R0000001"; + DbWhere where = new DbWhere(); + where.on("u.tenantCode", "=", TACD); + OrderPaging odpg = OrderPaging.of(new Paging(1, 5), "ur.roleId, u.id"); + PageList list = userService.getRoleUsersForXml(roleId, where, odpg); + + // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 5; + int total = 10; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (SysUserEntity item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersQuerySql32() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + DbWhere where = new DbWhere(); + where.on("u.tenantCode", "=", TACD); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10), "ur.roleId, u.id"); + PageList list = userService.getRoleUsersForXml(roleIds, where, odpg); + + // 1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + /////////////////////////////////////////////// + // 查询用户所分配的角色 + /////////////////////////////////////////////// + + @Test + public void testGetUserRolesMapQuery() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List> list = getUserRoleMaps(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (Map item : list) { + Assert.assertNotNull(item.get("userId"), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.get("id"), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.get("roleName"), "SysUserService.queryUserRoles[result.roleName]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + // 3.2.17修复的BUG, count语句缓存错误 + @Test + public void testGetRoleUsersMapQuery2() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000005"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList> list = getRoleUserMaps(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 8; + int total = 28; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (Map item : list) { + Assert.assertNotNull(item.get("id"), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.get("roleId"), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersMapQuery() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList> list = getRoleUserMaps(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (Map item : list) { + Assert.assertNotNull(item.get("roleId"), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.get("id"), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.get("userCode"), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.get("dataState"), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetUserRolesObjectQuery() throws IOException { + List userIds = Arrays.asList(TACD + "U0000001", TACD + "U0000002", TACD + "U0000008"); + List list = getUserRoleObjects(userIds, DbWhere.NONE, Orderings.NONE); + + // 用户1有2个角色, 2有4个, 3=6, 4=8, ... + int size = 22; + Assert.assertEquals(list.size(), size, "SysUserService.queryUserRoles"); + for (UserIdRoleResult item : list) { + Assert.assertNotNull(item.getUserId(), "SysUserService.queryUserRoles[result.userId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryUserRoles[result.id]"); + Assert.assertNotNull(item.getRoleName(), "SysUserService.queryUserRoles[result.roleName]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryUserRoles[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersObjectQuery() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList list = getRoleUserObjects(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Test + public void testGetRoleUsersObjectQuery2() throws IOException { + List roleIds = Arrays.asList(TACD + "R0000001", TACD + "R0000002", TACD + "R0000008"); + OrderPaging odpg = OrderPaging.of(new Paging(3, 10)); + PageList list = getRoleUserObjects2(roleIds, DbWhere.NONE, odpg); + + // 角色1/2分别有10个用户, 3/4=9, 5/6=8, 7/8=7, ..., 19/20=1 + int size = 7; + int total = 27; + Assert.assertEquals(list.size(), size, "SysUserService.queryRoleUsers[result.size]"); + Assert.assertTrue(list.getTotal() == total, "SysUserService.queryRoleUsers[result.total]"); + for (RoleIdUserResult item : list) { + Assert.assertNotNull(item.getRoleId(), "SysUserService.queryRoleUsers[result.roleId]"); + Assert.assertNotNull(item.getId(), "SysUserService.queryRoleUsers[result.id]"); + Assert.assertNotNull(item.getUserCode(), "SysUserService.queryRoleUsers[result.userCode]"); + Assert.assertNotNull(item.getDataState(), "SysUserService.queryRoleUsers[result.dataState]"); + } + } + + @Autowired + private QdbcBoot qdbcBoot; + + /** 批量查询用户所分配的角色信息 **/ + private List> getUserRoleMaps(List userIds, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*,ur.userId")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + String sqlId = "user.roles.query"; + return qdbcBoot.getSqlDao().listForMaps(sqlId, params); + } + + /** 批量查询指定角色下的用户信息 **/ + private PageList> getRoleUserMaps(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String sqlId = "SysUserMapper:queryRoleUsers"; + return qdbcBoot.getSqlDao().pageForMaps(sqlId, params, odpg); + } + + /** 批量查询用户所分配的角色信息 **/ + private List getUserRoleObjects(List userIds, DbWhere where, Orderings orderings) { + VerifyTools.requireNonNull(userIds, "userIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysRoleEntity.class, "r"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("r.*,ur.userId")); + params.put("userIds", userIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(orderings)) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); + } + + String sqlId = "SysUserMapper:queryUserRoles"; + return qdbcBoot.getSqlDao().listForObjects(sqlId, params, UserIdRoleResult.class); + } + + /** 批量查询指定角色下的用户信息 **/ + private PageList getRoleUserObjects(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String sqlId = "SysUserMapper:queryRoleUsers"; + return qdbcBoot.getSqlDao().pageForObjects(sqlId, params, odpg, RoleIdUserResult.class); + } + + /** 批量查询指定角色下的用户信息 **/ + private PageList getRoleUserObjects2(List roleIds, DbWhere where, OrderPaging odpg) { + VerifyTools.requireNonNull(roleIds, "roleIds"); + + TableJoin tables = TableJoin.of(SysUserRoleEntity.class, "ur", SysUserEntity.class, "u"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + params.put("selectColumns", sqlHelper.buildSelectFieldsSql("ur.roleId,u.*")); + params.put("roleIds", roleIds); + if (VerifyTools.isNotBlank(where)) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + if (VerifyTools.isNotBlank(odpg.getOrderings())) { + params.put("orderByCondition", sqlHelper.buildOrderBySql(odpg.getOrderings(), false)); + } + + String queryId = "SysUserMapper:queryRoleUsers"; + String countId = "SysUserMapper:countRoleUsers"; + return qdbcBoot.getSqlDao().pageForObjects(queryId, countId, params, odpg, RoleIdUserResult.class); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/QdbcManualTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/QdbcManualTest.java index dc4fd5e76cf28ac31c898f8fc040daf1fa78eb7f..78e950ed58dcbf6b18be3b4f10078f938ba74ead 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/QdbcManualTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/QdbcManualTest.java @@ -1,14 +1,14 @@ package com.gitee.qdbp.jdbc.test.simple; -import com.alibaba.druid.pool.DruidDataSource; 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.biz.QdbcBootImpl; -import com.gitee.qdbp.jdbc.support.AutoDruidDataSource; +import com.gitee.qdbp.jdbc.support.AutoHikariDataSource; import com.gitee.qdbp.jdbc.test.enums.SettingState; import com.gitee.qdbp.jdbc.test.model.SysSettingEntity; import com.gitee.qdbp.tools.utils.JsonTools; +import com.zaxxer.hikari.HikariDataSource; /** * 纯手工连接数据库测试类
@@ -32,7 +32,8 @@ public class QdbcManualTest { private static void testDbOperate(String jdbcUrl) { // 1. 获取数据源对象 - try (DruidDataSource datasource = AutoDruidDataSource.buildWith(jdbcUrl);) { + // DruidDataSource datasource = AutoDruidDataSource.buildWith(jdbcUrl); + try (HikariDataSource datasource = AutoHikariDataSource.buildWith(jdbcUrl);) { // 2. 获取数据库操作的构造器对象 QdbcBoot boot = QdbcBootImpl.buildWith(datasource); // 3. 针对具体的实体, 生成增删改查DAO对象 @@ -43,14 +44,16 @@ public class QdbcManualTest { } private static void testSettingEntity(CrudDao dao) { + String tenantCode = "QdbcManualTest"; { // 测试删除 DbWhere where = new DbWhere(); - where.on("name", "starts", "HelloQdbc-"); + where.andEquals("tenantCode", tenantCode); dao.physicalDelete(where); } String id; { // 测试新增 SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(tenantCode); entity.setName("HelloQdbc-1"); entity.setValue("测试新增 HelloQdbc-1"); entity.setVersion(1); @@ -61,6 +64,7 @@ public class QdbcManualTest { { // 测试修改 SysSettingEntity entity = new SysSettingEntity(); entity.setId(id); + entity.setTenantCode(tenantCode); entity.setValue("测试修改 HelloQdbc-1"); entity.setVersion(2); dao.update(entity); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/ReadDatabaseMetaData.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/ReadDatabaseMetaData.java index af0270ead9c930b8e173e61ad8cfd0621e06a87a..3a1522b795ab583d71ae97261a9a616a02eb054b 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/ReadDatabaseMetaData.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/simple/ReadDatabaseMetaData.java @@ -6,15 +6,15 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.Collection; import java.util.List; -import com.alibaba.druid.pool.DruidDataSource; import com.gitee.qdbp.jdbc.model.DbVersion; import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; import com.gitee.qdbp.jdbc.plugins.DbVersionFinder; -import com.gitee.qdbp.jdbc.support.AutoDruidDataSource; +import com.gitee.qdbp.jdbc.support.AutoHikariDataSource; import com.gitee.qdbp.tools.files.FileTools; import com.gitee.qdbp.tools.utils.ConvertTools; import com.gitee.qdbp.tools.utils.ReflectTools; import com.gitee.qdbp.tools.utils.StringTools; +import com.zaxxer.hikari.HikariDataSource; /** * 读取数据库信息 @@ -45,7 +45,7 @@ public class ReadDatabaseMetaData { StringBuilder messages = new StringBuilder(); DbVersion version = null; - try (DruidDataSource datasource = AutoDruidDataSource.buildWith(jdbcUrl); + try (HikariDataSource datasource = AutoHikariDataSource.buildWith(jdbcUrl); Connection connection = datasource.getConnection()) { try { DbVersionFinder finder = DbPluginContainer.defaults().getDbVersionFinder(); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.java new file mode 100644 index 0000000000000000000000000000000000000000..05ae4cd94c41656e1915a862369ed59941dc5d86 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.java @@ -0,0 +1,118 @@ +package com.gitee.qdbp.jdbc.test.sql.build; + +import java.io.IOException; +import java.net.URL; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.testng.AbstractTestNGSpringContextTests; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.condition.DbWhere; +import com.gitee.qdbp.able.jdbc.condition.TableJoin; +import com.gitee.qdbp.jdbc.model.MainDbType; +import com.gitee.qdbp.jdbc.plugins.DbPluginContainer; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.build.QuerySqlBuilder; +import com.gitee.qdbp.jdbc.test.condition.DeptIsolationBuilder; +import com.gitee.qdbp.jdbc.test.condition.DeptIsolationWhere; +import com.gitee.qdbp.jdbc.test.enums.UserType; +import com.gitee.qdbp.jdbc.test.model.SysUserEntity; +import com.gitee.qdbp.jdbc.test.model.SysUserRoleEntity; +import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.AssertTools; + +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class DbWhereStreamTest extends AbstractTestNGSpringContextTests { + + public static SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.MySQL); + + @BeforeClass + public void testVersionQuery() { + DbPluginContainer.defaults().addWhereSqlBuilder(new DeptIsolationBuilder()); + } + + @Test + public void test1() throws IOException { + // @formatter:off + DbWhere where = new DbWhere() + .andEquals("u.userType", UserType.USER) + .andSubCondition((w) -> { + w.orEquals("ur.userId", "U001") + .orIn("ur.roleId", "R001", "R003"); + }) + .andBizCondition(new DeptIsolationWhere("userId", "deptId")); + // @formatter:on + TableJoin tables = TableJoin.of(SysUserEntity.class, "u", SysUserRoleEntity.class, "ur"); + QuerySqlBuilder sqlBuilder = new QuerySqlBuilder(tables, dialect); + SqlBuffer buffer = sqlBuilder.helper().buildWhereSql(where, true); + + checkSqlString(buffer, "test1"); + } + + @Test + public void test2() throws IOException { + // @formatter:off + DbWhere where = new DbWhere() + .andEquals("u.userType", UserType.USER) + .andSubCondition((w) -> { + w.orEquals("ur.userId", "U001"); + }) + .andBizCondition(new DeptIsolationWhere("deptId")); + // @formatter:on + TableJoin tables = TableJoin.of(SysUserEntity.class, "u", SysUserRoleEntity.class, "ur"); + QuerySqlBuilder sqlBuilder = new QuerySqlBuilder(tables, dialect); + SqlBuffer buffer = sqlBuilder.helper().buildWhereSql(where, true); + + checkSqlString(buffer, "test2"); + } + + @Test + public void test3() throws IOException { + // @formatter:off + DbWhere where = new DbWhere() + .andNotEquals("u.userType", UserType.USER) + .andNotSubCondition((w) -> { + w.orEquals("ur.userId", "U001") + .orNotIn("ur.roleId", "R001", "R003"); + }) + .andNotBizCondition(new DeptIsolationWhere("userId", "deptId")); + // @formatter:on + TableJoin tables = TableJoin.of(SysUserEntity.class, "u", SysUserRoleEntity.class, "ur"); + QuerySqlBuilder sqlBuilder = new QuerySqlBuilder(tables, dialect); + SqlBuffer buffer = sqlBuilder.helper().buildWhereSql(where, true); + + checkSqlString(buffer, "test3"); + } + + @Test + public void test4() throws IOException { + // @formatter:off + DbWhere where = new DbWhere() + .andNotEquals("u.userType", UserType.USER) + .andNotSubCondition((w) -> { + w.orEquals("ur.userId", "U001"); + }) + .andNotBizCondition(new DeptIsolationWhere("deptId")); + // @formatter:on + TableJoin tables = TableJoin.of(SysUserEntity.class, "u", SysUserRoleEntity.class, "ur"); + QuerySqlBuilder sqlBuilder = new QuerySqlBuilder(tables, dialect); + SqlBuffer buffer = sqlBuilder.helper().buildWhereSql(where, true); + + checkSqlString(buffer, "test4"); + } + + private void checkSqlString(SqlBuffer buffer, String type) throws IOException { + String sqlFile = "DbWhereStreamTest." + type + ".sql"; + String sqlString = buffer.getLoggingSqlString(dialect); + System.out.println("/******************************************************\\"); + System.out.println("-- " + sqlFile); + System.out.println(sqlString); + System.out.println("\\******************************************************/"); + System.out.println(); + + 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/DbWhereStreamTest.test1.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test1.sql new file mode 100644 index 0000000000000000000000000000000000000000..d0a015d6dd2923af38bd9df4a06f179abf2ad15c --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test1.sql @@ -0,0 +1,8 @@ +WHERE u.USER_TYPE=2/*$1*/ +AND ( + ur.USER_ID='U001'/*$2*/ OR ur.ROLE_ID IN ('R001'/*$3*/,'R003'/*$4*/) +) +AND ( + ur.USER_ID='U0000001'/*$5*/ + OR u.DEPT_ID IS NULL OR u.DEPT_ID IN ('D0000001'/*$6*/,'D0000005'/*$7*/,'D0000012'/*$8*/) +) \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test2.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test2.sql new file mode 100644 index 0000000000000000000000000000000000000000..0e23b0f72b3a257cb916b51eaa5cbcf79e6d39ae --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test2.sql @@ -0,0 +1,4 @@ +WHERE u.USER_TYPE=2/*$1*/ AND ur.USER_ID='U001'/*$2*/ +AND ( + u.DEPT_ID IS NULL OR u.DEPT_ID IN ('D0000001'/*$3*/,'D0000005'/*$4*/,'D0000012'/*$5*/) +) \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test3.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test3.sql new file mode 100644 index 0000000000000000000000000000000000000000..d48b8ba69e2fcf424edf79c873ed3d2a63e90705 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test3.sql @@ -0,0 +1,8 @@ +WHERE u.USER_TYPE!=2/*$1*/ +AND NOT ( + ur.USER_ID='U001'/*$2*/ OR ur.ROLE_ID NOT IN ('R001'/*$3*/,'R003'/*$4*/) +) +AND NOT ( + ur.USER_ID='U0000001'/*$5*/ + OR u.DEPT_ID IS NULL OR u.DEPT_ID IN ('D0000001'/*$6*/,'D0000005'/*$7*/,'D0000012'/*$8*/) +) \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test4.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test4.sql new file mode 100644 index 0000000000000000000000000000000000000000..248f5e689dbfbe92fb53aa53d5838d9ec575cc00 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/DbWhereStreamTest.test4.sql @@ -0,0 +1,4 @@ +WHERE u.USER_TYPE!=2/*$1*/ AND NOT ( ur.USER_ID='U001'/*$2*/ ) +AND NOT ( + u.DEPT_ID IS NULL OR u.DEPT_ID IN ('D0000001'/*$3*/,'D0000005'/*$4*/,'D0000012'/*$5*/) +) \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlBufferTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBufferPrintTest.java similarity index 97% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlBufferTest.java rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBufferPrintTest.java index 1298567968a3d2bfa9f99e79777811f8bcebcadf..73302f453da1fa9c3cc813292fa44f157ddef381 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlBufferTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBufferPrintTest.java @@ -1,4 +1,4 @@ -package com.gitee.qdbp.jdbc.test.sql; +package com.gitee.qdbp.jdbc.test.sql.build; import com.gitee.qdbp.jdbc.model.MainDbType; import com.gitee.qdbp.jdbc.plugins.SqlDialect; @@ -6,7 +6,7 @@ import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.utils.DbTools; import com.gitee.qdbp.tools.utils.DateTools; -public class SqlBufferTest { +public class SqlBufferPrintTest { public static void main(String[] args) { SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.Oracle); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuildExistsTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuildExistsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..972fe9f6fb1404622d71ad12d453bf82bf172b36 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuildExistsTest.java @@ -0,0 +1,80 @@ +package com.gitee.qdbp.jdbc.test.sql.build; + +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.jdbc.sql.SqlBuilder; +import com.gitee.qdbp.jdbc.sql.SqlTools; + +@Test +public class SqlBuildExistsTest { + + @Test(priority = 1) + public void test1() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("OPERATOR").ad('=').var("10001"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), false, "OPERATOR exinsts 'OR'"); + } + + @Test(priority = 2) + public void test2() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("ORIGINAL").ad('=').var("10001"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), false, "ORIGINAL exinsts 'OR'"); + } + + @Test(priority = 3) + public void test3() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("AND ORIGINAL").ad('=').var("10001"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), false, "ORIGINAL exinsts 'OR'"); + } + + @Test(priority = 4) + public void test4() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("AND OPERATOR"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), false, "OPERATOR exinsts 'OR'"); + } + + @Test(priority = 5) + public void test5() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("OR CREATE_USER").ad('=').var("10001"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), true, "startsWith 'OR'"); + } + + @Test(priority = 6) + public void test6() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("(OR CREATE_USER").ad('=').var("10001").ad(')'); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), true, "startsWith 'OR'"); + } + + @Test(priority = 7) + public void test7() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("(DEPT_ID='D00001' OR CREATE_USER").ad('=').var("10001").ad(')'); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), true, "startsWith 'OR'"); + } + + @Test(priority = 8) + public void test8() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("(DEPT_ID='D00001'OR CREATE_USER").ad('=').var("10001").ad(')'); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), true, "startsWith 'OR'"); + } + + @Test(priority = 9) + public void test9() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("DEPT_ID='D00001'OR"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), true, "endsWith 'OR'"); + } + + @Test(priority = 10) + public void test10() { + SqlBuilder buffer = new SqlBuilder(); + buffer.ad("(DEPT_ID='D00001'OR)"); + Assert.assertEquals(SqlTools.Text.exists(buffer, "OR"), true, "endsWith 'OR'"); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..80d43210649629da8b76b3411e1246bb56b5be28 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.executable.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE('139'||'%') AND CREATE_USER='zhh' + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF') + AND USER_STATE IN(1,2) AND DATA_STATE=0 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002' AND USER_STATE IN(1,2) AND DATA_STATE=0 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..fbc55c85c652b9e999e3cbb8fdd3f3c121d8eeec --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.logging.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE('139'/*$2*/||'%') AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF')/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND DATA_STATE=0/*$7*/ +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002'/*$8*/ AND USER_STATE IN(1/*$9*/,2/*$10*/) AND DATA_STATE=0/*$11*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..b6a2bd9cadfb611ff710df3d15af14dd21e66c20 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.db2.prepared.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE(:$2||'%') AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND DATA_STATE=:$7 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$8 AND USER_STATE IN(:$9,:$10) AND DATA_STATE=:$11 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..adb702bc83c2aab2b2374dfb5f053a2745b1a3bd --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.executable.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE CONCAT('139','%') AND CREATE_USER='zhh' + AND CREATE_TIME>=PARSEDATETIME('2019-01-01 00:00:00.000','yyyy-MM-dd HH:mm:ss.SSS') + AND USER_STATE IN(1,2) AND DATA_STATE=0 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002' AND USER_STATE IN(1,2) AND DATA_STATE=0 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..19f79d13bf6a9dd750ebc303cb9d8c4ba0ef6fe3 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.logging.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE CONCAT('139'/*$2*/,'%') AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>=PARSEDATETIME('2019-01-01 00:00:00.000','yyyy-MM-dd HH:mm:ss.SSS')/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND DATA_STATE=0/*$7*/ +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002'/*$8*/ AND USER_STATE IN(1/*$9*/,2/*$10*/) AND DATA_STATE=0/*$11*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..169d0255ca4444464845d07e81d35f55cafb20de --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.h2.prepared.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE CONCAT(:$2,'%') AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND DATA_STATE=:$7 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$8 AND USER_STATE IN(:$9,:$10) AND DATA_STATE=:$11 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..d322570c98f440c2f29fe43516c545389bd97a38 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.executable.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE CONCAT('139','%') AND CREATE_USER='zhh' + AND CREATE_TIME>='2019-01-01 00:00:00.000' + AND USER_STATE IN(1,2) AND DATA_STATE=0 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002' AND USER_STATE IN(1,2) AND DATA_STATE=0 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..7dfab436bbcaec204635ed5aaafac1cb2bcf4260 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.logging.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE CONCAT('139'/*$2*/,'%') AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>='2019-01-01 00:00:00.000'/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND DATA_STATE=0/*$7*/ +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002'/*$8*/ AND USER_STATE IN(1/*$9*/,2/*$10*/) AND DATA_STATE=0/*$11*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..169d0255ca4444464845d07e81d35f55cafb20de --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.mysql.prepared.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE CONCAT(:$2,'%') AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND DATA_STATE=:$7 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$8 AND USER_STATE IN(:$9,:$10) AND DATA_STATE=:$11 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..80d43210649629da8b76b3411e1246bb56b5be28 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.executable.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE('139'||'%') AND CREATE_USER='zhh' + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF') + AND USER_STATE IN(1,2) AND DATA_STATE=0 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002' AND USER_STATE IN(1,2) AND DATA_STATE=0 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..fbc55c85c652b9e999e3cbb8fdd3f3c121d8eeec --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.logging.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE('139'/*$2*/||'%') AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF')/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND DATA_STATE=0/*$7*/ +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10002'/*$8*/ AND USER_STATE IN(1/*$9*/,2/*$10*/) AND DATA_STATE=0/*$11*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..b6a2bd9cadfb611ff710df3d15af14dd21e66c20 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test1.oracle.prepared.sql @@ -0,0 +1,8 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE(:$2||'%') AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND DATA_STATE=:$7 +UNION +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$8 AND USER_STATE IN(:$9,:$10) AND DATA_STATE=:$11 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..bdc8a76161d0ae3c1686d9f5c9ddeba2512039c0 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.executable.sql @@ -0,0 +1,11 @@ +SELECT * FROM ( + SELECT T_T.*, ROWNUMBER() OVER(ORDER BY ORDER OF T_T) AS R_N FROM ( + SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE('139'||'%') + AND CREATE_USER='zhh' + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF') + AND USER_STATE IN(1,2) AND EFTFLAG=0 + FETCH FIRST 20 ROWS ONLY + ) AS T_T +) WHERE R_N > 10 ORDER BY R_N \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..f52147aed2d8c9b29378dc43709993747cdae416 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.logging.sql @@ -0,0 +1,11 @@ +SELECT * FROM ( + SELECT T_T.*, ROWNUMBER() OVER(ORDER BY ORDER OF T_T) AS R_N FROM ( + SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE('139'/*$2*/||'%') + AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF')/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND EFTFLAG=0/*$7*/ + FETCH FIRST 20 ROWS ONLY + ) AS T_T +) WHERE R_N > 10 ORDER BY R_N \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..2f83a26efb1d05c0f0af01ab287bb8a2a75e2a61 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.db2.prepared.sql @@ -0,0 +1,11 @@ +SELECT * FROM ( + SELECT T_T.*, ROWNUMBER() OVER(ORDER BY ORDER OF T_T) AS R_N FROM ( + SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE(:$2||'%') + AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND EFTFLAG=:$7 + FETCH FIRST 20 ROWS ONLY + ) AS T_T +) WHERE R_N > 10 ORDER BY R_N \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..fc6f0a82edf8d3623e83b76ce4374a0ed1c82f1d --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.executable.sql @@ -0,0 +1,7 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE CONCAT('139','%') + AND CREATE_USER='zhh' + AND CREATE_TIME>=PARSEDATETIME('2019-01-01 00:00:00.000','yyyy-MM-dd HH:mm:ss.SSS') + AND USER_STATE IN(1,2) AND EFTFLAG=0 + LIMIT 10 OFFSET 10 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..39f6d639dff064cb654eed4d17ae33c011949f99 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.logging.sql @@ -0,0 +1,7 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE CONCAT('139'/*$2*/,'%') + AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>=PARSEDATETIME('2019-01-01 00:00:00.000','yyyy-MM-dd HH:mm:ss.SSS')/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND EFTFLAG=0/*$7*/ + LIMIT 10/*$8*/ OFFSET 10/*$9*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..0953595c9cda01eb225ceed75f2509e694f48cdf --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.h2.prepared.sql @@ -0,0 +1,7 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE CONCAT(:$2,'%') + AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND EFTFLAG=:$7 + LIMIT :$8 OFFSET :$9 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..c13f131ae1bb6ab0b10e3b627d79a7e80c7f71a2 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.executable.sql @@ -0,0 +1,7 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE CONCAT('139','%') + AND CREATE_USER='zhh' + AND CREATE_TIME>='2019-01-01 00:00:00.000' + AND USER_STATE IN(1,2) AND EFTFLAG=0 + LIMIT 10,10 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..6436bacce54f5112b503dbb945b157050382073a --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.logging.sql @@ -0,0 +1,7 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE CONCAT('139'/*$2*/,'%') + AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>='2019-01-01 00:00:00.000'/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND EFTFLAG=0/*$7*/ + LIMIT 10/*$8*/,10/*$9*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..65364ddcd0089eeaa079f09b2f6894e89fd5301f --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.mysql.prepared.sql @@ -0,0 +1,7 @@ +SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE CONCAT(:$2,'%') + AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND EFTFLAG=:$7 + LIMIT :$8,:$9 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.executable.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.executable.sql new file mode 100644 index 0000000000000000000000000000000000000000..4fe578a397422bff650075726f11febc575f6264 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.executable.sql @@ -0,0 +1,10 @@ +SELECT * FROM ( + SELECT T_T.*, ROWNUM R_N FROM ( + SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001' + AND PHONE LIKE('139'||'%') + AND CREATE_USER='zhh' + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF') + AND USER_STATE IN(1,2) AND EFTFLAG=0 + ) T_T WHERE ROWNUM <= 20 +) WHERE R_N > 10 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.logging.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.logging.sql new file mode 100644 index 0000000000000000000000000000000000000000..b2a4c00aae0b7127e6b54d2d1818af1b65f2f473 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.logging.sql @@ -0,0 +1,10 @@ +SELECT * FROM ( + SELECT T_T.*, ROWNUM R_N FROM ( + SELECT * FROM SYS_USER + WHERE DEPT_CODE='10001'/*$1*/ + AND PHONE LIKE('139'/*$2*/||'%') + AND CREATE_USER='zhh'/*$3*/ + AND CREATE_TIME>=TO_TIMESTAMP('2019-01-01 00:00:00.000','YYYY-MM-DD HH24:MI:SS.FF')/*$4*/ + AND USER_STATE IN(1/*$5*/,2/*$6*/) AND EFTFLAG=0/*$7*/ + ) T_T WHERE ROWNUM <= 20/*$8*/ +) WHERE R_N > 10/*$9*/ \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.prepared.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.prepared.sql new file mode 100644 index 0000000000000000000000000000000000000000..5f15cea9a4846ba64c4eda98e41a9084119cc3b9 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.test2.oracle.prepared.sql @@ -0,0 +1,10 @@ +SELECT * FROM ( + SELECT T_T.*, ROWNUM R_N FROM ( + SELECT * FROM SYS_USER + WHERE DEPT_CODE=:$1 + AND PHONE LIKE(:$2||'%') + AND CREATE_USER=:$3 + AND CREATE_TIME>=:$4 + AND USER_STATE IN(:$5,:$6) AND EFTFLAG=:$7 + ) T_T WHERE ROWNUM <= :$8 +) WHERE R_N > :$9 \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.testIndent.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.testIndent.sql new file mode 100644 index 0000000000000000000000000000000000000000..ac192375c1eba7dc2ac87861fc17def98da8ab3f --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrint.testIndent.sql @@ -0,0 +1,4 @@ +SELECT * FROM TEST + WHERE USER_CODE='U001' + AND GENDER=2 AND DATA_STATE=1 + ORDER BY CREATE_TIME DESC \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlBuilderTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrintTest.java similarity index 33% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlBuilderTest.java rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrintTest.java index b18446a7ff3b7d8f0a9b61149235939e9c4df5bb..fcf17b297c36300115272ee5b8210625affcf827 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlBuilderTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlBuilderPrintTest.java @@ -1,28 +1,39 @@ -package com.gitee.qdbp.jdbc.test.sql; +package com.gitee.qdbp.jdbc.test.sql.build; -import com.gitee.qdbp.jdbc.model.MainDbType; +import java.io.IOException; +import java.net.URL; +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; +import com.gitee.qdbp.able.jdbc.paging.Paging; +import com.gitee.qdbp.jdbc.api.QdbcBoot; import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.SqlBuilder; -import com.gitee.qdbp.jdbc.utils.DbTools; +import com.gitee.qdbp.jdbc.test.enums.DataState; +import com.gitee.qdbp.jdbc.test.enums.Gender; +import com.gitee.qdbp.tools.files.PathTools; +import com.gitee.qdbp.tools.utils.AssertTools; import com.gitee.qdbp.tools.utils.DateTools; -public class SqlBuilderTest { +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class SqlBuilderPrintTest extends AbstractTestNGSpringContextTests { - public static void main(String[] args) { - SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.Oracle); - test1(dialect); - test2(dialect); - } + @Autowired + private QdbcBoot qdbcBoot; - private static void test1(SqlDialect dialect) { + @Test + public void test1() throws IOException { + SqlDialect dialect = qdbcBoot.getSqlDialect(); // ad=append, pd=prepend, var=addVariable SqlBuilder builder = new SqlBuilder(); builder.ad("SELECT * FROM SYS_USER"); builder.newline().tab(); // 缩进1次 builder.ad("WHERE DEPT_CODE").ad('=').var("10001"); builder.newline().tab(); // 在上次的基础上再缩进1次 - builder.ad("AND PHONE").ad(dialect.buildLikeSql("139%")); + builder.ad("AND PHONE").ad(dialect.buildStartsWithSql("139")); builder.ad("AND CREATE_USER").ad('=').var("zhh"); builder.newline(); // 这里会继承前面的2次缩进 builder.ad("AND CREATE_TIME", ">=").var(DateTools.parse("2019-01-01")); @@ -38,10 +49,12 @@ public class SqlBuilderTest { builder.ad("AND USER_STATE", "IN").ad('(').var(1).ad(',').var(2).ad(')'); builder.ad("AND DATA_STATE").ad('=').var(0); - printSqlString(builder.out(), dialect); + checkSqlString(builder.out(), "test1"); } - private static void test2(SqlDialect dialect) { + @Test + public void test2() throws IOException { + SqlDialect dialect = qdbcBoot.getSqlDialect(); SqlBuilder buffer = new SqlBuilder(); buffer.ad("AND CREATE_USER").ad('=').var("zhh"); buffer.newline(); @@ -54,7 +67,8 @@ public class SqlBuilderTest { SqlBuilder prependBuffer = new SqlBuilder(); prependBuffer.ad("WHERE DEPT_CODE").ad('=').var("10001"); prependBuffer.newline().tab(); - prependBuffer.ad("AND PHONE").ad(dialect.buildLikeSql("139%")); + prependBuffer.ad("AND PHONE").ad(dialect.buildStartsWithSql("139")); + prependBuffer.newline(); buffer.newline().ad(appendBuffer); buffer.pd(prependBuffer); @@ -62,20 +76,87 @@ public class SqlBuilderTest { selectBuffer.ad("SELECT * FROM SYS_USER").newline().tab(); buffer.pd(selectBuffer); - printSqlString(buffer.out(), dialect); + SqlBuffer pagingSql = dialect.buildPagingSql(buffer.out(), new Paging(2, 10)); + + checkSqlString(pagingSql, "test2"); } - private static void printSqlString(SqlBuffer buffer, SqlDialect dialect) { + private void checkSqlString(SqlBuffer buffer, String type) throws IOException { + SqlDialect dialect = qdbcBoot.getSqlDialect(); + String preparedSqlString = buffer.getPreparedSqlString(dialect); + String executableSqlString = buffer.getExecutableSqlString(dialect); + String loggingSqlString = buffer.getLoggingSqlString(dialect); + System.out.println("/******************************************************\\"); - System.out.println("-- PreparedSqlString"); - System.out.println(buffer.getPreparedSqlString(dialect)); + System.out.println("-- " + type + " PreparedSqlString"); + System.out.println(preparedSqlString); System.out.println("--------------------------------------------------------"); - System.out.println("-- ExecutableSqlString"); - System.out.println(buffer.getExecutableSqlString(dialect)); + System.out.println("-- " + type + " ExecutableSqlString"); + System.out.println(executableSqlString); System.out.println("--------------------------------------------------------"); - System.out.println("-- LoggingSqlString"); - System.out.println(buffer.getLoggingSqlString(dialect)); + System.out.println("-- " + type + " LoggingSqlString"); + System.out.println(loggingSqlString); System.out.println("\\******************************************************/"); System.out.println(); + + String dbType = dialect.getDbVersion().getDbType().name().toLowerCase(); + String preparedSqlFile = "SqlBuilderPrint." + type + "." + dbType + ".prepared.sql"; + String executableSqlFile = "SqlBuilderPrint." + type + "." + dbType + ".executable.sql"; + String loggingSqlFile = "SqlBuilderPrint." + type + "." + dbType + ".logging.sql"; + + URL preparedResultUrl = PathTools.findClassResource(SqlBuilderPrintTest.class, preparedSqlFile); + AssertTools.assertTextLinesEquals(preparedSqlString, preparedResultUrl); + URL executableResultUrl = PathTools.findClassResource(SqlBuilderPrintTest.class, executableSqlFile); + AssertTools.assertTextLinesEquals(executableSqlString, executableResultUrl); + URL loggingResultUrl = PathTools.findClassResource(SqlBuilderPrintTest.class, loggingSqlFile); + AssertTools.assertTextLinesEquals(loggingSqlString, loggingResultUrl); + } + + @Test + public void testIndent1() throws IOException { + // @formatter:off + SqlBuilder buffer = new SqlBuilder("SELECT * FROM TEST").newline().tab(); + SqlBuilder whereSql = new SqlBuilder().tab(5) + .ad("WHERE USER_CODE=").var("U001") + .newline().tab() + .ad("AND GENDER=").var(Gender.FEMALE) + .ad("AND DATA_STATE=").var(DataState.NORMAL) + .newline().tab(-1) + .ad("ORDER BY CREATE_TIME DESC"); + // @formatter:on + buffer.ad(whereSql); + + checkIndentString(buffer.out(), "testIndent"); + } + + @Test + public void testIndent2() throws IOException { + // @formatter:off + SqlBuilder tableSql = new SqlBuilder("SELECT * FROM TEST").newline().tab(); + SqlBuilder buffer = new SqlBuilder().tab(5) + .ad("WHERE USER_CODE=").var("U001") + .newline().tab() + .ad("AND GENDER=").var(Gender.FEMALE) + .ad("AND DATA_STATE=").var(DataState.NORMAL) + .newline().tab(-1) + .ad("ORDER BY CREATE_TIME DESC"); + // @formatter:on + buffer.pd(tableSql); + + checkIndentString(buffer.out(), "testIndent"); + } + + private void checkIndentString(SqlBuffer buffer, String type) throws IOException { + SqlDialect dialect = qdbcBoot.getSqlDialect(); + String sqlFile = "SqlBuilderPrint." + type + ".sql"; + String sqlString = buffer.getExecutableSqlString(dialect); + System.out.println("/******************************************************\\"); + System.out.println("-- " + sqlFile); + System.out.println(sqlString); + System.out.println("\\******************************************************/"); + System.out.println(); + + 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/SqlFormatTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlFormatTest.java similarity index 96% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlFormatTest.java rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlFormatTest.java index 713a1b2c7cf82b74ef6deac158d68907041220e2..dfd1cdea5476ca24e85a6918ec6df67e399b2e04 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/SqlFormatTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/build/SqlFormatTest.java @@ -1,4 +1,4 @@ -package com.gitee.qdbp.jdbc.test.sql; +package com.gitee.qdbp.jdbc.test.sql.build; import java.util.HashMap; import java.util.Map; @@ -32,7 +32,7 @@ public class SqlFormatTest { // @formatter:on Map params = new HashMap<>(); params.put("eftflag", new SqlBuilder().var('A').ad(',').var('E')); - SqlBuffer buffer = SqlFragmentContainer.defaults().parse(sqlTemplate, params, dialect); + SqlBuffer buffer = SqlFragmentContainer.defaults().render(sqlTemplate, params, dialect); SqlBuffer paging1 = dialect.buildPagingSql(buffer, new Paging(1, 10)); SqlBuffer paging2 = dialect.buildPagingSql(buffer, new Paging(2, 10)); System.out.println(buffer.getPreparedSqlString(dialect)); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParseTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/BaseTemplateParseTest.java similarity index 74% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParseTest.java rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/BaseTemplateParseTest.java index c0d480b1d6557718a452e6d8d5effd5c86206dd8..b18815149ec988725429d355ad0016f238cb9595 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParseTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/BaseTemplateParseTest.java @@ -1,4 +1,4 @@ -package com.gitee.qdbp.jdbc.test.biz; +package com.gitee.qdbp.jdbc.test.sql.parse; import java.io.IOException; import java.net.URL; @@ -7,7 +7,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; 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; import com.gitee.qdbp.able.jdbc.condition.DbWhere; @@ -19,7 +18,6 @@ import com.gitee.qdbp.jdbc.plugins.SqlDialect; import com.gitee.qdbp.jdbc.sql.SqlBuffer; import com.gitee.qdbp.jdbc.sql.fragment.CrudFragmentHelper; import com.gitee.qdbp.jdbc.sql.fragment.QueryFragmentHelper; -import com.gitee.qdbp.jdbc.sql.parse.SqlFragmentContainer; import com.gitee.qdbp.jdbc.test.model.ActProcState; import com.gitee.qdbp.jdbc.test.model.ActTask; import com.gitee.qdbp.jdbc.test.model.SysDeptEntity; @@ -35,13 +33,13 @@ import com.gitee.qdbp.tools.utils.VerifyTools; * @author zhaohuihua * @version 20200911 */ -@Test -@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) -public class SqlTemplateParseTest extends AbstractTestNGSpringContextTests { +public abstract class BaseTemplateParseTest extends AbstractTestNGSpringContextTests { @Autowired private QdbcBoot qdbcBoot; + protected abstract String getRealSqlId(String sqlId); + @Test public void testGetUserRolesQuerySql11() throws IOException { String sqlId = "user.roles.query"; @@ -99,7 +97,7 @@ public class SqlTemplateParseTest extends AbstractTestNGSpringContextTests { QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); Map params = new HashMap<>(); - params.put("selectColumns", "*"); + params.put("selectColumns", "r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID"); params.put("userIds", userIds); if (VerifyTools.isNotBlank(where)) { params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); @@ -108,14 +106,14 @@ public class SqlTemplateParseTest extends AbstractTestNGSpringContextTests { params.put("orderByCondition", sqlHelper.buildOrderBySql(orderings, false)); } - SqlBuffer buffer = SqlFragmentContainer.defaults().render(sqlId, params, dialect); + String realSqlId = getRealSqlId(sqlId); + SqlBuffer buffer = qdbcBoot.getSqlDao().getSqlContent(realSqlId, params); String sqlText = buffer.getLoggingSqlString(dialect); String fileName = "SqlTemplateParse." + sqlId + "." + index + ".sql"; System.out.println("<<" + fileName + ">>" + '\n' + sqlText); - URL resultFile = PathTools.findClassResource(SqlTemplateParseTest.class, fileName); - String resultText = PathTools.downloadString(resultFile); - AssertTools.assertTextLinesEquals(sqlText, resultText, sqlId); + URL resultFile = PathTools.findClassResource(BaseTemplateParseTest.class, fileName); + AssertTools.assertTextLinesEquals(sqlText, resultFile, sqlId); } @Test @@ -145,14 +143,14 @@ public class SqlTemplateParseTest extends AbstractTestNGSpringContextTests { params.put("orderBy", sqlHelper.buildOrderBySql(orderings, false)); } String sqlId = "recursive.list.children.query"; - SqlBuffer buffer = SqlFragmentContainer.defaults().render(sqlId, params, dialect); + String realSqlId = getRealSqlId(sqlId); + SqlBuffer buffer = qdbcBoot.getSqlDao().getSqlContent(realSqlId, params); String sqlText = buffer.getLoggingSqlString(dialect); String fileName = "SqlTemplateParse." + sqlId + ".sql"; System.out.println("<<" + fileName + ">>" + '\n' + sqlText); - URL resultFile = PathTools.findClassResource(SqlTemplateParseTest.class, fileName); - String resultText = PathTools.downloadString(resultFile); - AssertTools.assertTextLinesEquals(sqlText, resultText, sqlId); + URL resultFile = PathTools.findClassResource(BaseTemplateParseTest.class, fileName); + AssertTools.assertTextLinesEquals(sqlText, resultFile, sqlId); } @Test @@ -208,13 +206,49 @@ public class SqlTemplateParseTest extends AbstractTestNGSpringContextTests { params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); } - SqlBuffer buffer = SqlFragmentContainer.defaults().render(sqlId, params, dialect); + String realSqlId = getRealSqlId(sqlId); + SqlBuffer buffer = qdbcBoot.getSqlDao().getSqlContent(realSqlId, params); + String sqlText = buffer.getLoggingSqlString(dialect); + + String fileName = "SqlTemplateParse." + sqlId + ".sql"; + System.out.println("<<" + fileName + ">>" + '\n' + sqlText); + URL resultFile = PathTools.findClassResource(BaseTemplateParseTest.class, fileName); + AssertTools.assertTextLinesEquals(sqlText, resultFile, sqlId); + } + + @Test + public void testGetBacklogMineQuerySql() throws IOException { + DbWhere where = new DbWhere().on("s.projectCode", "=", "P0000001"); + + String sqlId = "backlog.mine.query"; + testGetBacklogMineQuerySql(sqlId, where); + } + + @Test + public void testGetBacklogMineQuerySql2() throws IOException { + DbWhere where = new DbWhere().on("s.projectCode", "=", "P0000001"); + + String sqlId = "backlog.mine.query2"; + testGetBacklogMineQuerySql(sqlId, where); + } + + private void testGetBacklogMineQuerySql(String sqlId, DbWhere where) throws IOException { + SqlDialect dialect = qdbcBoot.getSqlDialect(); + TableJoin tables = TableJoin.of(ActTask.class, "T", ActProcState.class, "S"); + QueryFragmentHelper sqlHelper = qdbcBoot.buildSqlBuilder(tables).helper(); + + Map params = new HashMap<>(); + if (where != null && !where.isEmpty()) { + params.put("whereCondition", sqlHelper.buildWhereSql(where, false)); + } + + String realSqlId = getRealSqlId(sqlId); + SqlBuffer buffer = qdbcBoot.getSqlDao().getSqlContent(realSqlId, params); String sqlText = buffer.getLoggingSqlString(dialect); String fileName = "SqlTemplateParse." + sqlId + ".sql"; System.out.println("<<" + fileName + ">>" + '\n' + sqlText); - URL resultFile = PathTools.findClassResource(SqlTemplateParseTest.class, fileName); - String resultText = PathTools.downloadString(resultFile); - AssertTools.assertTextLinesEquals(sqlText, resultText, sqlId); + URL resultFile = PathTools.findClassResource(BaseTemplateParseTest.class, fileName); + AssertTools.assertTextLinesEquals(sqlText, resultFile, sqlId); } } 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 new file mode 100644 index 0000000000000000000000000000000000000000..f01954b4ab8d33e8a14c882c2a01ce7aa69b8b21 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateConflictTest.java @@ -0,0 +1,79 @@ +package com.gitee.qdbp.jdbc.test.sql.parse; + +import java.util.HashMap; +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; +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.exception.ServiceException; +import com.gitee.qdbp.jdbc.api.QdbcBoot; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; + +/** + * SQL模板冲突测试 + * + * @author zhaohuihua + * @version 20210228 + */ +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class SqlTemplateConflictTest extends AbstractTestNGSpringContextTests { + + private static Logger log = LoggerFactory.getLogger(SqlTemplateConflictTest.class); + + @Autowired + private QdbcBoot qdbcBoot; + + @Test(expectedExceptions = ServiceException.class) + public void testSqlIdConflict0() throws ServiceException { + String sqlId = "user.roles.query0"; // 不存在 + Map params = new HashMap<>(); + params.put("userIds", "UB0000001"); + try { + qdbcBoot.getSqlDao().listForMaps(sqlId, params); + } catch (Exception e) { + log.error("Failed to execute SqlDao.listForMaps('{}')", sqlId, e); + throw e; + } + } + + public void testSqlIdConflict1() throws ServiceException { + String sqlId = "user.roles.query1"; // 出现在select.001.sql和sqltest-0.0.1.jar包的同名文件中 + Map params = new HashMap<>(); + params.put("userIds", "UB0000001"); + SqlBuffer sql = qdbcBoot.getSqlDao().getSqlContent(sqlId, params); + // jar包中的SQL没有TENANT_CODE条件 + String string = sql.getExecutableSqlString(qdbcBoot.getSqlDialect()); + Assert.assertEquals(string.contains("TENANT_CODE"), true, "sql 'user.roles.query1' contains 'TENANT_CODE'"); + } + + @Test(expectedExceptions = ServiceException.class) + public void testSqlIdConflict2() throws ServiceException { + String sqlId = "user.roles.query2"; // 出现在select.001.sql和select.002.sql + Map params = new HashMap<>(); + params.put("userIds", "UB0000001"); + try { + qdbcBoot.getSqlDao().listForMaps(sqlId, params); + } catch (Exception e) { + log.error("Failed to execute SqlDao.listForMaps('{}')", sqlId, e); + throw e; + } + } + + @Test(expectedExceptions = ServiceException.class) + public void testSqlIdConflict5() { + String sqlId = "user.roles.query5"; // 出现在select.002.sql和select.003.sql, 但版本都不符合 + Map params = new HashMap<>(); + params.put("userIds", "UB0000001"); + try { + qdbcBoot.getSqlDao().listForMaps(sqlId, params); + } catch (Exception e) { + log.error("Failed to execute SqlDao.listForMaps('{}')", sqlId, e); + throw e; + } + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.mine.query.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.mine.query.sql new file mode 100644 index 0000000000000000000000000000000000000000..7a0787a3fa17f6fdd0a3fcd9796f0b9ad919714e --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.mine.query.sql @@ -0,0 +1,5 @@ +SELECT * FROM FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + WHERE S.PROJECT_CODE='P0000001'/*$1*/ + AND S.USER_ID='U0000001'/*$2*/ +ORDER BY S.CREATE_TIME DESC,S.BUSINESS_ID ASC \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.mine.query2.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.mine.query2.sql new file mode 100644 index 0000000000000000000000000000000000000000..7a0787a3fa17f6fdd0a3fcd9796f0b9ad919714e --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.mine.query2.sql @@ -0,0 +1,5 @@ +SELECT * FROM FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + WHERE S.PROJECT_CODE='P0000001'/*$1*/ + AND S.USER_ID='U0000001'/*$2*/ +ORDER BY S.CREATE_TIME DESC,S.BUSINESS_ID ASC \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.count.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.count.sql new file mode 100644 index 0000000000000000000000000000000000000000..ad0b8197d1ef596c7d22d9b9a06c947d855b593f --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.count.sql @@ -0,0 +1,31 @@ +SELECT SUM(CNT) FROM ( + SELECT COUNT(*) AS CNT + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_='U0000001'/*$1*/ + OR ( + T.ASSIGNEE_ IN ('R0000001'/*$2*/,'R0000006'/*$3*/) + AND ( + S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$4*/,'D0000005'/*$5*/,'D0000012'/*$6*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$7*/ +UNION ALL + SELECT COUNT(*) AS CNT + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE='U0000001'/*$8*/ + OR ( + S.ASSIGNEE IN ('R0000001'/*$9*/,'R0000006'/*$10*/) + AND ( + S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$11*/,'D0000005'/*$12*/,'D0000012'/*$13*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$14*/ +) R \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.count2.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.count2.sql new file mode 100644 index 0000000000000000000000000000000000000000..e63495a047678b864f9d9d6f62ed0139911aa172 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.count2.sql @@ -0,0 +1,33 @@ +SELECT SUM(CNT) FROM ( + SELECT COUNT(*) AS CNT + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_='U0000001'/*$1*/ + OR ( + T.ASSIGNEE_ IN ('R0000001'/*$2*/,'R0000006'/*$3*/) + AND ( + S.USER_ID='U0000001'/*$4*/ + OR S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$5*/,'D0000005'/*$6*/,'D0000012'/*$7*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$8*/ +UNION ALL + SELECT COUNT(*) AS CNT + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE='U0000001'/*$9*/ + OR ( + S.ASSIGNEE IN ('R0000001'/*$10*/,'R0000006'/*$11*/) + AND ( + S.USER_ID='U0000001'/*$12*/ + OR S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$13*/,'D0000005'/*$14*/,'D0000012'/*$15*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$16*/ +) R \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.query.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.query.sql new file mode 100644 index 0000000000000000000000000000000000000000..1d55e88de9ff6af555d7455838fd3b73d3e26186 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.query.sql @@ -0,0 +1,42 @@ +SELECT * FROM ( + SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, + T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_='U0000001'/*$1*/ + OR ( + T.ASSIGNEE_ IN ('R0000001'/*$2*/,'R0000006'/*$3*/) + AND ( + S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$4*/,'D0000005'/*$5*/,'D0000012'/*$6*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$7*/ +UNION ALL + SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, + S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE='U0000001'/*$8*/ + OR ( + S.ASSIGNEE IN ('R0000001'/*$9*/,'R0000006'/*$10*/) + AND ( + S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$11*/,'D0000005'/*$12*/,'D0000012'/*$13*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$14*/ +) R +ORDER BY R.RECEIVE_TIME DESC,R.CREATE_TIME DESC,R.BUSINESS_ID \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.query2.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.query2.sql new file mode 100644 index 0000000000000000000000000000000000000000..43bf6d566bbc6db74f636d752d697d47066aa724 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.backlog.todo.query2.sql @@ -0,0 +1,44 @@ +SELECT * FROM ( + SELECT '1' AS TYPE,S.IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,S.PROC_STATE_CODE, + T.ASSIGNEE_ AS ASSIGNEE,P.START_TIME_ AS CREATE_TIME,T.CREATE_TIME_ AS RECEIVE_TIME,S.BACKLOG_CONTENT,C.URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + T.ID_ AS TASK_ID,T.PROC_INST_ID_ AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.USER_ID START_USER_ID,S.CURR_APPROVER_CODES HANDLER_USER_CODE,S.CURR_APPROVER_NAMES HANDLER_USER + FROM ACT_RU_TASK T + INNER JOIN ACT_PROC_STATE S ON T.PROC_INST_ID_=S.PROC_INST_ID + INNER JOIN ACT_HI_PROCINST P ON T.PROC_INST_ID_=P.PROC_INST_ID_ + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + T.ASSIGNEE_='U0000001'/*$1*/ + OR ( + T.ASSIGNEE_ IN ('R0000001'/*$2*/,'R0000006'/*$3*/) + AND ( + S.USER_ID='U0000001'/*$4*/ + OR S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$5*/,'D0000005'/*$6*/,'D0000012'/*$7*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$8*/ +UNION ALL + SELECT '2' AS TYPE,NULL IS_START_NODE,C.TASK_NAME,S.BUSINESS_ID,S.BUS_CODE AS BUSCODE, + S.PROJECT_CODE,S.PROJECT_NAME,S.PROD_TYPE_CODE,S.PROD_TYPE_NAME,NULL PROC_STATE_CODE, + S.ASSIGNEE,S.CREATE_TIME,S.CREATE_TIME AS RECEIVE_TIME,S.TASK_CONTENT AS BACKLOG_CONTENT,C.URL URL, + S.KEYWORD_CODE1,S.KEYWORD_VALUE1,S.KEYWORD_CODE2,S.KEYWORD_VALUE2,S.KEYWORD_CODE3,S.KEYWORD_VALUE3, + S.ID AS TASK_ID,S.ID AS PROC_INST_ID,S.BUSINESS_PARAMS, + S.CREATE_USER START_USER_ID,NULL HANDLER_USER_CODE,NULL HANDLER_USER + FROM COMM_OPERATE_TASK S + LEFT JOIN ACT_BACKLOG_CONFIG C ON S.BUS_CODE=C.BUS_CODE + WHERE ( + S.ASSIGNEE='U0000001'/*$9*/ + OR ( + S.ASSIGNEE IN ('R0000001'/*$10*/,'R0000006'/*$11*/) + AND ( + S.USER_ID='U0000001'/*$12*/ + OR S.DEPT_ID IS NULL OR S.DEPT_ID IN ('D0000001'/*$13*/,'D0000005'/*$14*/,'D0000012'/*$15*/) + ) + ) + ) + AND S.PROJECT_CODE='P0000001'/*$16*/ +) R +ORDER BY R.RECEIVE_TIME DESC,R.CREATE_TIME DESC,R.BUSINESS_ID \ No newline at end of file diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.recursive.list.children.query.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.recursive.list.children.query.sql similarity index 76% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.recursive.list.children.query.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.recursive.list.children.query.sql index 1c59e30b7071dd388e30241748efd40b871cc0b4..fd681a267484247c7f3a526f72536179cf0fccd3 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.recursive.list.children.query.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.recursive.list.children.query.sql @@ -1,10 +1,10 @@ WITH RECURSIVE recursive_temp_table(temp_parent) AS ( SELECT DEPT_CODE temp_parent FROM TEST_DEPARTMENT_CORE_INFO - WHERE DEPT_CODE IN('D0000001'/*$1*/,'D0000002'/*$2*/) + WHERE DEPT_CODE IN ('D0000001'/*$1*/,'D0000002'/*$2*/) AND DATA_STATE='1'/*$3*/ UNION ALL SELECT DEPT_CODE FROM TEST_DEPARTMENT_CORE_INFO A, recursive_temp_table B - WHERE A.PARENT_CODE = B.temp_parent + WHERE A.PARENT_CODE=B.temp_parent AND DATA_STATE='1'/*$4*/ ) SELECT ID,TENANT_CODE,DEPT_CODE,DEPT_NAME,PARENT_CODE,SORT_INDEX,CREATOR_ID,CREATE_TIME,DATA_STATE diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.11.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.11.sql similarity index 41% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.11.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.11.sql index 0dae7e2016edf650389932c2c86aea84eb7add40..f9bf283038168108c04d88b7268b24b4a4f05298 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.11.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.11.sql @@ -1,4 +1,4 @@ -SELECT * +SELECT r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID FROM TEST_USER_ROLE_REF ur INNER JOIN TEST_ROLE_CORE_INFO r ON ur.ROLE_ID=r.ID diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.12.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.12.sql similarity index 55% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.12.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.12.sql index c25871f71f7d4b11c3f27df0fe45e6a5f231ac8c..0e8c9f58e7f3480189dba8e1323b04e244985003 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.12.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.12.sql @@ -1,4 +1,4 @@ -SELECT * +SELECT r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID FROM TEST_USER_ROLE_REF ur INNER JOIN TEST_ROLE_CORE_INFO r ON ur.ROLE_ID=r.ID diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.21.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.21.sql similarity index 49% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.21.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.21.sql index 3517865747613b5c362d90d53efd82da0702faa5..c54f08e4aeb6e93461b930f6d28ab9720df11c2a 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.21.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.21.sql @@ -1,4 +1,4 @@ -SELECT * +SELECT r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID FROM TEST_USER_ROLE_REF ur INNER JOIN TEST_ROLE_CORE_INFO r ON ur.ROLE_ID=r.ID diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.22.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.22.sql similarity index 53% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.22.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.22.sql index 2ce7179ce6ecbb9e18c8c0080f94ae26cf286ead..b7df668abc50c4c0b1b56b72ee203448b84b7eb3 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.22.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.22.sql @@ -1,4 +1,4 @@ -SELECT * +SELECT r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID FROM TEST_USER_ROLE_REF ur INNER JOIN TEST_ROLE_CORE_INFO r ON ur.ROLE_ID=r.ID diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.31.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.31.sql similarity index 54% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.31.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.31.sql index cccc744f29ee66a5c2700e08e3712cceb49a14c2..3bbd941549f4a7e01917f8bebd72121efeb9a454 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.31.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.31.sql @@ -1,4 +1,4 @@ -SELECT * +SELECT r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID FROM TEST_USER_ROLE_REF ur INNER JOIN TEST_ROLE_CORE_INFO r ON ur.ROLE_ID=r.ID diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.32.sql b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.32.sql similarity index 57% rename from jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.32.sql rename to jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.32.sql index 6ea05791f7aafee73b6430d67f582c6e32391136..6b162621e21560a82604e5442cb9e4b53552196c 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/biz/SqlTemplateParse.user.roles.query.32.sql +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParse.user.roles.query.32.sql @@ -1,4 +1,4 @@ -SELECT * +SELECT r.ID,r.TENANT_CODE,r.USER_TYPE,r.ROLE_NAME,r.ROLE_DESC,r.SORT_INDEX,r.DEFAULTS,r.OPTIONS,r.CREATOR_ID,r.CREATE_TIME,r.DATA_STATE,ur.USER_ID FROM TEST_USER_ROLE_REF ur INNER JOIN TEST_ROLE_CORE_INFO r ON ur.ROLE_ID=r.ID diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParseTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParseTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3e2155f2fbe0db219e678017da456442eb736bd4 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/SqlTemplateParseTest.java @@ -0,0 +1,20 @@ +package com.gitee.qdbp.jdbc.test.sql.parse; + +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +/** + * SQL解析测试 + * + * @author zhaohuihua + * @version 20200911 + */ +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class SqlTemplateParseTest extends BaseTemplateParseTest { + + @Override + protected String getRealSqlId(String sqlId) { + return sqlId; + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/XmlTemplateParseTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/XmlTemplateParseTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f72fb2bd875099f4b59bfc44d8e121466148a690 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/sql/parse/XmlTemplateParseTest.java @@ -0,0 +1,37 @@ +package com.gitee.qdbp.jdbc.test.sql.parse; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.test.context.ContextConfiguration; +import org.testng.annotations.Test; + +/** + * SQL解析测试 + * + * @author zhaohuihua + * @version 20200911 + */ +@Test +@ContextConfiguration(locations = { "classpath:settings/spring/spring.xml" }) +public class XmlTemplateParseTest extends BaseTemplateParseTest { + + private Map sqlIdMaps = new HashMap<>(); + + public XmlTemplateParseTest() { + sqlIdMaps.put("user.roles.query", "SysUserMapper:queryUserRoles"); + sqlIdMaps.put("role.users.query", "SysUserMapper:queryRoleUsers"); + sqlIdMaps.put("backlog.todo.query", "BizBacklog:queryTodoBacklog"); + sqlIdMaps.put("backlog.todo.count", "BizBacklog:countTodoBacklog"); + sqlIdMaps.put("backlog.mine.query", "BizBacklog:queryMineBacklog"); + sqlIdMaps.put("backlog.todo.query2", "BizBacklog2:queryTodoBacklog"); + sqlIdMaps.put("backlog.todo.count2", "BizBacklog2:countTodoBacklog"); + sqlIdMaps.put("backlog.mine.query2", "BizBacklog2:queryMineBacklog"); + sqlIdMaps.put("recursive.list.children.query", "GlobalRecursive:recursiveListChildrenQuery"); + sqlIdMaps.put("recursive.list.parents.query", "GlobalRecursive:recursiveListParentsQuery"); + } + + @Override + protected String getRealSqlId(String sqlId) { + return sqlIdMaps.get(sqlId); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/trans/TranscationalTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/trans/TranscationalTest.java index bfa56285b1c874bc41f0e884f347fabb2a023207..d9625c17096939c679d244381c8f272420554f8a 100644 --- a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/trans/TranscationalTest.java +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/trans/TranscationalTest.java @@ -18,17 +18,20 @@ public class TranscationalTest extends AbstractTestNGSpringContextTests { private static Logger log = LoggerFactory.getLogger(TranscationalTest.class); + private static final String TACD = "TranscationalTest1"; @Autowired private SysSettingService sysSettingService; + @Test public void test() { log.debug("------------------------------------------------------"); { // 清除全部记录 - sysSettingService.clearAllRecord(); + sysSettingService.clearAllRecord(TACD); } log.debug("------------------------------------------------------"); { // 测试新增记录1 SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName("TranscationalTest-" + 1); entity.setValue("测试 TranscationalTest-" + 1); sysSettingService.createSetting(entity); @@ -36,6 +39,7 @@ public class TranscationalTest extends AbstractTestNGSpringContextTests { log.debug("------------------------------------------------------"); { // 测试新增记录2 SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName("TranscationalTest-" + 2); entity.setValue("测试 TranscationalTest-" + 2); sysSettingService.createSetting(entity); @@ -43,6 +47,7 @@ public class TranscationalTest extends AbstractTestNGSpringContextTests { log.debug("------------------------------------------------------"); try { // 测试新增记录3 SysSettingEntity entity = new SysSettingEntity(); + entity.setTenantCode(TACD); entity.setName("TranscationalTest-" + 3); entity.setValue("测试 TranscationalTest-" + 3); sysSettingService.createSetting(entity, TestModel.mainErrorLogSuccess); @@ -51,7 +56,9 @@ public class TranscationalTest extends AbstractTestNGSpringContextTests { } log.debug("------------------------------------------------------"); { // 查询记录总数 - int count = sysSettingService.countSetting(DbWhere.NONE); + DbWhere where = new DbWhere(); + where.andEquals("tenantCode", TACD); + int count = sysSettingService.countSetting(where); log.warn("Total of setting record is [{}]", count); // 记录1/记录2成功, 记录3失败 Assert.assertEquals(count, 2, "Total of setting record"); diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/utils/IndentTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/utils/IndentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..15cfc4519d792b2877a96924e1c14bc41a1dc4fa --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/utils/IndentTest.java @@ -0,0 +1,153 @@ +package com.gitee.qdbp.jdbc.test.utils; + +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.tools.utils.IndentTools; + +@Test +public class IndentTest { + + @Test + public void test11() { + // \n\tABC\n\t\t --> \n\tABC\n[\tsMESSAGE\n]\t\tDEF // 换行模式(插入的信息与上一行缩进对齐) + String source = "\n\tABC\n\t\t"; + String target = IndentTools.tabs.insertMessageAfterLastNewline(source, "MESSAGE") + "DEF"; + String expected = "\n\tABC\n\tMESSAGE\n\t\tDEF"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test12() { + // \n\tABC\s --> \n\tABC\s[\sMESSAGE\s]DEF // 非换行模式 + String source = "\n\tABC "; + String target = IndentTools.tabs.insertMessageAfterLastNewline(source, "MESSAGE") + "DEF"; + String expected = "\n\tABC MESSAGE DEF"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test13() { + // \n\tABC --> \n\tABC[\sMESSAGE\s]DEF // 非换行模式 + String source = "\n\tABC"; + String target = IndentTools.tabs.insertMessageAfterLastNewline(source, "MESSAGE") + "DEF"; + String expected = "\n\tABC MESSAGE DEF"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test21() { + String source = "\tabc\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.indentAll(source, 2); + String expected = "\t\tabc\n\t\t\tdef\n\t\tghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test22() { + String source = "\tabc\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.indentAll(source, 0); + String expected = "abc\n\tdef\nghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test23() { + String source = "\tabc\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.tabAll(source, -1); + String expected = "abc\n\tdef\nghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test24() { + String source = "\tabc\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.tabAll(source, -5); + String expected = "abc\ndef\nghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test25() { + String source = "\tabc\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.tabAll(source, 1); + String expected = "\t\tabc\n\t\t\tdef\n\t\tghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test31() { + String source = "\tabc\n\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.tabAll(source, -1, false, true); + String expected = "\tabc\n\n\tdef\nghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test32() { + String source = "\tabc\n\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.tabAll(source, 1, false, false); + String expected = "\tabc\n\n\t\t\tdef\n\t\tghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test33() { + String source = "\tabc\n\n\t\tdef\n\tghi"; + String target = IndentTools.tabs.tabAll(source, 1, false, true); + String expected = "\tabc\n\t\n\t\t\tdef\n\t\tghi"; + print(source, target, expected); + Assert.assertEquals(target, expected); + } + + @Test + public void test41() { + String source = "\t\tabc\n\t\n\t\t\tdef\n\t\tghi"; + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, true), 1); + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, false), 2); + } + + @Test + public void test42() { + String source = "\t\tabc\n\t\n\t\t\tdef\n\t\tghi\n"; + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, true), 0); + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, false), 2); + } + + @Test + public void test43() { + String source = "\t\tabc\n\t\n\t\t\tdef\n\t\tghi\n\n\n"; + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, true), 0); + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, false), 2); + } + + @Test + public void test44() { + String source = "\t\tabc\n\t\n\t\t\tdef\n\t\tghi\n\n\t\t\n"; + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, true), 0); + Assert.assertEquals(IndentTools.countMinIndentSize(source, true, false), 2); + } + + private static void print(String source, String target, String expected) { + System.out.println("/******************************************************\\"); + System.out.println("-- source"); + System.out.println(source); + System.out.println("--------------------------------------------------------"); + System.out.println("-- target"); + System.out.println(target); + System.out.println("--------------------------------------------------------"); + System.out.println("-- expected"); + System.out.println(expected); + System.out.println("\\******************************************************/"); + System.out.println(); + } +} diff --git a/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/utils/OrderingsParseTest.java b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/utils/OrderingsParseTest.java new file mode 100644 index 0000000000000000000000000000000000000000..faa0dddda128d100493046f79c285b62878a06b1 --- /dev/null +++ b/jdbc-test/src/test/java/com/gitee/qdbp/jdbc/test/utils/OrderingsParseTest.java @@ -0,0 +1,176 @@ +package com.gitee.qdbp.jdbc.test.utils; + +import java.util.Arrays; +import org.testng.Assert; +import org.testng.annotations.Test; +import com.gitee.qdbp.able.jdbc.ordering.OrderType; +import com.gitee.qdbp.able.jdbc.ordering.Orderings; +import com.gitee.qdbp.jdbc.model.MainDbType; +import com.gitee.qdbp.jdbc.plugins.SqlDialect; +import com.gitee.qdbp.jdbc.plugins.impl.ColumnNamingConverter; +import com.gitee.qdbp.jdbc.sql.SqlBuffer; +import com.gitee.qdbp.jdbc.sql.SqlTools; +import com.gitee.qdbp.jdbc.utils.DbTools; + +@Test +public class OrderingsParseTest { + + String s01 = "userName"; + String s02 = "u.userName desc"; + String s05 = "userName asc, createTime desc"; + String s20 = "u.userName, pinyin(productName) asc, p.createTime desc, decodeBy(orderState, 'OrderList') ASC"; + String s21 = "pinyin(productName) asc, concat('test',t.orderState)"; + String s22 = "test('Let\\'s go') asc, decodeBy(orderState, 'Let\\'s go')"; + String s23 = "include(u.userType,'GlobalDecode.userType') asc, decode(t.orderState,'PAID',1,'CANCELED',2,10)"; + + @Test + public void testParseOrdering1() { + // "userName" + Orderings result = Orderings.of(s01); + + Assert.assertEquals(result.size(), 1); + Assert.assertEquals(result.get(0).getOriginal(), "userName"); + Assert.assertEquals(result.get(0).getOrderBy(), "userName"); + Assert.assertEquals(result.get(0).getOrderType(), OrderType.ASC); + Assert.assertNull(result.get(0).getFunctionName()); + Assert.assertNull(result.get(0).getFunctionParams()); + } + + @Test + public void testParseOrdering2() { + // "u.userName desc" + Orderings result = Orderings.of(s02); + + Assert.assertEquals(result.size(), 1); + Assert.assertEquals(result.get(0).getOriginal(), "u.userName desc"); + Assert.assertEquals(result.get(0).getOrderBy(), "u.userName"); + Assert.assertEquals(result.get(0).getOrderType(), OrderType.DESC); + Assert.assertNull(result.get(0).getFunctionName()); + Assert.assertNull(result.get(0).getFunctionParams()); + } + + @Test + public void testParseOrdering5() { + // "userName asc, createTime desc" + Orderings result = Orderings.of(s05); + + Assert.assertEquals(result.size(), 2); + Assert.assertEquals(result.get(0).getOriginal(), "userName asc"); + Assert.assertEquals(result.get(0).getOrderBy(), "userName"); + Assert.assertEquals(result.get(0).getOrderType(), OrderType.ASC); + Assert.assertNull(result.get(0).getFunctionName()); + Assert.assertNull(result.get(0).getFunctionParams()); + + Assert.assertEquals(result.get(1).getOriginal(), "createTime desc"); + Assert.assertEquals(result.get(1).getOrderBy(), "createTime"); + Assert.assertEquals(result.get(1).getOrderType(), OrderType.DESC); + Assert.assertNull(result.get(1).getFunctionName()); + Assert.assertNull(result.get(1).getFunctionParams()); + } + + @Test + public void testParseOrdering20() { + // "u.userName, pinyin(productName) asc, p.createTime desc, decodeBy(orderState, 'OrderList') ASC" + Orderings result = Orderings.of(s20); + + Assert.assertEquals(result.size(), 4); + Assert.assertEquals(result.get(0).getOriginal(), "u.userName"); + Assert.assertEquals(result.get(0).getOrderBy(), "u.userName"); + Assert.assertEquals(result.get(0).getOrderType(), OrderType.ASC); + Assert.assertNull(result.get(0).getFunctionName()); + Assert.assertNull(result.get(0).getFunctionParams()); + + Assert.assertEquals(result.get(1).getOriginal(), "pinyin(productName) asc"); + Assert.assertEquals(result.get(1).getOrderBy(), "productName"); + Assert.assertEquals(result.get(1).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(1).getFunctionName(), "pinyin"); + Assert.assertNotNull(result.get(1).getFunctionParams()); + Assert.assertEquals(result.get(1).getFunctionParams().size(), 0); + + Assert.assertEquals(result.get(2).getOriginal(), "p.createTime desc"); + Assert.assertEquals(result.get(2).getOrderBy(), "p.createTime"); + Assert.assertEquals(result.get(2).getOrderType(), OrderType.DESC); + Assert.assertNull(result.get(2).getFunctionName()); + Assert.assertNull(result.get(2).getFunctionParams()); + + Assert.assertEquals(result.get(3).getOriginal(), "decodeBy(orderState, 'OrderList') ASC"); + Assert.assertEquals(result.get(3).getOrderBy(), "orderState"); + Assert.assertEquals(result.get(3).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(3).getFunctionName(), "decodeBy"); + Assert.assertEquals(result.get(3).getFunctionParams(), Arrays.asList("orderState", "'OrderList'")); + Assert.assertEquals(result.get(3).getOnlyStringParam(), "OrderList"); // 第1个字符参数 + } + + @Test + public void testParseOrdering21() { + // pinyin(productName) asc, concat('test',t.orderState) + Orderings result = Orderings.of(s21); + + Assert.assertEquals(result.size(), 2); + Assert.assertEquals(result.get(0).getOriginal(), "pinyin(productName) asc"); + Assert.assertEquals(result.get(0).getOrderBy(), "productName"); + Assert.assertEquals(result.get(0).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(0).getFunctionName(), "pinyin"); + Assert.assertNotNull(result.get(0).getFunctionParams()); + Assert.assertEquals(result.get(0).getFunctionParams().size(), 0); + + Assert.assertEquals(result.get(1).getOriginal(), "concat('test',t.orderState)"); + Assert.assertEquals(result.get(1).getOrderBy(), "t.orderState"); + Assert.assertEquals(result.get(1).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(1).getFunctionName(), "concat"); + Assert.assertEquals(result.get(1).getFunctionParams(), Arrays.asList("'test'", "t.orderState")); + } + + @Test + public void testParseOrdering22() { + // "test('Let\\'s go') asc, decodeBy(orderState, 'Let\\'s go')" + Orderings result = Orderings.of(s22); + + Assert.assertEquals(result.size(), 2); + Assert.assertEquals(result.get(0).getOriginal(), "test('Let\\'s go') asc"); + Assert.assertEquals(result.get(0).getOrderBy(), null); // 没有找到字段名 + Assert.assertEquals(result.get(0).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(0).getFunctionName(), "test"); + Assert.assertEquals(result.get(0).getFunctionParams(), Arrays.asList("'Let\\'s go'")); + + Assert.assertEquals(result.get(1).getOriginal(), "decodeBy(orderState, 'Let\\'s go')"); + Assert.assertEquals(result.get(1).getOrderBy(), "orderState"); + Assert.assertEquals(result.get(1).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(1).getFunctionName(), "decodeBy"); + Assert.assertEquals(result.get(1).getFunctionParams(), Arrays.asList("orderState", "'Let\\'s go'")); + Assert.assertEquals(result.get(1).getOnlyStringParam(), "Let's go"); // 第1个字符参数 + } + + @Test + public void testParseOrdering23() { + // "include(u.userType,'GlobalDecode.userType') asc, decode(t.orderState,'PAID',1,'CANCELED',2,10)"; + Orderings result = Orderings.of(s23); + + Assert.assertEquals(result.size(), 2); + Assert.assertEquals(result.get(0).getOriginal(), "include(u.userType,'GlobalDecode.userType') asc"); + Assert.assertEquals(result.get(0).getOrderBy(), "u.userType"); + Assert.assertEquals(result.get(0).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(0).getFunctionName(), "include"); + Assert.assertEquals(result.get(0).getFunctionParams(), Arrays.asList("u.userType", "'GlobalDecode.userType'")); + Assert.assertEquals(result.get(0).getOnlyStringParam(), "GlobalDecode.userType"); // 第1个字符参数 + + Assert.assertEquals(result.get(1).getOriginal(), "decode(t.orderState,'PAID',1,'CANCELED',2,10)"); + Assert.assertEquals(result.get(1).getOrderBy(), "t.orderState"); + Assert.assertEquals(result.get(1).getOrderType(), OrderType.ASC); + Assert.assertEquals(result.get(1).getFunctionName(), "decode"); + Assert.assertEquals(result.get(1).getFunctionParams(), + Arrays.asList("t.orderState", "'PAID'", "1", "'CANCELED'", "2", "10")); + } + + String s50 = "userName(PINYIN), pinyin(productName) asc"; + + @Test + public void testParseOrdering50() { + SqlDialect dialect = DbTools.buildSqlDialect(MainDbType.MySQL); + Orderings orderings = Orderings.of(s50); + SqlBuffer sql = SqlTools.buildOrderBySql(orderings, ColumnNamingConverter.defaults(), dialect); + String sqlString = sql.getExecutableSqlString(dialect); + System.out.println(sqlString); + Assert.assertEquals(sqlString, "CONVERT(USER_NAME USING GBK) ASC,CONVERT(PRODUCT_NAME USING GBK) ASC"); + } +} diff --git a/jdbc-test/src/test/resources/jdbc.h2.properties b/jdbc-test/src/test/resources/jdbc.h2.properties new file mode 100644 index 0000000000000000000000000000000000000000..45492ae262fc835b5a426c2b6b3e2d1a4328e7fc --- /dev/null +++ b/jdbc-test/src/test/resources/jdbc.h2.properties @@ -0,0 +1,2 @@ +# jdbc +jdbc.sys = h2.mem@~/qdbpdev diff --git a/jdbc-test/src/test/resources/libs/sqltest-0.0.1.jar b/jdbc-test/src/test/resources/libs/sqltest-0.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..c5666b56066cabd3b5c5a2a15fcbfc0e3a9752ed Binary files /dev/null and b/jdbc-test/src/test/resources/libs/sqltest-0.0.1.jar differ diff --git a/jdbc-test/src/test/resources/logback.xml b/jdbc-test/src/test/resources/logback.xml index 0ecedc3bb0a485dde9e9aa58e67c5f54383df0f1..031f3a4f9adda82460b932040758ed856ce08271 100644 --- a/jdbc-test/src/test/resources/logback.xml +++ b/jdbc-test/src/test/resources/logback.xml @@ -1,21 +1,51 @@ - + + - ${pattern} + ${pattern.stdout} + + ../logs/qdbp-jdbc-test.log + + + ../logs/qdbp-jdbc-test-%d{yyyyMMdd}-%i.log + 30 + + + 50MB + + + + ${pattern.normal} + + + + + 0 + + 1024 + + + + + + + + + \ No newline at end of file diff --git a/jdbc-test/src/test/resources/settings/data/AreaDivision.txt b/jdbc-test/src/test/resources/settings/data/AreaDivision.txt new file mode 100644 index 0000000000000000000000000000000000000000..6cb00224c85d1f00567c1f99f7688e4c6789f833 --- /dev/null +++ b/jdbc-test/src/test/resources/settings/data/AreaDivision.txt @@ -0,0 +1,7 @@ + +exceltojson.main.self.name = areas +exceltojson.main.file.name = AreaDivision.xlsx +exceltojson.main.sheet.name = 行政区划 +exceltojson.main.header.rows = 1 +exceltojson.main.columns = *code|*name||parent|type|index +exceltojson.main.id.field = id diff --git a/jdbc-test/src/test/resources/settings/data/AreaDivision.xlsx b/jdbc-test/src/test/resources/settings/data/AreaDivision.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0168325d3c553912fe294fd266851d6bd6c420b7 Binary files /dev/null and b/jdbc-test/src/test/resources/settings/data/AreaDivision.xlsx differ diff --git a/jdbc-test/src/test/resources/settings/dbinit/create.tables.db2.sql b/jdbc-test/src/test/resources/settings/dbinit/create.tables.db2.sql index 29231d3ed5a8a3a92b466e115f5b08d67da53035..3ce69142883a5ff4f6db81f9d9e18a58d9b6a1bb 100644 --- a/jdbc-test/src/test/resources/settings/dbinit/create.tables.db2.sql +++ b/jdbc-test/src/test/resources/settings/dbinit/create.tables.db2.sql @@ -9,6 +9,7 @@ DROP TABLE TEST_DEPARTMENT_CORE_INFO; CREATE TABLE TEST_SETTING ( ID VARCHAR(50) NOT NULL, + TENANT_CODE VARCHAR(50) DEFAULT '0' NOT NULL, NAME VARCHAR(30) NOT NULL, VALUE VARCHAR(50) NOT NULL, VERSION INTEGER NOT NULL DEFAULT 1, @@ -18,10 +19,12 @@ CREATE TABLE TEST_SETTING ( UPDATE_TIME TIMESTAMP, DATA_STATE INTEGER NOT NULL DEFAULT 1, CONSTRAINT TEST_SETTING_PK PRIMARY KEY (ID), - CONSTRAINT TEST_SETTING_NAME UNIQUE (NAME,DATA_STATE) + CONSTRAINT TEST_SETTING_NAME UNIQUE (NAME,TENANT_CODE,DATA_STATE) ); +CREATE INDEX IDX_SETTING_TENANT_CODE ON TEST_SETTING (TENANT_CODE); COMMENT ON TABLE TEST_SETTING IS '系统配置表'; COMMENT ON COLUMN TEST_SETTING.ID IS '主键'; +COMMENT ON COLUMN TEST_SETTING.TENANT_CODE IS '租户编号'; COMMENT ON COLUMN TEST_SETTING.NAME IS '名称'; COMMENT ON COLUMN TEST_SETTING.VALUE IS '文本'; COMMENT ON COLUMN TEST_SETTING.VERSION IS '版本号'; @@ -34,15 +37,19 @@ COMMENT ON COLUMN TEST_SETTING.DATA_STATE IS '数据状态:1为正常|随机数 CREATE TABLE TEST_LOGGER ( ID VARCHAR(50) NOT NULL, + TENANT_CODE VARCHAR(50) DEFAULT '0' NOT NULL, NAME VARCHAR(30) NOT NULL, - CONTENT VARCHAR(4000) NOT NULL, + CONTENT CLOB NULL, SORT_INDEX INTEGER NOT NULL DEFAULT 1, CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, DATA_STATE INTEGER NOT NULL DEFAULT 1, CONSTRAINT TEST_LOGGER_PK PRIMARY KEY (ID) ); +CREATE INDEX IDX_LOGGER_TENANT_CODE ON TEST_LOGGER (TENANT_CODE); +CREATE INDEX IDX_SETTING_NAME ON TEST_SETTING (NAME); COMMENT ON TABLE TEST_LOGGER IS '日志测试表'; COMMENT ON COLUMN TEST_LOGGER.ID IS '主键'; +COMMENT ON COLUMN TEST_LOGGER.TENANT_CODE IS '租户编号'; COMMENT ON COLUMN TEST_LOGGER.NAME IS '名称'; COMMENT ON COLUMN TEST_LOGGER.CONTENT IS '内容'; COMMENT ON COLUMN TEST_LOGGER.SORT_INDEX IS '排序号'; @@ -66,6 +73,7 @@ CREATE TABLE "TEST_ROLE_CORE_INFO" ( CONSTRAINT "TEST_ROLE_CORE_PK" PRIMARY KEY ("ID"), CONSTRAINT "TEST_ROLE_CORE_NAME" UNIQUE ("TENANT_CODE", "ROLE_NAME", "DATA_STATE") ); +CREATE INDEX IDX_ROLE_TENANT_CODE ON TEST_ROLE_CORE_INFO (TENANT_CODE); COMMENT ON TABLE "TEST_ROLE_CORE_INFO" IS '角色信息表(pkg:permission)'; COMMENT ON COLUMN "TEST_ROLE_CORE_INFO"."ID" IS '角色ID'; @@ -85,7 +93,7 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "ID" VARCHAR(50) NOT NULL, "TENANT_CODE" VARCHAR(50) NOT NULL DEFAULT '0', "USER_TYPE" SMALLINT NOT NULL DEFAULT 1, - "DEPT_CODE" VARCHAR(50) NOT NULL DEFAULT '0', + "DEPT_ID" VARCHAR(50) NOT NULL DEFAULT '0', "USER_CODE" VARCHAR(50), "USER_NAME" VARCHAR(50), "NICK_NAME" VARCHAR(50), @@ -94,6 +102,7 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "EMAIL" VARCHAR(50), "GENDER" SMALLINT NOT NULL, "PHOTO" VARCHAR(120), + "BIRTHDAY" DATE, "CITY" VARCHAR(50), "IDENTITY" VARCHAR(20), "PASSWORD" VARCHAR(120), @@ -105,12 +114,13 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "DATA_STATE" INTEGER NOT NULL DEFAULT 1, CONSTRAINT "TEST_USER_CORE_PK" PRIMARY KEY ("ID") ); +CREATE INDEX IDX_USER_TENANT_CODE ON TEST_USER_CORE_INFO (TENANT_CODE); COMMENT ON TABLE "TEST_USER_CORE_INFO" IS '用户基础信息表(pkg:personnel)'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."ID" IS '用户ID'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."TENANT_CODE" IS '租户编号'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."USER_TYPE" IS '用户类型:1.管理员|2.用户(1.admin|2.user)'; -COMMENT ON COLUMN "TEST_USER_CORE_INFO"."DEPT_CODE" IS '部门编号'; +COMMENT ON COLUMN "TEST_USER_CORE_INFO"."DEPT_ID" IS '部门编号'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."USER_CODE" IS '账号/工号'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."USER_NAME" IS '登录用户名'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."NICK_NAME" IS '昵称'; @@ -119,6 +129,7 @@ COMMENT ON COLUMN "TEST_USER_CORE_INFO"."PHONE" IS '电话'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."EMAIL" IS '邮箱'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."GENDER" IS '性别:0.未知|1.男|2.女(gender:0.unknown|1.male|2.female)'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."PHOTO" IS '头像'; +COMMENT ON COLUMN "TEST_USER_CORE_INFO"."BIRTHDAY" IS '生日'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."CITY" IS '城市'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."IDENTITY" IS '身份证'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."PASSWORD" IS '密码'; @@ -164,6 +175,8 @@ CREATE TABLE "TEST_DEPARTMENT_CORE_INFO" ( CONSTRAINT "TEST_DEPT_CORE_CODE" UNIQUE ("TENANT_CODE", "DEPT_CODE", "DATA_STATE"), CONSTRAINT "TEST_DEPT_CORE_NAME" UNIQUE ("TENANT_CODE", "PARENT_CODE", "DEPT_NAME", "DATA_STATE") ); +CREATE INDEX IDX_DEPT_TENANT_CODE ON TEST_DEPARTMENT_CORE_INFO (TENANT_CODE); + COMMENT ON TABLE "TEST_DEPARTMENT_CORE_INFO" IS '部门信息表(pkg:personnel)'; COMMENT ON COLUMN "TEST_DEPARTMENT_CORE_INFO"."ID" IS '角色ID'; COMMENT ON COLUMN "TEST_DEPARTMENT_CORE_INFO"."TENANT_CODE" IS '租户编号'; diff --git a/jdbc-test/src/test/resources/settings/dbinit/create.tables.h2.sql b/jdbc-test/src/test/resources/settings/dbinit/create.tables.h2.sql new file mode 100644 index 0000000000000000000000000000000000000000..c4f727951079d93d9f452b193bb5400c84d627f9 --- /dev/null +++ b/jdbc-test/src/test/resources/settings/dbinit/create.tables.h2.sql @@ -0,0 +1,117 @@ + +DROP TABLE IF EXISTS TEST_SETTING; +DROP TABLE IF EXISTS TEST_LOGGER; +DROP TABLE IF EXISTS TEST_ROLE_CORE_INFO; +DROP TABLE IF EXISTS TEST_USER_CORE_INFO; +DROP TABLE IF EXISTS TEST_USER_ROLE_REF; +DROP TABLE IF EXISTS TEST_DEPARTMENT_CORE_INFO; + +CREATE TABLE IF NOT EXISTS TEST_SETTING ( + ID VARCHAR(50) NOT NULL COMMENT '主键', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', + NAME VARCHAR(30) NOT NULL COMMENT '名称', + VALUE VARCHAR(50) NOT NULL COMMENT '文本', + VERSION INTEGER(8) NOT NULL DEFAULT 1 COMMENT '版本号', + REMARK VARCHAR(200) COMMENT '备注', + STATE TINYINT(1) NOT NULL COMMENT '状态', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + UPDATE_TIME TIMESTAMP(3) COMMENT '修改时间', + DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', + PRIMARY KEY (ID), + UNIQUE KEY TEST_SETTING_NAME(NAME, TENANT_CODE, DATA_STATE) +) COMMENT='系统配置表'; +CREATE INDEX IDX_SETTING_TENANT_CODE ON TEST_SETTING (TENANT_CODE); +CREATE INDEX IDX_SETTING_NAME ON TEST_SETTING (NAME); + +CREATE TABLE IF NOT EXISTS TEST_LOGGER ( + ID VARCHAR(50) NOT NULL COMMENT '主键', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', + NAME VARCHAR(30) COMMENT '名称', + CONTENT LONGTEXT NULL COMMENT '内容', + SORT_INDEX INTEGER(8) NOT NULL DEFAULT 1 COMMENT '排序号', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', + PRIMARY KEY (ID) +) COMMENT='操作日志表'; +CREATE INDEX IDX_LOGGER_TENANT_CODE ON TEST_LOGGER (TENANT_CODE); + + +CREATE TABLE IF NOT EXISTS TEST_ROLE_CORE_INFO ( + ID VARCHAR(50) NOT NULL COMMENT '角色ID', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', + USER_TYPE TINYINT(1) NOT NULL DEFAULT 1 COMMENT '用户类型:1.管理员|2.用户(1.admin|2.user)', + ROLE_NAME VARCHAR(50) NOT NULL COMMENT '角色名称(name)', + ROLE_DESC VARCHAR(200) COMMENT '描述', + SORT_INDEX INTEGER(8) COMMENT '排序号(越小越靠前)', + CREATOR_ID VARCHAR(50) COMMENT '创建人ID', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + DEFAULTS BIT(1) NOT NULL DEFAULT 0 COMMENT '默认角色(如果用户没有任何角色,默认会赋予该角色)', + OPTIONS TEXT COMMENT '选项(options)', + DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', + PRIMARY KEY (ID), + UNIQUE KEY UQ_ROLE_NAME (TENANT_CODE, ROLE_NAME, DATA_STATE) +) COMMENT='角色信息表(pkg:permission)'; +CREATE INDEX IDX_ROLE_TENANT_CODE ON TEST_ROLE_CORE_INFO (TENANT_CODE); + + +CREATE TABLE IF NOT EXISTS TEST_USER_CORE_INFO ( + ID VARCHAR(50) NOT NULL COMMENT '用户ID', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', + USER_TYPE TINYINT(1) NOT NULL DEFAULT 1 COMMENT '用户类型:1.管理员|2.用户(1.admin|2.user)', + DEPT_ID VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '部门编号', + USER_CODE VARCHAR(50) COMMENT '账号/工号', + USER_NAME VARCHAR(50) COMMENT '登录用户名', + NICK_NAME VARCHAR(50) COMMENT '昵称', + REAL_NAME VARCHAR(20) COMMENT '真实姓名', + PHONE VARCHAR(20) COMMENT '电话', + EMAIL VARCHAR(50) COMMENT '邮箱', + GENDER TINYINT(1) NOT NULL COMMENT '性别:0.未知|1.男|2.女(Gender:0.unknown|1.male|2.female)', + PHOTO VARCHAR(120) COMMENT '头像', + BIRTHDAY DATE COMMENT '生日', + CITY VARCHAR(50) COMMENT '城市', + IDENTITY VARCHAR(20) COMMENT '身份证', + PASSWORD VARCHAR(120) COMMENT '密码', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + SUPERMAN BIT(1) NOT NULL DEFAULT 0 COMMENT '是否为超级用户', + USER_STATE TINYINT(1) NOT NULL DEFAULT 0 COMMENT '用户状态:0.正常|1.锁定|2.待激活|3.注销(UserState:0.normal|1.locked|2.unactivated|3.logoff)', + USER_SOURCE TINYINT(1) NOT NULL DEFAULT 0 COMMENT '用户来源:0.未知|1.录入(UserSource:0.unknown|1.input)', + OPTIONS TEXT COMMENT '选项(options)', + DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', + PRIMARY KEY (ID), + UNIQUE KEY UQ_USER_CODE (TENANT_CODE, USER_TYPE, USER_CODE, DATA_STATE), + UNIQUE KEY UQ_USER_NAME (TENANT_CODE, USER_TYPE, USER_NAME, DATA_STATE), + UNIQUE KEY UQ_USER_PHONE (TENANT_CODE, USER_TYPE, PHONE, DATA_STATE), + UNIQUE KEY UQ_USER_EMAIL (TENANT_CODE, USER_TYPE, EMAIL, DATA_STATE) +) COMMENT='用户基础信息表(pkg:personnel)'; +CREATE INDEX IDX_USER_TENANT_CODE ON TEST_USER_CORE_INFO (TENANT_CODE); + + +CREATE TABLE IF NOT EXISTS TEST_USER_ROLE_REF ( + ID VARCHAR(50) NOT NULL COMMENT '主键ID', + USER_ID VARCHAR(50) NOT NULL COMMENT '用户ID(list)', + ROLE_ID VARCHAR(50) NOT NULL COMMENT '角色ID(list)', + DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', + PRIMARY KEY (ID), + UNIQUE KEY UQ_USER_ROLE_REF (USER_ID, ROLE_ID, DATA_STATE) +) COMMENT='用户角色关系表(pkg:permission)'; + +CREATE INDEX IDX_USER_ID ON TEST_USER_ROLE_REF ( USER_ID ); +CREATE INDEX IDX_ROLE_ID ON TEST_USER_ROLE_REF ( ROLE_ID ); + + +CREATE TABLE TEST_DEPARTMENT_CORE_INFO ( + ID VARCHAR(50) NOT NULL COMMENT '部门ID', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', + DEPT_CODE VARCHAR(50) NOT NULL COMMENT '部门编号', + DEPT_NAME VARCHAR(50) NOT NULL COMMENT '部门名称', + PARENT_CODE VARCHAR(50) NOT NULL COMMENT '上级部门编号', + SORT_INDEX INTEGER(8) DEFAULT NULL COMMENT '排序号(越小越靠前)', + CREATOR_ID VARCHAR(50) DEFAULT NULL COMMENT '创建人ID', + CREATE_TIME DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|其他为删除', + PRIMARY KEY (ID), + UNIQUE KEY AK_UQ_DEPT_CODE (TENANT_CODE,DEPT_CODE,DATA_STATE), + UNIQUE KEY AK_UQ_DEPT_NAME (TENANT_CODE,PARENT_CODE,DEPT_NAME,DATA_STATE) +) COMMENT='部门信息表(pkg:personnel)'; +CREATE INDEX IDX_DEPT_TENANT_CODE ON TEST_DEPARTMENT_CORE_INFO (TENANT_CODE); + diff --git a/jdbc-test/src/test/resources/settings/dbinit/create.tables.mysql.sql b/jdbc-test/src/test/resources/settings/dbinit/create.tables.mysql.sql index a4ffb3df206897cc20dd03650439d23302359433..c4f727951079d93d9f452b193bb5400c84d627f9 100644 --- a/jdbc-test/src/test/resources/settings/dbinit/create.tables.mysql.sql +++ b/jdbc-test/src/test/resources/settings/dbinit/create.tables.mysql.sql @@ -8,27 +8,32 @@ DROP TABLE IF EXISTS TEST_DEPARTMENT_CORE_INFO; CREATE TABLE IF NOT EXISTS TEST_SETTING ( ID VARCHAR(50) NOT NULL COMMENT '主键', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', NAME VARCHAR(30) NOT NULL COMMENT '名称', VALUE VARCHAR(50) NOT NULL COMMENT '文本', VERSION INTEGER(8) NOT NULL DEFAULT 1 COMMENT '版本号', REMARK VARCHAR(200) COMMENT '备注', STATE TINYINT(1) NOT NULL COMMENT '状态', - CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - UPDATE_TIME TIMESTAMP COMMENT '修改时间', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', + UPDATE_TIME TIMESTAMP(3) COMMENT '修改时间', DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', PRIMARY KEY (ID), - UNIQUE KEY TEST_SETTING_NAME(NAME, DATA_STATE) + UNIQUE KEY TEST_SETTING_NAME(NAME, TENANT_CODE, DATA_STATE) ) COMMENT='系统配置表'; +CREATE INDEX IDX_SETTING_TENANT_CODE ON TEST_SETTING (TENANT_CODE); +CREATE INDEX IDX_SETTING_NAME ON TEST_SETTING (NAME); CREATE TABLE IF NOT EXISTS TEST_LOGGER ( ID VARCHAR(50) NOT NULL COMMENT '主键', + TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', NAME VARCHAR(30) COMMENT '名称', - CONTENT TEXT NOT NULL COMMENT '内容', + CONTENT LONGTEXT NULL COMMENT '内容', SORT_INDEX INTEGER(8) NOT NULL DEFAULT 1 COMMENT '排序号', - CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', PRIMARY KEY (ID) ) COMMENT='操作日志表'; +CREATE INDEX IDX_LOGGER_TENANT_CODE ON TEST_LOGGER (TENANT_CODE); CREATE TABLE IF NOT EXISTS TEST_ROLE_CORE_INFO ( @@ -39,19 +44,21 @@ CREATE TABLE IF NOT EXISTS TEST_ROLE_CORE_INFO ( ROLE_DESC VARCHAR(200) COMMENT '描述', SORT_INDEX INTEGER(8) COMMENT '排序号(越小越靠前)', CREATOR_ID VARCHAR(50) COMMENT '创建人ID', - CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', DEFAULTS BIT(1) NOT NULL DEFAULT 0 COMMENT '默认角色(如果用户没有任何角色,默认会赋予该角色)', OPTIONS TEXT COMMENT '选项(options)', DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|随机数为删除', PRIMARY KEY (ID), UNIQUE KEY UQ_ROLE_NAME (TENANT_CODE, ROLE_NAME, DATA_STATE) ) COMMENT='角色信息表(pkg:permission)'; +CREATE INDEX IDX_ROLE_TENANT_CODE ON TEST_ROLE_CORE_INFO (TENANT_CODE); + CREATE TABLE IF NOT EXISTS TEST_USER_CORE_INFO ( ID VARCHAR(50) NOT NULL COMMENT '用户ID', TENANT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '租户编号', USER_TYPE TINYINT(1) NOT NULL DEFAULT 1 COMMENT '用户类型:1.管理员|2.用户(1.admin|2.user)', - DEPT_CODE VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '部门编号', + DEPT_ID VARCHAR(50) NOT NULL DEFAULT '0' COMMENT '部门编号', USER_CODE VARCHAR(50) COMMENT '账号/工号', USER_NAME VARCHAR(50) COMMENT '登录用户名', NICK_NAME VARCHAR(50) COMMENT '昵称', @@ -60,10 +67,11 @@ CREATE TABLE IF NOT EXISTS TEST_USER_CORE_INFO ( EMAIL VARCHAR(50) COMMENT '邮箱', GENDER TINYINT(1) NOT NULL COMMENT '性别:0.未知|1.男|2.女(Gender:0.unknown|1.male|2.female)', PHOTO VARCHAR(120) COMMENT '头像', + BIRTHDAY DATE COMMENT '生日', CITY VARCHAR(50) COMMENT '城市', IDENTITY VARCHAR(20) COMMENT '身份证', PASSWORD VARCHAR(120) COMMENT '密码', - CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + CREATE_TIME TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT '创建时间', SUPERMAN BIT(1) NOT NULL DEFAULT 0 COMMENT '是否为超级用户', USER_STATE TINYINT(1) NOT NULL DEFAULT 0 COMMENT '用户状态:0.正常|1.锁定|2.待激活|3.注销(UserState:0.normal|1.locked|2.unactivated|3.logoff)', USER_SOURCE TINYINT(1) NOT NULL DEFAULT 0 COMMENT '用户来源:0.未知|1.录入(UserSource:0.unknown|1.input)', @@ -75,6 +83,7 @@ CREATE TABLE IF NOT EXISTS TEST_USER_CORE_INFO ( UNIQUE KEY UQ_USER_PHONE (TENANT_CODE, USER_TYPE, PHONE, DATA_STATE), UNIQUE KEY UQ_USER_EMAIL (TENANT_CODE, USER_TYPE, EMAIL, DATA_STATE) ) COMMENT='用户基础信息表(pkg:personnel)'; +CREATE INDEX IDX_USER_TENANT_CODE ON TEST_USER_CORE_INFO (TENANT_CODE); CREATE TABLE IF NOT EXISTS TEST_USER_ROLE_REF ( @@ -98,9 +107,11 @@ CREATE TABLE TEST_DEPARTMENT_CORE_INFO ( PARENT_CODE VARCHAR(50) NOT NULL COMMENT '上级部门编号', SORT_INDEX INTEGER(8) DEFAULT NULL COMMENT '排序号(越小越靠前)', CREATOR_ID VARCHAR(50) DEFAULT NULL COMMENT '创建人ID', - CREATE_TIME TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + CREATE_TIME DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', DATA_STATE INTEGER(10) NOT NULL DEFAULT 1 COMMENT '数据状态:1为正常|其他为删除', PRIMARY KEY (ID), UNIQUE KEY AK_UQ_DEPT_CODE (TENANT_CODE,DEPT_CODE,DATA_STATE), UNIQUE KEY AK_UQ_DEPT_NAME (TENANT_CODE,PARENT_CODE,DEPT_NAME,DATA_STATE) ) COMMENT='部门信息表(pkg:personnel)'; +CREATE INDEX IDX_DEPT_TENANT_CODE ON TEST_DEPARTMENT_CORE_INFO (TENANT_CODE); + diff --git a/jdbc-test/src/test/resources/settings/dbinit/create.tables.oracle.sql b/jdbc-test/src/test/resources/settings/dbinit/create.tables.oracle.sql index 19f1a11141088baf08a908ac5d417f685d772f78..1bb1ddc5312f9b1b8bd994ec1d58171a9deb6abc 100644 --- a/jdbc-test/src/test/resources/settings/dbinit/create.tables.oracle.sql +++ b/jdbc-test/src/test/resources/settings/dbinit/create.tables.oracle.sql @@ -9,6 +9,7 @@ DROP TABLE "TEST_DEPARTMENT_CORE_INFO" CASCADE CONSTRAINTS; CREATE TABLE TEST_SETTING ( ID VARCHAR2(50) NOT NULL, + TENANT_CODE VARCHAR2(50) DEFAULT '0' NOT NULL, NAME VARCHAR2(30) NOT NULL, VALUE VARCHAR2(50) NOT NULL, VERSION NUMBER(8) DEFAULT 1 NOT NULL, @@ -18,10 +19,13 @@ CREATE TABLE TEST_SETTING ( UPDATE_TIME TIMESTAMP, DATA_STATE NUMBER(10) DEFAULT 1 NOT NULL, CONSTRAINT TEST_SETTING_PK PRIMARY KEY (ID), - CONSTRAINT TEST_SETTING_NAME UNIQUE (NAME,DATA_STATE) + CONSTRAINT TEST_SETTING_NAME UNIQUE (NAME,TENANT_CODE,DATA_STATE) ); +CREATE INDEX IDX_SETTING_TENANT_CODE ON TEST_SETTING (TENANT_CODE); +CREATE INDEX IDX_SETTING_NAME ON TEST_SETTING (NAME); COMMENT ON TABLE TEST_SETTING IS '系统配置表'; COMMENT ON COLUMN TEST_SETTING.ID IS '主键'; +COMMENT ON COLUMN TEST_SETTING.TENANT_CODE IS '租户编号'; COMMENT ON COLUMN TEST_SETTING.NAME IS '名称'; COMMENT ON COLUMN TEST_SETTING.VALUE IS '文本'; COMMENT ON COLUMN TEST_SETTING.VERSION IS '版本号'; @@ -34,15 +38,18 @@ COMMENT ON COLUMN TEST_SETTING.DATA_STATE IS '数据状态:1为正常|随机数 CREATE TABLE TEST_LOGGER ( ID VARCHAR2(50) NOT NULL, + TENANT_CODE VARCHAR2(50) DEFAULT '0' NOT NULL, NAME VARCHAR2(30) NOT NULL, - CONTENT VARCHAR2(4000) NOT NULL, + CONTENT CLOB NULL, SORT_INDEX NUMBER(8) DEFAULT 1 NOT NULL, CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, DATA_STATE NUMBER(10) DEFAULT 1 NOT NULL, CONSTRAINT TEST_LOGGER_PK PRIMARY KEY (ID) ); +CREATE INDEX IDX_LOGGER_TENANT_CODE ON TEST_LOGGER (TENANT_CODE); COMMENT ON TABLE TEST_LOGGER IS '日志测试表'; COMMENT ON COLUMN TEST_LOGGER.ID IS '主键'; +COMMENT ON COLUMN TEST_LOGGER.TENANT_CODE IS '租户编号'; COMMENT ON COLUMN TEST_LOGGER.NAME IS '名称'; COMMENT ON COLUMN TEST_LOGGER.CONTENT IS '内容'; COMMENT ON COLUMN TEST_LOGGER.SORT_INDEX IS '排序号'; @@ -65,6 +72,8 @@ CREATE TABLE "TEST_ROLE_CORE_INFO" ( CONSTRAINT TEST_ROLE_CORE_PK PRIMARY KEY ("ID"), CONSTRAINT TEST_ROLE_CORE_NAME UNIQUE ("TENANT_CODE", "ROLE_NAME", "DATA_STATE") ); +CREATE INDEX IDX_ROLE_TENANT_CODE ON TEST_ROLE_CORE_INFO (TENANT_CODE); + COMMENT ON TABLE "TEST_ROLE_CORE_INFO" IS '角色信息表(pkg:permission)'; COMMENT ON COLUMN "TEST_ROLE_CORE_INFO"."ID" IS '角色ID'; COMMENT ON COLUMN "TEST_ROLE_CORE_INFO"."TENANT_CODE" IS '租户编号'; @@ -83,7 +92,7 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "ID" VARCHAR2(50) NOT NULL, "TENANT_CODE" VARCHAR2(50) DEFAULT '0' NOT NULL, "USER_TYPE" NUMBER(3) DEFAULT 1 NOT NULL, - "DEPT_CODE" VARCHAR2(50) DEFAULT '0' NOT NULL, + "DEPT_ID" VARCHAR2(50) DEFAULT '0' NOT NULL, "USER_CODE" VARCHAR2(50), "USER_NAME" VARCHAR2(50), "NICK_NAME" VARCHAR2(50), @@ -92,6 +101,7 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "EMAIL" VARCHAR2(50), "GENDER" NUMBER(3) NOT NULL, "PHOTO" VARCHAR2(120), + "BIRTHDAY" DATE, "CITY" VARCHAR2(50), "IDENTITY" VARCHAR2(20), "PASSWORD" VARCHAR2(120), @@ -103,12 +113,13 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "DATA_STATE" NUMBER(10) DEFAULT 1 NOT NULL, CONSTRAINT TEST_USER_CORE_PK PRIMARY KEY ("ID") ); +CREATE INDEX IDX_USER_TENANT_CODE ON TEST_USER_CORE_INFO (TENANT_CODE); COMMENT ON TABLE "TEST_USER_CORE_INFO" IS '用户基础信息表(pkg:personnel)'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."ID" IS '用户ID'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."TENANT_CODE" IS '租户编号'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."USER_TYPE" IS '用户类型:1.管理员|2.用户(1.admin|2.user)'; -COMMENT ON COLUMN "TEST_USER_CORE_INFO"."DEPT_CODE" IS '部门编号'; +COMMENT ON COLUMN "TEST_USER_CORE_INFO"."DEPT_ID" IS '部门ID'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."USER_CODE" IS '账号/工号'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."USER_NAME" IS '登录用户名'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."NICK_NAME" IS '昵称'; @@ -117,6 +128,7 @@ COMMENT ON COLUMN "TEST_USER_CORE_INFO"."PHONE" IS '电话'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."EMAIL" IS '邮箱'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."GENDER" IS '性别:0.未知|1.男|2.女(gender:0.unknown|1.male|2.female)'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."PHOTO" IS '头像'; +COMMENT ON COLUMN "TEST_USER_CORE_INFO"."BIRTHDAY" IS '生日'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."CITY" IS '城市'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."IDENTITY" IS '身份证'; COMMENT ON COLUMN "TEST_USER_CORE_INFO"."PASSWORD" IS '密码'; @@ -162,6 +174,8 @@ CREATE TABLE "TEST_DEPARTMENT_CORE_INFO" ( CONSTRAINT "TEST_DEPT_CORE_CODE" UNIQUE ("TENANT_CODE", "DEPT_CODE", "DATA_STATE"), CONSTRAINT "TEST_DEPT_CORE_NAME" UNIQUE ("TENANT_CODE", "PARENT_CODE", "DEPT_NAME", "DATA_STATE") ); +CREATE INDEX IDX_DEPT_TENANT_CODE ON TEST_DEPARTMENT_CORE_INFO (TENANT_CODE); + COMMENT ON TABLE "TEST_DEPARTMENT_CORE_INFO" IS '部门信息表(pkg:personnel)'; COMMENT ON COLUMN "TEST_DEPARTMENT_CORE_INFO"."ID" IS '角色ID'; COMMENT ON COLUMN "TEST_DEPARTMENT_CORE_INFO"."TENANT_CODE" IS '租户编号'; diff --git a/jdbc-test/src/test/resources/settings/dbinit/create.tables.sqlite.sql b/jdbc-test/src/test/resources/settings/dbinit/create.tables.sqlite.sql index f3793102f3952a65f79fcb92b410ed13fe3c9559..8054019ee030f25ca7b2c23ab5ab564d99f10ed9 100644 --- a/jdbc-test/src/test/resources/settings/dbinit/create.tables.sqlite.sql +++ b/jdbc-test/src/test/resources/settings/dbinit/create.tables.sqlite.sql @@ -7,6 +7,7 @@ DROP TABLE "TEST_DEPARTMENT_CORE_INFO"; CREATE TABLE TEST_SETTING ( ID VARCHAR(50) NOT NULL, + TENANT_CODE VARCHAR(50) DEFAULT '0' NOT NULL, NAME VARCHAR(30) NOT NULL, VALUE VARCHAR(50) NOT NULL, VERSION INTEGER(8) DEFAULT 1 NOT NULL, @@ -16,19 +17,23 @@ CREATE TABLE TEST_SETTING ( UPDATE_TIME TIMESTAMP, DATA_STATE INTEGER(10) DEFAULT 1 NOT NULL, CONSTRAINT TEST_SETTING_PK PRIMARY KEY (ID), - CONSTRAINT TEST_SETTING_NAME UNIQUE (NAME,DATA_STATE) + CONSTRAINT TEST_SETTING_NAME UNIQUE (NAME,TENANT_CODE,DATA_STATE) ); +CREATE INDEX IDX_SETTING_TENANT_CODE ON TEST_SETTING (TENANT_CODE); +CREATE INDEX IDX_SETTING_NAME ON TEST_SETTING (NAME); CREATE TABLE TEST_LOGGER ( ID VARCHAR(50) NOT NULL, + TENANT_CODE VARCHAR(50) DEFAULT '0' NOT NULL, NAME VARCHAR(30) NOT NULL, - CONTENT VARCHAR(4000) NOT NULL, + CONTENT TEXT NULL, SORT_INDEX INTEGER(8) DEFAULT 1 NOT NULL, CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, DATA_STATE INTEGER(10) DEFAULT 1 NOT NULL, CONSTRAINT TEST_LOGGER_PK PRIMARY KEY (ID) ); +CREATE INDEX IDX_LOGGER_TENANT_CODE ON TEST_LOGGER (TENANT_CODE); CREATE TABLE "TEST_ROLE_CORE_INFO" ( @@ -46,13 +51,14 @@ CREATE TABLE "TEST_ROLE_CORE_INFO" ( CONSTRAINT TEST_ROLE_CORE_PK PRIMARY KEY ("ID"), CONSTRAINT TEST_ROLE_CORE_NAME UNIQUE ("TENANT_CODE", "ROLE_NAME", "DATA_STATE") ); +CREATE INDEX IDX_ROLE_TENANT_CODE ON TEST_ROLE_CORE_INFO(TENANT_CODE); CREATE TABLE "TEST_USER_CORE_INFO" ( "ID" VARCHAR(50) NOT NULL, "TENANT_CODE" VARCHAR(50) DEFAULT '0' NOT NULL, "USER_TYPE" INTEGER(3) DEFAULT 1 NOT NULL, - "DEPT_CODE" VARCHAR(50) DEFAULT '0' NOT NULL, + "DEPT_ID" VARCHAR(50) DEFAULT '0' NOT NULL, "USER_CODE" VARCHAR(50), "USER_NAME" VARCHAR(50), "NICK_NAME" VARCHAR(50), @@ -61,10 +67,11 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "EMAIL" VARCHAR(50), "GENDER" TINYINT(3) NOT NULL, "PHOTO" VARCHAR(120), + "BIRTHDAY" DATE, "CITY" VARCHAR(50), "IDENTITY" VARCHAR(20), "PASSWORD" VARCHAR(120), - "CREATE_TIME" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, + "CREATE_TIME" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, "SUPERMAN" TINYINT(1) DEFAULT 0 NOT NULL, "USER_STATE" TINYINT(3) DEFAULT 0 NOT NULL, "USER_SOURCE" TINYINT(3) DEFAULT 0 NOT NULL, @@ -72,6 +79,7 @@ CREATE TABLE "TEST_USER_CORE_INFO" ( "DATA_STATE" INTEGER(10) DEFAULT 1 NOT NULL, CONSTRAINT TEST_USER_CORE_PK PRIMARY KEY ("ID") ); +CREATE INDEX IDX_USER_TENANT_CODE ON TEST_USER_CORE_INFO(TENANT_CODE); CREATE TABLE "TEST_USER_ROLE_REF" ( @@ -102,3 +110,4 @@ CREATE TABLE "TEST_DEPARTMENT_CORE_INFO" ( CONSTRAINT "TEST_DEPT_CORE_CODE" UNIQUE ("TENANT_CODE", "DEPT_CODE", "DATA_STATE"), CONSTRAINT "TEST_DEPT_CORE_NAME" UNIQUE ("TENANT_CODE", "PARENT_CODE", "DEPT_NAME", "DATA_STATE") ); +CREATE INDEX IDX_DEPT_TENANT_CODE ON TEST_DEPARTMENT_CORE_INFO(TENANT_CODE); diff --git a/jdbc-test/src/test/resources/settings/spring/base.xml b/jdbc-test/src/test/resources/settings/spring/base.xml new file mode 100644 index 0000000000000000000000000000000000000000..4247ab6269b36758b94fc5f229745c570d2f18f7 --- /dev/null +++ b/jdbc-test/src/test/resources/settings/spring/base.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + classpath:settings/spring/druid.properties + classpath:settings/spring/qdbc.properties + classpath:test.properties + classpath:jdbc.*.properties + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jdbc-test/src/test/resources/settings/spring/datasource-sys.xml b/jdbc-test/src/test/resources/settings/spring/datasource-sys.xml index cc4aecfdac4cb270d827d3b56cc8429c561d1b93..de917f20ef724dd827f0dff41b5ebb0e613e25d1 100644 --- a/jdbc-test/src/test/resources/settings/spring/datasource-sys.xml +++ b/jdbc-test/src/test/resources/settings/spring/datasource-sys.xml @@ -21,38 +21,6 @@ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> - - - - - - - - - - - - - - @@ -71,11 +39,7 @@ - - - - diff --git a/jdbc-test/src/test/resources/settings/spring/druid.xml b/jdbc-test/src/test/resources/settings/spring/druid.xml new file mode 100644 index 0000000000000000000000000000000000000000..e31db5854389e26d5d972d7f9175333c18c37a1b --- /dev/null +++ b/jdbc-test/src/test/resources/settings/spring/druid.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jdbc-test/src/test/resources/settings/spring/hikari.xml b/jdbc-test/src/test/resources/settings/spring/hikari.xml new file mode 100644 index 0000000000000000000000000000000000000000..dd79255df79a44579a722e9d9cfb39509325b1ac --- /dev/null +++ b/jdbc-test/src/test/resources/settings/spring/hikari.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jdbc-test/src/test/resources/settings/spring/qdbc.properties b/jdbc-test/src/test/resources/settings/spring/qdbc.properties index 8aae93803be83a94bea55e81851e2085c726038e..2e5eff92cfe0de9045a543f9a8e17b47ce5be959 100644 --- a/jdbc-test/src/test/resources/settings/spring/qdbc.properties +++ b/jdbc-test/src/test/resources/settings/spring/qdbc.properties @@ -17,6 +17,8 @@ qdbc.entityData.logicalDeleteClass = com.gitee.qdbp.jdbc.test.enums.DataState qdbc.entityData.dataEffectiveFlag = NORMAL ## 逻辑删除数据无效标记 qdbc.entityData.dataIneffectiveFlag = DELETED +## 生成SQL时, 表名和列名是否使用大写 +qdbc.naming.underlineUseUpperCase = true ## 公共包名 # qdbc.entityData.commonPackages = com.xxx.beans,com.yyy.beans ## 公共字段名 @@ -39,12 +41,16 @@ qdbc.entityData.dataIneffectiveFlag = DELETED ## 如果启动时不扫描, 则首次获取SQL模板时会扫描(很慢) ## 所以, 一般情况下都应该配置为true, 除非系统中未使用SQL模板 qdbc.sql.template.scanOnStartup = true -## 存放SQL模板的文件夹 (多个文件夹以逗号分隔) -qdbc.sql.template.folder = settings/sqls/ -## SQL模板文件后缀 -qdbc.sql.template.filter = *.sql +## SQL模板扫描路径 (多个配置以逗号分隔) +qdbc.sql.template.pattern = settings/sqls/**/*.sql, settings/sqls/**/*.xml, com/gitee/qdbp/jdbc/test/**/*Mapper.xml ## SQL标签库配置文件路径 -qdbc.sql.taglibPath = classpath:settings/dbtags/taglib.txt +qdbc.sql.taglibPath = classpath:settings/qdbc/qdbc.taglib.txt +## 是否允许通过fragmentId获取SQL片断 +## 如果不允许通过fragmentId获取SQL片断, 那么只能通过 fileName:fragmentId 获取SQL片断 +## 例如 global.recursive.sql 中存在 recursive.list.children.query 片断 +## 如果不允许, 那么只能通过 global.recursive:recursive.list.children.query 获取 +## 否则 global.recursive:recursive.list.children.query 和 recursive.list.children.query 都可以 +qdbc.sql.template.useFragmentIdQuery = true ## IN语句日志采样的省略策略 (配置为0时不启用省略) ## 配置为50:5时表示超过50项时省略, 首末各保留5项 @@ -54,13 +60,14 @@ qdbc.batch.sql.omitStrategy = 8:3 ## 批量操作处理类的数据库支持版本配置 # 一个INSERT对应多个VALUES的批量新增接口 (SqlServer需要2008以上版本) -qdbc.supports.BatchInsertByMultiRowsExecutor=mysql,mariadb,db2,sqlserver.2008,sqlite.3 -# UNION ALL FROM DUAL批量新增接口 -qdbc.supports.BatchInsertByUnionAllFromDualExecutor=oracle +qdbc.supports.BatchInsertByMultiRowsExecutor=mysql,mariadb,db2,h2,sqlserver.2008,sqlite.3 # UPDATE JOIN USING 批量更新接口 qdbc.supports.BatchUpdateByJoinUsingExecutor=mysql,mariadb # UPDATE CASE WHEN 批量更新接口 -qdbc.supports.BatchUpdateByCaseWhenExecutor=mysql,mariadb,oracle,db2 +qdbc.supports.BatchUpdateByCaseWhenExecutor=mysql,mariadb,db2,h2 +# 生成多个SQL一起执行 +# 解决CLOB字段批量新增时报错ORA-01790的问题 https://www.yuque.com/zhaohuihua/qdbc/mlxkha +qdbc.supports.BatchOperateByMultiSqlExecutor=oracle ## 递归语法的关键字 # MySQL8,PostgreSQL,SQLite的是WITH RECURSIVE; DB2,SqlServer的是WITH diff --git a/jdbc-test/src/test/resources/settings/spring/qdbc.xml b/jdbc-test/src/test/resources/settings/spring/qdbc.xml index 8fca8b635c41880a506c0bfcdc6fca002d70380b..361d2e90ea710aa5e05e625bb34edb1cbebf7ae4 100644 --- a/jdbc-test/src/test/resources/settings/spring/qdbc.xml +++ b/jdbc-test/src/test/resources/settings/spring/qdbc.xml @@ -23,8 +23,13 @@ - - + + + + + + + @@ -32,12 +37,32 @@ com.gitee.qdbp.jdbc.model.MainDbType + + + + + + + + + + + + + + + + + + + + @@ -46,6 +71,9 @@ + + + @@ -53,9 +81,6 @@ - - - @@ -122,16 +147,20 @@ + + + - - - + + + - - - + + + + diff --git a/jdbc-test/src/test/resources/settings/spring/spring.xml b/jdbc-test/src/test/resources/settings/spring/spring.xml index 3f1ea22f017c78321d7bf1bd87f4d296244906a5..b56b8dd4110874d488acf2ea7141d41a1e2a9ca1 100644 --- a/jdbc-test/src/test/resources/settings/spring/spring.xml +++ b/jdbc-test/src/test/resources/settings/spring/spring.xml @@ -1,64 +1,11 @@ - - - - - - - - - - - - - - - - - - - - classpath:settings/spring/druid.properties - classpath:settings/spring/qdbc.properties - classpath:test.properties - classpath:jdbc.*.properties - - - - - - - - - - - - - - - - - - - - - - + + + + + diff --git a/jdbc-test/src/test/resources/testng.xml b/jdbc-test/src/test/resources/testng.xml index 2be949bc79de8b9ec32223f5bd7d9d50c19fef0d..25117a6ef89cda43db0e69c85a45f1e4125c4b85 100644 --- a/jdbc-test/src/test/resources/testng.xml +++ b/jdbc-test/src/test/resources/testng.xml @@ -4,9 +4,13 @@ - + + + + +