From a4a11fcc19d9877c44c09496c847b4b334db4c9c Mon Sep 17 00:00:00 2001 From: mazhicheng Date: Sat, 18 Apr 2020 16:08:03 +0800 Subject: [PATCH] =?UTF-8?q?+=20=E5=8A=A8=E6=80=81Join=E5=85=B3=E8=81=94?= =?UTF-8?q?=E7=9A=84=E7=BB=91=E5=AE=9A=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- diboot-core-starter/pom.xml | 6 +- diboot-core/README.md | 149 +++++++----- diboot-core/pom.xml | 15 +- .../java/com/diboot/core/binding/Binder.java | 108 +++++++++ .../com/diboot/core/binding/JoinsBinder.java | 221 ++++++++++++++++++ .../com/diboot/core/binding/QueryBuilder.java | 175 ++++++++++---- .../diboot/core/binding/RelationsBinder.java | 2 +- .../core/binding/parser/ConditionManager.java | 3 +- .../core/binding/parser/ConditionParser.java | 20 ++ .../core/binding/parser/MiddleTable.java | 39 ++-- .../core/binding/parser/ParserCache.java | 163 ++++++++++++- .../diboot/core/binding/query/BindQuery.java | 13 ++ .../binding/query/dynamic/AnnoJoiner.java | 89 +++++++ .../dynamic/DynamicJoinQueryWrapper.java | 85 +++++++ .../query/dynamic/DynamicSqlProvider.java | 137 +++++++++++ .../query/dynamic/ExtQueryWrapper.java | 97 ++++++++ .../query/dynamic/JoinConditionParser.java | 163 +++++++++++++ .../java/com/diboot/core/config/Cons.java | 4 + .../core/controller/BaseController.java | 11 +- .../com/diboot/core/entity/BaseEntity.java | 2 +- .../core/mapper/DynamicQueryMapper.java | 60 +++++ .../core/service/impl/BaseServiceImpl.java | 10 +- .../com/diboot/core/util/SqlExecutor.java | 2 +- .../src/main/java/com/diboot/core/util/V.java | 2 +- .../java/com/diboot/core/vo/Pagination.java | 7 +- .../core/test/binder/TestEntityBinder.java | 6 +- .../test/binder/TestEntityListBinder.java | 8 +- .../core/test/binder/TestFieldBinder.java | 6 +- .../core/test/binder/TestJoinQuery.java | 171 ++++++++++++++ .../core/test/binder/dto/DepartmentDTO.java | 58 +++++ .../core/test/binder/entity/Department.java | 3 + .../binder/service/DepartmentService.java | 6 + .../service/impl/DepartmentServiceImpl.java | 20 ++ .../core/test/binder/vo/DepartmentVO.java | 14 ++ .../core/test/service/BaseServiceTest.java | 8 +- diboot-core/src/test/resources/init-mysql.sql | 2 +- diboot-docs/guide/diboot-core/无SQL关联.md | 6 +- diboot-file-starter/pom.xml | 13 +- .../java/com/diboot/file/util/HttpHelper.java | 17 +- iam-base-starter/pom.xml | 8 +- .../iam/service/impl/IamUserServiceImpl.java | 4 +- pom.xml | 16 +- 42 files changed, 1735 insertions(+), 214 deletions(-) create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/Binder.java create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/JoinsBinder.java create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/AnnoJoiner.java create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicJoinQueryWrapper.java create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/ExtQueryWrapper.java create mode 100644 diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/JoinConditionParser.java create mode 100644 diboot-core/src/main/java/com/diboot/core/mapper/DynamicQueryMapper.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/TestJoinQuery.java create mode 100644 diboot-core/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java diff --git a/diboot-core-starter/pom.xml b/diboot-core-starter/pom.xml index f9ea81e..c049326 100644 --- a/diboot-core-starter/pom.xml +++ b/diboot-core-starter/pom.xml @@ -7,11 +7,11 @@ com.diboot diboot-root - 2.0.5 + 2.0.6 diboot-core-spring-boot-starter - 2.0.5 + 2.0.6 jar diboot core starter project @@ -25,7 +25,7 @@ com.diboot diboot-core - 2.0.5 + 2.0.6 diff --git a/diboot-core/README.md b/diboot-core/README.md index ac1243a..eacb6da 100644 --- a/diboot-core/README.md +++ b/diboot-core/README.md @@ -1,19 +1,20 @@ -# diboot-core: 全新优化内核 +## diboot-core: 全新优化内核 主要实现: 1. 单表CRUD和多表关联查询的无SQL化 -2. Entity/DTO自动转换为QueryWrapper(@BindQuery注解绑定字段参数对应的查询条件,无注解默认映射为等于=条件) +2. Entity/DTO自动转换为QueryWrapper(@BindQuery注解绑定字段参数的查询条件,可自动构建关联查询) 3. 提供其他常用开发场景的最佳实践封装。 -## ** 一. 单表CRUD无SQL +### ** 一. 单表CRUD无SQL > 依赖Mybatis-plus实现(Mybatis-plus具备通用Mapper方案和灵活的查询构造器) -## ** 二. 多表关联查询无SQL(通过注解绑定关联,自动拆分成单表查询并绑定结果) - > 通过注解实现多数场景下的关联查询无SQL -### 1. 注解自动绑定数据字典(自定义枚举)的显示值Label +### ** 二. 多表关联查询无SQL + > 通过@Bind*注解绑定关联,自动拆分成单表查询并绑定结果 + [(了解拆解关联查询的价值)](https://www.kancloud.cn/ddupl/sql_optimize/1141077) +#### 1. 注解自动绑定数据字典(自定义枚举)的显示值Label ~~~java @BindDict(type="USER_STATUS", field = "status") private String statusLabel; ~~~ -### 2. 注解自动绑定其他表的字段 +#### 2. 注解自动绑定其他表的字段 ~~~java // 支持关联条件+附加条件绑定字段 @BindField(entity=Department.class, field="name", condition="department_id=id AND parent_id>=0") @@ -23,7 +24,7 @@ private String deptName; @BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id") private String orgName; ~~~ -### 3. 注解自动绑定其他表实体Entity +#### 3. 注解自动绑定其他表实体Entity/VO ~~~java // 支持关联条件+附加条件绑定Entity @BindEntity(entity = Department.class, condition="department_id=id") @@ -33,7 +34,7 @@ private Department department; @BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.deleted=0") private Organization organization; ~~~ -### 4. 注解自动绑定其他表实体集合List +#### 4. 注解自动绑定其他表实体集合List ~~~java // 支持关联条件+附加条件绑定多个Entity @BindEntityList(entity = Department.class, condition = "id=parent_id") @@ -44,8 +45,82 @@ private List children; private List roleList; ~~~ -## ** 三. 注解绑定关联的使用方式 -### 1. 引入依赖 +### ** 三. 注解绑定关联的使用方式 + +#### 1. 定义你的Entity对应的Service(继承diboot的BaseService)及Mapper +> * 启用diboot-devtools,自动生成后端各层代码。 +#### 2. 参照以上注解说明在VO中定义你的关联 + +#### 3. 使用注解绑定: +调用Binder自动绑定注解相关关联: +##### 方式1. 自动绑定关联(不需要转型) +~~~java +//List voList = ...; +Binder.bindRelations(voList); +~~~ +##### 方式2. 自动转型并绑定关联(需要转型) +~~~java +// 查询单表获取Entity集合 +// List entityList = userService.list(queryWrapper); +List voList = Binder.convertAndBindRelations(userList, MyUserVO.class); +~~~ + +### ** 四. Entity/DTO自动转换QueryWrapper自动构建Join查询 +#### 1. Entity/DTO中声明映射查询条件 +示例代码: +~~~java +public class UserDTO{ + // 无@BindQuery注解默认会映射为=条件 + private Long gender; + + // 有注解,映射为注解指定条件 + @BindQuery(comparison = Comparison.LIKE) + private String realname; + + // join其他表 + @BindQuery(comparison = Comparison.STARTSWITH, entity=Organization.class, field="name", condition="this.org_id=id") + private String orgName; +} +~~~ +#### 2. 调用QueryBuilder.toQueryWrapper(entityOrDto)进行转换 +~~~java +/** + * url参数示例: /list?gender=M&realname=张 + * 将映射为 queryWrapper.eq("gender", "M").like("realname", "张") + */ +@GetMapping("/list") +public JsonResult getVOList(UserDto userDto, HttpServletRequest request) throws Exception{ + //调用super.buildQueryWrapper(entityOrDto, request) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换 + QueryWrapper queryWrapper = super.buildQueryWrapper(userDto, request); + // 或者 + //QueryWrapper queryWrapper = QueryBuilder.buildQueryWrapper(userDto); + + //... 查询list + return JsonResult.OK(list); +} +~~~ + +#### 3. 动态Join的关联查询与结果绑定 +> 动态查询的调用方式有以下两种: +##### 方式1. 通过QueryBuilder链式调用 +~~~java +QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(Department.class); +~~~ +##### 方式2. 通过QueryBuilder构建QueryWrapper,再调用Binder或JoinsBinder +~~~java +// 构建QueryWrapper +QueryWrapper queryWrapper = QueryBuilder.toQueryWrapper(dto); +// 调用join关联查询绑定 +List list = Binder.joinQueryList(queryWrapper, Department.class); +~~~ + +自动按需构建类似如下动态SQL并绑定结果: +> SELECT self.* FROM user self +LEFT OUTER JOIN organization r1 ON self.org_id=r1.id +WHERE (r1.name LIKE ?) AND self.is_deleted=0 + +### 五. 使用步骤与样例参考 +#### 1. 引入依赖 Gradle: ~~~gradle compile("com.diboot:diboot-core-spring-boot-starter:2.0.5") @@ -58,53 +133,7 @@ compile("com.diboot:diboot-core-spring-boot-starter:2.0.5") 2.0.5 ~~~ -> * 使用diboot-devtools,会自动引入diboot-core,无需配置此依赖。 -> * @BindDict注解需要依赖dictionary表,初次启动时starter会自动创建该表。 +> * @BindDict注解需要依赖dictionary表,启用diboot-devtools,初次启动时starter会自动创建该表。 -### 2. 定义你的Service(继承diboot的BaseService或Mybatis-plus的ISerivice)及Mapper - -### 3. 使用注解绑定: -调用RelationsBinder自动绑定注解相关关联: -#### 方式1. 自动绑定关联(不需要转型) -~~~java -//List voList = ...; -RelationsBinder.bind(voList); -~~~ -#### 方式2. 自动转型并绑定关联(需要转型) -~~~java -// 查询单表获取Entity集合 -// List entityList = userService.list(queryWrapper); -List voList = RelationsBinder.convertAndBind(userList, MyUserVO.class); -~~~ - -## ** 四. Entity/DTO自动转换为QueryWrapper的使用方式 -### 1. Entity/DTO中声明映射查询条件 -示例代码: -~~~java -public class UserDTO{ - // 无@BindQuery注解默认会映射为=条件 - private Long gender; - - // 有注解,映射为注解指定条件 - @BindQuery(comparison = Comparison.LIKE) - private String realname; - - //... getter, setter -} -~~~ -### 2. 调用QueryBuilder.toQueryWrapper(entityOrDto)进行转换 -~~~java -/** - * url参数示例: /list?gender=M&realname=张 - * 将映射为 queryWrapper.eq("gender", "M").like("realname", "张") - */ -@GetMapping("/list") -public JsonResult getVOList(UserDto userDto, HttpServletRequest request) throws Exception{ - //调用super.buildQueryWrapper(entityOrDto, request) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换 - QueryWrapper queryWrapper = super.buildQueryWrapper(userDto, request); - //... 查询list - return JsonResult.OK(list); -} -~~~ - -## 五. 样例参考 - [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example) +#### 2. 详细文档 - [diboot-core 官方文档](https://www.diboot.com/guide/diboot-core/%E5%AE%89%E8%A3%85.html) +#### 3. 参考样例 - [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example) diff --git a/diboot-core/pom.xml b/diboot-core/pom.xml index 0ace9e4..6eed697 100644 --- a/diboot-core/pom.xml +++ b/diboot-core/pom.xml @@ -7,11 +7,11 @@ com.diboot diboot-root - 2.0.5 + 2.0.6 diboot-core - 2.0.5 + 2.0.6 jar diboot core project @@ -36,6 +36,17 @@ src/main/resources + + src/test/java + + **/*.xml + **/*.dtd + + false + + + src/test/resources + diff --git a/diboot-core/src/main/java/com/diboot/core/binding/Binder.java b/diboot-core/src/main/java/com/diboot/core/binding/Binder.java new file mode 100644 index 0000000..e58d74d --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/Binder.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.vo.Pagination; + +import java.util.List; + +/** + * 绑定器统一调用入口类 + * @author Mazc@dibo.ltd + * @version v2.0 + * @date 2020/04/18 + */ +public class Binder { + + /** + * 关联查询一条数据 + * @param queryWrapper + * @param entityClazz 返回结果entity/vo类 + * @return + * @throws Exception + */ + public static E joinQueryOne(QueryWrapper queryWrapper, Class entityClazz){ + return JoinsBinder.queryOne(queryWrapper, entityClazz); + } + + /** + * 关联查询符合条件的全部数据集合(不分页) + * @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例 + * @param entityClazz 返回结果entity/vo类 + * @return + * @throws Exception + */ + public static List joinQueryList(QueryWrapper queryWrapper, Class entityClazz){ + return JoinsBinder.queryList(queryWrapper, entityClazz); + } + + /** + * 关联查询符合条件的指定页数据(分页) + * @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例 + * @param entityClazz 返回结果entity/vo类 + * @param pagination 分页 + * @return + * @throws Exception + */ + public static List joinQueryList(QueryWrapper queryWrapper, Class entityClazz, Pagination pagination){ + return JoinsBinder.queryList(queryWrapper, entityClazz, pagination); + } + + /** + * 自动转换和绑定单个VO中的注解关联(禁止循环调用,多个对象请调用convertAndBind(voList, voClass)) + * @param voClass 需要转换的VO class + * @param + * @param + * @return + */ + public static VO convertAndBindRelations(E entity, Class voClass){ + return RelationsBinder.convertAndBind(entity, voClass); + } + + /** + * 自动转换和绑定多个VO中的注解关联 + * @param entityList 需要转换的VO list + * @param voClass VO class + * @param + * @param + * @return + */ + public static List convertAndBindRelations(List entityList, Class voClass){ + return RelationsBinder.convertAndBind(entityList, voClass); + } + + /** + * 自动绑定单个VO的关联对象(禁止循环调用,多个对象请调用bind(voList)) + * @param vo 需要注解绑定的对象 + * @return + * @throws Exception + */ + public static void bindRelations(VO vo){ + RelationsBinder.bind(vo); + } + + /** + * 自动绑定多个VO集合的关联对象 + * @param voList 需要注解绑定的对象集合 + * @return + * @throws Exception + */ + public static void bindRelations(List voList){ + RelationsBinder.bind(voList); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/JoinsBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/JoinsBinder.java new file mode 100644 index 0000000..6376647 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/JoinsBinder.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.diboot.core.binding.parser.ParserCache; +import com.diboot.core.binding.query.dynamic.AnnoJoiner; +import com.diboot.core.binding.query.dynamic.DynamicJoinQueryWrapper; +import com.diboot.core.config.BaseConfig; +import com.diboot.core.exception.BusinessException; +import com.diboot.core.mapper.DynamicQueryMapper; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.ContextHelper; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import com.diboot.core.vo.Pagination; +import com.diboot.core.vo.Status; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * join连接查询绑定器 + * @author Mazc@dibo.ltd + * @version v2.0.5 + * @date 2020/04/15 + */ +@Slf4j +public class JoinsBinder { + + /** + * 关联查询一条数据 + * @param queryWrapper + * @param entityClazz 返回结果entity/vo类 + * @return + * @throws Exception + */ + public static E queryOne(QueryWrapper queryWrapper, Class entityClazz){ + List list = executeJoinQuery(queryWrapper, entityClazz, null, true); + if(V.notEmpty(list)){ + return list.get(0); + } + return null; + } + + /** + * 关联查询符合条件的全部数据集合(不分页) + * @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例 + * @param entityClazz 返回结果entity/vo类 + * @return + * @throws Exception + */ + public static List queryList(QueryWrapper queryWrapper, Class entityClazz){ + return queryList(queryWrapper, entityClazz, null); + } + + /** + * 关联查询符合条件的指定页数据(分页) + * @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例 + * @param entityClazz 返回结果entity/vo类 + * @param pagination 分页 + * @return + * @throws Exception + */ + public static List queryList(QueryWrapper queryWrapper, Class entityClazz, Pagination pagination){ + return executeJoinQuery(queryWrapper, entityClazz, pagination, false); + } + + /** + * 关联查询(分页) + * @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例 + * @param entityClazz 返回结果entity/vo类 + * @param pagination 分页 + * @return + * @throws Exception + */ + private static List executeJoinQuery(QueryWrapper queryWrapper, Class entityClazz, Pagination pagination, boolean limit1){ + // 非动态查询,走BaseService + if(queryWrapper instanceof DynamicJoinQueryWrapper == false){ + BaseService baseService = ContextHelper.getBaseServiceByEntity(entityClazz); + if(baseService != null){ + return baseService.getEntityList(queryWrapper, pagination); + } + else{ + throw new BusinessException(Status.FAIL_INVALID_PARAM, "单表查询对象无BaseService实现: "+entityClazz.getSimpleName()); + } + } + long begin = System.currentTimeMillis(); + // 转换为queryWrapper + DynamicJoinQueryWrapper dynamicJoinWrapper = (DynamicJoinQueryWrapper)queryWrapper; + dynamicJoinWrapper.setMainEntityClass(entityClazz); + List> mapList = null; + if(pagination == null){ + if(limit1){ + Map oneResult = getDynamicQueryMapper().query(dynamicJoinWrapper); + if(oneResult != null){ + mapList = new ArrayList<>(); + mapList.add(oneResult); + } + } + else{ + mapList = getDynamicQueryMapper().queryForList(dynamicJoinWrapper); + } + } + else{ + // 格式化orderBy + formatOrderBy(dynamicJoinWrapper, pagination); + IPage> pageResult = getDynamicQueryMapper().queryForListWithPage(pagination.toPage(), dynamicJoinWrapper); + pagination.setTotalCount(pageResult.getTotal()); + mapList = pageResult.getRecords(); + } + long ms = (System.currentTimeMillis() - begin); + if(ms > 5000){ + log.warn("{} 动态Join查询执行耗时 {} ms,建议优化", dynamicJoinWrapper.getDtoClass().getSimpleName(), ms); + } + if(V.isEmpty(mapList)){ + return Collections.emptyList(); + } + if(mapList.size() > BaseConfig.getBatchSize()){ + log.warn("{} 动态Join查询记录数过大( {} 条), 建议优化", dynamicJoinWrapper.getDtoClass().getSimpleName(), mapList.size()); + } + // 转换查询结果 + List entityList = new ArrayList<>(); + for(Map colValueMap : mapList){ + Map fieldValueMap = new HashMap<>(); + // 格式化map + for(Map.Entry entry : colValueMap.entrySet()){ + String fieldName = S.toLowerCaseCamel(entry.getKey()); + // 如果是布尔类型,检查entity中的定义是Boolean/boolean + if(entry.getValue() instanceof Boolean && S.startsWithIgnoreCase(entry.getKey(),"is_")){ + // 检查有is前缀的Boolean类型 + Field boolType = BeanUtils.extractField(entityClazz, fieldName); + if(boolType == null){ + // 检查无is前缀的boolean类型 + String tempFieldName = S.toLowerCaseCamel(S.substringAfter(entry.getKey(), "_")); + boolType = BeanUtils.extractField(entityClazz, tempFieldName); + if(boolType != null){ + fieldName = tempFieldName; + } + } + } + fieldValueMap.put(fieldName, entry.getValue()); + } + // 绑定map到entity + try{ + E entityInst = entityClazz.newInstance(); + BeanUtils.bindProperties(entityInst, fieldValueMap); + entityList.add(entityInst); + } + catch (Exception e){ + log.warn("new实例并绑定属性值异常", e); + } + } + return entityList; + } + + /** + * 格式化orderBy + * @param queryWrapper + * @param pagination + */ + private static void formatOrderBy(DynamicJoinQueryWrapper queryWrapper, Pagination pagination){ + if(V.notEmpty(pagination.getOrderBy())){ + List orderByList = new ArrayList<>(); + String[] orderByFields = S.split(pagination.getOrderBy()); + for(String field : orderByFields){ + String fieldName = field, orderType = null; + if(field.contains(":")){ + String[] fieldAndOrder = S.split(field, ":"); + fieldName = fieldAndOrder[0]; + orderType = fieldAndOrder[1]; + } + // 获取列定义的AnnoJoiner 得到别名 + AnnoJoiner joiner = ParserCache.getAnnoJoiner(queryWrapper.getDtoClass(), fieldName); + if(joiner != null){ + if(V.notEmpty(joiner.getAlias())){ + fieldName = joiner.getAlias() + "." + joiner.getColumnName(); + } + else{ + fieldName = "self." + joiner.getColumnName(); + } + } + else{ + fieldName = "self." + S.toSnakeCase(fieldName); + } + if(V.notEmpty(orderType)){ + orderByList.add(fieldName + ":" + orderType); + } + else{ + orderByList.add(fieldName); + } + } + pagination.setOrderBy(S.join(orderByList)); + } + } + + /** + * 获取mapper实例 + * @return + */ + private static DynamicQueryMapper getDynamicQueryMapper(){ + return ContextHelper.getBean(DynamicQueryMapper.class); + } + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java b/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java index ea4e900..1039c3a 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/QueryBuilder.java @@ -18,8 +18,12 @@ package com.diboot.core.binding; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.binding.parser.ParserCache; import com.diboot.core.binding.query.BindQuery; import com.diboot.core.binding.query.Comparison; +import com.diboot.core.binding.query.dynamic.AnnoJoiner; +import com.diboot.core.binding.query.dynamic.DynamicJoinQueryWrapper; +import com.diboot.core.binding.query.dynamic.ExtQueryWrapper; import com.diboot.core.util.BeanUtils; import com.diboot.core.util.S; import com.diboot.core.util.V; @@ -29,10 +33,12 @@ import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; /** - * QueryWrapper构建器 - Entity,DTO -> 注解绑定查询条件 并转换为QueryWrapper对象 + * QueryWrapper构建器 * @author mazc@dibo.ltd * @version v2.0 * @date 2019/07/27 @@ -43,97 +49,123 @@ public class QueryBuilder { /** * Entity或者DTO对象转换为QueryWrapper * @param dto - * @param * @param * @return */ - public static QueryWrapper toQueryWrapper(DTO dto){ - QueryWrapper wrapper = new QueryWrapper<>(); - return (QueryWrapper) dtoToWrapper(wrapper, dto, null); + public static QueryWrapper toQueryWrapper(DTO dto){ + return dtoToWrapper(dto, null); } /** * Entity或者DTO对象转换为QueryWrapper * @param dto * @param fields 指定参与转换的属性值 - * @param * @param * @return */ - public static QueryWrapper toQueryWrapper(DTO dto, Collection fields){ - QueryWrapper wrapper = new QueryWrapper<>(); - return (QueryWrapper) dtoToWrapper(wrapper, dto, fields); + public static QueryWrapper toQueryWrapper(DTO dto, Collection fields){ + return dtoToWrapper(dto, fields); + } + + /** + * Entity或者DTO对象转换为QueryWrapper + * @param dto + * @param + * @return + */ + public static ExtQueryWrapper toDynamicJoinQueryWrapper(DTO dto){ + return toDynamicJoinQueryWrapper(dto, null); + } + + /** + * Entity或者DTO对象转换为QueryWrapper + * @param dto + * @param fields 指定参与转换的属性值 + * @param + * @return + */ + public static ExtQueryWrapper toDynamicJoinQueryWrapper(DTO dto, Collection fields){ + QueryWrapper queryWrapper = dtoToWrapper(dto, fields); + if(queryWrapper instanceof DynamicJoinQueryWrapper == false){ + return (ExtQueryWrapper)queryWrapper; + } + return (DynamicJoinQueryWrapper)queryWrapper; } /** * Entity或者DTO对象转换为LambdaQueryWrapper * @param dto - * @param * @return */ - public static LambdaQueryWrapper toLambdaQueryWrapper(DTO dto){ - return (LambdaQueryWrapper) toQueryWrapper(dto).lambda(); + public static LambdaQueryWrapper toLambdaQueryWrapper(DTO dto){ + return (LambdaQueryWrapper) toQueryWrapper(dto).lambda(); } - /** * Entity或者DTO对象转换为LambdaQueryWrapper * @param dto * @param fields 指定参与转换的属性值 - * @param * @return */ - public static LambdaQueryWrapper toLambdaQueryWrapper(DTO dto, Collection fields){ - return (LambdaQueryWrapper) toQueryWrapper(dto, fields).lambda(); + public static LambdaQueryWrapper toLambdaQueryWrapper(DTO dto, Collection fields){ + return (LambdaQueryWrapper) toQueryWrapper(dto, fields).lambda(); } /** * 转换具体实现 - * @param wrapper * @param dto - * @param * @return */ - private static QueryWrapper dtoToWrapper(QueryWrapper wrapper, DTO dto, Collection fields){ + private static QueryWrapper dtoToWrapper(DTO dto, Collection fields){ // 转换 - List declaredFields = BeanUtils.extractAllFields(dto.getClass()); - for (Field field : declaredFields) { - // 非指定属性,非逻辑删除字段,跳过 - if(fields != null && !fields.contains(field.getName())){ - continue; - } - //忽略static,以及final,transient - boolean isStatic = Modifier.isStatic(field.getModifiers()); - boolean isFinal = Modifier.isFinal(field.getModifiers()); - boolean isTransient = Modifier.isTransient(field.getModifiers()); - if(isStatic || isFinal || isTransient){ - continue; - } - //忽略注解 @TableField(exist = false) 的字段 - TableField tableField = field.getAnnotation(TableField.class); - if(tableField != null && tableField.exist() == false){ - continue; + LinkedHashMap fieldValuesMap = extractNotNullValues(dto, fields); + if(V.isEmpty(fieldValuesMap)){ + return new QueryWrapper(); + } + QueryWrapper wrapper; + // 是否有join联表查询 + boolean hasJoinTable = ParserCache.hasJoinTable(dto, fieldValuesMap.keySet()); + if(hasJoinTable){ + wrapper = new DynamicJoinQueryWrapper<>(dto.getClass(), fields); + } + else{ + wrapper = new ExtQueryWrapper<>(); + } + // 构建QueryWrapper + for(Map.Entry entry : fieldValuesMap.entrySet()){ + Field field = BeanUtils.extractField(dto.getClass(), entry.getKey()); + //单表场景,忽略注解 @TableField(exist = false) 的字段 + if(hasJoinTable == false){ + TableField tableField = field.getAnnotation(TableField.class); + if(tableField != null && tableField.exist() == false){ + continue; + } } + //忽略字段 BindQuery query = field.getAnnotation(BindQuery.class); - if(query != null && query.ignore()){ //忽略字段 - continue; - } - //打开私有访问 获取值 - field.setAccessible(true); - Object value = null; - try { - value = field.get(dto); - } - catch (IllegalAccessException e) { - log.error("通过反射获取属性值出错:" + e); - } - if(value == null){ + if(query != null && query.ignore()){ continue; } + Object value = entry.getValue(); // 对比类型 - Comparison comparison = (query != null)? query.comparison() : Comparison.EQ; + Comparison comparison = Comparison.EQ; // 转换条件 - String columnName = getColumnName(field); + String columnName = getColumnName(field);; + if(query != null){ + comparison = query.comparison(); + AnnoJoiner annoJoiner = ParserCache.getAnnoJoiner(dto.getClass(), entry.getKey()); + if(annoJoiner != null && V.notEmpty(annoJoiner.getJoin())){ + // 获取注解Table + columnName = annoJoiner.getAlias() + "." + annoJoiner.getColumnName(); + } + else if(hasJoinTable){ + columnName = "self."+columnName; + } + } + else if(hasJoinTable){ + columnName = "self."+columnName; + } + // 构建对象 switch (comparison) { case EQ: wrapper.eq(columnName, value); @@ -216,6 +248,9 @@ public class QueryBuilder { String columnName = null; if (field.isAnnotationPresent(BindQuery.class)) { columnName = field.getAnnotation(BindQuery.class).field(); + if(V.notEmpty(columnName)){ + columnName = S.toSnakeCase(columnName); + } } else if (field.isAnnotationPresent(TableField.class)) { columnName = field.getAnnotation(TableField.class).value(); @@ -223,4 +258,42 @@ public class QueryBuilder { return V.notEmpty(columnName) ? columnName : S.toSnakeCase(field.getName()); } + /** + * 提取非空字段及值 + * @param dto + * @param fields + * @param + * @return + */ + private static LinkedHashMap extractNotNullValues(DTO dto, Collection fields){ + LinkedHashMap resultMap = new LinkedHashMap<>(); + // 转换 + List declaredFields = BeanUtils.extractAllFields(dto.getClass()); + for (Field field : declaredFields) { + // 非指定属性,非逻辑删除字段,跳过 + if (fields != null && !fields.contains(field.getName())) { + continue; + } + //忽略static,以及final,transient + boolean isStatic = Modifier.isStatic(field.getModifiers()); + boolean isFinal = Modifier.isFinal(field.getModifiers()); + boolean isTransient = Modifier.isTransient(field.getModifiers()); + if (isStatic || isFinal || isTransient) { + continue; + } + //打开私有访问 获取值 + field.setAccessible(true); + Object value = null; + try { + value = field.get(dto); + } catch (IllegalAccessException e) { + log.error("通过反射获取属性值出错:" + e); + } + if (value != null) { + resultMap.put(field.getName(), value); + } + } + return resultMap; + } + } \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/binding/RelationsBinder.java b/diboot-core/src/main/java/com/diboot/core/binding/RelationsBinder.java index 0a0fc74..76cd365 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/RelationsBinder.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/RelationsBinder.java @@ -44,7 +44,7 @@ import java.util.List; import java.util.Map; /** - * 绑定管理器 + * 关联关系绑定管理器 * @author mazc@dibo.ltd * @version v2.0 * @date 2019/7/18 diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java index f561b52..f34b212 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionManager.java @@ -50,7 +50,7 @@ public class ConditionManager { * @param condition * @return */ - private static List getExpressionList(String condition){ + public static List getExpressionList(String condition){ if(V.isEmpty(condition)){ return null; } @@ -72,6 +72,7 @@ public class ConditionManager { /** * 附加条件到binder + * @param condition * @param binder * @throws Exception */ diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java index b143132..8528208 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ConditionParser.java @@ -123,6 +123,12 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor { } expressList.add(isNullExpression); } + + @Override + public void visit(IsBooleanExpression isBooleanExpression) { + + } + @Override public void visit(InExpression inExpression) { if(!(inExpression.getLeftExpression() instanceof Column)){ @@ -130,6 +136,12 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor { } expressList.add(inExpression); } + + @Override + public void visit(FullTextSearch fullTextSearch) { + + } + @Override public void visit(Between between) { if(!(between.getLeftExpression() instanceof Column)){ @@ -273,6 +285,10 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor { public void visit(SimilarToExpression aThis) { } + @Override + public void visit(ArrayExpression arrayExpression) { + } + @Override public void visit(BitwiseRightShift aThis) { } @@ -324,6 +340,10 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor { @Override public void visit(Division division) { } + + @Override + public void visit(IntegerDivision integerDivision) { + } @Override public void visit(Multiplication multiplication) { } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java index f31f751..ac8c3a4 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/MiddleTable.java @@ -20,6 +20,7 @@ import com.diboot.core.config.Cons; import com.diboot.core.util.S; import com.diboot.core.util.SqlExecutor; import com.diboot.core.util.V; +import org.apache.ibatis.jdbc.SQL; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -137,30 +138,26 @@ public class MiddleTable { if(V.isEmpty(annoObjectForeignKeyList)){ return null; } - // 构建SQL - StringBuilder sb = new StringBuilder(); - sb.append("SELECT ").append(this.equalsToAnnoObjectFKColumn).append(Cons.SEPARATOR_COMMA) - .append(this.equalsToRefEntityPkColumn).append(" FROM ").append(this.table) - .append(" WHERE ").append(this.equalsToAnnoObjectFKColumn).append(" IN ("); String params = S.repeat("?", ",", annoObjectForeignKeyList.size()); - sb.append(params).append(")"); - // 添加删除标记 - boolean appendDeleteFlag = true; - if(this.additionalConditions != null){ - for(String condition : this.additionalConditions){ - sb.append(" AND (").append(condition).append(")"); - if(S.containsIgnoreCase(condition, "is_" + Cons.FieldName.deleted.name())){ - appendDeleteFlag = false; + // 构建SQL + return new SQL(){{ + SELECT(equalsToAnnoObjectFKColumn + Cons.SEPARATOR_COMMA + equalsToRefEntityPkColumn); + FROM(table); + WHERE(equalsToAnnoObjectFKColumn + " IN (" + params + ")"); + // 添加删除标记 + boolean appendDeleteFlag = true; + if(additionalConditions != null){ + for(String condition : additionalConditions){ + WHERE(condition); + if(S.containsIgnoreCase(condition, Cons.COLUMN_IS_DELETED)){ + appendDeleteFlag = false; + } } } - } - // 如果需要删除 - if(appendDeleteFlag){ - if(ParserCache.hasDeletedColumn(this.table)){ - sb.append(" AND is_deleted = ").append(BaseConfig.getActiveFlagValue()); + // 如果需要删除 + if(appendDeleteFlag && ParserCache.hasDeletedColumn(table)){ + WHERE(Cons.COLUMN_IS_DELETED + " = " + BaseConfig.getActiveFlagValue()); } - } - return sb.toString(); + }}.toString(); } - } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java b/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java index 6af8bff..85abbf3 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/parser/ParserCache.java @@ -15,20 +15,26 @@ */ package com.diboot.core.binding.parser; +import com.baomidou.mybatisplus.annotation.TableName; +import com.diboot.core.binding.query.BindQuery; +import com.diboot.core.binding.query.dynamic.AnnoJoiner; +import com.diboot.core.config.Cons; import com.diboot.core.util.BeanUtils; +import com.diboot.core.util.S; import com.diboot.core.util.SqlExecutor; import com.diboot.core.util.V; +import org.apache.ibatis.jdbc.SQL; +import org.springframework.core.annotation.AnnotationUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** - * VO对象中的绑定注解 缓存管理类 + * 对象中的绑定注解 缓存管理类 * @author mazc@dibo.ltd
* @version 2.0
* @date 2019/04/03
@@ -42,6 +48,14 @@ public class ParserCache { * 中间表是否包含is_deleted列 缓存 */ private static Map middleTableHasDeletedCacheMap = new ConcurrentHashMap<>(); + /** + * entity类-表名的缓存 + */ + private static Map entityClassTableCacheMap = new ConcurrentHashMap<>(); + /** + * dto类-BindQuery注解的缓存 + */ + private static Map> dtoClassBindQueryCacheMap = new ConcurrentHashMap<>(); /** * 获取指定class对应的Bind相关注解 @@ -90,8 +104,149 @@ public class ParserCache { if(middleTableHasDeletedCacheMap.containsKey(middleTable)){ return middleTableHasDeletedCacheMap.get(middleTable); } - boolean hasColumn = SqlExecutor.validateQuery("SELECT is_deleted FROM "+middleTable); + boolean hasColumn = SqlExecutor.validateQuery(buildCheckDeletedColSql(middleTable)); middleTableHasDeletedCacheMap.put(middleTable, hasColumn); return hasColumn; } + + /** + * 构建检测是否有删除字段的sql + * @param table + * @return + */ + private static String buildCheckDeletedColSql(String table){ + return new SQL(){{ + SELECT(Cons.COLUMN_IS_DELETED); + FROM(table); + LIMIT(1); + }}.toString(); + } + + /** + * 获取entity对应的表名 + * @param entityClass + * @return + */ + public static String getEntityTableName(Class entityClass){ + String entityClassName = entityClass.getName(); + String tableName = entityClassTableCacheMap.get(entityClassName); + if(tableName == null){ + TableName tableNameAnno = AnnotationUtils.findAnnotation(entityClass, TableName.class); + if(tableNameAnno != null){ + tableName = tableNameAnno.value(); + } + else{ + tableName = S.toSnakeCase(entityClass.getSimpleName()); + } + entityClassTableCacheMap.put(entityClassName, tableName); + } + return tableName; + } + + /** + * 当前DTO是否有Join绑定 + * @param dto dto对象 + * @param fieldNameSet 有值属性集合 + * @param + * @return + */ + public static boolean hasJoinTable(DTO dto, Set fieldNameSet){ + List annoList = getBindQueryAnnos(dto.getClass()); + if(V.notEmpty(annoList)){ + for(AnnoJoiner anno : annoList){ + if(V.notEmpty(anno.getJoin()) && fieldNameSet != null && fieldNameSet.contains(anno.getFieldName())){ + return true; + } + } + } + return false; + } + + /** + * 获取dto类中定义的BindQuery注解 + * @param dtoClass + * @return + */ + public static List getBindQueryAnnos(Class dtoClass){ + String dtoClassName = dtoClass.getName(); + if(dtoClassBindQueryCacheMap.containsKey(dtoClassName)){ + return dtoClassBindQueryCacheMap.get(dtoClassName); + } + // 初始化 + List annos = null; + List declaredFields = BeanUtils.extractAllFields(dtoClass); + int index = 1; + Map joinOn2Alias = new HashMap<>(); + for (Field field : declaredFields) { + BindQuery query = field.getAnnotation(BindQuery.class); + if(query == null || query.ignore()){ + continue; + } + if(annos == null){ + annos = new ArrayList<>(); + } + AnnoJoiner annoJoiner = new AnnoJoiner(field, query); + // 关联对象,设置别名 + if(V.notEmpty(annoJoiner.getJoin())){ + String key = annoJoiner.getJoin() + ":" + annoJoiner.getCondition(); + String alias = joinOn2Alias.get(key); + if(alias == null){ + alias = "r"+index; + annoJoiner.setAlias(alias); + index++; + joinOn2Alias.put(key, alias); + } + else{ + annoJoiner.setAlias(alias); + } + } + annos.add(annoJoiner); + } + dtoClassBindQueryCacheMap.put(dtoClassName, annos); + return annos; + } + + /** + * 获取注解joiner + * @param dtoClass + * @param fieldNames + * @return + */ + public static List getAnnoJoiners(Class dtoClass, Collection fieldNames) { + List annoList = getBindQueryAnnos(dtoClass); + // 不过滤 返回全部 + if(fieldNames == null){ + return annoList; + } + // 过滤 + if(V.notEmpty(annoList)){ + List matchedAnnoList = new ArrayList<>(); + for(AnnoJoiner anno : annoList){ + if(fieldNames.contains(anno.getFieldName())){ + matchedAnnoList.add(anno); + } + } + return matchedAnnoList; + } + return Collections.emptyList(); + } + + /** + * 获取注解joiner + * @param dtoClass + * @param key + * @return + */ + public static AnnoJoiner getAnnoJoiner(Class dtoClass, String key) { + List annoList = getBindQueryAnnos(dtoClass); + if(V.notEmpty(annoList)){ + for(AnnoJoiner anno : annoList){ + if(key.equals(anno.getFieldName())){ + return anno; + } + } + } + return null; + } + } diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java b/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java index d36ce48..1a5a5f8 100644 --- a/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/BindQuery.java @@ -15,6 +15,7 @@ */ package com.diboot.core.binding.query; +import javax.lang.model.type.NullType; import java.lang.annotation.*; /** @@ -40,6 +41,18 @@ public @interface BindQuery { */ String field() default ""; + /*** + * 绑定的Entity类 + * @return + */ + Class entity() default NullType.class; + + /*** + * JOIN连接条件 + * @return + */ + String condition() default ""; + /** * 忽略该字段 * @return diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/AnnoJoiner.java b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/AnnoJoiner.java new file mode 100644 index 0000000..928abff --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/AnnoJoiner.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding.query.dynamic; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.binding.parser.ParserCache; +import com.diboot.core.binding.query.BindQuery; +import com.diboot.core.binding.query.Comparison; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import lombok.Getter; +import lombok.Setter; + +import javax.lang.model.type.NullType; +import java.io.Serializable; +import java.lang.reflect.Field; + +/** + * BindQuery注解连接器 + * @author Mazc@dibo.ltd + * @version v2.0 + * @date 2020/04/16 + */ +@Getter @Setter +public class AnnoJoiner implements Serializable { + private static final long serialVersionUID = 5998965277333389063L; + + public AnnoJoiner(Field field, BindQuery query){ + this.fieldName = field.getName(); + this.comparison = query.comparison(); + // 列名 + if (V.notEmpty(query.field())) { + this.columnName = S.toSnakeCase(query.field()); + } + else if (field.isAnnotationPresent(TableField.class)) { + this.columnName = field.getAnnotation(TableField.class).value(); + } + if(V.isEmpty(this.columnName)){ + this.columnName = S.toSnakeCase(field.getName()); + } + // join 表名 + if(!NullType.class.equals(query.entity())){ + this.join = ParserCache.getEntityTableName(query.entity()); + } + // 条件 + if(V.notEmpty(query.condition())){ + this.condition = query.condition(); + } + } + + private Comparison comparison; + + private String fieldName; + + private String columnName; + + private String join; + + /** + * 别名 + */ + private String alias; + + private String condition; + + /** + * 获取On条件 + * @return + */ + public String getOnSegment(){ + if(V.notEmpty(condition)){ + return JoinConditionParser.parseJoinCondition(this.condition, this.alias); + } + return null; + } +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicJoinQueryWrapper.java b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicJoinQueryWrapper.java new file mode 100644 index 0000000..7dcfe1b --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicJoinQueryWrapper.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding.query.dynamic; + +import com.diboot.core.binding.JoinsBinder; +import com.diboot.core.binding.parser.ParserCache; +import com.diboot.core.vo.Pagination; +import lombok.Getter; + +import java.util.Collection; +import java.util.List; + +/** + * 动态查询wrapper + * @author Mazc@dibo.ltd + * @version v2.0 + * @date 2020/04/16 + */ +public class DynamicJoinQueryWrapper extends ExtQueryWrapper { + public DynamicJoinQueryWrapper(Class dtoClass, Collection fields){ + this.dtoClass = dtoClass; + this.fields = fields; + } + + /** + * DTO类 + */ + @Getter + private Class dtoClass; + /** + * 字段 + */ + private Collection fields; + + /** + * dto字段和值 + */ + public List getAnnoJoiners(){ + return ParserCache.getAnnoJoiners(this.dtoClass, fields); + } + + /** + * 查询一条数据 + * @param entityClazz + * @return + */ + @Override + public E queryOne(Class entityClazz){ + return JoinsBinder.queryOne(this, entityClazz); + } + + /** + * 查询一条数据 + * @param entityClazz + * @return + */ + @Override + public List queryList(Class entityClazz){ + return JoinsBinder.queryList(this, entityClazz); + } + + /** + * 查询一条数据 + * @param entityClazz + * @return + */ + @Override + public List queryList(Class entityClazz, Pagination pagination){ + return JoinsBinder.queryList(this, entityClazz, pagination); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java new file mode 100644 index 0000000..12a26a2 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/DynamicSqlProvider.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding.query.dynamic; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.diboot.core.binding.parser.ParserCache; +import com.diboot.core.config.BaseConfig; +import com.diboot.core.config.Cons; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import org.apache.ibatis.jdbc.SQL; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * 动态SQL构建Provider + * @author Mazc@dibo.ltd + * @version v2.0 + * @date 2020/04/15 + */ +public class DynamicSqlProvider { + /** + * 构建动态SQL + * @param ew + * @return + */ + public String buildSql(QueryWrapper ew){ + return buildDynamicSql(null, ew, true); + } + + /** + * 构建动态SQL + * @param ew + * @return + */ + public String buildSqlForList(QueryWrapper ew){ + return buildDynamicSql(null, ew, false); + } + + /** + * 构建动态SQL + * @param page 分页参数,用于MP分页插件AOP,不可删除 + * @param ew + * @return + */ + public String buildSqlForListWithPage(Page page, QueryWrapper ew){ + return buildDynamicSql(page, ew, false); + } + + /** + * 构建动态SQL + * @param page 分页参数,用于MP分页插件AOP,不可删除 + * @param ew + * @return + */ + private String buildDynamicSql(Page page, QueryWrapper ew, boolean limit1){ + DynamicJoinQueryWrapper wrapper = (DynamicJoinQueryWrapper)ew; + return new SQL() {{ + if(V.isEmpty(ew.getSqlSelect())){ + SELECT("self.*"); + } + else{ + SELECT(formatSqlSelect(ew.getSqlSelect())); + } + FROM(wrapper.getEntityTable()+" self"); + //提取字段,根据查询条件中涉及的表,动态join + List annoJoinerList = wrapper.getAnnoJoiners(); + if(V.notEmpty(annoJoinerList)){ + Set tempSet = new HashSet<>(); + for(AnnoJoiner joiner : annoJoinerList){ + if(V.notEmpty(joiner.getJoin()) && V.notEmpty(joiner.getOnSegment())){ + String joinSegment = joiner.getJoin() + " " + joiner.getAlias() + " ON " + joiner.getOnSegment(); + if(!tempSet.contains(joinSegment)){ + LEFT_OUTER_JOIN(joinSegment); + tempSet.add(joinSegment); + } + } + } + tempSet = null; + } + MergeSegments segments = ew.getExpression(); + if(segments != null){ + String normalSql = segments.getNormal().getSqlSegment(); + WHERE(normalSql); + // 动态为主表添加is_deleted=0 + if(ParserCache.hasDeletedColumn(wrapper.getEntityTable())){ + WHERE("self."+ Cons.COLUMN_IS_DELETED+" = "+ BaseConfig.getActiveFlagValue()); + } + if(segments.getOrderBy() != null){ + String orderBySql = segments.getOrderBy().getSqlSegment(); + int beginIndex = S.indexOfIgnoreCase(orderBySql,"ORDER BY "); + if(beginIndex >= 0){ + orderBySql = S.substring(orderBySql, beginIndex+"ORDER BY ".length()); + ORDER_BY(orderBySql); + } + } + } + if(limit1){ + LIMIT(1); + } + }}.toString(); + } + + /** + * 格式化sql select列语句 + * @param sqlSelect + * @return + */ + private String formatSqlSelect(String sqlSelect){ + String[] columns = S.split(sqlSelect); + List selects = new ArrayList<>(columns.length); + for(String column : columns){ + column = S.removeDuplicateBlank(column).trim(); + selects.add("self."+S.toSnakeCase(column)); + } + return S.join(selects); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/ExtQueryWrapper.java b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/ExtQueryWrapper.java new file mode 100644 index 0000000..f841535 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/ExtQueryWrapper.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding.query.dynamic; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.binding.parser.ParserCache; +import com.diboot.core.exception.BusinessException; +import com.diboot.core.service.BaseService; +import com.diboot.core.util.ContextHelper; +import com.diboot.core.vo.Pagination; +import com.diboot.core.vo.Status; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +/** + * 动态查询wrapper + * @author Mazc@dibo.ltd + * @version v2.0 + * @date 2020/04/16 + */ +public class ExtQueryWrapper extends QueryWrapper { + /** + * 主实体class + */ + @Getter @Setter + private Class mainEntityClass; + + /** + * 获取entity表名 + * @return + */ + public String getEntityTable(){ + return ParserCache.getEntityTableName(getMainEntityClass()); + } + + /** + * 查询一条数据 + * @param entityClazz + * @return + */ + public E queryOne(Class entityClazz){ + this.mainEntityClass = entityClazz; + BaseService baseService = ContextHelper.getBaseServiceByEntity(this.mainEntityClass); + if(baseService != null){ + return (E)baseService.getEntity(this); + } + else{ + throw new BusinessException(Status.FAIL_INVALID_PARAM, "单表查询对象无BaseService实现: "+this.mainEntityClass.getSimpleName()); + } + } + + /** + * 查询一条数据 + * @param entityClazz + * @return + */ + public List queryList(Class entityClazz){ + BaseService baseService = ContextHelper.getBaseServiceByEntity(entityClazz); + if(baseService != null){ + return (List)baseService.getEntityList(this); + } + else{ + throw new BusinessException(Status.FAIL_INVALID_PARAM, "单表查询对象无BaseService实现: "+entityClazz.getSimpleName()); + } + } + + /** + * 查询一条数据 + * @param entityClazz + * @return + */ + public List queryList(Class entityClazz, Pagination pagination){ + BaseService baseService = ContextHelper.getBaseServiceByEntity(entityClazz); + if(baseService != null){ + return (List)baseService.getEntityList(this, pagination); + } + else{ + throw new BusinessException(Status.FAIL_INVALID_PARAM, "单表查询对象无BaseService实现: "+entityClazz.getSimpleName()); + } + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/JoinConditionParser.java b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/JoinConditionParser.java new file mode 100644 index 0000000..6e34751 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/binding/query/dynamic/JoinConditionParser.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.binding.query.dynamic; + +import com.diboot.core.binding.parser.ConditionManager; +import com.diboot.core.util.S; +import com.diboot.core.util.V; +import net.sf.jsqlparser.expression.BinaryExpression; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.*; +import net.sf.jsqlparser.schema.Column; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * Join条件表达式的管理器 + * @author mazc@dibo.ltd + * @version v2.0 + * @date 2019/4/1 + */ +public class JoinConditionParser { + private static final Logger log = LoggerFactory.getLogger(JoinConditionParser.class); + + /** + * 解析condition条件 + * @param condition + * @param alias + * @throws Exception + */ + public static String parseJoinCondition(String condition, String alias) { + List expressionList = ConditionManager.getExpressionList(condition); + if(V.isEmpty(expressionList)){ + log.warn("无法解析注解条件: {} ", condition); + return null; + } + // 解析join + return parseJoinOn(alias, expressionList); + } + + /** + * 解析直接关联 + * @param alias + * @param expressionList + */ + private static String parseJoinOn(String alias, List expressionList) { + List segments = new ArrayList<>(); + // 解析直接关联 + for(Expression operator : expressionList){ + if(operator instanceof BinaryExpression){ + BinaryExpression expression = (BinaryExpression)operator; + String left = formatLeft(expression.getLeftExpression().toString()); + String right = formatRight(expression.getRightExpression().toString()); + if(expression.getRightExpression() instanceof Column){ + right = alias + "." + right; + } + if(operator instanceof EqualsTo){ + segments.add(left + " = " + right); + } + else if(operator instanceof NotEqualsTo){ + segments.add(left + " != " + right); + } + else if(operator instanceof GreaterThan){ + segments.add(left + " > " + right); + } + else if(operator instanceof GreaterThanEquals){ + segments.add(left + " >= " + right); + } + else if(operator instanceof MinorThan){ + segments.add(left + " < " + right); + } + else if(operator instanceof MinorThanEquals){ + segments.add(left + " <= " + right); + } + else{ + log.warn("暂不支持的条件: "+ expression.toString()); + } + } + else if(operator instanceof IsNullExpression){ + IsNullExpression expression = (IsNullExpression)operator; + String left = formatLeft(expression.getLeftExpression().toString()); + if(expression.isNot() == false){ + segments.add(left + " IS NULL"); + } + else{ + segments.add(left + " IS NOT NULL"); + } + } + else if(operator instanceof InExpression){ + InExpression expression = (InExpression)operator; + String left = formatLeft(expression.getLeftExpression().toString()); + if(expression.isNot() == false){ + segments.add(left + " IN " + expression.getRightItemsList().toString()); + } + else{ + segments.add(left + " NOT IN " + expression.getRightItemsList().toString()); + } + } + else if(operator instanceof Between){ + Between expression = (Between)operator; + String left = formatLeft(expression.getLeftExpression().toString()); + if(expression.isNot() == false){ + segments.add(left + " BETWEEN " + expression.getBetweenExpressionStart().toString() + " AND " + expression.getBetweenExpressionEnd().toString()); + } + else{ + segments.add(left + " NOT BETWEEN " + expression.getBetweenExpressionStart().toString() + " AND " + expression.getBetweenExpressionEnd().toString()); + } + } + else if(operator instanceof LikeExpression){ + LikeExpression expression = (LikeExpression)operator; + String left = formatLeft(expression.getLeftExpression().toString()); + if(expression.isNot() == false){ + segments.add(left + " LIKE " + expression.getStringExpression()); + } + else{ + segments.add(left + " NOT LIKE " + expression.getStringExpression()); + } + } + else{ + log.warn("不支持的条件: "+operator.toString()); + } + } + if(segments.isEmpty()){ + return null; + } + return S.join(segments, " AND "); + } + + /** + * 注解列 + * @return + */ + private static String formatLeft(String annoColumn){ + if(annoColumn.contains("this.")){ + annoColumn = S.replace(annoColumn, "this.", "self."); + } + return S.toSnakeCase(annoColumn); + } + + /** + * 格式化右侧列 + * @return + */ + private static String formatRight(String annoColumn){ + return S.toSnakeCase(annoColumn); + } + +} diff --git a/diboot-core/src/main/java/com/diboot/core/config/Cons.java b/diboot-core/src/main/java/com/diboot/core/config/Cons.java index dae1f52..77e64f9 100644 --- a/diboot-core/src/main/java/com/diboot/core/config/Cons.java +++ b/diboot-core/src/main/java/com/diboot/core/config/Cons.java @@ -38,6 +38,10 @@ public class Cons { * 排序 - 降序标记 */ public static final String ORDER_DESC = "DESC"; + /** + * 逻辑删除列名 + */ + public static final String COLUMN_IS_DELETED = "is_deleted"; /*** * 默认字段名定义 */ diff --git a/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java b/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java index 3d604d4..b9e07e5 100644 --- a/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java +++ b/diboot-core/src/main/java/com/diboot/core/controller/BaseController.java @@ -17,8 +17,8 @@ package com.diboot.core.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.binding.Binder; import com.diboot.core.binding.QueryBuilder; -import com.diboot.core.binding.RelationsBinder; import com.diboot.core.config.Cons; import com.diboot.core.util.S; import com.diboot.core.util.V; @@ -40,24 +40,21 @@ public class BaseController { /*** * 构建查询QueryWrapper (根据BindQuery注解构建相应的查询条件) * @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解,默认构建为为EQ相等条件) - * @param * @return */ - public QueryWrapper buildQueryWrapper(DTO entityOrDto, HttpServletRequest request) throws Exception{ + public QueryWrapper buildQueryWrapper(DTO entityOrDto, HttpServletRequest request) throws Exception{ if(entityOrDto instanceof HttpServletRequest){ throw new Exception("参数错误:buildQueryWrapper()参数为Entity/DTO对象!"); } - return QueryBuilder.toQueryWrapper(entityOrDto, extractParams(request)); } /*** * 构建查询LambdaQueryWrapper (根据BindQuery注解构建相应的查询条件) * @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解,默认构建为为EQ相等条件) - * @param * @return */ - public LambdaQueryWrapper buildLambdaQueryWrapper(DTO entityOrDto, HttpServletRequest request) throws Exception{ + public LambdaQueryWrapper buildLambdaQueryWrapper(DTO entityOrDto, HttpServletRequest request) throws Exception{ if(entityOrDto instanceof HttpServletRequest){ throw new Exception("参数错误:buildQueryWrapper()参数为Entity/DTO对象!"); } @@ -174,7 +171,7 @@ public class BaseController { */ protected List convertToVoAndBindRelations(List entityList, Class voClass) { // 转换为VO - List voList = RelationsBinder.convertAndBind(entityList, voClass); + List voList = Binder.convertAndBindRelations(entityList, voClass); return voList; } diff --git a/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java b/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java index fbf1e2e..4b12a14 100644 --- a/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java +++ b/diboot-core/src/main/java/com/diboot/core/entity/BaseEntity.java @@ -52,7 +52,7 @@ public abstract class BaseEntity implements Serializable { */ @TableLogic @JSONField(serialize = false) - @TableField("is_deleted") + @TableField(Cons.COLUMN_IS_DELETED) private boolean deleted = false; /*** diff --git a/diboot-core/src/main/java/com/diboot/core/mapper/DynamicQueryMapper.java b/diboot-core/src/main/java/com/diboot/core/mapper/DynamicQueryMapper.java new file mode 100644 index 0000000..03da8d9 --- /dev/null +++ b/diboot-core/src/main/java/com/diboot/core/mapper/DynamicQueryMapper.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.diboot.core.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.diboot.core.binding.query.dynamic.DynamicSqlProvider; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.SelectProvider; + +import java.util.List; +import java.util.Map; + +/** + * 通用联表查询Mapper + * @author mazc@dibo.ltd + * @version 2018/12/22 + */ +@Mapper +public interface DynamicQueryMapper { + + /** + * 动态SQL查询 + * @return + */ + @SelectProvider(type= DynamicSqlProvider.class, method="buildSql") + Map query(@Param(Constants.WRAPPER) QueryWrapper ew); + + /** + * 动态SQL查询 + * @return + */ + @SelectProvider(type= DynamicSqlProvider.class, method="buildSqlForList") + List> queryForList(@Param(Constants.WRAPPER) QueryWrapper ew); + + /** + * 动态SQL查询 + * @param page + * @return + */ + @SelectProvider(type= DynamicSqlProvider.class, method="buildSqlForListWithPage") + IPage> queryForListWithPage(Page page, @Param(Constants.WRAPPER) QueryWrapper ew); + +} \ No newline at end of file diff --git a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java index 1ea1221..4fe5b0d 100644 --- a/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java +++ b/diboot-core/src/main/java/com/diboot/core/service/impl/BaseServiceImpl.java @@ -21,7 +21,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import com.diboot.core.binding.RelationsBinder; +import com.diboot.core.binding.Binder; import com.diboot.core.binding.binder.EntityBinder; import com.diboot.core.binding.binder.EntityListBinder; import com.diboot.core.binding.binder.FieldBinder; @@ -343,7 +343,7 @@ public class BaseServiceImpl, T> extends ServiceImpl @Override public List> getMapList(Wrapper queryWrapper, Pagination pagination) { if(pagination != null){ - IPage page = convertToIPage(queryWrapper, pagination); + IPage page = convertToIPage(queryWrapper, pagination); IPage> resultPage = super.pageMaps(page, queryWrapper); // 如果重新执行了count进行查询,则更新pagination中的总数 if(page.isSearchCount()){ @@ -438,7 +438,7 @@ public class BaseServiceImpl, T> extends ServiceImpl List enityList = new ArrayList<>(); enityList.add(entity); // 绑定 - List voList = RelationsBinder.convertAndBind(enityList, voClass); + List voList = Binder.convertAndBindRelations(enityList, voClass); return voList.get(0); } @@ -446,7 +446,7 @@ public class BaseServiceImpl, T> extends ServiceImpl public List getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class voClass) { List entityList = getEntityList(queryWrapper, pagination); // 自动转换为VO并绑定关联对象 - List voList = RelationsBinder.convertAndBind(entityList, voClass); + List voList = Binder.convertAndBindRelations(entityList, voClass); return voList; } @@ -473,7 +473,7 @@ public class BaseServiceImpl, T> extends ServiceImpl } } } - return (Page)pagination.toIPage(); + return (Page)pagination.toPage(); } /** diff --git a/diboot-core/src/main/java/com/diboot/core/util/SqlExecutor.java b/diboot-core/src/main/java/com/diboot/core/util/SqlExecutor.java index c6d3265..a73d267 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/SqlExecutor.java +++ b/diboot-core/src/main/java/com/diboot/core/util/SqlExecutor.java @@ -53,7 +53,7 @@ public class SqlExecutor { try(SqlSession session = sqlSessionFactory.openSession(); Connection conn = session.getConnection(); PreparedStatement stmt = conn.prepareStatement(sqlStatement)){ ResultSet rs = stmt.executeQuery(); rs.close(); - log.trace("执行验证SQL:{} 成功", sqlStatement); + log.debug("==> {}", sqlStatement); return true; } catch(Exception e){ diff --git a/diboot-core/src/main/java/com/diboot/core/util/V.java b/diboot-core/src/main/java/com/diboot/core/util/V.java index 186a2a0..deb21da 100644 --- a/diboot-core/src/main/java/com/diboot/core/util/V.java +++ b/diboot-core/src/main/java/com/diboot/core/util/V.java @@ -319,7 +319,7 @@ public class V { } } else{ - //TODO 无法识别的格式 + // 无法识别的格式 } } // 返回校验不通过的结果 diff --git a/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java b/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java index a4cfc51..8372eff 100644 --- a/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java +++ b/diboot-core/src/main/java/com/diboot/core/vo/Pagination.java @@ -16,7 +16,6 @@ package com.diboot.core.vo; import com.alibaba.fastjson.annotation.JSONField; -import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.diboot.core.config.BaseConfig; @@ -119,7 +118,7 @@ public class Pagination implements Serializable { * @param * @return */ - public IPage toIPage(){ + public Page toPage(){ List orderItemList = null; // 解析排序 if(V.notEmpty(this.orderBy)){ @@ -142,13 +141,13 @@ public class Pagination implements Serializable { } } } - IPage page = new Page() + Page page = new Page() .setCurrent(getPageIndex()) .setSize(getPageSize()) // 如果前端传递过来了缓存的总数,则本次不再count统计 .setTotal(getTotalCount() > 0? -1 : getTotalCount()); if(orderItemList != null){ - ((Page) page).addOrder(orderItemList); + page.addOrder(orderItemList); } return page; } diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java index 54a969b..d6e5a41 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityBinder.java @@ -16,7 +16,7 @@ package diboot.core.test.binder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.diboot.core.binding.RelationsBinder; +import com.diboot.core.binding.Binder; import com.diboot.core.util.BeanUtils; import com.diboot.core.util.JSON; import com.diboot.core.util.V; @@ -56,7 +56,7 @@ public class TestEntityBinder { queryWrapper.in(User::getId, 1001L, 1002L); List userList = userService.list(queryWrapper); // 自动绑定 - List voList = RelationsBinder.convertAndBind(userList, EntityBinderVO.class); + List voList = Binder.convertAndBindRelations(userList, EntityBinderVO.class); // 验证绑定结果 Assert.assertTrue(V.notEmpty(voList)); for(EntityBinderVO vo : voList){ @@ -70,7 +70,7 @@ public class TestEntityBinder { } // 单个entity接口测试 EntityBinderVO singleVO = BeanUtils.convert(userList.get(0), EntityBinderVO.class); - RelationsBinder.bind(singleVO); + Binder.bindRelations(singleVO); // 验证直接关联和通过中间表间接关联的绑定 Assert.assertEquals(singleVO.getDepartmentId(), singleVO.getDepartment().getId()); Assert.assertNotNull(singleVO.getDepartment().getOrgId()); diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java index daf1d3f..b80dc4d 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestEntityListBinder.java @@ -16,7 +16,7 @@ package diboot.core.test.binder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.diboot.core.binding.RelationsBinder; +import com.diboot.core.binding.Binder; import com.diboot.core.entity.Dictionary; import com.diboot.core.service.DictionaryService; import com.diboot.core.util.JSON; @@ -71,7 +71,7 @@ public class TestEntityListBinder { queryWrapper.eq(Department::getId, 10001L); List entityList = departmentService.getEntityList(queryWrapper); // 自动绑定 - List voList = RelationsBinder.convertAndBind(entityList, EntityListSimpleBinderVO.class); + List voList = Binder.convertAndBindRelations(entityList, EntityListSimpleBinderVO.class); // 验证绑定结果 Assert.assertTrue(V.notEmpty(voList)); for(EntityListSimpleBinderVO vo : voList){ @@ -97,7 +97,7 @@ public class TestEntityListBinder { queryWrapper.in(User::getId, 1001L, 1002L); List userList = userService.list(queryWrapper); // 自动绑定 - List voList = RelationsBinder.convertAndBind(userList, EntityListComplexBinderVO.class); + List voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class); // 验证绑定结果 Assert.assertTrue(V.notEmpty(voList)); for(EntityListComplexBinderVO vo : voList){ @@ -114,7 +114,7 @@ public class TestEntityListBinder { queryWrapper.eq(Dictionary::getType, "GENDER"); Dictionary dictionary = dictionaryService.getSingleEntity(queryWrapper); - DictionaryVO vo = RelationsBinder.convertAndBind(dictionary, DictionaryVO.class); + DictionaryVO vo = Binder.convertAndBindRelations(dictionary, DictionaryVO.class); Assert.assertTrue(vo.getChildren().size() > 0); } } diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java b/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java index 1d0b71f..41ba885 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestFieldBinder.java @@ -16,7 +16,7 @@ package diboot.core.test.binder; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.diboot.core.binding.RelationsBinder; +import com.diboot.core.binding.Binder; import com.diboot.core.util.JSON; import com.diboot.core.util.V; import diboot.core.test.StartupApplication; @@ -59,7 +59,7 @@ public class TestFieldBinder { queryWrapper.in(User::getId, 1001L, 1002L); List userList = userService.list(queryWrapper); // 自动绑定 - List voList = RelationsBinder.convertAndBind(userList, FieldBinderVO.class); + List voList = Binder.convertAndBindRelations(userList, FieldBinderVO.class); // 验证绑定结果 Assert.assertTrue(V.notEmpty(voList)); for(FieldBinderVO vo : voList){ @@ -81,7 +81,7 @@ public class TestFieldBinder { queryWrapper.in(User::getId, 1001L, 1002L); List userList = userService.list(queryWrapper); // 自动绑定 - List voList = RelationsBinder.convertAndBind(userList, UserVO.class); + List voList = Binder.convertAndBindRelations(userList, UserVO.class); if(V.notEmpty(voList)){ for(UserVO vo : voList){ Assert.assertNotNull(vo.getDeptName()); diff --git a/diboot-core/src/test/java/diboot/core/test/binder/TestJoinQuery.java b/diboot-core/src/test/java/diboot/core/test/binder/TestJoinQuery.java new file mode 100644 index 0000000..0325a00 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/TestJoinQuery.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package diboot.core.test.binder; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.diboot.core.binding.Binder; +import com.diboot.core.binding.JoinsBinder; +import com.diboot.core.binding.QueryBuilder; +import com.diboot.core.config.Cons; +import com.diboot.core.vo.Pagination; +import diboot.core.test.StartupApplication; +import diboot.core.test.binder.dto.DepartmentDTO; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.service.DepartmentService; +import diboot.core.test.binder.vo.DepartmentVO; +import diboot.core.test.config.SpringMvcConfig; +import org.apache.ibatis.jdbc.SQL; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Arrays; +import java.util.List; + +/** + * BindQuery测试 + * @author Mazc@dibo.ltd + * @version v2.0.6 + * @date 2020/04/14 + */ +@RunWith(SpringRunner.class) +@ContextConfiguration(classes = {SpringMvcConfig.class}) +@SpringBootTest(classes = {StartupApplication.class}) +public class TestJoinQuery { + + @Autowired + DepartmentService departmentService; + + @Test + public void testSingleTableQuery(){ + Department entity = new Department(); + entity.setParentId(10001L); + entity.setName("测试组"); + entity.setOrgId(100001L); + + QueryWrapper queryWrapper = QueryBuilder.toQueryWrapper(entity); + System.out.println(queryWrapper.getExpression()); + List list = Binder.joinQueryList(queryWrapper, Department.class); + Assert.assertTrue(list.size() == 1); + Assert.assertTrue(queryWrapper.getSqlSegment().contains("parent_id")); + Assert.assertTrue(queryWrapper.getSqlSegment().contains("name")); + Assert.assertTrue(queryWrapper.getSqlSegment().contains("org_id")); + + List fields = Arrays.asList("name", "orgId", "parentId"); + queryWrapper.clear(); + queryWrapper = QueryBuilder.toQueryWrapper(entity, fields); + Assert.assertTrue(queryWrapper.getSqlSegment().contains("parent_id")); + Assert.assertTrue(queryWrapper.getSqlSegment().contains("name")); + Assert.assertTrue(queryWrapper.getSqlSegment().contains("org_id")); + Assert.assertTrue(queryWrapper.getParamNameValuePairs().size() == fields.size()); + + list = Binder.joinQueryList(queryWrapper, Department.class); + Assert.assertTrue(list.size() == 1); + } + + @Test + public void testDynamicSqlQuery(){ + // 初始化DTO,测试不涉及关联的情况 + DepartmentDTO dto = new DepartmentDTO(); + dto.setParentId(10001L); + + // 验证 转换后的wrapper可以直接查询 + QueryWrapper queryWrapper = QueryBuilder.toQueryWrapper(dto); + List departments = departmentService.getEntityList(queryWrapper); + Assert.assertTrue(departments.size() == 3); + + // builder直接查询,不分页 3条结果 + List builderResultList = QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(Department.class); + Assert.assertTrue(builderResultList.size() == 3); + + // 初始化DTO + dto = new DepartmentDTO(); + dto.setParentId(10001L); + dto.setParentName("产品部"); + //boolean类型 + dto.setOrgName("苏州帝博"); + + // 转换为queryWrapper + queryWrapper.clear(); + queryWrapper = QueryBuilder.toQueryWrapper(dto); + queryWrapper.select("id,name,parentId,org_id"); + + // 验证直接查询指定字段 + List fields = Arrays.asList("parentId", "parentName", "orgName"); + builderResultList = QueryBuilder.toDynamicJoinQueryWrapper(dto, fields).queryList(Department.class); + Assert.assertTrue(builderResultList.size() == 3); + + // 查询单条记录 + Department department = Binder.joinQueryOne(queryWrapper, Department.class); + Assert.assertTrue(department.getName() != null); + + // 不分页 3条结果 + List list = JoinsBinder.queryList(queryWrapper, Department.class); + Assert.assertTrue(list.size() == 3); + // 不分页,直接用wrapper查 + list = QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(Department.class); + Assert.assertTrue(list.size() == 3); + + // 测试继续绑定VO 是否有影响 + List voList = Binder.convertAndBindRelations(list, DepartmentVO.class); + Assert.assertTrue(voList.size() == 3); + Assert.assertTrue(voList.get(0).getDepartment() != null); + Assert.assertTrue(voList.get(0).getOrganizationVO() != null); + + // 分页 + Pagination pagination = new Pagination(); + pagination.setPageSize(2); + pagination.setPageIndex(1); + + // 第一页 2条结果 + list = Binder.joinQueryList(queryWrapper, Department.class, pagination); + Assert.assertTrue(list.size() == pagination.getPageSize()); + + // 测试排序 + pagination.setOrderBy("orgName:DESC,parentName"); + pagination.setPageIndex(2); + // 第二页 1条结果 + list = Binder.joinQueryList(queryWrapper, Department.class, pagination); + Assert.assertTrue(list.size() == 1); + } + + @Test + public void test(){ + String sql = buildCheckDeletedColSql("test"); + Assert.assertTrue(sql.contains("SELECT is_deleted")); + Assert.assertTrue(sql.contains("FROM test")); + Assert.assertTrue(sql.contains("LIMIT 1")); + } + + /** + * 构建检测是否有删除字段的sql + * @param middleTable + * @return + */ + private static String buildCheckDeletedColSql(String middleTable){ + return new SQL(){ + { + SELECT(Cons.COLUMN_IS_DELETED); + FROM(middleTable); + LIMIT(1); + } + }.toString(); + } +} diff --git a/diboot-core/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java b/diboot-core/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java new file mode 100644 index 0000000..ae597e4 --- /dev/null +++ b/diboot-core/src/test/java/diboot/core/test/binder/dto/DepartmentDTO.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package diboot.core.test.binder.dto; + +import com.diboot.core.binding.query.BindQuery; +import com.diboot.core.binding.query.Comparison; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.Organization; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serializable; + +/** + * 定时任务 + * @author mazc@dibo.ltd + * @version v2.0 + * @date 2018/12/27 + */ +@Getter +@Setter +@Accessors(chain = true) +public class DepartmentDTO implements Serializable { + private static final long serialVersionUID = 8670003133709715087L; + + private Long parentId; + + private Long orgId; + + @BindQuery(comparison = Comparison.CONTAINS) + private String name; + + // 绑定查询 + @BindQuery(comparison = Comparison.STARTSWITH, entity = Organization.class, field = "name", condition = "this.org_id=id") + private String orgName; + + // 绑定查询 + @BindQuery(comparison = Comparison.STARTSWITH, entity = Organization.class, field = "name2", condition = "this.org_id=id") + private String orgName2; + + // 绑定查询 + @BindQuery(entity = Department.class, field = "name", condition = "this.parent_id=id") + private String parentName; +} \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java b/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java index a404aa1..09f39a7 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/entity/Department.java @@ -16,6 +16,8 @@ package diboot.core.test.binder.entity; import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.binding.query.BindQuery; +import com.diboot.core.binding.query.Comparison; import com.diboot.core.entity.BaseEntity; import lombok.Getter; import lombok.Setter; @@ -39,6 +41,7 @@ public class Department extends BaseEntity { @TableField private Long orgId; + @BindQuery(comparison = Comparison.CONTAINS) @TableField private String name; } \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java b/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java index 1f62784..9fa2d3c 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/DepartmentService.java @@ -15,9 +15,14 @@ */ package diboot.core.test.binder.service; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.diboot.core.service.BaseService; +import com.diboot.core.vo.Pagination; +import diboot.core.test.binder.dto.DepartmentDTO; import diboot.core.test.binder.entity.Department; +import java.util.List; + /** * 部门相关Service * @author mazc@dibo.ltd @@ -26,4 +31,5 @@ import diboot.core.test.binder.entity.Department; */ public interface DepartmentService extends BaseService { + List getDepartmentSqlList(QueryWrapper queryWrapper, Pagination pagination); } diff --git a/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java index 2716d8b..e9ace9e 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/service/impl/DepartmentServiceImpl.java @@ -15,19 +15,39 @@ */ package diboot.core.test.binder.service.impl; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.diboot.core.service.impl.BaseServiceImpl; +import com.diboot.core.vo.Pagination; +import diboot.core.test.binder.dto.DepartmentDTO; import diboot.core.test.binder.entity.Department; import diboot.core.test.binder.mapper.DepartmentMapper; import diboot.core.test.binder.service.DepartmentService; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.List; + /** * 部门相关Service实现 * @author mazc@dibo.ltd * @version v2.0 * @date 2019/1/30 */ +@Slf4j @Service public class DepartmentServiceImpl extends BaseServiceImpl implements DepartmentService { + @Override + public List getDepartmentSqlList(QueryWrapper queryWrapper, Pagination pagination) { + // 如果是单表,return super. + boolean isSingleTableQuery = false; + if(isSingleTableQuery){ + return super.getEntityList(queryWrapper, pagination); + } + else{ + + return null; + } + } + } diff --git a/diboot-core/src/test/java/diboot/core/test/binder/vo/DepartmentVO.java b/diboot-core/src/test/java/diboot/core/test/binder/vo/DepartmentVO.java index a6cbf8f..d062217 100644 --- a/diboot-core/src/test/java/diboot/core/test/binder/vo/DepartmentVO.java +++ b/diboot-core/src/test/java/diboot/core/test/binder/vo/DepartmentVO.java @@ -16,6 +16,9 @@ package diboot.core.test.binder.vo; import com.baomidou.mybatisplus.annotation.TableField; +import com.diboot.core.binding.annotation.BindEntity; +import diboot.core.test.binder.entity.Department; +import diboot.core.test.binder.entity.Organization; import lombok.Getter; import lombok.Setter; import lombok.experimental.Accessors; @@ -38,4 +41,15 @@ public class DepartmentVO { @TableField(exist = false) private String name; + @TableField + private Long orgId; + + // 通过中间表关联Entity + @BindEntity(entity = Department.class, condition = "this.parent_id=id") // AND ... + private Department department; + + // 通过中间表关联Entity + @BindEntity(entity = Organization.class, condition = "this.org_id=id") // AND ... + private OrganizationVO organizationVO; + } \ No newline at end of file diff --git a/diboot-core/src/test/java/diboot/core/test/service/BaseServiceTest.java b/diboot-core/src/test/java/diboot/core/test/service/BaseServiceTest.java index dd6517b..bd83652 100644 --- a/diboot-core/src/test/java/diboot/core/test/service/BaseServiceTest.java +++ b/diboot-core/src/test/java/diboot/core/test/service/BaseServiceTest.java @@ -32,7 +32,6 @@ import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; @@ -52,7 +51,6 @@ import java.util.*; public class BaseServiceTest { @Autowired - @Qualifier("dictionaryService") DictionaryServiceImpl dictionaryService; @Test @@ -87,6 +85,11 @@ public class BaseServiceTest { List ids = BeanUtils.collectIdToList(dictionaryList); dictionaryList = dictionaryService.getEntityListByIds(ids); Assert.assertTrue(V.notEmpty(dictionaryList)); + + // 获取map + List> mapList = dictionaryService.getMapList(null, new Pagination()); + Assert.assertTrue(mapList.size() > 0 && mapList.size() <= BaseConfig.getPageSize()); + } @Test @@ -144,6 +147,7 @@ public class BaseServiceTest { dictionaryList.get(2).setItemValue("HZ2"); dictionaryService.updateEntity(dictionaryList.get(2)); Assert.assertTrue(success); + } @Test diff --git a/diboot-core/src/test/resources/init-mysql.sql b/diboot-core/src/test/resources/init-mysql.sql index 04d35ad..18534ac 100644 --- a/diboot-core/src/test/resources/init-mysql.sql +++ b/diboot-core/src/test/resources/init-mysql.sql @@ -71,7 +71,7 @@ create table user_role -- 初始化样例数据 INSERT INTO department (id, parent_id, org_id, name) -VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组'); +VALUES (10001, 0, 100001, '产品部'), (10002, 10001, 100001, '研发组'), (10003, 10001, 100001, '测试组'), (10004, 10001, 100001, 'UI组'); INSERT INTO dictionary (id, parent_id, type, item_name, item_value, description, extdata, sort_id, `is_deletable`, is_editable) VALUES (1, 0, 'GENDER', '性别', null, '', null, 99, 0, 1), (2, 1, 'GENDER', '男', 'M', null, null, 99, 0, 1), (3, 1, 'GENDER', '女', 'F', null, null, 99, 0, 1); diff --git a/diboot-docs/guide/diboot-core/无SQL关联.md b/diboot-docs/guide/diboot-core/无SQL关联.md index 882f923..1d6f837 100644 --- a/diboot-docs/guide/diboot-core/无SQL关联.md +++ b/diboot-docs/guide/diboot-core/无SQL关联.md @@ -27,19 +27,19 @@ public class DemoVO extends Demo { ## 处理方式 1. 通过在VO类中添加相关字段,以及对应的关联绑定注解,来定义我们的绑定类型和需要得到的结果以及额外的条件等信息; -2. 绑定完成后,我们需要调用**RelationsBinder**类中的相关方法类执行这个绑定关系,我们目前提供了两种方式可供处理: +2. 绑定完成后,我们需要调用**Binder**类中的相关方法(*bindRelations)执行这个绑定关系,我们目前提供了两种方式可供处理: ### 自动绑定关联 > 该关联会自动将相关信息查询并设置到voList中,适用于对已有的voList做处理,如: ```java //List voList = ...; -RelationsBinder.bind(voList); +Binder.bindRelations(voList); ``` ### 自动转型并绑定关联 > 该关联会自动将vo所继承的父类的实体列表进行绑定并自动转型为voList,适用于对于非voList的实体列表等做处理,如: ```java // 查询单表获取Entity集合 // List entityList = userService.list(queryWrapper); -List voList = RelationsBinder.convertAndBind(userList, MyUserVO.class); +List voList = Binder.convertAndBindRelations(userList, MyUserVO.class); ``` ## 数据字典关联绑定 diff --git a/diboot-file-starter/pom.xml b/diboot-file-starter/pom.xml index 4ae6354..4e6acd0 100644 --- a/diboot-file-starter/pom.xml +++ b/diboot-file-starter/pom.xml @@ -7,11 +7,11 @@ diboot-root com.diboot - 2.0.5 + 2.0.6 diboot-file-spring-boot-starter - 2.0.5 + 2.0.6 jar diboot file component project @@ -25,7 +25,7 @@ com.diboot diboot-core-spring-boot-starter - 2.0.5 + 2.0.6 @@ -38,25 +38,26 @@ com.alibaba easyexcel - 2.1.6 + 2.1.7 com.squareup.okhttp3 okhttp - 4.3.1 + 4.4.1 net.coobird thumbnailator - 0.4.9 + 0.4.11 com.github.whvcse easy-captcha 1.6.2 + provided diff --git a/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java b/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java index 8165aea..2fe38d3 100644 --- a/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java +++ b/diboot-file-starter/src/main/java/com/diboot/file/util/HttpHelper.java @@ -93,22 +93,7 @@ public class HttpHelper { put("xsd", "text/xml"); put("xsl", "text/xml"); put("xslt", "text/xml"); - put("apk", "application/vnd.android./* - * Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd). - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - *

- * https://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package-archive"); + put("apk", "application/vnd.android.package-archive"); }}; /** diff --git a/iam-base-starter/pom.xml b/iam-base-starter/pom.xml index 93449f6..337f05f 100644 --- a/iam-base-starter/pom.xml +++ b/iam-base-starter/pom.xml @@ -7,11 +7,11 @@ com.diboot diboot-root - 2.0.5 + 2.0.6 diboot-iam-base-spring-boot-starter - 2.0.5.1 + 2.0.6 jar diboot IAM base project @@ -25,14 +25,14 @@ com.diboot diboot-core-spring-boot-starter - 2.0.5 + 2.0.6 org.apache.shiro shiro-spring-boot-web-starter - 1.4.1 + 1.5.2 io.jsonwebtoken diff --git a/iam-base-starter/src/main/java/com/diboot/iam/service/impl/IamUserServiceImpl.java b/iam-base-starter/src/main/java/com/diboot/iam/service/impl/IamUserServiceImpl.java index 7e6d703..6a515f0 100644 --- a/iam-base-starter/src/main/java/com/diboot/iam/service/impl/IamUserServiceImpl.java +++ b/iam-base-starter/src/main/java/com/diboot/iam/service/impl/IamUserServiceImpl.java @@ -16,7 +16,7 @@ package com.diboot.iam.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import com.diboot.core.binding.RelationsBinder; +import com.diboot.core.binding.Binder; import com.diboot.core.exception.BusinessException; import com.diboot.core.util.S; import com.diboot.core.util.V; @@ -94,7 +94,7 @@ public class IamUserServiceImpl extends BaseIamServiceImpl org.springframework.boot spring-boot-starter-parent - 2.2.4.RELEASE + 2.2.6.RELEASE com.diboot diboot-root - 2.0.5 + 2.0.6 pom @@ -25,7 +25,7 @@ 1.8 - 2.2.4.RELEASE + 2.2.6.RELEASE @@ -33,7 +33,7 @@ org.projectlombok lombok - 1.18.10 + 1.18.12 provided @@ -65,22 +65,22 @@ com.baomidou mybatis-plus-boot-starter - 3.2.0 + 3.3.1 org.hibernate.validator hibernate-validator - 6.0.18.Final + 6.0.19.Final com.alibaba fastjson - 1.2.60 + 1.2.68 org.apache.commons commons-lang3 - 3.9 + 3.10