Merge remote-tracking branch 'remotes/origin/develop'
# Conflicts: # README.md
This commit is contained in:
commit
430030edda
51
README.md
51
README.md
|
@ -1,6 +1,6 @@
|
||||||
> 提示:diboot v2.0.5版本暂不支持spring boot 2.3,请切换至2.2.x版本。
|
> v2.1版本发布,可以告别常规SQL和CRUD了!
|
||||||
|
|
||||||
# diboot
|
# diboot - 化繁为简,以简驭繁
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank">
|
<a href="http://www.apache.org/licenses/LICENSE-2.0.html" target="_blank">
|
||||||
<img src="https://img.shields.io/hexpm/l/plug.svg">
|
<img src="https://img.shields.io/hexpm/l/plug.svg">
|
||||||
|
@ -10,34 +10,41 @@
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
> [设计目标](https://segmentfault.com/a/1190000020906742):面向开发人员的低代码开发平台,将重复性的工作自动化,提高质量、效率、可维护性。
|
> 设计目标:面向开发人员的低代码开发平台,将重复性的工作自动化,提高质量、效率、可维护性。
|
||||||
|
|
||||||
![diboot平台组成结构图](diboot-docs/.vuepress/public/structure.png)
|
![diboot平台组成结构图](diboot-docs/.vuepress/public/structure.png)
|
||||||
diboot v2版本,目前实现: diboot-core全新内核 + diboot-devtools开发助理 + IAM身份认证、file文件处理等基础组件 + diboot-*-admin基础后台。
|
|
||||||
|
diboot v2版本,目前实现: diboot-core高效内核 + diboot-devtools开发助理 + IAM身份认证、file文件处理等基础组件 + diboot-*-admin基础后台。
|
||||||
|
|
||||||
## 一、 diboot-core: 精简优化内核
|
## 一、 diboot-core: 精简优化内核
|
||||||
全新精简内核,(基于diboot-core 2.x版本的CRUD和简单关联的常规功能实现,代码量比1.x版本减少70%+),主要实现:
|
高效精简内核,重构查询方式(拆解关联查询,程序中Join),简化开发,主要实现:
|
||||||
#### 1. 单表CRUD无SQL
|
#### 1. 单表CRUD无SQL
|
||||||
> 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器)
|
> 基于Mybatis-Plus实现(Mybatis-Plus具备通用Mapper方案和灵活的查询构造器)
|
||||||
#### 2. 关联查询无SQL(注解自动绑定)
|
#### 2. 关联绑定无SQL(注解自动绑定)
|
||||||
> 扩展实现了多表关联查询的无SQL方案,只需要一个简单注解@Bind*,就可以实现关联对象(含字段、实体、实体集合等)的数据绑定,且实现方案是将关联查询拆解为单表查询,保障最佳性能。
|
> 扩展实现了多表关联查询的无SQL方案,只需要一个简单注解@Bind*,就可以实现关联对象(含字段、字段集合、实体、实体集合等)的数据绑定,且实现方案是将关联查询拆解为单表查询,保障最佳性能。
|
||||||
#### 3. 数据字典无SQL(注解自动绑定)
|
#### 3. 数据字典无SQL(注解自动绑定)
|
||||||
> 通过@BindDict注解实现数据字典(枚举)的存储值value与显示值name的转换。
|
> 通过@BindDict注解实现数据字典(枚举)的存储值value与显示值name的转换。
|
||||||
#### 4. Entity/DTO自动转换为QueryWrapper
|
#### 4. 跨表查询无SQL(自动构建QueryWrapper与查询)
|
||||||
> @BindQuery注解绑定字段参数对应的查询条件类型,Controller中直接绑定转换为QueryWrapper,无需再手动构建QueryWrapper查询条件
|
> @BindQuery注解绑定字段查询方式及关联表,自动构建QueryWrapper,并动态执行单表或Join联表查询。
|
||||||
#### 5. 其他常用Service接口、工具类的最佳实践封装
|
#### 5. BaseService扩展增强,支持常规的单表及关联开发场景接口
|
||||||
> 字符串处理、常用校验、BeanUtils、DateUtils等
|
> createEntityAndRelatedEntities、getValuesOfField、exists、getKeyValueList、getViewObject*等接口
|
||||||
|
#### 6. 其他常用工具类、状态码、异常处理的最佳实践封装
|
||||||
|
> JsonResult、字符串处理、常用校验、BeanUtils、DateUtils等
|
||||||
|
|
||||||
|
基于diboot-core 2.x版本的CRUD和简单关联的常规功能实现,代码量比传统Mybatis项目减少80%+),且实现更高效更易维护。
|
||||||
更多介绍请查看: [diboot-core README](https://github.com/dibo-software/diboot-v2/tree/master/diboot-core "注解自动绑定多表关联").
|
更多介绍请查看: [diboot-core README](https://github.com/dibo-software/diboot-v2/tree/master/diboot-core "注解自动绑定多表关联").
|
||||||
|
|
||||||
|
|
||||||
## 二、 diboot-devtools 自动化开发助理
|
## 二、 diboot-devtools 自动化开发助理
|
||||||
|
|
||||||
|
* 使用简单(UI界面操作,引入依赖配置参数后,即可随SpringBoot本地项目启动运行)
|
||||||
|
* 功能很强大:
|
||||||
|
* 单表与关联场景CRUD导入导出的完整功能全自动生成,无需手写代码
|
||||||
|
* 数据结构变更与代码联动同步,自动记录变更SQL、维护索引
|
||||||
|
* 一键生成代码&非覆盖式更新本地后端代码
|
||||||
|
* 配置很灵活(可按需配置生成代码路径,是否启用`Lombok`、`Swagger`等)
|
||||||
|
* SQL与代码很标准(devtools标准化了数据结构定义与代码实现)
|
||||||
* 支持多数据库(MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL)
|
* 支持多数据库(MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL)
|
||||||
* 使用很简单(UI界面操作,引入依赖,配置参数后,即可随SpringBoot本地项目启动运行)
|
|
||||||
* 功能很强大(数据结构变更与代码联动同步,一键生成&非覆盖式更新代码,自动记录变更SQL、维护索引)
|
|
||||||
* 配置很灵活(可按需配置生成代码是否启用`Lombok`、`Swagger`等)
|
|
||||||
* SQL与代码很标准(devtools标准化了数据结构定义与代码实现,降低维护成本)
|
|
||||||
> [我要试试](https://www.diboot.com/guide/diboot-devtools/%E4%BB%8B%E7%BB%8D.html)
|
> [我要试试](https://www.diboot.com/guide/diboot-devtools/%E4%BB%8B%E7%BB%8D.html)
|
||||||
|
|
||||||
## 三、iam-base 身份认证基础组件 及 配套VUE前端框架(diboot-antd-admin、diboot-element-admin)
|
## 三、iam-base 身份认证基础组件 及 配套VUE前端框架(diboot-antd-admin、diboot-element-admin)
|
||||||
|
@ -45,27 +52,29 @@ diboot v2版本,目前实现: diboot-core全新内核 + diboot-devtools开发
|
||||||
* RBAC角色权限模型 + JWT的认证授权 实现,支持刷新token
|
* RBAC角色权限模型 + JWT的认证授权 实现,支持刷新token
|
||||||
* 简化的BindPermission注解,支持兼容shiro的简化权限绑定与自动鉴权
|
* 简化的BindPermission注解,支持兼容shiro的简化权限绑定与自动鉴权
|
||||||
* 自动提取需要验证的后端接口, 借助前端功能方便绑定前后端菜单按钮权限
|
* 自动提取需要验证的后端接口, 借助前端功能方便绑定前后端菜单按钮权限
|
||||||
|
* 支持基于注解的数据权限实现
|
||||||
* 支持灵活的扩展能力(扩展多种登录方式、灵活替换用户实体类、自定义缓存等)
|
* 支持灵活的扩展能力(扩展多种登录方式、灵活替换用户实体类、自定义缓存等)
|
||||||
* Starter启动自动安装依赖的数据表
|
|
||||||
* 启用devtools,自动生成初始controller代码到本地
|
|
||||||
|
|
||||||
更多介绍请查看: [iam-base-starter README](https://github.com/dibo-software/diboot-v2/tree/master/iam-base-starter "身份认证管理组件").
|
更多介绍请查看: [iam-base-starter README](https://github.com/dibo-software/diboot-v2/tree/master/iam-base-starter "身份认证管理组件").
|
||||||
|
|
||||||
## 四、diboot-file 文件相关处理组件
|
## 四、diboot-file 文件相关处理组件
|
||||||
|
|
||||||
* EasyExcel轻量封装,支持Java注解校验与@BindDict注解实现字典name-value转换,提供完善的校验错误提示
|
* EasyExcel轻量封装,支持Java注解校验与@ExcelBind*注解实现字典及关联字段的name-value转换,并提供完善的校验错误提示
|
||||||
* 封装常用的文件本地存储、上传下载、图片压缩水印等常用处理
|
* 封装常用的文件本地存储、上传下载、图片压缩水印等常用处理
|
||||||
* Starter启动自动安装依赖的数据表
|
|
||||||
* 启用devtools,自动生成初始样例controller代码到本地
|
|
||||||
|
|
||||||
更多介绍请查看: [diboot-file-starter README](https://github.com/dibo-software/diboot-v2/tree/master/diboot-file-starter "文件组件").
|
更多介绍请查看: [diboot-file-starter README](https://github.com/dibo-software/diboot-v2/tree/master/diboot-file-starter "文件组件").
|
||||||
|
|
||||||
> 其他组件逐步开发中 ...
|
> 其他组件逐步开发中 ...
|
||||||
|
|
||||||
## 五、技术交流群
|
## 五、捐助支持
|
||||||
|
![捐助二维码](https://www.diboot.com/assets/img/donate.a25badf5.jpg)
|
||||||
|
|
||||||
|
感谢JetBrains提供Open Source license,[JetBrains IDEA](https://www.jetbrains.com/?from=diboot) 是最好的Java IDE。
|
||||||
|
|
||||||
|
## 六、技术交流群
|
||||||
如果您有技术问题,欢迎加群交流:
|
如果您有技术问题,欢迎加群交流:
|
||||||
|
|
||||||
> QQ群: [731690096]()
|
> QQ群: [731690096]()
|
||||||
|
|
||||||
> 微信群(备注diboot)加: [wx20201024]()
|
> 微信群(备注diboot)加: [wx20201024]()
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.diboot</groupId>
|
<groupId>com.diboot</groupId>
|
||||||
<artifactId>diboot-root</artifactId>
|
<artifactId>diboot-root</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>2.1.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>diboot-core-spring-boot-starter</artifactId>
|
<artifactId>diboot-core-spring-boot-starter</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>2.1.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<description>diboot core starter project</description>
|
<description>diboot core starter project</description>
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.diboot</groupId>
|
<groupId>com.diboot</groupId>
|
||||||
<artifactId>diboot-core</artifactId>
|
<artifactId>diboot-core</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>2.1.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -18,8 +18,9 @@ package com.diboot.core.starter;
|
||||||
import com.alibaba.fastjson.serializer.SerializerFeature;
|
import com.alibaba.fastjson.serializer.SerializerFeature;
|
||||||
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
import com.alibaba.fastjson.support.config.FastJsonConfig;
|
||||||
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
import com.diboot.core.util.D;
|
import com.diboot.core.util.DateConverter;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
@ -30,8 +31,10 @@ import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
import org.springframework.format.FormatterRegistry;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.converter.HttpMessageConverter;
|
import org.springframework.http.converter.HttpMessageConverter;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -48,7 +51,7 @@ import java.util.List;
|
||||||
@ComponentScan(basePackages={"com.diboot.core"})
|
@ComponentScan(basePackages={"com.diboot.core"})
|
||||||
@MapperScan(basePackages = {"com.diboot.core.mapper"})
|
@MapperScan(basePackages = {"com.diboot.core.mapper"})
|
||||||
@Order(1)
|
@Order(1)
|
||||||
public class CoreAutoConfiguration{
|
public class CoreAutoConfiguration implements WebMvcConfigurer {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
Environment environment;
|
Environment environment;
|
||||||
|
@ -70,6 +73,7 @@ public class CoreAutoConfiguration{
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
public HttpMessageConverters fastJsonHttpMessageConverters() {
|
public HttpMessageConverters fastJsonHttpMessageConverters() {
|
||||||
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
|
||||||
converter.setDefaultCharset(Charset.forName(Cons.CHARSET_UTF8));
|
converter.setDefaultCharset(Charset.forName(Cons.CHARSET_UTF8));
|
||||||
|
@ -81,11 +85,29 @@ public class CoreAutoConfiguration{
|
||||||
// 设置fastjson的序列化参数:禁用循环依赖检测,数据兼容浏览器端(避免JS端Long精度丢失问题)
|
// 设置fastjson的序列化参数:禁用循环依赖检测,数据兼容浏览器端(避免JS端Long精度丢失问题)
|
||||||
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
|
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
|
||||||
SerializerFeature.BrowserCompatible);
|
SerializerFeature.BrowserCompatible);
|
||||||
fastJsonConfig.setDateFormat(D.FORMAT_DATETIME_Y4MDHM);
|
|
||||||
converter.setFastJsonConfig(fastJsonConfig);
|
converter.setFastJsonConfig(fastJsonConfig);
|
||||||
|
|
||||||
HttpMessageConverter<?> httpMsgConverter = converter;
|
HttpMessageConverter<?> httpMsgConverter = converter;
|
||||||
return new HttpMessageConverters(httpMsgConverter);
|
return new HttpMessageConverters(httpMsgConverter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mybatis-plus分页插件
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public PaginationInterceptor paginationInterceptor() {
|
||||||
|
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
|
||||||
|
return paginationInterceptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认支持String-Date类型转换
|
||||||
|
* @param registry
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addFormatters(FormatterRegistry registry) {
|
||||||
|
registry.addConverter(new DateConverter());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,19 +1,20 @@
|
||||||
# diboot-core: 全新优化内核
|
## diboot-core: 高效精简内核
|
||||||
主要实现:
|
主要实现:
|
||||||
1. 单表CRUD和多表关联查询的无SQL化
|
1. 单表CRUD和多表关联查询的无SQL化
|
||||||
2. Entity/DTO自动转换为QueryWrapper(@BindQuery注解绑定字段参数对应的查询条件,无注解默认映射为等于=条件)
|
2. Entity/DTO自动转换为QueryWrapper(@BindQuery注解绑定字段参数的查询条件,可自动构建关联查询)
|
||||||
3. 提供其他常用开发场景的最佳实践封装。
|
3. 提供其他常用开发场景的最佳实践封装。
|
||||||
|
|
||||||
## ** 一. 单表CRUD无SQL
|
### ** 一. 单表CRUD无SQL
|
||||||
> 依赖Mybatis-plus实现(Mybatis-plus具备通用Mapper方案和灵活的查询构造器)
|
> 依赖Mybatis-plus实现(Mybatis-plus具备通用Mapper方案和灵活的查询构造器)
|
||||||
## ** 二. 多表关联查询无SQL(通过注解绑定关联,自动拆分成单表查询并绑定结果)
|
### ** 二. 多表关联查询无SQL
|
||||||
> 通过注解实现多数场景下的关联查询无SQL
|
> 通过@Bind*注解绑定关联,自动拆分成单表查询并绑定结果
|
||||||
### 1. 注解自动绑定数据字典(自定义枚举)的显示值Label
|
[(了解拆解关联查询的价值)](https://www.kancloud.cn/ddupl/sql_optimize/1141077)
|
||||||
|
#### 1. 注解自动绑定数据字典(自定义枚举)的显示值Label
|
||||||
~~~java
|
~~~java
|
||||||
@BindDict(type="USER_STATUS", field = "status")
|
@BindDict(type="USER_STATUS", field = "status")
|
||||||
private String statusLabel;
|
private String statusLabel;
|
||||||
~~~
|
~~~
|
||||||
### 2. 注解自动绑定其他表的字段
|
#### 2. 注解自动绑定其他表的字段
|
||||||
~~~java
|
~~~java
|
||||||
// 支持关联条件+附加条件绑定字段
|
// 支持关联条件+附加条件绑定字段
|
||||||
@BindField(entity=Department.class, field="name", condition="department_id=id AND parent_id>=0")
|
@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")
|
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
||||||
private String orgName;
|
private String orgName;
|
||||||
~~~
|
~~~
|
||||||
### 3. 注解自动绑定其他表实体Entity
|
#### 3. 注解自动绑定其他表实体Entity/VO
|
||||||
~~~java
|
~~~java
|
||||||
// 支持关联条件+附加条件绑定Entity
|
// 支持关联条件+附加条件绑定Entity
|
||||||
@BindEntity(entity = Department.class, condition="department_id=id")
|
@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")
|
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id AND department.deleted=0")
|
||||||
private Organization organization;
|
private Organization organization;
|
||||||
~~~
|
~~~
|
||||||
### 4. 注解自动绑定其他表实体集合List<Entity>
|
#### 4. 注解自动绑定其他表实体集合List<Entity>
|
||||||
~~~java
|
~~~java
|
||||||
// 支持关联条件+附加条件绑定多个Entity
|
// 支持关联条件+附加条件绑定多个Entity
|
||||||
@BindEntityList(entity = Department.class, condition = "id=parent_id")
|
@BindEntityList(entity = Department.class, condition = "id=parent_id")
|
||||||
|
@ -43,45 +44,42 @@ private List<Department> children;
|
||||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
private List<Role> roleList;
|
private List<Role> roleList;
|
||||||
~~~
|
~~~
|
||||||
|
#### 5. 注解自动绑定其他表某字段集合List<>
|
||||||
|
~~~java
|
||||||
|
// 支持关联条件+附加条件绑定多个Entity的某字段
|
||||||
|
@BindFieldList(entity = Department.class, field="id", condition = "id=parent_id")
|
||||||
|
private List<Long> childrenIds;
|
||||||
|
|
||||||
## ** 三. 注解绑定关联的使用方式
|
// 通过中间表的 多对多关联 绑定Entity某字段(支持附加条件)
|
||||||
### 1. 引入依赖
|
@BindEntityList(entity = Role.class, field="code", condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
Gradle:
|
private List<String> roleCodes;
|
||||||
~~~gradle
|
|
||||||
compile("com.diboot:diboot-core-spring-boot-starter:2.0.5")
|
|
||||||
~~~
|
~~~
|
||||||
或Maven
|
|
||||||
~~~xml
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.diboot</groupId>
|
|
||||||
<artifactId>diboot-core-spring-boot-starter</artifactId>
|
|
||||||
<version>2.0.5</version>
|
|
||||||
</dependency>
|
|
||||||
~~~
|
|
||||||
> * 使用diboot-devtools,会自动引入diboot-core,无需配置此依赖。
|
|
||||||
> * @BindDict注解需要依赖dictionary表,初次启动时starter会自动创建该表。
|
|
||||||
|
|
||||||
### 2. 定义你的Service(继承diboot的BaseService或Mybatis-plus的ISerivice)及Mapper
|
### ** 三. 注解绑定关联的使用方式
|
||||||
|
|
||||||
### 3. 使用注解绑定:
|
#### 1. 定义你的Entity对应的Service(继承diboot的BaseService)及Mapper
|
||||||
调用RelationsBinder自动绑定注解相关关联:
|
> * 启用diboot-devtools,自动生成后端各层代码。
|
||||||
#### 方式1. 自动绑定关联(不需要转型)
|
#### 2. 参照以上注解说明在VO中定义你的关联
|
||||||
|
|
||||||
|
#### 3. 使用注解绑定:
|
||||||
|
调用Binder自动绑定注解相关关联:
|
||||||
|
##### 方式1. 自动绑定关联(不需要转型)
|
||||||
~~~java
|
~~~java
|
||||||
//List<MyUserVO> voList = ...;
|
//List<MyUserVO> voList = ...;
|
||||||
RelationsBinder.bind(voList);
|
Binder.bindRelations(voList);
|
||||||
~~~
|
~~~
|
||||||
#### 方式2. 自动转型并绑定关联(需要转型)
|
##### 方式2. 自动转型并绑定关联(需要转型)
|
||||||
~~~java
|
~~~java
|
||||||
// 查询单表获取Entity集合
|
// 查询单表获取Entity集合
|
||||||
// List<User> entityList = userService.list(queryWrapper);
|
// List<User> entityList = userService.list(queryWrapper);
|
||||||
List<MyUserVO> voList = RelationsBinder.convertAndBind(userList, MyUserVO.class);
|
List<MyUserVO> voList = Binder.convertAndBindRelations(userList, MyUserVO.class);
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## ** 四. Entity/DTO自动转换为QueryWrapper的使用方式
|
### ** 四. Entity/DTO自动转换QueryWrapper自动构建Join查询
|
||||||
### 1. Entity/DTO中声明映射查询条件
|
#### 1. Entity/DTO中声明映射查询条件
|
||||||
示例代码:
|
示例代码:
|
||||||
~~~java
|
~~~java
|
||||||
public class UserDTO{
|
public class UserDTO {
|
||||||
// 无@BindQuery注解默认会映射为=条件
|
// 无@BindQuery注解默认会映射为=条件
|
||||||
private Long gender;
|
private Long gender;
|
||||||
|
|
||||||
|
@ -89,22 +87,76 @@ public class UserDTO{
|
||||||
@BindQuery(comparison = Comparison.LIKE)
|
@BindQuery(comparison = Comparison.LIKE)
|
||||||
private String realname;
|
private String realname;
|
||||||
|
|
||||||
//... getter, setter
|
// join其他表
|
||||||
|
@BindQuery(comparison = Comparison.STARTSWITH, entity=Organization.class, field="name", condition="this.org_id=id")
|
||||||
|
private String orgName;
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
### 2. 调用QueryBuilder.toQueryWrapper(entityOrDto)进行转换
|
#### 2. 调用QueryBuilder.toQueryWrapper(entityOrDto)进行转换
|
||||||
~~~java
|
~~~java
|
||||||
/**
|
/**
|
||||||
* url参数示例: /list?gender=M&realname=张
|
* url参数示例: /list?gender=M&realname=张
|
||||||
* 将映射为 queryWrapper.eq("gender", "M").like("realname", "张")
|
* 将映射为 queryWrapper.eq("gender", "M").like("realname", "张")
|
||||||
*/
|
*/
|
||||||
@GetMapping("/list")
|
@GetMapping("/list")
|
||||||
public JsonResult getVOList(UserDto userDto, HttpServletRequest request) throws Exception{
|
public JsonResult getVOList(UserDto userDto) throws Exception{
|
||||||
//调用super.buildQueryWrapper(entityOrDto, request) 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行转换
|
//调用super.buildQueryWrapper(entityOrDto) 进行转换
|
||||||
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto, request);
|
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto);
|
||||||
|
// 或者直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 转换
|
||||||
|
//QueryWrapper<User> queryWrapper = QueryBuilder.buildQueryWrapper(userDto);
|
||||||
|
|
||||||
//... 查询list
|
//... 查询list
|
||||||
return JsonResult.OK(list);
|
return JsonResult.OK(list);
|
||||||
}
|
}
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
## 五. 样例参考 - [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example)
|
#### 3. 支持动态Join的关联查询与结果绑定
|
||||||
|
> 动态查询的调用方式有以下两种:
|
||||||
|
##### 方式1. 通过QueryBuilder链式调用
|
||||||
|
~~~java
|
||||||
|
QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(Department.class);
|
||||||
|
~~~
|
||||||
|
##### 方式2. 通过QueryBuilder构建QueryWrapper,再调用Binder或JoinsBinder
|
||||||
|
~~~java
|
||||||
|
// 构建QueryWrapper
|
||||||
|
QueryWrapper<DTO> queryWrapper = QueryBuilder.toQueryWrapper(dto);
|
||||||
|
// 调用join关联查询绑定
|
||||||
|
List<Entity> list = Binder.joinQueryList(queryWrapper, Department.class);
|
||||||
|
~~~
|
||||||
|
|
||||||
|
绑定调用将自动按需(有表的查询字段时才Join)构建类似如下动态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:{latestVersion}")
|
||||||
|
~~~
|
||||||
|
或Maven
|
||||||
|
~~~xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.diboot</groupId>
|
||||||
|
<artifactId>diboot-core-spring-boot-starter</artifactId>
|
||||||
|
<version>{latestVersion}</version>
|
||||||
|
</dependency>
|
||||||
|
~~~
|
||||||
|
|
||||||
|
#### 2. 配置数据源
|
||||||
|
以Mysql为例:
|
||||||
|
~~~properties
|
||||||
|
#datasource config
|
||||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/diboot_example?characterEncoding=utf8&serverTimezone=GMT%2B8
|
||||||
|
spring.datasource.username=diboot
|
||||||
|
spring.datasource.password=123456
|
||||||
|
spring.datasource.hikari.maximum-pool-size=5
|
||||||
|
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
~~~
|
||||||
|
|
||||||
|
> * @BindDict注解需要依赖dictionary表,依赖diboot-core-spring-boot-starter,初次启动时starter会自动创建该表。
|
||||||
|
|
||||||
|
#### 3. 详细文档 - [diboot-core 官方文档](https://www.diboot.com/guide/diboot-core/%E5%AE%89%E8%A3%85.html)
|
||||||
|
|
||||||
|
#### 4. 参考样例 - [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example)
|
||||||
|
|
|
@ -7,11 +7,11 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.diboot</groupId>
|
<groupId>com.diboot</groupId>
|
||||||
<artifactId>diboot-root</artifactId>
|
<artifactId>diboot-root</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>2.1.1</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>diboot-core</artifactId>
|
<artifactId>diboot-core</artifactId>
|
||||||
<version>2.0.5</version>
|
<version>2.1.1</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<description>diboot core project</description>
|
<description>diboot core project</description>
|
||||||
|
|
||||||
|
@ -36,6 +36,17 @@
|
||||||
<resource>
|
<resource>
|
||||||
<directory>src/main/resources</directory>
|
<directory>src/main/resources</directory>
|
||||||
</resource>
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/test/java</directory>
|
||||||
|
<includes>
|
||||||
|
<include>**/*.xml</include>
|
||||||
|
<include>**/*.dtd</include>
|
||||||
|
</includes>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/test/resources</directory>
|
||||||
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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 <DTO,E> E joinQueryOne(QueryWrapper<DTO> queryWrapper, Class<E> entityClazz){
|
||||||
|
return JoinsBinder.queryOne(queryWrapper, entityClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联查询符合条件的全部主表数据集合(不分页)
|
||||||
|
* @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例
|
||||||
|
* @param entityClazz 返回结果entity/vo类
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <DTO,E> List<E> joinQueryList(QueryWrapper<DTO> queryWrapper, Class<E> entityClazz){
|
||||||
|
return JoinsBinder.queryList(queryWrapper, entityClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联查询符合条件的指定页数据(分页)
|
||||||
|
* @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例
|
||||||
|
* @param entityClazz 返回结果entity/vo类
|
||||||
|
* @param pagination 分页
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <DTO,E> List<E> joinQueryList(QueryWrapper<DTO> queryWrapper, Class<E> entityClazz, Pagination pagination){
|
||||||
|
return JoinsBinder.queryList(queryWrapper, entityClazz, pagination);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动转换和绑定单个VO中的注解关联(禁止循环调用,多个对象请调用convertAndBind(voList, voClass))
|
||||||
|
* @param voClass 需要转换的VO class
|
||||||
|
* @param <E>
|
||||||
|
* @param <VO>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <E, VO> VO convertAndBindRelations(E entity, Class<VO> voClass){
|
||||||
|
return RelationsBinder.convertAndBind(entity, voClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动转换和绑定多个VO中的注解关联
|
||||||
|
* @param entityList 需要转换的VO list
|
||||||
|
* @param voClass VO class
|
||||||
|
* @param <E>
|
||||||
|
* @param <VO>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <E, VO> List<VO> convertAndBindRelations(List<E> entityList, Class<VO> voClass){
|
||||||
|
return RelationsBinder.convertAndBind(entityList, voClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动绑定单个VO的关联对象(禁止循环调用,多个对象请调用bind(voList))
|
||||||
|
* @param vo 需要注解绑定的对象
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <VO> void bindRelations(VO vo){
|
||||||
|
RelationsBinder.bind(vo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动绑定多个VO集合的关联对象
|
||||||
|
* @param voList 需要注解绑定的对象集合
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <VO> void bindRelations(List<VO> voList){
|
||||||
|
RelationsBinder.bind(voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.config.Cons;
|
||||||
|
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.1
|
||||||
|
* @date 2020/04/15
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class JoinsBinder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联查询一条数据
|
||||||
|
* @param queryWrapper
|
||||||
|
* @param entityClazz 返回结果entity/vo类
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <DTO,E> E queryOne(QueryWrapper<DTO> queryWrapper, Class<E> entityClazz){
|
||||||
|
List<E> 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 <DTO,E> List<E> queryList(QueryWrapper<DTO> queryWrapper, Class<E> entityClazz){
|
||||||
|
return queryList(queryWrapper, entityClazz, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联查询符合条件的指定页数据(分页)
|
||||||
|
* @param queryWrapper 调用QueryBuilder.to*QueryWrapper得到的实例
|
||||||
|
* @param entityClazz 返回结果entity/vo类
|
||||||
|
* @param pagination 分页
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static <DTO,E> List<E> queryList(QueryWrapper<DTO> queryWrapper, Class<E> 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 <DTO,E> List<E> executeJoinQuery(QueryWrapper<DTO> queryWrapper, Class<E> 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<Map<String, Object>> mapList = null;
|
||||||
|
if(pagination == null){
|
||||||
|
if(limit1){
|
||||||
|
Map<String, Object> oneResult = getDynamicQueryMapper().query(dynamicJoinWrapper);
|
||||||
|
if(oneResult != null){
|
||||||
|
mapList = new ArrayList<>();
|
||||||
|
mapList.add(oneResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
mapList = getDynamicQueryMapper().queryForList(dynamicJoinWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 格式化orderBy
|
||||||
|
formatOrderBy(dynamicJoinWrapper, entityClazz, pagination);
|
||||||
|
IPage<Map<String, Object>> 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<E> entityList = new ArrayList<>();
|
||||||
|
for(Map<String, Object> colValueMap : mapList){
|
||||||
|
Map<String, Object> fieldValueMap = new HashMap<>();
|
||||||
|
// 格式化map
|
||||||
|
for(Map.Entry<String, Object> 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 <E> void formatOrderBy(DynamicJoinQueryWrapper queryWrapper, Class<E> entityClazz, Pagination pagination){
|
||||||
|
// 如果是默认id排序,检查是否有id字段
|
||||||
|
if(pagination.isDefaultOrderBy()){
|
||||||
|
// 优化排序
|
||||||
|
String pk = ContextHelper.getPrimaryKey(entityClazz);
|
||||||
|
// 主键非有序id字段,需要清空默认排序
|
||||||
|
if (!Cons.FieldName.id.name().equals(pk)) {
|
||||||
|
pagination.clearDefaultOrder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 格式化排序
|
||||||
|
if(V.notEmpty(pagination.getOrderBy())){
|
||||||
|
List<String> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -16,23 +16,35 @@
|
||||||
package com.diboot.core.binding;
|
package com.diboot.core.binding;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.segments.NormalSegmentList;
|
||||||
|
import com.diboot.core.binding.data.CheckpointType;
|
||||||
|
import com.diboot.core.binding.data.DataAccessAnnoCache;
|
||||||
|
import com.diboot.core.binding.data.DataAccessCheckInterface;
|
||||||
|
import com.diboot.core.binding.parser.ParserCache;
|
||||||
import com.diboot.core.binding.query.BindQuery;
|
import com.diboot.core.binding.query.BindQuery;
|
||||||
import com.diboot.core.binding.query.Comparison;
|
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.config.Cons;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
|
import com.diboot.core.util.ContextHelper;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* QueryWrapper构建器 - Entity,DTO -> 注解绑定查询条件 并转换为QueryWrapper对象
|
* QueryWrapper构建器
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/07/27
|
* @date 2019/07/27
|
||||||
|
@ -43,97 +55,128 @@ public class QueryBuilder {
|
||||||
/**
|
/**
|
||||||
* Entity或者DTO对象转换为QueryWrapper
|
* Entity或者DTO对象转换为QueryWrapper
|
||||||
* @param dto
|
* @param dto
|
||||||
* @param <T>
|
|
||||||
* @param <DTO>
|
* @param <DTO>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T,DTO> QueryWrapper<T> toQueryWrapper(DTO dto){
|
public static <DTO> QueryWrapper toQueryWrapper(DTO dto){
|
||||||
QueryWrapper<T> wrapper = new QueryWrapper<>();
|
return dtoToWrapper(dto, null);
|
||||||
return (QueryWrapper<T>) dtoToWrapper(wrapper, dto, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity或者DTO对象转换为QueryWrapper
|
* Entity或者DTO对象转换为QueryWrapper
|
||||||
* @param dto
|
* @param dto
|
||||||
* @param fields 指定参与转换的属性值
|
* @param fields 指定参与转换的属性值
|
||||||
* @param <T>
|
|
||||||
* @param <DTO>
|
* @param <DTO>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T,DTO> QueryWrapper<T> toQueryWrapper(DTO dto, Collection<String> fields){
|
public static <DTO> QueryWrapper toQueryWrapper(DTO dto, Collection<String> fields){
|
||||||
QueryWrapper<T> wrapper = new QueryWrapper<>();
|
return dtoToWrapper(dto, fields);
|
||||||
return (QueryWrapper<T>) dtoToWrapper(wrapper, dto, fields);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity或者DTO对象转换为QueryWrapper
|
||||||
|
* @param dto
|
||||||
|
* @param <DTO>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <DTO> ExtQueryWrapper toDynamicJoinQueryWrapper(DTO dto){
|
||||||
|
return toDynamicJoinQueryWrapper(dto, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity或者DTO对象转换为QueryWrapper
|
||||||
|
* @param dto
|
||||||
|
* @param fields 指定参与转换的属性值
|
||||||
|
* @param <DTO>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <DTO> ExtQueryWrapper toDynamicJoinQueryWrapper(DTO dto, Collection<String> fields){
|
||||||
|
QueryWrapper queryWrapper = dtoToWrapper(dto, fields);
|
||||||
|
if(queryWrapper instanceof DynamicJoinQueryWrapper == false){
|
||||||
|
return (ExtQueryWrapper)queryWrapper;
|
||||||
|
}
|
||||||
|
return (DynamicJoinQueryWrapper)queryWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity或者DTO对象转换为LambdaQueryWrapper
|
* Entity或者DTO对象转换为LambdaQueryWrapper
|
||||||
* @param dto
|
* @param dto
|
||||||
* @param <T>
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T,DTO> LambdaQueryWrapper<T> toLambdaQueryWrapper(DTO dto){
|
public static <DTO> LambdaQueryWrapper<DTO> toLambdaQueryWrapper(DTO dto){
|
||||||
return (LambdaQueryWrapper<T>) toQueryWrapper(dto).lambda();
|
return (LambdaQueryWrapper<DTO>) toQueryWrapper(dto).lambda();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity或者DTO对象转换为LambdaQueryWrapper
|
* Entity或者DTO对象转换为LambdaQueryWrapper
|
||||||
* @param dto
|
* @param dto
|
||||||
* @param fields 指定参与转换的属性值
|
* @param fields 指定参与转换的属性值
|
||||||
* @param <T>
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T,DTO> LambdaQueryWrapper<T> toLambdaQueryWrapper(DTO dto, Collection<String> fields){
|
public static <DTO> LambdaQueryWrapper<DTO> toLambdaQueryWrapper(DTO dto, Collection<String> fields){
|
||||||
return (LambdaQueryWrapper<T>) toQueryWrapper(dto, fields).lambda();
|
return (LambdaQueryWrapper<DTO>) toQueryWrapper(dto, fields).lambda();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换具体实现
|
* 转换具体实现
|
||||||
* @param wrapper
|
|
||||||
* @param dto
|
* @param dto
|
||||||
* @param <T>
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static <T,DTO> QueryWrapper<T> dtoToWrapper(QueryWrapper wrapper, DTO dto, Collection<String> fields){
|
private static <DTO> QueryWrapper<DTO> dtoToWrapper(DTO dto, Collection<String> fields){
|
||||||
|
QueryWrapper wrapper;
|
||||||
// 转换
|
// 转换
|
||||||
List<Field> declaredFields = BeanUtils.extractAllFields(dto.getClass());
|
LinkedHashMap<String, Object> fieldValuesMap = extractNotNullValues(dto, fields);
|
||||||
for (Field field : declaredFields) {
|
if(V.isEmpty(fieldValuesMap)){
|
||||||
// 非指定属性,非逻辑删除字段,跳过
|
wrapper = new QueryWrapper<>();
|
||||||
if(fields != null && !fields.contains(field.getName())){
|
// 附加数据访问条件
|
||||||
continue;
|
attachDataAccessCondition(wrapper, dto.getClass());
|
||||||
}
|
return wrapper;
|
||||||
//忽略static,以及final,transient
|
}
|
||||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
// 只解析有值的
|
||||||
boolean isFinal = Modifier.isFinal(field.getModifiers());
|
fields = fieldValuesMap.keySet();
|
||||||
boolean isTransient = Modifier.isTransient(field.getModifiers());
|
// 是否有join联表查询
|
||||||
if(isStatic || isFinal || isTransient){
|
boolean hasJoinTable = ParserCache.hasJoinTable(dto, fields);
|
||||||
continue;
|
if(hasJoinTable){
|
||||||
}
|
wrapper = new DynamicJoinQueryWrapper<>(dto.getClass(), fields);
|
||||||
//忽略注解 @TableField(exist = false) 的字段
|
}
|
||||||
TableField tableField = field.getAnnotation(TableField.class);
|
else{
|
||||||
if(tableField != null && tableField.exist() == false){
|
wrapper = new ExtQueryWrapper<>();
|
||||||
continue;
|
}
|
||||||
|
// 构建QueryWrapper
|
||||||
|
for(Map.Entry<String, Object> 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);
|
BindQuery query = field.getAnnotation(BindQuery.class);
|
||||||
if(query != null && query.ignore()){ //忽略字段
|
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){
|
|
||||||
continue;
|
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) {
|
switch (comparison) {
|
||||||
case EQ:
|
case EQ:
|
||||||
wrapper.eq(columnName, value);
|
wrapper.eq(columnName, value);
|
||||||
|
@ -142,7 +185,7 @@ public class QueryBuilder {
|
||||||
if(value.getClass().isArray()){
|
if(value.getClass().isArray()){
|
||||||
Object[] valueArray = (Object[])value;
|
Object[] valueArray = (Object[])value;
|
||||||
if(valueArray.length == 1){
|
if(valueArray.length == 1){
|
||||||
wrapper.in(columnName, valueArray[0]);
|
wrapper.eq(columnName, valueArray[0]);
|
||||||
}
|
}
|
||||||
else if(valueArray.length >= 2){
|
else if(valueArray.length >= 2){
|
||||||
wrapper.in(columnName, valueArray);
|
wrapper.in(columnName, valueArray);
|
||||||
|
@ -189,6 +232,15 @@ public class QueryBuilder {
|
||||||
wrapper.between(columnName, valueArray[0], valueArray[1]);
|
wrapper.between(columnName, valueArray[0], valueArray[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if(value instanceof List){
|
||||||
|
List valueList = (List)value;
|
||||||
|
if(valueList.size() == 1){
|
||||||
|
wrapper.ge(columnName, valueList.get(0));
|
||||||
|
}
|
||||||
|
else if(valueList.size() >= 2){
|
||||||
|
wrapper.between(columnName, valueList.get(0), valueList.get(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
// 支持逗号分隔的字符串
|
// 支持逗号分隔的字符串
|
||||||
else if(value instanceof String && ((String) value).contains(",")){
|
else if(value instanceof String && ((String) value).contains(",")){
|
||||||
Object[] valueArray = ((String) value).split(",");
|
Object[] valueArray = ((String) value).split(",");
|
||||||
|
@ -201,9 +253,55 @@ public class QueryBuilder {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 附加数据访问条件
|
||||||
|
attachDataAccessCondition(wrapper, dto.getClass());
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 扩展接口
|
||||||
|
private static DataAccessCheckInterface dataAccessCheckInstance;
|
||||||
|
private static boolean dataAccessCheckInstanceChecked = false;
|
||||||
|
/**
|
||||||
|
* 附加数据访问权限条件
|
||||||
|
* @param queryWrapper
|
||||||
|
* @param dtoClass
|
||||||
|
* @param <DTO>
|
||||||
|
*/
|
||||||
|
public static <DTO> void attachDataAccessCondition(QueryWrapper<DTO> queryWrapper, Class<DTO> dtoClass){
|
||||||
|
if(dataAccessCheckInstanceChecked == false){
|
||||||
|
dataAccessCheckInstance = ContextHelper.getBean(DataAccessCheckInterface.class);
|
||||||
|
dataAccessCheckInstanceChecked = true;
|
||||||
|
}
|
||||||
|
if(dataAccessCheckInstance != null && DataAccessAnnoCache.hasDataAccessCheckpoint(dtoClass)){
|
||||||
|
NormalSegmentList segments = queryWrapper.getExpression().getNormal();
|
||||||
|
for(CheckpointType type : CheckpointType.values()){
|
||||||
|
String idCol = DataAccessAnnoCache.getDataPermissionColumn(dtoClass, type);
|
||||||
|
if(V.isEmpty(idCol)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List<Long> idValues = dataAccessCheckInstance.getAccessibleIds(type);
|
||||||
|
if(V.isEmpty(idValues)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 联表查询,附加别名
|
||||||
|
if(queryWrapper instanceof DynamicJoinQueryWrapper){
|
||||||
|
idCol = "self."+idCol;
|
||||||
|
}
|
||||||
|
// 检查是否已包含该条件,如是则warn并退出
|
||||||
|
if(checkHasColumn(segments, idCol)){
|
||||||
|
log.warn("附加数据访问条件未生效,因查询条件已包含列: " + idCol);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(idValues.size() == 1){
|
||||||
|
queryWrapper.eq(idCol, idValues.get(0));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
queryWrapper.in(idCol, idValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取数据表的列名(驼峰转下划线蛇形命名)
|
* 获取数据表的列名(驼峰转下划线蛇形命名)
|
||||||
* <br>
|
* <br>
|
||||||
|
@ -212,10 +310,13 @@ public class QueryBuilder {
|
||||||
* @param field
|
* @param field
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static String getColumnName(Field field){
|
public static String getColumnName(Field field){
|
||||||
String columnName = null;
|
String columnName = null;
|
||||||
if (field.isAnnotationPresent(BindQuery.class)) {
|
if (field.isAnnotationPresent(BindQuery.class)) {
|
||||||
columnName = field.getAnnotation(BindQuery.class).field();
|
columnName = field.getAnnotation(BindQuery.class).field();
|
||||||
|
if(V.notEmpty(columnName)){
|
||||||
|
columnName = S.toSnakeCase(columnName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (field.isAnnotationPresent(TableField.class)) {
|
else if (field.isAnnotationPresent(TableField.class)) {
|
||||||
columnName = field.getAnnotation(TableField.class).value();
|
columnName = field.getAnnotation(TableField.class).value();
|
||||||
|
@ -223,4 +324,81 @@ public class QueryBuilder {
|
||||||
return V.notEmpty(columnName) ? columnName : S.toSnakeCase(field.getName());
|
return V.notEmpty(columnName) ? columnName : S.toSnakeCase(field.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取非空字段及值
|
||||||
|
* @param dto
|
||||||
|
* @param fields
|
||||||
|
* @param <DTO>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static <DTO> LinkedHashMap<String, Object> extractNotNullValues(DTO dto, Collection<String> fields){
|
||||||
|
LinkedHashMap<String, Object> resultMap = new LinkedHashMap<>();
|
||||||
|
Class<?> dtoClass = dto.getClass();
|
||||||
|
|
||||||
|
// 转换
|
||||||
|
List<Field> declaredFields = BeanUtils.extractAllFields(dtoClass);
|
||||||
|
for (Field field : declaredFields) {
|
||||||
|
// 非指定属性,非逻辑删除字段,跳过;
|
||||||
|
if (fields != null && !fields.contains(field.getName())) {
|
||||||
|
//Date 属性放过
|
||||||
|
if (!V.equals(field.getType().getName(), "java.util.Date")) {
|
||||||
|
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);
|
||||||
|
if (V.isEmpty(value)) {
|
||||||
|
String prefix = V.equals("boolean", field.getType().getName()) ? "is" : "get";
|
||||||
|
Method method = dtoClass.getMethod(prefix + S.capFirst(field.getName()));
|
||||||
|
value = method.invoke(dto);
|
||||||
|
}
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
log.error("通过反射获取属性值出错:{}", e.getMessage());
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
log.warn("通过反射获取属性方法不存在:{}", e.getMessage());
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
log.warn("通过反射执行属性方法出错:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
// 忽略逻辑删除字段
|
||||||
|
if(Cons.FieldName.deleted.name().equals(field.getName())
|
||||||
|
&& "boolean".equals(field.getType().getName())
|
||||||
|
&& (Boolean)value == false
|
||||||
|
){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value != null) {
|
||||||
|
resultMap.put(field.getName(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查是否包含列
|
||||||
|
* @param segments
|
||||||
|
* @param idCol
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean checkHasColumn(NormalSegmentList segments, String idCol){
|
||||||
|
if(segments.size() > 0){
|
||||||
|
Iterator<ISqlSegment> iterable = segments.iterator();
|
||||||
|
while(iterable.hasNext()){
|
||||||
|
ISqlSegment segment = iterable.next();
|
||||||
|
if(segment.getSqlSegment().equalsIgnoreCase(idCol)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -16,14 +16,8 @@
|
||||||
package com.diboot.core.binding;
|
package com.diboot.core.binding;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.diboot.core.binding.annotation.BindDict;
|
import com.diboot.core.binding.annotation.*;
|
||||||
import com.diboot.core.binding.annotation.BindEntity;
|
import com.diboot.core.binding.binder.*;
|
||||||
import com.diboot.core.binding.annotation.BindEntityList;
|
|
||||||
import com.diboot.core.binding.annotation.BindField;
|
|
||||||
import com.diboot.core.binding.binder.BaseBinder;
|
|
||||||
import com.diboot.core.binding.binder.EntityBinder;
|
|
||||||
import com.diboot.core.binding.binder.EntityListBinder;
|
|
||||||
import com.diboot.core.binding.binder.FieldBinder;
|
|
||||||
import com.diboot.core.binding.parser.BindAnnotationGroup;
|
import com.diboot.core.binding.parser.BindAnnotationGroup;
|
||||||
import com.diboot.core.binding.parser.ConditionManager;
|
import com.diboot.core.binding.parser.ConditionManager;
|
||||||
import com.diboot.core.binding.parser.FieldAnnotation;
|
import com.diboot.core.binding.parser.FieldAnnotation;
|
||||||
|
@ -44,7 +38,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定管理器
|
* 关联关系绑定管理器
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/7/18
|
* @date 2019/7/18
|
||||||
|
@ -135,6 +129,11 @@ public class RelationsBinder {
|
||||||
doBindingEntityList(voList, anno);
|
doBindingEntityList(voList, anno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 绑定Entity field List
|
||||||
|
List<FieldAnnotation> fieldListAnnoList = bindAnnotationGroup.getBindFieldListAnnotations();
|
||||||
|
if(fieldListAnnoList != null){
|
||||||
|
doBindingFieldList(voList, fieldListAnnoList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +202,7 @@ public class RelationsBinder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 绑定Entity
|
* 绑定EntityList
|
||||||
* @param voList
|
* @param voList
|
||||||
* @param fieldAnnotation
|
* @param fieldAnnotation
|
||||||
* @param <VO>
|
* @param <VO>
|
||||||
|
@ -219,6 +218,34 @@ public class RelationsBinder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 绑定FieldList
|
||||||
|
* @param voList
|
||||||
|
* @param fieldListAnnoList
|
||||||
|
* @param <VO>
|
||||||
|
*/
|
||||||
|
private static <VO> void doBindingFieldList(List<VO> voList, List<FieldAnnotation> fieldListAnnoList) {
|
||||||
|
//多个字段,合并查询,以减少SQL数
|
||||||
|
Map<String, List<FieldAnnotation>> clazzToListMap = new HashMap<>();
|
||||||
|
for(FieldAnnotation anno : fieldListAnnoList){
|
||||||
|
BindFieldList bindField = (BindFieldList) anno.getAnnotation();
|
||||||
|
String key = bindField.entity().getName() + ":" + bindField.condition();
|
||||||
|
List<FieldAnnotation> list = clazzToListMap.computeIfAbsent(key, k -> new ArrayList<>());
|
||||||
|
list.add(anno);
|
||||||
|
}
|
||||||
|
// 解析条件并且执行绑定
|
||||||
|
for(Map.Entry<String, List<FieldAnnotation>> entry : clazzToListMap.entrySet()){
|
||||||
|
List<FieldAnnotation> list = entry.getValue();
|
||||||
|
BindFieldList bindAnnotation = (BindFieldList) list.get(0).getAnnotation();
|
||||||
|
FieldListBinder binder = buildFieldListBinder(bindAnnotation, voList);
|
||||||
|
for(FieldAnnotation anno : list){
|
||||||
|
BindFieldList bindField = (BindFieldList) anno.getAnnotation();
|
||||||
|
binder.link(bindField.field(), anno.getFieldName());
|
||||||
|
}
|
||||||
|
parseConditionsAndBinding(binder, bindAnnotation.condition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 解析条件并且执行绑定
|
* 解析条件并且执行绑定
|
||||||
* @param condition
|
* @param condition
|
||||||
|
@ -276,6 +303,20 @@ public class RelationsBinder {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建FieldListBinder
|
||||||
|
* @param annotation
|
||||||
|
* @param voList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static FieldListBinder buildFieldListBinder(Annotation annotation, List voList){
|
||||||
|
IService service = getService(annotation);
|
||||||
|
if(service != null){
|
||||||
|
return new FieldListBinder<>(service, voList);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 通过Entity获取对应的Service实现类
|
* 通过Entity获取对应的Service实现类
|
||||||
* @param annotation
|
* @param annotation
|
||||||
|
@ -298,6 +339,10 @@ public class RelationsBinder {
|
||||||
BindEntityList bindAnnotation = (BindEntityList)annotation;
|
BindEntityList bindAnnotation = (BindEntityList)annotation;
|
||||||
entityClass = bindAnnotation.entity();
|
entityClass = bindAnnotation.entity();
|
||||||
}
|
}
|
||||||
|
else if(annotation instanceof BindFieldList){
|
||||||
|
BindFieldList bindAnnotation = (BindFieldList)annotation;
|
||||||
|
entityClass = bindAnnotation.entity();
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
log.warn("非预期的注解: "+ annotation.getClass().getSimpleName());
|
log.warn("非预期的注解: "+ annotation.getClass().getSimpleName());
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.diboot.core.binding.annotation;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 绑定字典注解
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/1/21
|
* @date 2019/1/21
|
||||||
|
|
|
@ -18,7 +18,7 @@ package com.diboot.core.binding.annotation;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定Entity 注解定义
|
* 绑定Entity 注解定义(1-1)
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/1/21
|
* @date 2019/1/21
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.diboot.core.binding.annotation;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 绑定Entity集合注解(1-n)
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/1/21
|
* @date 2019/1/21
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.diboot.core.binding.annotation;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 绑定字段 (1-1)
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/1/21
|
* @date 2019/1/21
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定字段集合(1-n)
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2019/1/21
|
||||||
|
*/
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Inherited
|
||||||
|
@Documented
|
||||||
|
public @interface BindFieldList {
|
||||||
|
/***
|
||||||
|
* 绑定的Entity类
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Class entity();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 绑定字段
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String field();
|
||||||
|
|
||||||
|
/***
|
||||||
|
* JOIN连接条件
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String condition();
|
||||||
|
}
|
|
@ -24,14 +24,12 @@ import com.diboot.core.service.BaseService;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.IGetter;
|
import com.diboot.core.util.IGetter;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 关系绑定Binder父类
|
* 关系绑定Binder父类
|
||||||
|
@ -70,6 +68,18 @@ public abstract class BaseBinder<T> {
|
||||||
|
|
||||||
protected Class<T> referencedEntityClass;
|
protected Class<T> referencedEntityClass;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 构造方法
|
||||||
|
* @param serviceInstance
|
||||||
|
* @param voList
|
||||||
|
*/
|
||||||
|
public BaseBinder(IService<T> serviceInstance, List voList){
|
||||||
|
this.referencedService = serviceInstance;
|
||||||
|
this.annoObjectList = voList;
|
||||||
|
this.queryWrapper = new QueryWrapper<T>();
|
||||||
|
this.referencedEntityClass = BeanUtils.getGenericityClass(referencedService, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* join连接条件,指定当前VO的取值方法和关联entity的取值方法
|
* join连接条件,指定当前VO的取值方法和关联entity的取值方法
|
||||||
* @param annoObjectFkGetter 当前VO的取值方法
|
* @param annoObjectFkGetter 当前VO的取值方法
|
||||||
|
@ -210,6 +220,26 @@ public abstract class BaseBinder<T> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从Map中提取ID的值
|
||||||
|
* @param middleTableResultMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected List extractIdValueFromMap(Map<String, List> middleTableResultMap) {
|
||||||
|
List entityIdList = new ArrayList();
|
||||||
|
for(Map.Entry<String, List> entry : middleTableResultMap.entrySet()){
|
||||||
|
if(V.isEmpty(entry.getValue())){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for(Object id : entry.getValue()){
|
||||||
|
if(!entityIdList.contains(id)){
|
||||||
|
entityIdList.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entityIdList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查list,结果过多打印warn
|
* 检查list,结果过多打印warn
|
||||||
* @param list
|
* @param list
|
||||||
|
@ -245,5 +275,4 @@ public abstract class BaseBinder<T> {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.binder;
|
package com.diboot.core.binding.binder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.ISetter;
|
import com.diboot.core.util.ISetter;
|
||||||
|
@ -47,17 +46,13 @@ public class EntityBinder<T> extends BaseBinder<T> {
|
||||||
*/
|
*/
|
||||||
protected Class<?> annoObjectFieldClass;
|
protected Class<?> annoObjectFieldClass;
|
||||||
|
|
||||||
public EntityBinder(){}
|
|
||||||
/***
|
/***
|
||||||
* 构造方法
|
* 构造方法
|
||||||
* @param referencedService
|
* @param referencedService
|
||||||
* @param voList
|
* @param voList
|
||||||
*/
|
*/
|
||||||
public EntityBinder(IService<T> referencedService, List voList){
|
public EntityBinder(IService<T> referencedService, List voList){
|
||||||
this.referencedService = referencedService;
|
super(referencedService, voList);
|
||||||
this.annoObjectList = voList;
|
|
||||||
this.queryWrapper = new QueryWrapper<T>();
|
|
||||||
this.referencedEntityClass = BeanUtils.getGenericityClass(referencedService, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.binder;
|
package com.diboot.core.binding.binder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
|
@ -43,10 +42,7 @@ public class EntityListBinder<T> extends EntityBinder<T> {
|
||||||
* @param voList
|
* @param voList
|
||||||
*/
|
*/
|
||||||
public EntityListBinder(IService<T> serviceInstance, List voList){
|
public EntityListBinder(IService<T> serviceInstance, List voList){
|
||||||
this.referencedService = serviceInstance;
|
super(serviceInstance, voList);
|
||||||
this.annoObjectList = voList;
|
|
||||||
this.queryWrapper = new QueryWrapper<T>();
|
|
||||||
this.referencedEntityClass = BeanUtils.getGenericityClass(referencedService, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -117,24 +113,6 @@ public class EntityListBinder<T> extends EntityBinder<T> {
|
||||||
BeanUtils.bindPropValueOfList(annoObjectField, annoObjectList, annoObjectForeignKey, valueEntityListMap);
|
BeanUtils.bindPropValueOfList(annoObjectField, annoObjectList, annoObjectForeignKey, valueEntityListMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 从Map中提取ID的值
|
|
||||||
* @param middleTableResultMap
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private List extractIdValueFromMap(Map<String, List> middleTableResultMap) {
|
|
||||||
List entityIdList = new ArrayList();
|
|
||||||
for(Map.Entry<String, List> entry : middleTableResultMap.entrySet()){
|
|
||||||
if(V.isEmpty(entry.getValue())){
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for(Object id : entry.getValue()){
|
|
||||||
if(!entityIdList.contains(id)){
|
|
||||||
entityIdList.add(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return entityIdList;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.binder;
|
package com.diboot.core.binding.binder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.diboot.core.util.*;
|
import com.diboot.core.util.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -35,11 +34,11 @@ public class FieldBinder<T> extends BaseBinder<T> {
|
||||||
/**
|
/**
|
||||||
* VO对象绑定赋值的属性名列表
|
* VO对象绑定赋值的属性名列表
|
||||||
*/
|
*/
|
||||||
private List<String> annoObjectSetterPropNameList;
|
protected List<String> annoObjectSetterPropNameList;
|
||||||
/**
|
/**
|
||||||
* DO/Entity对象对应的getter取值属性名列表
|
* DO/Entity对象对应的getter取值属性名列表
|
||||||
*/
|
*/
|
||||||
private List<String> referencedGetterColumnNameList;
|
protected List<String> referencedGetterColumnNameList;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 构造方法
|
* 构造方法
|
||||||
|
@ -47,10 +46,7 @@ public class FieldBinder<T> extends BaseBinder<T> {
|
||||||
* @param voList
|
* @param voList
|
||||||
*/
|
*/
|
||||||
public FieldBinder(IService<T> serviceInstance, List voList){
|
public FieldBinder(IService<T> serviceInstance, List voList){
|
||||||
this.referencedService = serviceInstance;
|
super(serviceInstance, voList);
|
||||||
this.annoObjectList = voList;
|
|
||||||
this.queryWrapper = new QueryWrapper<T>();
|
|
||||||
this.referencedEntityClass = BeanUtils.getGenericityClass(referencedService, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.binder;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.diboot.core.util.BeanUtils;
|
||||||
|
import com.diboot.core.util.S;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联字段绑定
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2019/1/19
|
||||||
|
*/
|
||||||
|
public class FieldListBinder<T> extends FieldBinder<T> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(FieldListBinder.class);
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 构造方法
|
||||||
|
* @param serviceInstance
|
||||||
|
* @param voList
|
||||||
|
*/
|
||||||
|
public FieldListBinder(IService<T> serviceInstance, List voList) {
|
||||||
|
super(serviceInstance, voList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bind() {
|
||||||
|
if(V.isEmpty(annoObjectList)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(referencedGetterColumnNameList == null){
|
||||||
|
log.error("调用错误:字段绑定必须调用link()指定字段赋值和取值的setter/getter方法!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 解析默认主键字段名
|
||||||
|
String referencedEntityPkName = S.toSnakeCase(referencedEntityPrimaryKey);
|
||||||
|
String annoObjectFkFieldName = S.toLowerCaseCamel(annoObjectForeignKey);
|
||||||
|
// 提取主键pk列表
|
||||||
|
List annoObjectForeignKeyList = BeanUtils.collectToList(annoObjectList, annoObjectFkFieldName);
|
||||||
|
if(V.isEmpty(annoObjectForeignKeyList)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Map<String, List> valueEntityListMap = new HashMap<>();
|
||||||
|
//@BindFieldList(entity = Role.class, field="name", condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
|
//List<String> roleNameList;
|
||||||
|
// 构建查询条件
|
||||||
|
List<String> selectColumns = new ArrayList<>(referencedGetterColumnNameList.size()+1);
|
||||||
|
selectColumns.add(referencedEntityPkName);
|
||||||
|
selectColumns.addAll(referencedGetterColumnNameList);
|
||||||
|
queryWrapper.select(S.toStringArray(selectColumns));
|
||||||
|
// 处理中间表
|
||||||
|
if(middleTable != null){
|
||||||
|
// 将结果转换成map
|
||||||
|
Map<String, List> middleTableResultMap = middleTable.executeOneToManyQuery(annoObjectForeignKeyList);
|
||||||
|
if(V.isEmpty(middleTableResultMap)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 收集查询结果values集合
|
||||||
|
List entityIdList = extractIdValueFromMap(middleTableResultMap);
|
||||||
|
// 构建查询条件
|
||||||
|
queryWrapper.in(referencedEntityPkName, entityIdList);
|
||||||
|
// 查询entity列表: List<Role>
|
||||||
|
List<T> list = getEntityList(queryWrapper);
|
||||||
|
if(V.isEmpty(list)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 转换entity列表为Map<ID, Entity>
|
||||||
|
Map<String, T> entityMap = BeanUtils.convertToStringKeyObjectMap(list, S.toLowerCaseCamel(referencedEntityPrimaryKey));
|
||||||
|
for(Map.Entry<String, List> entry : middleTableResultMap.entrySet()){
|
||||||
|
// List<roleId>
|
||||||
|
List annoObjFKList = entry.getValue();
|
||||||
|
if(V.isEmpty(annoObjFKList)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
List valueList = new ArrayList();
|
||||||
|
for(Object obj : annoObjFKList){
|
||||||
|
T ent = entityMap.get(String.valueOf(obj));
|
||||||
|
if(ent != null){
|
||||||
|
valueList.add(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
valueEntityListMap.put(entry.getKey(), valueList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
queryWrapper.in(referencedEntityPkName, annoObjectForeignKeyList);
|
||||||
|
// 查询entity列表: List<Role>
|
||||||
|
List<T> list = getEntityList(queryWrapper);
|
||||||
|
if(V.isEmpty(list)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(T entity : list){
|
||||||
|
String keyValue = BeanUtils.getStringProperty(entity, S.toLowerCaseCamel(referencedEntityPrimaryKey));
|
||||||
|
List entityList = valueEntityListMap.get(keyValue);
|
||||||
|
if(entityList == null){
|
||||||
|
entityList = new ArrayList<>();
|
||||||
|
valueEntityListMap.put(keyValue, entityList);
|
||||||
|
}
|
||||||
|
entityList.add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 遍历list并赋值
|
||||||
|
for(Object annoObject : annoObjectList){
|
||||||
|
// 将数子类型转换成字符串,以解决类型不一致的问题
|
||||||
|
String annoObjectId = BeanUtils.getStringProperty(annoObject, annoObjectFkFieldName);
|
||||||
|
List entityList = valueEntityListMap.get(annoObjectId);
|
||||||
|
if(entityList != null){
|
||||||
|
for(int i = 0; i< annoObjectSetterPropNameList.size(); i++){
|
||||||
|
List valObjList = BeanUtils.collectToList(entityList, S.toLowerCaseCamel(referencedGetterColumnNameList.get(i)));
|
||||||
|
BeanUtils.setProperty(annoObject, annoObjectSetterPropNameList.get(i), valObjList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.copy;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拷贝字段时的非同名字段处理
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.1
|
||||||
|
* @date 2020/06/04
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Documented
|
||||||
|
public @interface Accept {
|
||||||
|
/**
|
||||||
|
* 接收来源对象的属性名
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String name();
|
||||||
|
/**
|
||||||
|
* source该字段有值时是否覆盖
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean override() default false;
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.copy;
|
||||||
|
|
||||||
|
import com.diboot.core.util.BeanUtils;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept注解拷贝器
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v1.0
|
||||||
|
* @date 2020/06/04
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class AcceptAnnoCopier {
|
||||||
|
/**
|
||||||
|
* 注解缓存
|
||||||
|
*/
|
||||||
|
private static Map<String, List<String[]>> CLASS_ACCEPT_ANNO_CACHE_MAP = new ConcurrentHashMap<>();
|
||||||
|
// 下标
|
||||||
|
private static final int IDX_TARGET_FIELD = 0, IDX_SOURCE_FIELD = 1, IDX_OVERRIDE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于注解拷贝属性
|
||||||
|
* @param source
|
||||||
|
* @param target
|
||||||
|
*/
|
||||||
|
public static void copyAcceptProperties(Object source, Object target){
|
||||||
|
String key = target.getClass().getName();
|
||||||
|
// 初始化
|
||||||
|
if(!CLASS_ACCEPT_ANNO_CACHE_MAP.containsKey(key)){
|
||||||
|
List<Field> annoFieldList = BeanUtils.extractFields(target.getClass(), Accept.class);
|
||||||
|
if(V.isEmpty(annoFieldList)){
|
||||||
|
CLASS_ACCEPT_ANNO_CACHE_MAP.put(key, Collections.EMPTY_LIST);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
List<String[]> annoDefList = new ArrayList<>(annoFieldList.size());
|
||||||
|
for(Field fld : annoFieldList){
|
||||||
|
Accept accept = fld.getAnnotation(Accept.class);
|
||||||
|
String[] annoDef = {fld.getName(), accept.name(), accept.override()? "1":"0"};
|
||||||
|
annoDefList.add(annoDef);
|
||||||
|
}
|
||||||
|
CLASS_ACCEPT_ANNO_CACHE_MAP.put(key, annoDefList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 解析copy
|
||||||
|
List<String[]> acceptAnnos = CLASS_ACCEPT_ANNO_CACHE_MAP.get(key);
|
||||||
|
if(V.isEmpty(acceptAnnos)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(String[] annoDef : acceptAnnos){
|
||||||
|
boolean override = !"0".equals(annoDef[IDX_OVERRIDE]);
|
||||||
|
if(!override){
|
||||||
|
Object targetValue = BeanUtils.getProperty(target, annoDef[IDX_TARGET_FIELD]);
|
||||||
|
if(targetValue != null){
|
||||||
|
log.debug("目标对象{}已有值{},copyAcceptProperties将忽略.", key, targetValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Object sourceValue = BeanUtils.getProperty(source, annoDef[IDX_SOURCE_FIELD]);
|
||||||
|
if(sourceValue != null){
|
||||||
|
BeanUtils.setProperty(target, annoDef[IDX_TARGET_FIELD], sourceValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checkpoint类型
|
||||||
|
* @author Mazc@dibo.ltd
|
||||||
|
* @version v2.1
|
||||||
|
* @date 2020/04/24
|
||||||
|
*/
|
||||||
|
public enum CheckpointType {
|
||||||
|
|
||||||
|
USER(0), // 用户范围
|
||||||
|
ORG(1), // 组织范围
|
||||||
|
POSITION(2), // 岗位范围
|
||||||
|
|
||||||
|
EXT_OBJ(3); // 扩展对象范围
|
||||||
|
|
||||||
|
private int index;
|
||||||
|
CheckpointType(int index){
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int index(){
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import com.diboot.core.binding.QueryBuilder;
|
||||||
|
import com.diboot.core.exception.BusinessException;
|
||||||
|
import com.diboot.core.util.BeanUtils;
|
||||||
|
import com.diboot.core.util.S;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
|
import com.diboot.core.vo.Status;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据访问权限的注解缓存
|
||||||
|
* @author Mazc@dibo.ltd
|
||||||
|
* @version v2.1
|
||||||
|
* @date 2020/04/24
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DataAccessAnnoCache {
|
||||||
|
/**
|
||||||
|
* 注解缓存
|
||||||
|
*/
|
||||||
|
private static Map<String, String[]> DATA_PERMISSION_ANNO_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有检查点注解
|
||||||
|
* @param entityDto
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean hasDataAccessCheckpoint(Class<?> entityDto){
|
||||||
|
initClassCheckpoint(entityDto);
|
||||||
|
String[] columns = DATA_PERMISSION_ANNO_CACHE.get(entityDto.getName());
|
||||||
|
if(V.isEmpty(columns)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(String type : columns){
|
||||||
|
if(V.notEmpty(type)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取数据权限的用户类型列名
|
||||||
|
* @param entityDto
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getDataPermissionColumn(Class<?> entityDto, CheckpointType type){
|
||||||
|
initClassCheckpoint(entityDto);
|
||||||
|
int typeIndex = type.index();
|
||||||
|
String key = entityDto.getName();
|
||||||
|
String[] columns = DATA_PERMISSION_ANNO_CACHE.get(key);
|
||||||
|
if(columns != null && (columns.length-1) >= typeIndex){
|
||||||
|
return columns[typeIndex];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化entityDto的检查点缓存
|
||||||
|
* @param entityDto
|
||||||
|
*/
|
||||||
|
private static void initClassCheckpoint(Class<?> entityDto){
|
||||||
|
String key = entityDto.getName();
|
||||||
|
if(!DATA_PERMISSION_ANNO_CACHE.containsKey(key)){
|
||||||
|
String[] results = {"", "", "", "", ""};
|
||||||
|
List<Field> fieldList = BeanUtils.extractFields(entityDto, DataAccessCheckpoint.class);
|
||||||
|
if(V.notEmpty(fieldList)){
|
||||||
|
for(Field fld : fieldList){
|
||||||
|
DataAccessCheckpoint checkpoint = fld.getAnnotation(DataAccessCheckpoint.class);
|
||||||
|
if(V.notEmpty(results[checkpoint.type().index()])){
|
||||||
|
throw new BusinessException(Status.FAIL_VALIDATION, entityDto.getSimpleName() + "中DataPermissionCheckpoint同类型注解重复!");
|
||||||
|
}
|
||||||
|
results[checkpoint.type().index()] = QueryBuilder.getColumnName(fld);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DATA_PERMISSION_ANNO_CACHE.put(key, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限校验扩展接口
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.1
|
||||||
|
* @date 2020/04/24
|
||||||
|
*/
|
||||||
|
public interface DataAccessCheckInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可访问的对象ID
|
||||||
|
*/
|
||||||
|
List<Long> getAccessibleIds(CheckpointType type);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.data;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据权限检查点 - 添加在entity/dto字段上的注解,可以支持自动检查数据权限
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.1
|
||||||
|
* @date 2020/04/23
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ElementType.FIELD})
|
||||||
|
@Documented
|
||||||
|
public @interface DataAccessCheckpoint {
|
||||||
|
/**
|
||||||
|
* 数据权限类型
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
CheckpointType type();
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.parser;
|
||||||
|
|
||||||
|
import com.diboot.core.util.S;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
|
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
|
||||||
|
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
||||||
|
import net.sf.jsqlparser.schema.Column;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 条件管理器base类
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2020/06/02
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BaseConditionManager {
|
||||||
|
/**
|
||||||
|
* 表达式缓存Map
|
||||||
|
*/
|
||||||
|
private static Map<String, List<Expression>> expressionParseResultMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取解析后的Expression列表
|
||||||
|
* @param condition
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected static List<Expression> getExpressionList(String condition){
|
||||||
|
if(V.isEmpty(condition)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<Expression> expressionList = expressionParseResultMap.get(condition);
|
||||||
|
if(expressionList == null){
|
||||||
|
ConditionParser visitor = new ConditionParser();
|
||||||
|
try{
|
||||||
|
Expression expression = CCJSqlParserUtil.parseCondExpression(condition);
|
||||||
|
expression.accept(visitor);
|
||||||
|
expressionList = visitor.getExpressList();
|
||||||
|
expressionParseResultMap.put(condition, expressionList);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
log.error("关联条件解析异常", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expressionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取中间表表对象名
|
||||||
|
* @param expressionList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected static String extractMiddleTableName(List<Expression> expressionList){
|
||||||
|
Set<String> tableNameSet = new HashSet<>();
|
||||||
|
for(Expression operator : expressionList){
|
||||||
|
if(operator instanceof EqualsTo){
|
||||||
|
EqualsTo express = (EqualsTo)operator;
|
||||||
|
// 均为列
|
||||||
|
if(express.getLeftExpression() instanceof Column && express.getRightExpression() instanceof Column){
|
||||||
|
// 统计左侧列中出现的表名
|
||||||
|
String leftColumn = express.getLeftExpression().toString();
|
||||||
|
collectTableName(tableNameSet, leftColumn);
|
||||||
|
// 统计右侧列中出现的表名
|
||||||
|
String rightColumn = express.getRightExpression().toString();
|
||||||
|
collectTableName(tableNameSet, rightColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(tableNameSet.isEmpty()){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(tableNameSet.size() > 1){
|
||||||
|
log.warn("中间表关联条件暂只支持1张中间表!");
|
||||||
|
}
|
||||||
|
return tableNameSet.iterator().next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计表名出现的次数
|
||||||
|
* @param tableNameSet
|
||||||
|
* @param columnStr
|
||||||
|
*/
|
||||||
|
private static void collectTableName(Set<String> tableNameSet, String columnStr) {
|
||||||
|
if(!columnStr.contains(".")){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 如果是中间表(非this,self标识的当前表)
|
||||||
|
if(!isCurrentObjColumn(columnStr)){
|
||||||
|
String tempTableName = S.substringBefore(columnStr, ".");
|
||||||
|
tableNameSet.add(tempTableName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否为VO自身属性(以this开头的)
|
||||||
|
* @param expression
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected static boolean isCurrentObjColumn(String expression){
|
||||||
|
String tempTableName = S.substringBefore(expression, ".");
|
||||||
|
// 如果是中间表(非this,self标识的当前表)
|
||||||
|
return "this".equals(tempTableName) || "self".equals(tempTableName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.parser;
|
package com.diboot.core.binding.parser;
|
||||||
|
|
||||||
import com.diboot.core.binding.annotation.BindDict;
|
import com.diboot.core.binding.annotation.*;
|
||||||
import com.diboot.core.binding.annotation.BindEntity;
|
|
||||||
import com.diboot.core.binding.annotation.BindEntityList;
|
|
||||||
import com.diboot.core.binding.annotation.BindField;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -47,6 +44,10 @@ public class BindAnnotationGroup {
|
||||||
* 实体集合关联注解
|
* 实体集合关联注解
|
||||||
*/
|
*/
|
||||||
private List<FieldAnnotation> bindEntityListAnnotations;
|
private List<FieldAnnotation> bindEntityListAnnotations;
|
||||||
|
/**
|
||||||
|
* 实体集合关联注解
|
||||||
|
*/
|
||||||
|
private List<FieldAnnotation> bindFieldListAnnotations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加注解
|
* 添加注解
|
||||||
|
@ -78,6 +79,12 @@ public class BindAnnotationGroup {
|
||||||
}
|
}
|
||||||
bindEntityListAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
|
bindEntityListAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
|
||||||
}
|
}
|
||||||
|
else if(annotation instanceof BindFieldList){
|
||||||
|
if(bindFieldListAnnotations == null){
|
||||||
|
bindFieldListAnnotations = new ArrayList<>();
|
||||||
|
}
|
||||||
|
bindFieldListAnnotations.add(new FieldAnnotation(fieldName, fieldClass, annotation));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FieldAnnotation> getBindDictAnnotations() {
|
public List<FieldAnnotation> getBindDictAnnotations() {
|
||||||
|
@ -96,7 +103,12 @@ public class BindAnnotationGroup {
|
||||||
return bindEntityListAnnotations;
|
return bindEntityListAnnotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotEmpty() {
|
public List<FieldAnnotation> getBindFieldListAnnotations() {
|
||||||
return bindDictAnnotations != null || bindFieldAnnotations != null || bindEntityAnnotations != null || bindEntityListAnnotations != null;
|
return bindFieldListAnnotations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isNotEmpty() {
|
||||||
|
return bindDictAnnotations != null || bindFieldAnnotations != null || bindEntityAnnotations != null || bindEntityListAnnotations != null || bindFieldListAnnotations != null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,18 +18,13 @@ package com.diboot.core.binding.parser;
|
||||||
import com.diboot.core.binding.binder.BaseBinder;
|
import com.diboot.core.binding.binder.BaseBinder;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.sf.jsqlparser.expression.Expression;
|
import net.sf.jsqlparser.expression.Expression;
|
||||||
import net.sf.jsqlparser.expression.operators.relational.*;
|
import net.sf.jsqlparser.expression.operators.relational.*;
|
||||||
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
|
||||||
import net.sf.jsqlparser.schema.Column;
|
import net.sf.jsqlparser.schema.Column;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 条件表达式的管理器
|
* 条件表达式的管理器
|
||||||
|
@ -37,41 +32,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/4/1
|
* @date 2019/4/1
|
||||||
*/
|
*/
|
||||||
public class ConditionManager {
|
@Slf4j
|
||||||
private static final Logger log = LoggerFactory.getLogger(ConditionManager.class);
|
public class ConditionManager extends BaseConditionManager{
|
||||||
|
|
||||||
/**
|
|
||||||
* 表达式缓存Map
|
|
||||||
*/
|
|
||||||
private static Map<String, List<Expression>> expressionParseResultMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取解析后的Expression列表
|
|
||||||
* @param condition
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static List<Expression> getExpressionList(String condition){
|
|
||||||
if(V.isEmpty(condition)){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
List<Expression> expressionList = expressionParseResultMap.get(condition);
|
|
||||||
if(expressionList == null){
|
|
||||||
ConditionParser visitor = new ConditionParser();
|
|
||||||
try{
|
|
||||||
Expression expression = CCJSqlParserUtil.parseCondExpression(condition);
|
|
||||||
expression.accept(visitor);
|
|
||||||
expressionList = visitor.getExpressList();
|
|
||||||
expressionParseResultMap.put(condition, expressionList);
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
log.error("关联条件解析异常", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expressionList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 附加条件到binder
|
* 附加条件到binder
|
||||||
|
* @param condition
|
||||||
* @param binder
|
* @param binder
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
|
@ -240,7 +206,7 @@ public class ConditionManager {
|
||||||
// 绑定左手边连接列
|
// 绑定左手边连接列
|
||||||
String leftHandColumn = removeLeftAlias(leftColumn);
|
String leftHandColumn = removeLeftAlias(leftColumn);
|
||||||
// this. 开头的vo对象字段
|
// this. 开头的vo对象字段
|
||||||
if(isVoColumn(leftColumn)){
|
if(isCurrentObjColumn(leftColumn)){
|
||||||
// 识别到vo对象的属性 departmentId
|
// 识别到vo对象的属性 departmentId
|
||||||
annoObjectForeignKey = leftHandColumn;
|
annoObjectForeignKey = leftHandColumn;
|
||||||
// 对应中间表的关联字段
|
// 对应中间表的关联字段
|
||||||
|
@ -258,7 +224,7 @@ public class ConditionManager {
|
||||||
if(leftColumn.startsWith(tableName+".")){
|
if(leftColumn.startsWith(tableName+".")){
|
||||||
// 绑定右手边连接列
|
// 绑定右手边连接列
|
||||||
String rightHandColumn = removeLeftAlias(rightColumn);
|
String rightHandColumn = removeLeftAlias(rightColumn);
|
||||||
if(isVoColumn(rightColumn)){
|
if(isCurrentObjColumn(rightColumn)){
|
||||||
// 识别到vo对象的属性 departmentId
|
// 识别到vo对象的属性 departmentId
|
||||||
annoObjectForeignKey = rightHandColumn;
|
annoObjectForeignKey = rightHandColumn;
|
||||||
// 对应中间表的关联字段
|
// 对应中间表的关联字段
|
||||||
|
@ -336,61 +302,6 @@ public class ConditionManager {
|
||||||
return additionalExpressions;
|
return additionalExpressions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 提取中间表表对象名
|
|
||||||
* @param expressionList
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static String extractMiddleTableName(List<Expression> expressionList){
|
|
||||||
Map<String, Integer> tableNameCountMap = new HashMap<>();
|
|
||||||
for(Expression operator : expressionList){
|
|
||||||
if(operator instanceof EqualsTo){
|
|
||||||
EqualsTo express = (EqualsTo)operator;
|
|
||||||
// 均为列
|
|
||||||
if(express.getLeftExpression() instanceof Column && express.getRightExpression() instanceof Column){
|
|
||||||
// 统计左侧列中出现的表名
|
|
||||||
String leftColumn = express.getLeftExpression().toString();
|
|
||||||
countTableName(tableNameCountMap, leftColumn);
|
|
||||||
// 统计右侧列中出现的表名
|
|
||||||
String rightColumn = express.getRightExpression().toString();
|
|
||||||
countTableName(tableNameCountMap, rightColumn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(tableNameCountMap.isEmpty()){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String tableName = null;
|
|
||||||
int count = 1;
|
|
||||||
for(Map.Entry<String, Integer> entry : tableNameCountMap.entrySet()){
|
|
||||||
if(entry.getValue() > count){
|
|
||||||
count = entry.getValue();
|
|
||||||
tableName = entry.getKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tableName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 统计表名出现的次数
|
|
||||||
* @param tableNameCountMap
|
|
||||||
* @param columnStr
|
|
||||||
*/
|
|
||||||
private static void countTableName(Map<String, Integer> tableNameCountMap, String columnStr) {
|
|
||||||
if(columnStr.contains(".")){
|
|
||||||
// 如果是中间表(非this,self标识的当前表)
|
|
||||||
if(!isVoColumn(columnStr)){
|
|
||||||
String tempTableName = S.substringBefore(columnStr, ".");
|
|
||||||
Integer count = tableNameCountMap.get(tempTableName);
|
|
||||||
if(count == null){
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
tableNameCountMap.put(tempTableName, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注解列
|
* 注解列
|
||||||
* @return
|
* @return
|
||||||
|
@ -402,15 +313,4 @@ public class ConditionManager {
|
||||||
return annoColumn;
|
return annoColumn;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 是否为VO自身属性(以this开头的)
|
|
||||||
* @param expression
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private static boolean isVoColumn(String expression){
|
|
||||||
String tempTableName = S.substringBefore(expression, ".");
|
|
||||||
// 如果是中间表(非this,self标识的当前表)
|
|
||||||
return "this".equals(tempTableName) || "self".equals(tempTableName);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,12 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor {
|
||||||
}
|
}
|
||||||
expressList.add(isNullExpression);
|
expressList.add(isNullExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IsBooleanExpression isBooleanExpression) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InExpression inExpression) {
|
public void visit(InExpression inExpression) {
|
||||||
if(!(inExpression.getLeftExpression() instanceof Column)){
|
if(!(inExpression.getLeftExpression() instanceof Column)){
|
||||||
|
@ -130,6 +136,12 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor {
|
||||||
}
|
}
|
||||||
expressList.add(inExpression);
|
expressList.add(inExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(FullTextSearch fullTextSearch) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Between between) {
|
public void visit(Between between) {
|
||||||
if(!(between.getLeftExpression() instanceof Column)){
|
if(!(between.getLeftExpression() instanceof Column)){
|
||||||
|
@ -273,6 +285,10 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor {
|
||||||
public void visit(SimilarToExpression aThis) {
|
public void visit(SimilarToExpression aThis) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ArrayExpression arrayExpression) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(BitwiseRightShift aThis) {
|
public void visit(BitwiseRightShift aThis) {
|
||||||
}
|
}
|
||||||
|
@ -324,6 +340,10 @@ public class ConditionParser implements ExpressionVisitor,ItemsListVisitor {
|
||||||
@Override
|
@Override
|
||||||
public void visit(Division division) {
|
public void visit(Division division) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(IntegerDivision integerDivision) {
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public void visit(Multiplication multiplication) {
|
public void visit(Multiplication multiplication) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.parser;
|
package com.diboot.core.binding.parser;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.diboot.core.config.BaseConfig;
|
import com.diboot.core.config.BaseConfig;
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
|
import com.diboot.core.util.ContextHelper;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
import com.diboot.core.util.SqlExecutor;
|
import com.diboot.core.util.SqlExecutor;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
|
import org.apache.ibatis.jdbc.SQL;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -100,15 +104,21 @@ public class MiddleTable {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> executeOneToOneQuery(List annoObjectForeignKeyList){
|
public Map<String, Object> executeOneToOneQuery(List annoObjectForeignKeyList){
|
||||||
// 提取中间表查询SQL: SELECT id, org_id FROM department WHERE id IN(?)
|
//id //org_id
|
||||||
String sql = toSQL(annoObjectForeignKeyList);
|
String keyName = getEqualsToAnnoObjectFKColumn(), valueName = getEqualsToRefEntityPkColumn();
|
||||||
// 执行查询并合并结果
|
TableLinkage linkage = ParserCache.getTableLinkage(table);
|
||||||
//id
|
// 有定义mapper,首选mapper
|
||||||
String keyName = getEqualsToAnnoObjectFKColumn(),
|
if(linkage != null){
|
||||||
//org_id
|
List<Map<String, Object>> resultSetMapList = queryByMapper(linkage, annoObjectForeignKeyList);
|
||||||
valueName = getEqualsToRefEntityPkColumn();
|
return SqlExecutor.convertToOneToOneResult(resultSetMapList, keyName, valueName);
|
||||||
Map<String, Object> middleTableResultMap = SqlExecutor.executeQueryAndMergeOneToOneResult(sql, annoObjectForeignKeyList, keyName, valueName);
|
}
|
||||||
return middleTableResultMap;
|
else{
|
||||||
|
// 提取中间表查询SQL: SELECT id, org_id FROM department WHERE id IN(?)
|
||||||
|
String sql = toSQL(annoObjectForeignKeyList);
|
||||||
|
// 执行查询并合并结果
|
||||||
|
Map<String, Object> middleTableResultMap = SqlExecutor.executeQueryAndMergeOneToOneResult(sql, annoObjectForeignKeyList, keyName, valueName);
|
||||||
|
return middleTableResultMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,15 +127,42 @@ public class MiddleTable {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map<String, List> executeOneToManyQuery(List annoObjectForeignKeyList){
|
public Map<String, List> executeOneToManyQuery(List annoObjectForeignKeyList){
|
||||||
// 提取中间表查询SQL: SELECT user_id, role_id FROM user_role WHERE user_id IN(?)
|
//user_id //role_id
|
||||||
String sql = toSQL(annoObjectForeignKeyList);
|
String keyName = getEqualsToAnnoObjectFKColumn(), valueName = getEqualsToRefEntityPkColumn();
|
||||||
// 执行查询并合并结果
|
TableLinkage linkage = ParserCache.getTableLinkage(table);
|
||||||
//user_id
|
// 有定义mapper,首选mapper
|
||||||
String keyName = getEqualsToAnnoObjectFKColumn(),
|
if(linkage != null){
|
||||||
//role_id
|
List<Map<String, Object>> resultSetMapList = queryByMapper(linkage, annoObjectForeignKeyList);
|
||||||
valueName = getEqualsToRefEntityPkColumn();
|
return SqlExecutor.convertToOneToManyResult(resultSetMapList, keyName, valueName);
|
||||||
Map<String, List> middleTableResultMap = SqlExecutor.executeQueryAndMergeOneToManyResult(sql, annoObjectForeignKeyList, keyName, valueName);
|
}
|
||||||
return middleTableResultMap;
|
else{
|
||||||
|
// 提取中间表查询SQL: SELECT user_id, role_id FROM user_role WHERE user_id IN(?)
|
||||||
|
String sql = toSQL(annoObjectForeignKeyList);
|
||||||
|
// 执行查询并合并结果
|
||||||
|
Map<String, List> middleTableResultMap = SqlExecutor.executeQueryAndMergeOneToManyResult(sql, annoObjectForeignKeyList, keyName, valueName);
|
||||||
|
return middleTableResultMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过定义的Mapper查询结果
|
||||||
|
* @param linkage
|
||||||
|
* @param annoObjectForeignKeyList
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private List<Map<String, Object>> queryByMapper(TableLinkage linkage, List annoObjectForeignKeyList){
|
||||||
|
BaseMapper mapper = (BaseMapper) ContextHelper.getBean(linkage.getMapperClass());
|
||||||
|
QueryWrapper queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.setEntityClass(linkage.getEntityClass());
|
||||||
|
queryWrapper.select(equalsToAnnoObjectFKColumn, equalsToRefEntityPkColumn);
|
||||||
|
queryWrapper.in(equalsToAnnoObjectFKColumn, annoObjectForeignKeyList);
|
||||||
|
if(additionalConditions != null){
|
||||||
|
for(String condition : additionalConditions){
|
||||||
|
queryWrapper.apply(condition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> resultSetMapList = mapper.selectMaps(queryWrapper);
|
||||||
|
return resultSetMapList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,34 +170,30 @@ public class MiddleTable {
|
||||||
* @param annoObjectForeignKeyList 注解外键值的列表,用于拼接SQL查询
|
* @param annoObjectForeignKeyList 注解外键值的列表,用于拼接SQL查询
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String toSQL(List annoObjectForeignKeyList){
|
private String toSQL(List annoObjectForeignKeyList){
|
||||||
if(V.isEmpty(annoObjectForeignKeyList)){
|
if(V.isEmpty(annoObjectForeignKeyList)){
|
||||||
return null;
|
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());
|
String params = S.repeat("?", ",", annoObjectForeignKeyList.size());
|
||||||
sb.append(params).append(")");
|
// 构建SQL
|
||||||
// 添加删除标记
|
return new SQL(){{
|
||||||
boolean appendDeleteFlag = true;
|
SELECT(equalsToAnnoObjectFKColumn + Cons.SEPARATOR_COMMA + equalsToRefEntityPkColumn);
|
||||||
if(this.additionalConditions != null){
|
FROM(table);
|
||||||
for(String condition : this.additionalConditions){
|
WHERE(equalsToAnnoObjectFKColumn + " IN (" + params + ")");
|
||||||
sb.append(" AND (").append(condition).append(")");
|
// 添加删除标记
|
||||||
if(S.containsIgnoreCase(condition, "is_" + Cons.FieldName.deleted.name())){
|
boolean appendDeleteFlag = true;
|
||||||
appendDeleteFlag = false;
|
if(additionalConditions != null){
|
||||||
|
for(String condition : additionalConditions){
|
||||||
|
WHERE(condition);
|
||||||
|
if(S.containsIgnoreCase(condition, Cons.COLUMN_IS_DELETED)){
|
||||||
|
appendDeleteFlag = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
// 如果需要删除
|
||||||
// 如果需要删除
|
if(appendDeleteFlag && ParserCache.hasDeletedColumn(table)){
|
||||||
if(appendDeleteFlag){
|
WHERE(Cons.COLUMN_IS_DELETED + " = " + BaseConfig.getActiveFlagValue());
|
||||||
if(ParserCache.hasDeletedColumn(this.table)){
|
|
||||||
sb.append(" AND is_deleted = ").append(BaseConfig.getActiveFlagValue());
|
|
||||||
}
|
}
|
||||||
}
|
}}.toString();
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,33 +15,55 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.parser;
|
package com.diboot.core.binding.parser;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.diboot.core.binding.query.BindQuery;
|
||||||
|
import com.diboot.core.binding.query.dynamic.AnnoJoiner;
|
||||||
|
import com.diboot.core.exception.BusinessException;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.SqlExecutor;
|
import com.diboot.core.util.ContextHelper;
|
||||||
|
import com.diboot.core.util.S;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
|
import com.diboot.core.vo.Status;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VO对象中的绑定注解 缓存管理类
|
* 对象中的绑定注解 缓存管理类
|
||||||
* @author mazc@dibo.ltd<br>
|
* @author mazc@dibo.ltd<br>
|
||||||
* @version 2.0<br>
|
* @version 2.0<br>
|
||||||
* @date 2019/04/03 <br>
|
* @date 2019/04/03 <br>
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
public class ParserCache {
|
public class ParserCache {
|
||||||
/**
|
/**
|
||||||
* VO类-绑定注解缓存
|
* VO类-绑定注解缓存
|
||||||
*/
|
*/
|
||||||
private static Map<Class, BindAnnotationGroup> allVoBindAnnotationCacheMap = new ConcurrentHashMap<>();
|
private static Map<Class, BindAnnotationGroup> allVoBindAnnotationCacheMap = new ConcurrentHashMap<>();
|
||||||
/**
|
/**
|
||||||
* 中间表是否包含is_deleted列 缓存
|
* 表及相关信息的缓存
|
||||||
*/
|
*/
|
||||||
private static Map<String, Boolean> middleTableHasDeletedCacheMap = new ConcurrentHashMap<>();
|
private static Map<String, TableLinkage> tableToLinkageCacheMap = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* entity类-表名的缓存
|
||||||
|
*/
|
||||||
|
private static Map<String, String> entityClassTableCacheMap = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* entity类小驼峰实例名-entity类
|
||||||
|
*/
|
||||||
|
private static Map<String, Class<?>> entityName2EntityClassCacheMap = new ConcurrentHashMap<>();
|
||||||
|
/**
|
||||||
|
* dto类-BindQuery注解的缓存
|
||||||
|
*/
|
||||||
|
private static Map<String, List<AnnoJoiner>> dtoClassBindQueryCacheMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定class对应的Bind相关注解
|
* 获取指定class对应的Bind相关注解
|
||||||
|
@ -82,16 +104,210 @@ public class ParserCache {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化Table的相关对象信息
|
||||||
|
*/
|
||||||
|
private static void initTableToLinkageCacheMap(){
|
||||||
|
if(tableToLinkageCacheMap.isEmpty()){
|
||||||
|
SqlSessionFactory sqlSessionFactory = ContextHelper.getBean(SqlSessionFactory.class);
|
||||||
|
Collection<Class<?>> mappers = sqlSessionFactory.getConfiguration().getMapperRegistry().getMappers();
|
||||||
|
if(V.notEmpty(mappers)){
|
||||||
|
mappers.forEach(m->{
|
||||||
|
Type[] types = m.getGenericInterfaces();
|
||||||
|
try{
|
||||||
|
if(types != null && types.length > 0 && types[0] != null){
|
||||||
|
ParameterizedType genericType = (ParameterizedType) types[0];
|
||||||
|
Type[] superTypes = genericType.getActualTypeArguments();
|
||||||
|
if(superTypes != null && superTypes.length > 0 && superTypes[0] != null){
|
||||||
|
String entityClassName = superTypes[0].getTypeName();
|
||||||
|
if(entityClassName.length() > 1){
|
||||||
|
Class<?> entityClass = Class.forName(entityClassName);
|
||||||
|
TableLinkage linkage = new TableLinkage(entityClass, m);
|
||||||
|
tableToLinkageCacheMap.put(linkage.getTable(), linkage);
|
||||||
|
entityName2EntityClassCacheMap.put(entityClass.getSimpleName(), entityClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
log.warn("解析mapper异常", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否有is_deleted列
|
* 是否有is_deleted列
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static boolean hasDeletedColumn(String middleTable){
|
public static boolean hasDeletedColumn(String table){
|
||||||
if(middleTableHasDeletedCacheMap.containsKey(middleTable)){
|
TableLinkage linkage = getTableLinkage(table);
|
||||||
return middleTableHasDeletedCacheMap.get(middleTable);
|
return linkage != null && linkage.isHasDeleted();
|
||||||
}
|
|
||||||
boolean hasColumn = SqlExecutor.validateQuery("SELECT is_deleted FROM "+middleTable);
|
|
||||||
middleTableHasDeletedCacheMap.put(middleTable, hasColumn);
|
|
||||||
return hasColumn;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取table相关信息
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static TableLinkage getTableLinkage(String table){
|
||||||
|
initTableToLinkageCacheMap();
|
||||||
|
TableLinkage linkage = tableToLinkageCacheMap.get(table);
|
||||||
|
return linkage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据entity类获取mapper实例
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BaseMapper getMapperInstance(Class<?> entityClass){
|
||||||
|
String tableName = getEntityTableName(entityClass);
|
||||||
|
TableLinkage linkage = getTableLinkage(tableName);
|
||||||
|
if(linkage == null){
|
||||||
|
throw new BusinessException(Status.FAIL_INVALID_PARAM, "未找到 "+entityClass.getName()+" 的Mapper定义!");
|
||||||
|
}
|
||||||
|
BaseMapper mapper = (BaseMapper) ContextHelper.getBean(linkage.getMapperClass());
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据类的entity类名获取EntityClass
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Class<?> getEntityClassByClassName(String className){
|
||||||
|
initTableToLinkageCacheMap();
|
||||||
|
return entityName2EntityClassCacheMap.get(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前DTO是否有Join绑定
|
||||||
|
* @param dto dto对象
|
||||||
|
* @param fieldNameSet 有值属性集合
|
||||||
|
* @param <DTO>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <DTO> boolean hasJoinTable(DTO dto, Collection<String> fieldNameSet){
|
||||||
|
List<AnnoJoiner> 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<AnnoJoiner> getBindQueryAnnos(Class<?> dtoClass){
|
||||||
|
String dtoClassName = dtoClass.getName();
|
||||||
|
if(dtoClassBindQueryCacheMap.containsKey(dtoClassName)){
|
||||||
|
return dtoClassBindQueryCacheMap.get(dtoClassName);
|
||||||
|
}
|
||||||
|
// 初始化
|
||||||
|
List<AnnoJoiner> annos = null;
|
||||||
|
List<Field> declaredFields = BeanUtils.extractFields(dtoClass, BindQuery.class);
|
||||||
|
int index = 1;
|
||||||
|
Map<String, String> 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);
|
||||||
|
}
|
||||||
|
annoJoiner.parse();
|
||||||
|
}
|
||||||
|
annos.add(annoJoiner);
|
||||||
|
}
|
||||||
|
if(annos == null){
|
||||||
|
annos = Collections.emptyList();
|
||||||
|
}
|
||||||
|
dtoClassBindQueryCacheMap.put(dtoClassName, annos);
|
||||||
|
return annos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取注解joiner
|
||||||
|
* @param dtoClass
|
||||||
|
* @param fieldNames
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<AnnoJoiner> getAnnoJoiners(Class<?> dtoClass, Collection<String> fieldNames) {
|
||||||
|
List<AnnoJoiner> annoList = getBindQueryAnnos(dtoClass);
|
||||||
|
// 不过滤 返回全部
|
||||||
|
if(fieldNames == null){
|
||||||
|
return annoList;
|
||||||
|
}
|
||||||
|
// 过滤
|
||||||
|
if(V.notEmpty(annoList)){
|
||||||
|
List<AnnoJoiner> 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<AnnoJoiner> annoList = getBindQueryAnnos(dtoClass);
|
||||||
|
if(V.notEmpty(annoList)){
|
||||||
|
for(AnnoJoiner anno : annoList){
|
||||||
|
if(key.equals(anno.getFieldName())){
|
||||||
|
return anno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.parser;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.diboot.core.config.Cons;
|
||||||
|
import com.diboot.core.entity.BaseEntity;
|
||||||
|
import com.diboot.core.util.BeanUtils;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* table的相关线索信息
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v1.0
|
||||||
|
* @date 2020/06/02
|
||||||
|
*/
|
||||||
|
@Getter @Setter
|
||||||
|
public class TableLinkage implements Serializable {
|
||||||
|
private static final long serialVersionUID = 4416187849283913895L;
|
||||||
|
|
||||||
|
public TableLinkage(Class<?> entityClass, Class<?> mapperClass){
|
||||||
|
this.entityClass = entityClass;
|
||||||
|
this.mapperClass = mapperClass;
|
||||||
|
this.table = ParserCache.getEntityTableName(entityClass);
|
||||||
|
// 初始化是否有is_deleted
|
||||||
|
Field field = BeanUtils.extractField(entityClass, Cons.FieldName.deleted.name());
|
||||||
|
if(field != null){
|
||||||
|
TableField tableField = field.getAnnotation(TableField.class);
|
||||||
|
if(tableField != null && tableField.exist() == true){
|
||||||
|
this.hasDeleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String table;
|
||||||
|
/**
|
||||||
|
* 表对应的entity类
|
||||||
|
*/
|
||||||
|
private Class<?> entityClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表对应的mapper类
|
||||||
|
*/
|
||||||
|
private Class<?> mapperClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否有逻辑删除字段
|
||||||
|
*/
|
||||||
|
private boolean hasDeleted = false;
|
||||||
|
|
||||||
|
}
|
|
@ -15,11 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package com.diboot.core.binding.query;
|
package com.diboot.core.binding.query;
|
||||||
|
|
||||||
|
import javax.lang.model.type.NullType;
|
||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 绑定管理器
|
* 绑定管理器
|
||||||
* @author Xieshuang
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/7/18
|
* @date 2019/7/18
|
||||||
*/
|
*/
|
||||||
|
@ -40,6 +41,18 @@ public @interface BindQuery {
|
||||||
*/
|
*/
|
||||||
String field() default "";
|
String field() default "";
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 绑定的Entity类
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Class entity() default NullType.class;
|
||||||
|
|
||||||
|
/***
|
||||||
|
* JOIN连接条件,支持动态的跨表JOIN查询
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
String condition() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 忽略该字段
|
* 忽略该字段
|
||||||
* @return
|
* @return
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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 condition;
|
||||||
|
|
||||||
|
private String join;
|
||||||
|
/**
|
||||||
|
* 别名
|
||||||
|
*/
|
||||||
|
private String alias;
|
||||||
|
/**
|
||||||
|
* on条件
|
||||||
|
*/
|
||||||
|
private String onSegment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中间表
|
||||||
|
*/
|
||||||
|
private String middleTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中间表别名
|
||||||
|
*/
|
||||||
|
public String getMiddleTableAlias(){
|
||||||
|
if(middleTable != null && alias != null){
|
||||||
|
return alias+"m";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 中间表on
|
||||||
|
*/
|
||||||
|
private String middleTableOnSegment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析
|
||||||
|
*/
|
||||||
|
public void parse(){
|
||||||
|
// 解析查询
|
||||||
|
JoinConditionManager.parseJoinCondition(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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<DTO,E> extends ExtQueryWrapper<DTO,E> {
|
||||||
|
public DynamicJoinQueryWrapper(Class<DTO> dtoClass, Collection<String> fields){
|
||||||
|
this.dtoClass = dtoClass;
|
||||||
|
this.fields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO类
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private Class<DTO> dtoClass;
|
||||||
|
/**
|
||||||
|
* 字段
|
||||||
|
*/
|
||||||
|
private Collection<String> fields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dto字段和值
|
||||||
|
*/
|
||||||
|
public List<AnnoJoiner> getAnnoJoiners(){
|
||||||
|
return ParserCache.getAnnoJoiners(this.dtoClass, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条数据
|
||||||
|
* @param entityClazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public E queryOne(Class<E> entityClazz){
|
||||||
|
return JoinsBinder.queryOne(this, entityClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条数据
|
||||||
|
* @param entityClazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<E> queryList(Class<E> entityClazz){
|
||||||
|
return JoinsBinder.queryList(this, entityClazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条数据
|
||||||
|
* @param entityClazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<E> queryList(Class<E> entityClazz, Pagination pagination){
|
||||||
|
return JoinsBinder.queryList(this, entityClazz, pagination);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.QueryBuilder;
|
||||||
|
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 <DTO> String buildSqlForListWithPage(Page<?> page, QueryWrapper<DTO> ew){
|
||||||
|
return buildDynamicSql(page, ew, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建动态SQL
|
||||||
|
* @param page 分页参数,用于MP分页插件AOP,不可删除
|
||||||
|
* @param ew
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private <DTO> String buildDynamicSql(Page<?> page, QueryWrapper<DTO> 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<AnnoJoiner> annoJoinerList = wrapper.getAnnoJoiners();
|
||||||
|
if(V.notEmpty(annoJoinerList)){
|
||||||
|
Set<String> tempSet = new HashSet<>();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for(AnnoJoiner joiner : annoJoinerList){
|
||||||
|
if(V.notEmpty(joiner.getJoin()) && V.notEmpty(joiner.getOnSegment())){
|
||||||
|
if(joiner.getMiddleTable() != null){
|
||||||
|
sb.setLength(0);
|
||||||
|
sb.append(joiner.getMiddleTable()).append(" ").append(joiner.getMiddleTableAlias()).append(" ON ").append(joiner.getMiddleTableOnSegment());
|
||||||
|
if(S.containsIgnoreCase(joiner.getMiddleTable(), " "+Cons.COLUMN_IS_DELETED) == false && ParserCache.hasDeletedColumn(joiner.getMiddleTable())){
|
||||||
|
sb.append(" AND ").append(joiner.getMiddleTableAlias()).append(".").append(Cons.COLUMN_IS_DELETED).append(" = ").append(BaseConfig.getActiveFlagValue());
|
||||||
|
}
|
||||||
|
String joinSegment = sb.toString();
|
||||||
|
if(!tempSet.contains(joinSegment)){
|
||||||
|
LEFT_OUTER_JOIN(joinSegment);
|
||||||
|
tempSet.add(joinSegment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.setLength(0);
|
||||||
|
sb.append(joiner.getJoin()).append(" ").append(joiner.getAlias()).append(" ON ").append(joiner.getOnSegment());
|
||||||
|
if(S.containsIgnoreCase(joiner.getOnSegment(), " "+Cons.COLUMN_IS_DELETED) == false && ParserCache.hasDeletedColumn(joiner.getJoin())){
|
||||||
|
sb.append(" AND ").append(joiner.getAlias()).append(".").append(Cons.COLUMN_IS_DELETED).append(" = ").append(BaseConfig.getActiveFlagValue());
|
||||||
|
}
|
||||||
|
String joinSegment = sb.toString();
|
||||||
|
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();
|
||||||
|
if(V.notEmpty(normalSql)){
|
||||||
|
WHERE(formatNormalSql(normalSql));
|
||||||
|
// 动态为主表添加is_deleted=0
|
||||||
|
String isDeletedSection = "self."+ Cons.COLUMN_IS_DELETED;
|
||||||
|
if(QueryBuilder.checkHasColumn(segments.getNormal(), isDeletedSection) == false && ParserCache.hasDeletedColumn(wrapper.getEntityTable())){
|
||||||
|
WHERE(isDeletedSection+ " = " +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<String> selects = new ArrayList<>(columns.length);
|
||||||
|
for(String column : columns){
|
||||||
|
column = S.removeDuplicateBlank(column).trim();
|
||||||
|
selects.add("self."+S.toSnakeCase(column));
|
||||||
|
}
|
||||||
|
return S.join(selects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化where条件的sql
|
||||||
|
* @param normalSql
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String formatNormalSql(String normalSql){
|
||||||
|
if(normalSql.startsWith("(") && normalSql.endsWith(")")){
|
||||||
|
return S.substring(normalSql,1,normalSql.length()-1);
|
||||||
|
}
|
||||||
|
return normalSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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<DTO,E> extends QueryWrapper<DTO> {
|
||||||
|
/**
|
||||||
|
* 主实体class
|
||||||
|
*/
|
||||||
|
@Getter @Setter
|
||||||
|
private Class<E> mainEntityClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取entity表名
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getEntityTable(){
|
||||||
|
return ParserCache.getEntityTableName(getMainEntityClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条数据
|
||||||
|
* @param entityClazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public E queryOne(Class<E> 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<E> queryList(Class<E> entityClazz){
|
||||||
|
BaseService baseService = ContextHelper.getBaseServiceByEntity(entityClazz);
|
||||||
|
if(baseService != null){
|
||||||
|
return (List<E>)baseService.getEntityList(this);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new BusinessException(Status.FAIL_INVALID_PARAM, "单表查询对象无BaseService实现: "+entityClazz.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询一条数据
|
||||||
|
* @param entityClazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<E> queryList(Class<E> entityClazz, Pagination pagination){
|
||||||
|
BaseService baseService = ContextHelper.getBaseServiceByEntity(entityClazz);
|
||||||
|
if(baseService != null){
|
||||||
|
return (List<E>)baseService.getEntityList(this, pagination);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
throw new BusinessException(Status.FAIL_INVALID_PARAM, "单表查询对象无BaseService实现: "+entityClazz.getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.BaseConditionManager;
|
||||||
|
import com.diboot.core.exception.BusinessException;
|
||||||
|
import com.diboot.core.util.S;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
|
import com.diboot.core.vo.Status;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join条件表达式的管理器
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2019/4/1
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class JoinConditionManager extends BaseConditionManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析condition条件
|
||||||
|
* @param joiner
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static void parseJoinCondition(AnnoJoiner joiner) {
|
||||||
|
List<Expression> expressionList = getExpressionList(joiner.getCondition());
|
||||||
|
if(V.isEmpty(expressionList)){
|
||||||
|
log.warn("无法解析注解条件: {} ", joiner.getCondition());
|
||||||
|
throw new BusinessException(Status.FAIL_VALIDATION);
|
||||||
|
}
|
||||||
|
// 解析中间表关联
|
||||||
|
String tableName = extractMiddleTableName(expressionList);
|
||||||
|
if(tableName != null){
|
||||||
|
joiner.setMiddleTable(tableName);
|
||||||
|
}
|
||||||
|
// 解析join
|
||||||
|
parseJoinOn(joiner, expressionList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析直接关联
|
||||||
|
* @param joiner
|
||||||
|
* @param expressionList
|
||||||
|
*/
|
||||||
|
private static void parseJoinOn(AnnoJoiner joiner, List<Expression> expressionList) {
|
||||||
|
List<String> segments = new ArrayList<>(), middleTableOnSegments = new ArrayList<>();
|
||||||
|
// 解析直接关联
|
||||||
|
for(Expression operator : expressionList){
|
||||||
|
// 默认当前表条件
|
||||||
|
List<String> currentSegments = segments;
|
||||||
|
if(operator instanceof BinaryExpression){
|
||||||
|
BinaryExpression expression = (BinaryExpression)operator;
|
||||||
|
String left = formatColumn(expression.getLeftExpression(), joiner);
|
||||||
|
String right = formatColumn(expression.getRightExpression(), joiner);
|
||||||
|
// 中间表条件
|
||||||
|
if(joiner.getMiddleTable() != null &&
|
||||||
|
(left.startsWith(joiner.getMiddleTableAlias() + ".") || right.startsWith(joiner.getMiddleTableAlias() + "."))){
|
||||||
|
if(left.startsWith(joiner.getAlias()+".") || right.startsWith(joiner.getAlias()+".")){
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
currentSegments = middleTableOnSegments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(operator instanceof EqualsTo){
|
||||||
|
currentSegments.add(left + " = " + right);
|
||||||
|
}
|
||||||
|
else if(operator instanceof NotEqualsTo){
|
||||||
|
currentSegments.add(left + " != " + right);
|
||||||
|
}
|
||||||
|
else if(operator instanceof GreaterThan){
|
||||||
|
currentSegments.add(left + " > " + right);
|
||||||
|
}
|
||||||
|
else if(operator instanceof GreaterThanEquals){
|
||||||
|
currentSegments.add(left + " >= " + right);
|
||||||
|
}
|
||||||
|
else if(operator instanceof MinorThan){
|
||||||
|
currentSegments.add(left + " < " + right);
|
||||||
|
}
|
||||||
|
else if(operator instanceof MinorThanEquals){
|
||||||
|
currentSegments.add(left + " <= " + right);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
log.warn("暂不支持的条件: "+ expression.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(operator instanceof IsNullExpression){
|
||||||
|
IsNullExpression expression = (IsNullExpression)operator;
|
||||||
|
String left = formatColumn(expression.getLeftExpression(), joiner);
|
||||||
|
// 中间表条件
|
||||||
|
if(joiner.getMiddleTable() != null && left.startsWith(joiner.getMiddleTableAlias() + ".")){
|
||||||
|
currentSegments = middleTableOnSegments;
|
||||||
|
}
|
||||||
|
if(expression.isNot() == false){
|
||||||
|
currentSegments.add(left + " IS NULL");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
currentSegments.add(left + " IS NOT NULL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(operator instanceof InExpression){
|
||||||
|
InExpression expression = (InExpression)operator;
|
||||||
|
String left = formatColumn(expression.getLeftExpression(), joiner);
|
||||||
|
// 中间表条件
|
||||||
|
if(joiner.getMiddleTable() != null && left.startsWith(joiner.getMiddleTableAlias() + ".")){
|
||||||
|
currentSegments = middleTableOnSegments;
|
||||||
|
}
|
||||||
|
if(expression.isNot() == false){
|
||||||
|
currentSegments.add(left + " IN " + expression.getRightItemsList().toString());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
currentSegments.add(left + " NOT IN " + expression.getRightItemsList().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(operator instanceof Between){
|
||||||
|
Between expression = (Between)operator;
|
||||||
|
String left = formatColumn(expression.getLeftExpression(), joiner);
|
||||||
|
// 中间表条件
|
||||||
|
if(joiner.getMiddleTable() != null && left.startsWith(joiner.getMiddleTableAlias() + ".")){
|
||||||
|
currentSegments = middleTableOnSegments;
|
||||||
|
}
|
||||||
|
if(expression.isNot() == false){
|
||||||
|
currentSegments.add(left + " BETWEEN " + expression.getBetweenExpressionStart().toString() + " AND " + expression.getBetweenExpressionEnd().toString());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
currentSegments.add(left + " NOT BETWEEN " + expression.getBetweenExpressionStart().toString() + " AND " + expression.getBetweenExpressionEnd().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(operator instanceof LikeExpression){
|
||||||
|
LikeExpression expression = (LikeExpression)operator;
|
||||||
|
String left = formatColumn(expression.getLeftExpression(), joiner);
|
||||||
|
// 中间表条件
|
||||||
|
if(joiner.getMiddleTable() != null && left.startsWith(joiner.getMiddleTableAlias() + ".")){
|
||||||
|
currentSegments = middleTableOnSegments;
|
||||||
|
}
|
||||||
|
if(expression.isNot() == false){
|
||||||
|
currentSegments.add(left + " LIKE " + expression.getStringExpression());
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
currentSegments.add(left + " NOT LIKE " + expression.getStringExpression());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
log.warn("不支持的条件: "+operator.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(segments.isEmpty() && middleTableOnSegments.isEmpty()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
joiner.setOnSegment(S.join(segments, " AND "));
|
||||||
|
if(V.notEmpty(middleTableOnSegments)){
|
||||||
|
joiner.setMiddleTableOnSegment(S.join(middleTableOnSegments, " AND "));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化左侧
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private static String formatColumn(Expression expression, AnnoJoiner joiner){
|
||||||
|
String annoColumn = S.toSnakeCase(expression.toString());
|
||||||
|
if(expression instanceof Column){
|
||||||
|
// 其他表列
|
||||||
|
if(annoColumn.contains(".")){
|
||||||
|
String tableName = S.substringBefore(annoColumn, ".");
|
||||||
|
// 当前表替换别名
|
||||||
|
if(tableName.equals("this")){
|
||||||
|
annoColumn = "self." + S.substringAfter(annoColumn, "this.");
|
||||||
|
}
|
||||||
|
else if(tableName.equals("self")){
|
||||||
|
}
|
||||||
|
else if(tableName.equals(joiner.getMiddleTable())){
|
||||||
|
annoColumn = joiner.getMiddleTableAlias() + "." + S.substringAfter(annoColumn, ".");
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
log.warn("无法识别的条件: {}", annoColumn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 当前表列
|
||||||
|
else{
|
||||||
|
annoColumn = joiner.getAlias() + "." + annoColumn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return annoColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -93,7 +93,7 @@ public class BaseConfig {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static int getPageSize() {
|
public static int getPageSize() {
|
||||||
Integer length = PropertiesUtils.getInteger("pagination.default.pageSize");
|
Integer length = PropertiesUtils.getInteger("system.pagination.pageSize");
|
||||||
if(length != null){
|
if(length != null){
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,10 @@ public class BaseConfig {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static int getBatchSize() {
|
public static int getBatchSize() {
|
||||||
|
Integer length = PropertiesUtils.getInteger("system.batch.size");
|
||||||
|
if(length != null){
|
||||||
|
return length;
|
||||||
|
}
|
||||||
return 1000;
|
return 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,10 @@ public class Cons {
|
||||||
* 排序 - 降序标记
|
* 排序 - 降序标记
|
||||||
*/
|
*/
|
||||||
public static final String ORDER_DESC = "DESC";
|
public static final String ORDER_DESC = "DESC";
|
||||||
|
/**
|
||||||
|
* 逻辑删除列名
|
||||||
|
*/
|
||||||
|
public static final String COLUMN_IS_DELETED = "is_deleted";
|
||||||
/***
|
/***
|
||||||
* 默认字段名定义
|
* 默认字段名定义
|
||||||
*/
|
*/
|
||||||
|
@ -61,7 +65,15 @@ public class Cons {
|
||||||
/**
|
/**
|
||||||
* 创建时间字段
|
* 创建时间字段
|
||||||
*/
|
*/
|
||||||
createTime
|
createTime,
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
updateTime,
|
||||||
|
/**
|
||||||
|
* 创建人
|
||||||
|
*/
|
||||||
|
createBy
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,14 @@ package com.diboot.core.controller;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
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.QueryBuilder;
|
||||||
import com.diboot.core.binding.RelationsBinder;
|
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -37,48 +38,46 @@ import java.util.*;
|
||||||
public class BaseController {
|
public class BaseController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(BaseController.class);
|
private static final Logger log = LoggerFactory.getLogger(BaseController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected HttpServletRequest request;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 构建查询QueryWrapper (根据BindQuery注解构建相应的查询条件)
|
* 构建查询QueryWrapper (根据BindQuery注解构建相应的查询条件)
|
||||||
* @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解,默认构建为为EQ相等条件)
|
* @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解,默认构建为为EQ相等条件)
|
||||||
* @param <T>
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public <T,DTO> QueryWrapper<T> buildQueryWrapper(DTO entityOrDto, HttpServletRequest request) throws Exception{
|
public <DTO> QueryWrapper<DTO> buildQueryWrapper(DTO entityOrDto) throws Exception{
|
||||||
if(entityOrDto instanceof HttpServletRequest){
|
if(entityOrDto instanceof HttpServletRequest){
|
||||||
throw new Exception("参数错误:buildQueryWrapper()参数为Entity/DTO对象!");
|
throw new Exception("参数错误:buildQueryWrapper()参数为Entity/DTO对象!");
|
||||||
}
|
}
|
||||||
|
return QueryBuilder.toQueryWrapper(entityOrDto, extractQueryParams());
|
||||||
return QueryBuilder.toQueryWrapper(entityOrDto, extractParams(request));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 构建查询LambdaQueryWrapper (根据BindQuery注解构建相应的查询条件)
|
* 构建查询LambdaQueryWrapper (根据BindQuery注解构建相应的查询条件)
|
||||||
* @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解,默认构建为为EQ相等条件)
|
* @param entityOrDto Entity对象或者DTO对象 (属性若无BindQuery注解,默认构建为为EQ相等条件)
|
||||||
* @param <T>
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public <T,DTO> LambdaQueryWrapper<T> buildLambdaQueryWrapper(DTO entityOrDto, HttpServletRequest request) throws Exception{
|
public <DTO> LambdaQueryWrapper<DTO> buildLambdaQueryWrapper(DTO entityOrDto) throws Exception{
|
||||||
if(entityOrDto instanceof HttpServletRequest){
|
if(entityOrDto instanceof HttpServletRequest){
|
||||||
throw new Exception("参数错误:buildQueryWrapper()参数为Entity/DTO对象!");
|
throw new Exception("参数错误:buildQueryWrapper()参数为Entity/DTO对象!");
|
||||||
}
|
}
|
||||||
return QueryBuilder.toLambdaQueryWrapper(entityOrDto, extractParams(request));
|
return QueryBuilder.toLambdaQueryWrapper(entityOrDto, extractQueryParams());
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 获取请求参数Map
|
* 获取请求参数Map
|
||||||
* @param request
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map<String, Object> getParamsMap(HttpServletRequest request) throws Exception{
|
public Map<String, Object> getParamsMap() throws Exception{
|
||||||
return getParamsMap(request, null);
|
return getParamsMap(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 获取请求参数Map
|
* 获取请求参数Map
|
||||||
* @param request
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private Map<String, Object> getParamsMap(HttpServletRequest request, List<String> paramList) throws Exception{
|
private Map<String, Object> getParamsMap(List<String> paramList) throws Exception{
|
||||||
Map<String, Object> result = new HashMap<>(8);
|
Map<String, Object> result = new HashMap<>(8);
|
||||||
Enumeration paramNames = request.getParameterNames();
|
Enumeration paramNames = request.getParameterNames();
|
||||||
while (paramNames.hasMoreElements()){
|
while (paramNames.hasMoreElements()){
|
||||||
|
@ -111,10 +110,9 @@ public class BaseController {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 获取请求URI (去除contextPath)
|
* 获取请求URI (去除contextPath)
|
||||||
* @param request
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected static String getRequestMappingURI(HttpServletRequest request){
|
protected String getRequestMappingURI(){
|
||||||
String contextPath = request.getContextPath();
|
String contextPath = request.getContextPath();
|
||||||
if(V.notEmpty(contextPath)){
|
if(V.notEmpty(contextPath)){
|
||||||
return S.replace(request.getRequestURI(), contextPath, "");
|
return S.replace(request.getRequestURI(), contextPath, "");
|
||||||
|
@ -124,11 +122,10 @@ public class BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提取请求参数名集合
|
* 提取请求参数名集合
|
||||||
* @param request
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static Set<String> extractParams(HttpServletRequest request){
|
protected Set<String> extractQueryParams(){
|
||||||
Map<String, Object> paramValueMap = convertParams2Map(request);
|
Map<String, Object> paramValueMap = convertParams2Map();
|
||||||
if(V.notEmpty(paramValueMap)){
|
if(V.notEmpty(paramValueMap)){
|
||||||
return paramValueMap.keySet();
|
return paramValueMap.keySet();
|
||||||
}
|
}
|
||||||
|
@ -137,10 +134,9 @@ public class BaseController {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 将请求参数值转换为Map
|
* 将请求参数值转换为Map
|
||||||
* @param request
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Map<String, Object> convertParams2Map(HttpServletRequest request){
|
protected Map<String, Object> convertParams2Map(){
|
||||||
Map<String, Object> result = new HashMap<>(8);
|
Map<String, Object> result = new HashMap<>(8);
|
||||||
if(request == null){
|
if(request == null){
|
||||||
return result;
|
return result;
|
||||||
|
@ -174,15 +170,14 @@ public class BaseController {
|
||||||
*/
|
*/
|
||||||
protected <VO> List<VO> convertToVoAndBindRelations(List entityList, Class<VO> voClass) {
|
protected <VO> List<VO> convertToVoAndBindRelations(List entityList, Class<VO> voClass) {
|
||||||
// 转换为VO
|
// 转换为VO
|
||||||
List<VO> voList = RelationsBinder.convertAndBind(entityList, voClass);
|
List<VO> voList = Binder.convertAndBindRelations(entityList, voClass);
|
||||||
return voList;
|
return voList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 打印所有参数信息
|
* 打印所有参数信息
|
||||||
* @param request
|
|
||||||
*/
|
*/
|
||||||
protected static void dumpParams(HttpServletRequest request){
|
protected void dumpParams(){
|
||||||
Map<String, String[]> params = request.getParameterMap();
|
Map<String, String[]> params = request.getParameterMap();
|
||||||
if(params != null && !params.isEmpty()){
|
if(params != null && !params.isEmpty()){
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -198,74 +193,67 @@ public class BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取Long参数
|
* 从request获取Long参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Long getLong(HttpServletRequest request, String param){
|
protected Long getLong(String param){
|
||||||
return S.toLong(request.getParameter(param));
|
return S.toLong(request.getParameter(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取Long参数
|
* 从request获取Long参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @param defaultValue
|
* @param defaultValue
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public long getLong(HttpServletRequest request, String param, Long defaultValue){
|
protected long getLong(String param, Long defaultValue){
|
||||||
return S.toLong(request.getParameter(param), defaultValue);
|
return S.toLong(request.getParameter(param), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取Int参数
|
* 从request获取Int参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Integer getInteger(HttpServletRequest request, String param){
|
protected Integer getInteger(String param){
|
||||||
return S.toInt(request.getParameter(param));
|
return S.toInt(request.getParameter(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取Int参数
|
* 从request获取Int参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @param defaultValue
|
* @param defaultValue
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int getInt(HttpServletRequest request, String param, Integer defaultValue){
|
protected int getInt(String param, Integer defaultValue){
|
||||||
return S.toInt(request.getParameter(param), defaultValue);
|
return S.toInt(request.getParameter(param), defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 从request中获取boolean值
|
* 从request中获取boolean值
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean getBoolean(HttpServletRequest request, String param){
|
protected boolean getBoolean(String param){
|
||||||
return S.toBoolean(request.getParameter(param));
|
return S.toBoolean(request.getParameter(param));
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 从request中获取boolean值
|
* 从request中获取boolean值
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @param defaultBoolean
|
* @param defaultBoolean
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean getBoolean(HttpServletRequest request, String param, boolean defaultBoolean){
|
protected boolean getBoolean(String param, boolean defaultBoolean){
|
||||||
return S.toBoolean(request.getParameter(param), defaultBoolean);
|
return S.toBoolean(request.getParameter(param), defaultBoolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取Double参数
|
* 从request获取Double参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Double getDouble(HttpServletRequest request, String param){
|
protected Double getDouble(String param){
|
||||||
if(V.notEmpty(request.getParameter(param))){
|
if(V.notEmpty(request.getParameter(param))){
|
||||||
return Double.parseDouble(request.getParameter(param));
|
return Double.parseDouble(request.getParameter(param));
|
||||||
}
|
}
|
||||||
|
@ -274,12 +262,11 @@ public class BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取Double参数
|
* 从request获取Double参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @param defaultValue
|
* @param defaultValue
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Double getDouble(HttpServletRequest request, String param, Double defaultValue){
|
protected Double getDouble(String param, Double defaultValue){
|
||||||
if(V.notEmpty(request.getParameter(param))){
|
if(V.notEmpty(request.getParameter(param))){
|
||||||
return Double.parseDouble(request.getParameter(param));
|
return Double.parseDouble(request.getParameter(param));
|
||||||
}
|
}
|
||||||
|
@ -288,11 +275,10 @@ public class BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取String参数
|
* 从request获取String参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String getString(HttpServletRequest request, String param){
|
protected String getString(String param){
|
||||||
if(V.notEmpty(request.getParameter(param))){
|
if(V.notEmpty(request.getParameter(param))){
|
||||||
return request.getParameter(param);
|
return request.getParameter(param);
|
||||||
}
|
}
|
||||||
|
@ -301,12 +287,11 @@ public class BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取String参数
|
* 从request获取String参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @param defaultValue
|
* @param defaultValue
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String getString(HttpServletRequest request, String param, String defaultValue){
|
protected String getString(String param, String defaultValue){
|
||||||
if(V.notEmpty(request.getParameter(param))){
|
if(V.notEmpty(request.getParameter(param))){
|
||||||
return request.getParameter(param);
|
return request.getParameter(param);
|
||||||
}
|
}
|
||||||
|
@ -315,11 +300,10 @@ public class BaseController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从request获取String[]参数
|
* 从request获取String[]参数
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String[] getStringArray(HttpServletRequest request, String param){
|
protected String[] getStringArray(String param){
|
||||||
if(request.getParameterValues(param) != null){
|
if(request.getParameterValues(param) != null){
|
||||||
return request.getParameterValues(param);
|
return request.getParameterValues(param);
|
||||||
}
|
}
|
||||||
|
@ -328,12 +312,11 @@ public class BaseController {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 从request里获取String列表
|
* 从request里获取String列表
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<String> getStringList(HttpServletRequest request, String param){
|
protected List<String> getStringList(String param){
|
||||||
String[] strArray = getStringArray(request, param);
|
String[] strArray = getStringArray(param);
|
||||||
if(V.isEmpty(strArray)){
|
if(V.isEmpty(strArray)){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -342,12 +325,11 @@ public class BaseController {
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 从request里获取Long列表
|
* 从request里获取Long列表
|
||||||
* @param request
|
|
||||||
* @param param
|
* @param param
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<Long> getLongList(HttpServletRequest request, String param){
|
protected List<Long> getLongList(String param){
|
||||||
String[] strArray = getStringArray(request, param);
|
String[] strArray = getStringArray(param);
|
||||||
if(V.isEmpty(strArray)){
|
if(V.isEmpty(strArray)){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.diboot.core.config.Cons;
|
||||||
import com.diboot.core.entity.BaseEntity;
|
import com.diboot.core.entity.BaseEntity;
|
||||||
import com.diboot.core.exception.BusinessException;
|
import com.diboot.core.exception.BusinessException;
|
||||||
import com.diboot.core.service.BaseService;
|
import com.diboot.core.service.BaseService;
|
||||||
|
import com.diboot.core.service.DictionaryService;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.ContextHelper;
|
import com.diboot.core.util.ContextHelper;
|
||||||
import com.diboot.core.util.S;
|
import com.diboot.core.util.S;
|
||||||
|
@ -30,8 +31,8 @@ import com.diboot.core.vo.Pagination;
|
||||||
import com.diboot.core.vo.Status;
|
import com.diboot.core.vo.Status;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -44,26 +45,27 @@ import java.util.Map;
|
||||||
* @version 2.0
|
* @version 2.0
|
||||||
* @date 2019/01/01
|
* @date 2019/01/01
|
||||||
*/
|
*/
|
||||||
public class BaseCrudRestController<E extends BaseEntity, VO extends Serializable> extends BaseController {
|
public class BaseCrudRestController<E extends BaseEntity> extends BaseController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(BaseCrudRestController.class);
|
private static final Logger log = LoggerFactory.getLogger(BaseCrudRestController.class);
|
||||||
/**
|
/**
|
||||||
* Entity,VO对应的class
|
* Entity,VO对应的class
|
||||||
*/
|
*/
|
||||||
private Class<E> entityClass;
|
private Class<E> entityClass;
|
||||||
private Class<VO> voClasss;
|
|
||||||
/**
|
/**
|
||||||
* Service实现类
|
* Service实现类
|
||||||
*/
|
*/
|
||||||
private BaseService baseService;
|
private BaseService baseService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
protected DictionaryService dictionaryService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询ViewObject,用于子类重写的方法
|
* 查询ViewObject,用于子类重写的方法
|
||||||
* @param id
|
* @param id
|
||||||
* @param request
|
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected JsonResult getViewObject(Serializable id, HttpServletRequest request) throws Exception{
|
protected <VO> JsonResult getViewObject(Serializable id, Class<VO> voClass) throws Exception{
|
||||||
// 检查String类型id
|
// 检查String类型id
|
||||||
if(id instanceof String && !S.isNumeric((String)id)){
|
if(id instanceof String && !S.isNumeric((String)id)){
|
||||||
String pk = ContextHelper.getPrimaryKey(getEntityClass());
|
String pk = ContextHelper.getPrimaryKey(getEntityClass());
|
||||||
|
@ -71,7 +73,7 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
throw new BusinessException(Status.FAIL_INVALID_PARAM, "参数id类型不匹配!");
|
throw new BusinessException(Status.FAIL_INVALID_PARAM, "参数id类型不匹配!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VO vo = (VO)getService().getViewObject(id, getVOClass());
|
VO vo = (VO)getService().getViewObject(id, voClass);
|
||||||
return new JsonResult(vo);
|
return new JsonResult(vo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,10 +85,10 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected JsonResult getViewObjectList(E entity, Pagination pagination, HttpServletRequest request) throws Exception {
|
protected <VO> JsonResult getViewObjectList(E entity, Pagination pagination, Class<VO> voClass) throws Exception {
|
||||||
QueryWrapper<E> queryWrapper = super.buildQueryWrapper(entity, request);
|
QueryWrapper<E> queryWrapper = super.buildQueryWrapper(entity);
|
||||||
// 查询当前页的数据
|
// 查询当前页的数据
|
||||||
List<VO> voList = getService().getViewObjectList(queryWrapper, pagination, getVOClass());
|
List<VO> voList = getService().getViewObjectList(queryWrapper, pagination, voClass);
|
||||||
// 返回结果
|
// 返回结果
|
||||||
return JsonResult.OK(voList).bindPagination(pagination);
|
return JsonResult.OK(voList).bindPagination(pagination);
|
||||||
}
|
}
|
||||||
|
@ -125,7 +127,7 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected JsonResult createEntity(E entity, HttpServletRequest request) throws Exception {
|
protected JsonResult createEntity(E entity) throws Exception {
|
||||||
// 执行创建资源前的操作
|
// 执行创建资源前的操作
|
||||||
String validateResult = this.beforeCreate(entity);
|
String validateResult = this.beforeCreate(entity);
|
||||||
if (validateResult != null) {
|
if (validateResult != null) {
|
||||||
|
@ -152,7 +154,7 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected JsonResult updateEntity(Serializable id, E entity, HttpServletRequest request) throws Exception {
|
protected JsonResult updateEntity(Serializable id, E entity) throws Exception {
|
||||||
// 如果前端没有指定entity.id,在此设置,以兼容前端不传的情况
|
// 如果前端没有指定entity.id,在此设置,以兼容前端不传的情况
|
||||||
if(entity.getId() == null){
|
if(entity.getId() == null){
|
||||||
String pk = ContextHelper.getPrimaryKey(getEntityClass());
|
String pk = ContextHelper.getPrimaryKey(getEntityClass());
|
||||||
|
@ -188,7 +190,7 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected JsonResult deleteEntity(Serializable id, HttpServletRequest request) throws Exception {
|
protected JsonResult deleteEntity(Serializable id) throws Exception {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return new JsonResult(Status.FAIL_INVALID_PARAM, "请选择需要删除的条目!");
|
return new JsonResult(Status.FAIL_INVALID_PARAM, "请选择需要删除的条目!");
|
||||||
}
|
}
|
||||||
|
@ -218,7 +220,7 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
* @return
|
* @return
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
protected JsonResult batchDeleteEntities(Collection<? extends Serializable> ids, HttpServletRequest request) throws Exception {
|
protected JsonResult batchDeleteEntities(Collection<? extends Serializable> ids) throws Exception {
|
||||||
if (V.isEmpty(ids)) {
|
if (V.isEmpty(ids)) {
|
||||||
return new JsonResult(Status.FAIL_INVALID_PARAM, "请选择需要删除的条目!");
|
return new JsonResult(Status.FAIL_INVALID_PARAM, "请选择需要删除的条目!");
|
||||||
}
|
}
|
||||||
|
@ -355,17 +357,4 @@ public class BaseCrudRestController<E extends BaseEntity, VO extends Serializabl
|
||||||
return this.entityClass;
|
return this.entityClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取VO的class
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected Class<VO> getVOClass(){
|
|
||||||
if(this.voClasss == null){
|
|
||||||
this.voClasss = BeanUtils.getGenericityClass(this, 1);
|
|
||||||
if(this.voClasss == null) {
|
|
||||||
log.warn("无法从 {} 类定义中获取泛型类voClasss", this.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.voClasss;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -20,6 +20,7 @@ import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.ContextHelper;
|
import com.diboot.core.util.ContextHelper;
|
||||||
|
import com.diboot.core.util.D;
|
||||||
import com.diboot.core.util.JSON;
|
import com.diboot.core.util.JSON;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -41,23 +42,24 @@ import java.util.Map;
|
||||||
public abstract class BaseEntity implements Serializable {
|
public abstract class BaseEntity implements Serializable {
|
||||||
private static final long serialVersionUID = 10203L;
|
private static final long serialVersionUID = 10203L;
|
||||||
|
|
||||||
/***
|
/**
|
||||||
* 默认主键字段id,类型为Long型自增,转json时转换为String
|
* 默认主键字段id,类型为Long型自增,转json时转换为String
|
||||||
*/
|
*/
|
||||||
@TableId(type = IdType.AUTO)
|
@TableId(type = IdType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/***
|
/**
|
||||||
* 默认逻辑删除标记,is_deleted=0有效
|
* 默认逻辑删除标记,is_deleted=0有效
|
||||||
*/
|
*/
|
||||||
@TableLogic
|
@TableLogic
|
||||||
@JSONField(serialize = false)
|
@JSONField(serialize = false)
|
||||||
@TableField("is_deleted")
|
@TableField(Cons.COLUMN_IS_DELETED)
|
||||||
private boolean deleted = false;
|
private boolean deleted = false;
|
||||||
|
|
||||||
/***
|
/**
|
||||||
* 默认记录创建时间字段,新建时由数据库赋值
|
* 默认记录创建时间字段,新建时由数据库赋值
|
||||||
*/
|
*/
|
||||||
|
@JSONField(format = D.FORMAT_DATETIME_Y4MDHMS)
|
||||||
@TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER)
|
@TableField(insertStrategy = FieldStrategy.NEVER, updateStrategy = FieldStrategy.NEVER)
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,12 @@ import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 附带extdata扩展字段的Entity父类
|
* 附带extdata扩展字段的Entity父类 (已废弃)
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2018/12/27
|
* @date 2018/12/27
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class BaseExtEntity extends BaseEntity {
|
public abstract class BaseExtEntity extends BaseEntity {
|
||||||
private static final long serialVersionUID = 10204L;
|
private static final long serialVersionUID = 10204L;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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<String, Object> query(@Param(Constants.WRAPPER) QueryWrapper ew);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态SQL查询
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@SelectProvider(type= DynamicSqlProvider.class, method="buildSqlForList")
|
||||||
|
List<Map<String, Object>> queryForList(@Param(Constants.WRAPPER) QueryWrapper ew);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态SQL查询
|
||||||
|
* @param page
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@SelectProvider(type= DynamicSqlProvider.class, method="buildSqlForListWithPage")
|
||||||
|
IPage<Map<String, Object>> queryForListWithPage(Page<?> page, @Param(Constants.WRAPPER) QueryWrapper ew);
|
||||||
|
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ package com.diboot.core.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||||
import com.diboot.core.binding.binder.EntityBinder;
|
import com.diboot.core.binding.binder.EntityBinder;
|
||||||
import com.diboot.core.binding.binder.EntityListBinder;
|
import com.diboot.core.binding.binder.EntityListBinder;
|
||||||
import com.diboot.core.binding.binder.FieldBinder;
|
import com.diboot.core.binding.binder.FieldBinder;
|
||||||
|
@ -75,6 +76,17 @@ public interface BaseService<T> {
|
||||||
*/
|
*/
|
||||||
<RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
<RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建或更新n-n关联
|
||||||
|
* (在主对象的service中调用,不依赖中间表service实现中间表操作)
|
||||||
|
* @param driverIdGetter 驱动对象getter
|
||||||
|
* @param driverId 驱动对象ID
|
||||||
|
* @param followerIdGetter 从动对象getter
|
||||||
|
* @param followerIdList 从动对象id集合
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
<R> boolean createOrUpdateN2NRelations(SFunction<R, ?> driverIdGetter, Object driverId, SFunction<R, ?> followerIdGetter, List<? extends Serializable> followerIdList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新Entity实体
|
* 更新Entity实体
|
||||||
* @param entity
|
* @param entity
|
||||||
|
@ -182,6 +194,15 @@ public interface BaseService<T> {
|
||||||
*/
|
*/
|
||||||
List<T> getEntityList(Wrapper queryWrapper, Pagination pagination);
|
List<T> getEntityList(Wrapper queryWrapper, Pagination pagination);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定条件的Entity ID集合
|
||||||
|
* @param queryWrapper
|
||||||
|
* @param getterFn
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
<FT> List<FT> getValuesOfField(Wrapper queryWrapper, SFunction<T, ?> getterFn);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定条件的Entity集合
|
* 获取指定条件的Entity集合
|
||||||
* @param ids
|
* @param ids
|
||||||
|
@ -200,7 +221,7 @@ public interface BaseService<T> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取符合条件的一个Entity实体
|
* 获取符合条件的一个Entity实体
|
||||||
* @param queryWrapper 主键
|
* @param queryWrapper
|
||||||
* @return entity
|
* @return entity
|
||||||
*/
|
*/
|
||||||
T getSingleEntity(Wrapper queryWrapper);
|
T getSingleEntity(Wrapper queryWrapper);
|
||||||
|
|
|
@ -17,21 +17,29 @@ package com.diboot.core.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.LambdaUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
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.EntityBinder;
|
||||||
import com.diboot.core.binding.binder.EntityListBinder;
|
import com.diboot.core.binding.binder.EntityListBinder;
|
||||||
import com.diboot.core.binding.binder.FieldBinder;
|
import com.diboot.core.binding.binder.FieldBinder;
|
||||||
|
import com.diboot.core.binding.query.dynamic.DynamicJoinQueryWrapper;
|
||||||
import com.diboot.core.config.BaseConfig;
|
import com.diboot.core.config.BaseConfig;
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
|
import com.diboot.core.exception.BusinessException;
|
||||||
import com.diboot.core.mapper.BaseCrudMapper;
|
import com.diboot.core.mapper.BaseCrudMapper;
|
||||||
import com.diboot.core.service.BaseService;
|
import com.diboot.core.service.BaseService;
|
||||||
import com.diboot.core.util.*;
|
import com.diboot.core.util.*;
|
||||||
import com.diboot.core.vo.KeyValue;
|
import com.diboot.core.vo.KeyValue;
|
||||||
import com.diboot.core.vo.Pagination;
|
import com.diboot.core.vo.Pagination;
|
||||||
|
import com.diboot.core.vo.Status;
|
||||||
|
import org.apache.ibatis.reflection.property.PropertyNamer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -70,10 +78,23 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
warning("createEntity", "参数entity为null");
|
warning("createEntity", "参数entity为null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return super.save(entity);
|
return save(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
public boolean save(T entity) {
|
||||||
|
beforeCreateEntity(entity);
|
||||||
|
return super.save(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于创建之前的自动填充等场景调用
|
||||||
|
*/
|
||||||
|
protected void beforeCreateEntity(T entity){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public <RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter) {
|
public <RE, R> boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter) {
|
||||||
boolean success = createEntity(entity);
|
boolean success = createEntity(entity);
|
||||||
if(!success){
|
if(!success){
|
||||||
|
@ -83,13 +104,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
if(V.isEmpty(relatedEntities)){
|
if(V.isEmpty(relatedEntities)){
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
Class relatedEntityClass = BeanUtils.getTargetClass(relatedEntities.get(0));
|
Class relatedEntityClass = relatedEntities.get(0).getClass();
|
||||||
// 获取关联对象对应的Service
|
|
||||||
BaseService relatedEntityService = ContextHelper.getBaseServiceByEntity(relatedEntityClass);
|
|
||||||
if(relatedEntityService == null){
|
|
||||||
log.error("未能识别到Entity: {} 的Service实现,请检查!", relatedEntityClass.getName());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 获取主键
|
// 获取主键
|
||||||
Object pkValue = getPrimaryKeyValue(entity);
|
Object pkValue = getPrimaryKeyValue(entity);
|
||||||
String attributeName = BeanUtils.convertToFieldName(relatedEntitySetter);
|
String attributeName = BeanUtils.convertToFieldName(relatedEntitySetter);
|
||||||
|
@ -97,7 +112,20 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
relatedEntities.stream().forEach(relatedEntity->{
|
relatedEntities.stream().forEach(relatedEntity->{
|
||||||
BeanUtils.setProperty(relatedEntity, attributeName, pkValue);
|
BeanUtils.setProperty(relatedEntity, attributeName, pkValue);
|
||||||
});
|
});
|
||||||
return relatedEntityService.createEntities(relatedEntities);
|
// 获取关联对象对应的Service
|
||||||
|
BaseService relatedEntityService = ContextHelper.getBaseServiceByEntity(relatedEntityClass);
|
||||||
|
if(relatedEntityService != null){
|
||||||
|
return relatedEntityService.createEntities(relatedEntities);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 查找mapper
|
||||||
|
BaseMapper mapper = ContextHelper.getBaseMapperByEntity(entity.getClass());
|
||||||
|
// 新增关联,无service只能循环插入
|
||||||
|
for(RE relation : relatedEntities){
|
||||||
|
mapper.insert(relation);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -114,7 +142,26 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// 批量插入
|
// 批量插入
|
||||||
return super.saveBatch(entityList, BaseConfig.getBatchSize());
|
return saveBatch(entityList, BaseConfig.getBatchSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean saveBatch(Collection<T> entityList, int batchSize){
|
||||||
|
// 批量插入
|
||||||
|
beforeCreateEntities(entityList);
|
||||||
|
return super.saveBatch(entityList, batchSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用于创建之前的自动填充等场景调用
|
||||||
|
*/
|
||||||
|
protected void beforeCreateEntities(Collection<T> entityList){
|
||||||
|
if(V.isEmpty(entityList)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for(T entity : entityList){
|
||||||
|
beforeCreateEntity(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +184,11 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean updateEntities(Collection<T> entityList) {
|
public boolean updateEntities(Collection<T> entityList) {
|
||||||
|
if(V.isEmpty(entityList)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
boolean success = super.updateBatchById(entityList);
|
boolean success = super.updateBatchById(entityList);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
@ -159,6 +210,67 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
return super.saveOrUpdateBatch(entityList, BaseConfig.getBatchSize());
|
return super.saveOrUpdateBatch(entityList, BaseConfig.getBatchSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新n-n关联
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public <R> boolean createOrUpdateN2NRelations(SFunction<R, ?> driverIdGetter, Object driverId,
|
||||||
|
SFunction<R, ?> followerIdGetter, List<? extends Serializable> followerIdList)
|
||||||
|
{
|
||||||
|
if(driverId == null){
|
||||||
|
throw new BusinessException(Status.FAIL_INVALID_PARAM, "主动ID值不能为空!");
|
||||||
|
}
|
||||||
|
// 从getter中获取class和fieldName
|
||||||
|
com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda lambda = LambdaUtils.resolve(driverIdGetter);
|
||||||
|
Class<R> middleTableClass = (Class<R>) lambda.getImplClass();
|
||||||
|
// 获取主动从动字段名
|
||||||
|
String driverFieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
|
||||||
|
String followerFieldName = convertGetterToFieldName(followerIdGetter);
|
||||||
|
List<R> n2nRelations = null;
|
||||||
|
if(V.notEmpty(followerIdList)){
|
||||||
|
n2nRelations = new ArrayList<>(followerIdList.size());
|
||||||
|
try{
|
||||||
|
for(Serializable followerId : followerIdList){
|
||||||
|
R relation = middleTableClass.newInstance();
|
||||||
|
BeanUtils.setProperty(relation, driverFieldName, driverId);
|
||||||
|
BeanUtils.setProperty(relation, followerFieldName, followerId);
|
||||||
|
n2nRelations.add(relation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
throw new BusinessException(Status.FAIL_EXCEPTION, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 删除已有关联
|
||||||
|
LambdaQueryWrapper<R> queryWrapper = new QueryWrapper<R>().lambda()
|
||||||
|
.eq(driverIdGetter, driverId);
|
||||||
|
// 查找service
|
||||||
|
BaseService baseService = ContextHelper.getBaseServiceByEntity(middleTableClass);
|
||||||
|
if(baseService != null){
|
||||||
|
// 条件为空,不删除
|
||||||
|
baseService.deleteEntities(queryWrapper);
|
||||||
|
// 添加
|
||||||
|
if(V.notEmpty(n2nRelations)){
|
||||||
|
baseService.createEntities(n2nRelations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 查找mapper
|
||||||
|
BaseMapper mapper = ContextHelper.getBaseMapperByEntity(middleTableClass);
|
||||||
|
// 条件为空,不删除
|
||||||
|
mapper.delete(queryWrapper);
|
||||||
|
// 新增关联,无service只能循环插入
|
||||||
|
if(V.notEmpty(n2nRelations)){
|
||||||
|
for(R relation : n2nRelations){
|
||||||
|
mapper.insert(relation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public <RE,R> boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE,R> relatedEntitySetter) {
|
public <RE,R> boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE,R> relatedEntitySetter) {
|
||||||
|
@ -260,7 +372,11 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean deleteEntities(Collection<? extends Serializable> entityIds) {
|
public boolean deleteEntities(Collection<? extends Serializable> entityIds) {
|
||||||
|
if(V.isEmpty(entityIds)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return super.removeByIds(entityIds);
|
return super.removeByIds(entityIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,6 +392,11 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> getEntityList(Wrapper queryWrapper, Pagination pagination) {
|
public List<T> getEntityList(Wrapper queryWrapper, Pagination pagination) {
|
||||||
|
// 如果是动态join,则调用JoinsBinder
|
||||||
|
if(queryWrapper instanceof DynamicJoinQueryWrapper){
|
||||||
|
return Binder.joinQueryList((DynamicJoinQueryWrapper)queryWrapper, entityClass, pagination);
|
||||||
|
}
|
||||||
|
// 否则,调用MP默认实现
|
||||||
if(pagination != null){
|
if(pagination != null){
|
||||||
IPage<T> page = convertToIPage(queryWrapper, pagination);
|
IPage<T> page = convertToIPage(queryWrapper, pagination);
|
||||||
page = super.page(page, queryWrapper);
|
page = super.page(page, queryWrapper);
|
||||||
|
@ -297,9 +418,58 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定条件的Entity ID集合
|
||||||
|
* @param queryWrapper
|
||||||
|
* @param getterFn
|
||||||
|
* @return
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <FT> List<FT> getValuesOfField(Wrapper queryWrapper, SFunction<T, ?> getterFn){
|
||||||
|
String fieldName = convertGetterToFieldName(getterFn);
|
||||||
|
String columnName = S.toSnakeCase(fieldName);
|
||||||
|
// 优化SQL,只查询当前字段
|
||||||
|
if(queryWrapper instanceof QueryWrapper){
|
||||||
|
if(V.isEmpty(queryWrapper.getSqlSelect())){
|
||||||
|
((QueryWrapper)queryWrapper).select(columnName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(queryWrapper instanceof LambdaQueryWrapper){
|
||||||
|
if(V.isEmpty(queryWrapper.getSqlSelect())){
|
||||||
|
((LambdaQueryWrapper) queryWrapper).select(getterFn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<Map<String, Object>> mapList = getMapList(queryWrapper);
|
||||||
|
if(V.isEmpty(mapList)){
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
String columnNameUC = V.notEmpty(columnName)? columnName.toUpperCase() : null;
|
||||||
|
List<FT> fldValues = new ArrayList<>(mapList.size());
|
||||||
|
for(Map<String, Object> map : mapList){
|
||||||
|
if(V.isEmpty(map)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(map.containsKey(columnName)){
|
||||||
|
FT value = (FT) map.get(columnName);
|
||||||
|
if(!fldValues.contains(value)){
|
||||||
|
fldValues.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(columnNameUC != null && map.containsKey(columnNameUC)){
|
||||||
|
FT value = (FT) map.get(columnNameUC);
|
||||||
|
if(!fldValues.contains(value)){
|
||||||
|
fldValues.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fldValues;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<T> getEntityListLimit(Wrapper queryWrapper, int limitCount) {
|
public List<T> getEntityListLimit(Wrapper queryWrapper, int limitCount) {
|
||||||
IPage<T> page = new Page<>(1, limitCount);
|
Page<T> page = new Page<>(1, limitCount);
|
||||||
|
page.setSearchCount(false);
|
||||||
page = super.page(page, queryWrapper);
|
page = super.page(page, queryWrapper);
|
||||||
return page.getRecords();
|
return page.getRecords();
|
||||||
}
|
}
|
||||||
|
@ -343,7 +513,7 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
@Override
|
@Override
|
||||||
public List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination) {
|
public List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination) {
|
||||||
if(pagination != null){
|
if(pagination != null){
|
||||||
IPage<T> page = convertToIPage(queryWrapper, pagination);
|
IPage page = convertToIPage(queryWrapper, pagination);
|
||||||
IPage<Map<String, Object>> resultPage = super.pageMaps(page, queryWrapper);
|
IPage<Map<String, Object>> resultPage = super.pageMaps(page, queryWrapper);
|
||||||
// 如果重新执行了count进行查询,则更新pagination中的总数
|
// 如果重新执行了count进行查询,则更新pagination中的总数
|
||||||
if(page.isSearchCount()){
|
if(page.isSearchCount()){
|
||||||
|
@ -380,6 +550,10 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
String[] keyValueArray = sqlSelect.split(Cons.SEPARATOR_COMMA);
|
String[] keyValueArray = sqlSelect.split(Cons.SEPARATOR_COMMA);
|
||||||
List<KeyValue> keyValueList = new ArrayList<>(mapList.size());
|
List<KeyValue> keyValueList = new ArrayList<>(mapList.size());
|
||||||
for(Map<String, Object> map : mapList){
|
for(Map<String, Object> map : mapList){
|
||||||
|
// 如果key和value的的值都为null的时候map也为空,则不处理此项
|
||||||
|
if (V.isEmpty(map)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
String key = keyValueArray[0], value = keyValueArray[1], ext = null;
|
String key = keyValueArray[0], value = keyValueArray[1], ext = null;
|
||||||
// 兼容oracle大写
|
// 兼容oracle大写
|
||||||
if(map.containsKey(key) == false && map.containsKey(key.toUpperCase())){
|
if(map.containsKey(key) == false && map.containsKey(key.toUpperCase())){
|
||||||
|
@ -435,18 +609,15 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
if(entity == null){
|
if(entity == null){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<T> enityList = new ArrayList<>();
|
|
||||||
enityList.add(entity);
|
|
||||||
// 绑定
|
// 绑定
|
||||||
List<VO> voList = RelationsBinder.convertAndBind(enityList, voClass);
|
return Binder.convertAndBindRelations(entity, voClass);
|
||||||
return voList.get(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <VO> List<VO> getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class<VO> voClass) {
|
public <VO> List<VO> getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class<VO> voClass) {
|
||||||
List<T> entityList = getEntityList(queryWrapper, pagination);
|
List<T> entityList = getEntityList(queryWrapper, pagination);
|
||||||
// 自动转换为VO并绑定关联对象
|
// 自动转换为VO并绑定关联对象
|
||||||
List<VO> voList = RelationsBinder.convertAndBind(entityList, voClass);
|
List<VO> voList = Binder.convertAndBindRelations(entityList, voClass);
|
||||||
return voList;
|
return voList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,17 +634,24 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
// 如果是默认id排序
|
// 如果是默认id排序
|
||||||
if(pagination.isDefaultOrderBy()){
|
if(pagination.isDefaultOrderBy()){
|
||||||
// 优化排序
|
// 优化排序
|
||||||
Class entityClass = BeanUtils.getGenericityClass(this, 1);
|
String pk = getPrimaryKeyField();
|
||||||
if(entityClass != null){
|
// 主键非有序id字段,需要清空默认排序以免报错
|
||||||
String pk = ContextHelper.getPrimaryKey(entityClass);
|
if(!Cons.FieldName.id.name().equals(pk)){
|
||||||
// 主键非有序id字段,需要清空默认排序以免报错
|
// log.warn("{} 的主键非有序id,无法自动设置排序字段,请自行指定!", entityClass.getName());
|
||||||
if(!Cons.FieldName.id.name().equals(pk)){
|
pagination.clearDefaultOrder();
|
||||||
log.warn("{} 的主键非有序id,无法自动设置排序字段,请自行指定!", entityClass.getName());
|
//设置时间排序
|
||||||
pagination.clearDefaultOrder();
|
pagination.setDefaultCreateTimeOrderBy();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (Page<T>)pagination.toIPage();
|
return (Page<T>)pagination.toPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前主键字段名
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String getPrimaryKeyField(){
|
||||||
|
return ContextHelper.getPrimaryKey(entityClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -486,13 +664,25 @@ public class BaseServiceImpl<M extends BaseCrudMapper<T>, T> extends ServiceImpl
|
||||||
return BeanUtils.getProperty(entity, pk);
|
return BeanUtils.getProperty(entity, pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换SFunction为属性名
|
||||||
|
* @param getterFn
|
||||||
|
* @param <T>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private <T> String convertGetterToFieldName(SFunction<T, ?> getterFn) {
|
||||||
|
com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda lambda = LambdaUtils.resolve(getterFn);
|
||||||
|
String fieldName = PropertyNamer.methodToProperty(lambda.getImplMethodName());
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 打印警告信息
|
* 打印警告信息
|
||||||
* @param method
|
* @param method
|
||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
private void warning(String method, String message){
|
private void warning(String method, String message){
|
||||||
log.warn(this.getClass().getName() + "."+ method +" 调用错误: "+message+", 请检查!");
|
log.warn(this.getClass().getSimpleName() + ".{} 调用错误: {}, 请检查!", method, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -16,11 +16,13 @@
|
||||||
package com.diboot.core.util;
|
package com.diboot.core.util;
|
||||||
|
|
||||||
|
|
||||||
|
import com.diboot.core.binding.copy.AcceptAnnoCopier;
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
import com.diboot.core.entity.BaseEntity;
|
import com.diboot.core.entity.BaseEntity;
|
||||||
import com.diboot.core.exception.BusinessException;
|
import com.diboot.core.exception.BusinessException;
|
||||||
import com.diboot.core.vo.KeyValue;
|
import com.diboot.core.vo.KeyValue;
|
||||||
import com.diboot.core.vo.Status;
|
import com.diboot.core.vo.Status;
|
||||||
|
import org.apache.ibatis.reflection.property.PropertyNamer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.aop.support.AopUtils;
|
import org.springframework.aop.support.AopUtils;
|
||||||
|
@ -30,6 +32,7 @@ import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.invoke.SerializedLambda;
|
import java.lang.invoke.SerializedLambda;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -69,6 +72,8 @@ public class BeanUtils {
|
||||||
public static Object copyProperties(Object source, Object target){
|
public static Object copyProperties(Object source, Object target){
|
||||||
// 链式调用无法使用BeanCopier拷贝,换用BeanUtils
|
// 链式调用无法使用BeanCopier拷贝,换用BeanUtils
|
||||||
org.springframework.beans.BeanUtils.copyProperties(source, target);
|
org.springframework.beans.BeanUtils.copyProperties(source, target);
|
||||||
|
// 处理Accept注解标识的不同字段名拷贝
|
||||||
|
AcceptAnnoCopier.copyAcceptProperties(source, target);
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,7 +124,8 @@ public class BeanUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
log.warn("对象转换异常, class: {}, error: {}", clazz.getName(), e.getMessage());
|
log.error("对象转换异常, class: {}, error: {}", clazz.getName(), e);
|
||||||
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return resultList;
|
return resultList;
|
||||||
}
|
}
|
||||||
|
@ -156,8 +162,14 @@ public class BeanUtils {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Object getProperty(Object obj, String field){
|
public static Object getProperty(Object obj, String field){
|
||||||
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(obj);
|
try {
|
||||||
return wrapper.getPropertyValue(field);
|
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(obj);
|
||||||
|
return wrapper.getPropertyValue(field);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
log.warn("获取对象属性值出错,返回null", e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -488,7 +500,7 @@ public class BeanUtils {
|
||||||
try{
|
try{
|
||||||
for(E object : objectList){
|
for(E object : objectList){
|
||||||
Object fieldValue = getProperty(object, getterPropName);
|
Object fieldValue = getProperty(object, getterPropName);
|
||||||
if(!fieldValueList.contains(fieldValue)){
|
if(fieldValue != null && !fieldValueList.contains(fieldValue)){
|
||||||
fieldValueList.add(fieldValue);
|
fieldValueList.add(fieldValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,18 +588,7 @@ public class BeanUtils {
|
||||||
*/
|
*/
|
||||||
public static <T> String convertToFieldName(IGetter<T> fn) {
|
public static <T> String convertToFieldName(IGetter<T> fn) {
|
||||||
SerializedLambda lambda = getSerializedLambda(fn);
|
SerializedLambda lambda = getSerializedLambda(fn);
|
||||||
String methodName = lambda.getImplMethodName();
|
return PropertyNamer.methodToProperty(lambda.getImplMethodName());
|
||||||
String prefix = null;
|
|
||||||
if(methodName.startsWith("get")){
|
|
||||||
prefix = "get";
|
|
||||||
}
|
|
||||||
else if(methodName.startsWith("is")){
|
|
||||||
prefix = "is";
|
|
||||||
}
|
|
||||||
if(prefix == null){
|
|
||||||
log.warn("无效的getter方法: "+methodName);
|
|
||||||
}
|
|
||||||
return S.uncapFirst(S.substringAfter(methodName, prefix));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -597,11 +598,7 @@ public class BeanUtils {
|
||||||
*/
|
*/
|
||||||
public static <T,R> String convertToFieldName(ISetter<T,R> fn) {
|
public static <T,R> String convertToFieldName(ISetter<T,R> fn) {
|
||||||
SerializedLambda lambda = getSerializedLambda(fn);
|
SerializedLambda lambda = getSerializedLambda(fn);
|
||||||
String methodName = lambda.getImplMethodName();
|
return PropertyNamer.methodToProperty(lambda.getImplMethodName());
|
||||||
if(!methodName.startsWith("set")){
|
|
||||||
log.warn("无效的setter方法: "+methodName);
|
|
||||||
}
|
|
||||||
return S.uncapFirst(S.substringAfter(methodName, "set"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -627,6 +624,30 @@ public class BeanUtils {
|
||||||
return fieldList;
|
return fieldList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取类所有属性(包含父类中属性)
|
||||||
|
* @param clazz
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static List<Field> extractFields(Class<?> clazz, Class<? extends Annotation> annotation){
|
||||||
|
List<Field> fieldList = new ArrayList<>();
|
||||||
|
Set<String> fieldNameSet = new HashSet<>();
|
||||||
|
while (clazz != null) {
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
if(V.notEmpty(fields)){ //被重写属性,以子类override的为准
|
||||||
|
Arrays.stream(fields).forEach((field)->{
|
||||||
|
if(!fieldNameSet.contains(field.getName()) && field.getAnnotation(annotation) != null){
|
||||||
|
fieldList.add(field);
|
||||||
|
fieldNameSet.add(field.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
clazz = clazz.getSuperclass();
|
||||||
|
}
|
||||||
|
return fieldList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取类的指定属性(包含父类中属性)
|
* 获取类的指定属性(包含父类中属性)
|
||||||
* @param clazz
|
* @param clazz
|
||||||
|
|
|
@ -18,8 +18,10 @@ package com.diboot.core.util;
|
||||||
import com.baomidou.mybatisplus.annotation.DbType;
|
import com.baomidou.mybatisplus.annotation.DbType;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
||||||
|
import com.diboot.core.binding.parser.ParserCache;
|
||||||
import com.diboot.core.config.Cons;
|
import com.diboot.core.config.Cons;
|
||||||
import com.diboot.core.service.BaseService;
|
import com.diboot.core.service.BaseService;
|
||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
@ -59,11 +61,11 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
/**
|
/**
|
||||||
* Entity-对应的Service缓存
|
* Entity-对应的Service缓存
|
||||||
*/
|
*/
|
||||||
private static Map<String, IService> ENTITY_SERVICE_CACHE = null;
|
private static Map<String, IService> ENTITY_SERVICE_CACHE = new ConcurrentHashMap<>();
|
||||||
/**
|
/**
|
||||||
* Entity-对应的BaseService缓存
|
* Entity-对应的BaseService缓存
|
||||||
*/
|
*/
|
||||||
private static Map<String, BaseService> ENTITY_BASE_SERVICE_CACHE = null;
|
private static Map<String, BaseService> ENTITY_BASE_SERVICE_CACHE = new ConcurrentHashMap<>();
|
||||||
/**
|
/**
|
||||||
* 存储主键字段非id的Entity
|
* 存储主键字段非id的Entity
|
||||||
*/
|
*/
|
||||||
|
@ -75,9 +77,10 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
if(APPLICATION_CONTEXT == null){
|
APPLICATION_CONTEXT = applicationContext;
|
||||||
APPLICATION_CONTEXT = applicationContext;
|
ENTITY_SERVICE_CACHE.clear();
|
||||||
}
|
ENTITY_BASE_SERVICE_CACHE.clear();
|
||||||
|
PK_NID_ENTITY_CACHE.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -85,7 +88,10 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
*/
|
*/
|
||||||
public static ApplicationContext getApplicationContext() {
|
public static ApplicationContext getApplicationContext() {
|
||||||
if (APPLICATION_CONTEXT == null){
|
if (APPLICATION_CONTEXT == null){
|
||||||
return ContextLoader.getCurrentWebApplicationContext();
|
APPLICATION_CONTEXT = ContextLoader.getCurrentWebApplicationContext();
|
||||||
|
}
|
||||||
|
if(APPLICATION_CONTEXT == null){
|
||||||
|
log.warn("无法获取ApplicationContext,请在Spring初始化之后调用!");
|
||||||
}
|
}
|
||||||
return APPLICATION_CONTEXT;
|
return APPLICATION_CONTEXT;
|
||||||
}
|
}
|
||||||
|
@ -105,17 +111,22 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T> T getBean(Class<T> clazz){
|
public static <T> T getBean(Class<T> clazz){
|
||||||
return getApplicationContext().getBean(clazz);
|
try{
|
||||||
|
return getApplicationContext().getBean(clazz);
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
log.debug("无法找到 bean: {}", clazz.getSimpleName());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 获取指定类型的全部实例
|
* 获取指定类型的全部实现类
|
||||||
* @param type
|
* @param type
|
||||||
* @param <T>
|
* @param <T>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T> List<T> getBeans(Class<T> type){
|
public static <T> List<T> getBeans(Class<T> type){
|
||||||
// 获取所有的定时任务实现类
|
|
||||||
Map<String, T> map = getApplicationContext().getBeansOfType(type);
|
Map<String, T> map = getApplicationContext().getBeansOfType(type);
|
||||||
if(V.isEmpty(map)){
|
if(V.isEmpty(map)){
|
||||||
return null;
|
return null;
|
||||||
|
@ -141,7 +152,7 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据Entity获取对应的Service (已废弃,请调用getIServiceByEntity)
|
* 根据Entity获取对应的Service (已废弃,请调用getBaseServiceByEntity)
|
||||||
* @param entity
|
* @param entity
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@ -157,8 +168,7 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static IService getIServiceByEntity(Class entity){
|
public static IService getIServiceByEntity(Class entity){
|
||||||
if(ENTITY_SERVICE_CACHE == null){
|
if(ENTITY_SERVICE_CACHE.isEmpty()){
|
||||||
ENTITY_SERVICE_CACHE = new ConcurrentHashMap<>();
|
|
||||||
Map<String, IService> serviceMap = getApplicationContext().getBeansOfType(IService.class);
|
Map<String, IService> serviceMap = getApplicationContext().getBeansOfType(IService.class);
|
||||||
if(V.notEmpty(serviceMap)){
|
if(V.notEmpty(serviceMap)){
|
||||||
for(Map.Entry<String, IService> entry : serviceMap.entrySet()){
|
for(Map.Entry<String, IService> entry : serviceMap.entrySet()){
|
||||||
|
@ -182,8 +192,7 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static BaseService getBaseServiceByEntity(Class entity){
|
public static BaseService getBaseServiceByEntity(Class entity){
|
||||||
if(ENTITY_BASE_SERVICE_CACHE == null){
|
if(ENTITY_BASE_SERVICE_CACHE.isEmpty()){
|
||||||
ENTITY_BASE_SERVICE_CACHE = new ConcurrentHashMap<>();
|
|
||||||
Map<String, BaseService> serviceMap = getApplicationContext().getBeansOfType(BaseService.class);
|
Map<String, BaseService> serviceMap = getApplicationContext().getBeansOfType(BaseService.class);
|
||||||
if(V.notEmpty(serviceMap)){
|
if(V.notEmpty(serviceMap)){
|
||||||
for(Map.Entry<String, BaseService> entry : serviceMap.entrySet()){
|
for(Map.Entry<String, BaseService> entry : serviceMap.entrySet()){
|
||||||
|
@ -196,11 +205,20 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
}
|
}
|
||||||
BaseService baseService = ENTITY_BASE_SERVICE_CACHE.get(entity.getName());
|
BaseService baseService = ENTITY_BASE_SERVICE_CACHE.get(entity.getName());
|
||||||
if(baseService == null){
|
if(baseService == null){
|
||||||
log.error("未能识别到Entity: "+entity.getName()+" 的Service实现!");
|
log.info("未能识别到Entity: "+entity.getName()+" 的Service实现!");
|
||||||
}
|
}
|
||||||
return baseService;
|
return baseService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据Entity获取对应的BaseMapper实现
|
||||||
|
* @param entityClass
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static BaseMapper getBaseMapperByEntity(Class entityClass){
|
||||||
|
return ParserCache.getMapperInstance(entityClass);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取Entity主键
|
* 获取Entity主键
|
||||||
* @return
|
* @return
|
||||||
|
@ -235,6 +253,9 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
public static String getJdbcUrl() {
|
public static String getJdbcUrl() {
|
||||||
Environment environment = getApplicationContext().getEnvironment();
|
Environment environment = getApplicationContext().getEnvironment();
|
||||||
String jdbcUrl = environment.getProperty("spring.datasource.url");
|
String jdbcUrl = environment.getProperty("spring.datasource.url");
|
||||||
|
if(jdbcUrl == null){
|
||||||
|
jdbcUrl = environment.getProperty("spring.datasource.druid.url");
|
||||||
|
}
|
||||||
if(jdbcUrl == null){
|
if(jdbcUrl == null){
|
||||||
String master = environment.getProperty("spring.datasource.dynamic.primary");
|
String master = environment.getProperty("spring.datasource.dynamic.primary");
|
||||||
jdbcUrl = environment.getProperty("spring.datasource.dynamic.datasource."+master+".url");
|
jdbcUrl = environment.getProperty("spring.datasource.dynamic.datasource."+master+".url");
|
||||||
|
@ -265,7 +286,7 @@ public class ContextHelper implements ApplicationContextAware {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(DATABASE_TYPE == null){
|
if(DATABASE_TYPE == null){
|
||||||
log.warn("无法识别数据库类型,请检查配置!");
|
log.warn("无法识别数据库类型,请检查数据源配置:spring.datasource.url等");
|
||||||
}
|
}
|
||||||
return DATABASE_TYPE;
|
return DATABASE_TYPE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,6 +141,18 @@ public class D extends DateUtils{
|
||||||
return sdf.format(date);
|
return sdf.format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取日期的下一天
|
||||||
|
* @param date 基准日期
|
||||||
|
* @return yyyy-MM-dd
|
||||||
|
*/
|
||||||
|
public static Date nextDay(Date date){
|
||||||
|
if(date == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return addDays(date, 1);
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 获取格式化的日期时间
|
* 获取格式化的日期时间
|
||||||
* @param date
|
* @param date
|
||||||
|
|
|
@ -27,11 +27,6 @@ import org.springframework.core.env.Environment;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class PropertiesUtils {
|
public class PropertiesUtils {
|
||||||
|
|
||||||
/**
|
|
||||||
* Spring配置环境变量
|
|
||||||
*/
|
|
||||||
private static Environment environment;
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 读取配置项的值
|
* 读取配置项的值
|
||||||
* @param key
|
* @param key
|
||||||
|
@ -39,11 +34,9 @@ public class PropertiesUtils {
|
||||||
*/
|
*/
|
||||||
public static String get(String key){
|
public static String get(String key){
|
||||||
// 获取配置值
|
// 获取配置值
|
||||||
|
Environment environment = ContextHelper.getApplicationContext().getEnvironment();
|
||||||
if(environment == null){
|
if(environment == null){
|
||||||
environment = ContextHelper.getApplicationContext().getEnvironment();
|
log.warn("无法获取上下文Environment,请在Spring初始化之后调用!");
|
||||||
}
|
|
||||||
if(environment == null){
|
|
||||||
log.warn("无法获取上下文Environment !");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String value = environment.getProperty(key);
|
String value = environment.getProperty(key);
|
||||||
|
|
|
@ -177,19 +177,20 @@ public class S extends StringUtils{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 包含_
|
// 包含_
|
||||||
String result = null;
|
StringBuilder sb = null;
|
||||||
String[] words = snakeCaseStr.split(Cons.SEPARATOR_UNDERSCORE);
|
String[] words = snakeCaseStr.split(Cons.SEPARATOR_UNDERSCORE);
|
||||||
for(String word : words){
|
for(String word : words){
|
||||||
if(V.notEmpty(word)){
|
if(V.isEmpty(word)){
|
||||||
if(result == null){
|
continue;
|
||||||
result = word.toLowerCase();
|
}
|
||||||
}
|
if(sb == null){
|
||||||
else{
|
sb = new StringBuilder(word.toLowerCase());
|
||||||
result += word.substring(0, 1).toUpperCase() + word.substring(1).toLowerCase();
|
}
|
||||||
}
|
else{
|
||||||
|
sb.append(word.substring(0, 1).toUpperCase()).append(word.substring(1).toLowerCase());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return sb != null? sb.toString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class SqlExecutor {
|
||||||
try(SqlSession session = sqlSessionFactory.openSession(); Connection conn = session.getConnection(); PreparedStatement stmt = conn.prepareStatement(sqlStatement)){
|
try(SqlSession session = sqlSessionFactory.openSession(); Connection conn = session.getConnection(); PreparedStatement stmt = conn.prepareStatement(sqlStatement)){
|
||||||
ResultSet rs = stmt.executeQuery();
|
ResultSet rs = stmt.executeQuery();
|
||||||
rs.close();
|
rs.close();
|
||||||
log.trace("执行验证SQL:{} 成功", sqlStatement);
|
log.debug("==> {}", sqlStatement);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch(Exception e){
|
catch(Exception e){
|
||||||
|
@ -115,7 +115,6 @@ public class SqlExecutor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 执行1-1关联查询和合并结果并将结果Map的key类型转成String
|
* 执行1-1关联查询和合并结果并将结果Map的key类型转成String
|
||||||
*
|
*
|
||||||
|
@ -130,6 +129,18 @@ public class SqlExecutor {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("执行查询异常", e);
|
log.warn("执行查询异常", e);
|
||||||
}
|
}
|
||||||
|
return convertToOneToOneResult(resultSetMapList, keyName, valueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并为1-1的map结果
|
||||||
|
* @param resultSetMapList
|
||||||
|
* @param keyName
|
||||||
|
* @param valueName
|
||||||
|
* @param <E>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <E> Map<String, Object> convertToOneToOneResult(List<Map<String, E>> resultSetMapList, String keyName, String valueName) {
|
||||||
// 合并list为map
|
// 合并list为map
|
||||||
Map<String, Object> resultMap = new HashMap<>();
|
Map<String, Object> resultMap = new HashMap<>();
|
||||||
if(V.notEmpty(resultSetMapList)){
|
if(V.notEmpty(resultSetMapList)){
|
||||||
|
@ -162,6 +173,18 @@ public class SqlExecutor {
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("执行查询异常", e);
|
log.warn("执行查询异常", e);
|
||||||
}
|
}
|
||||||
|
return convertToOneToManyResult(resultSetMapList, keyName, valueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并为1-n的map结果
|
||||||
|
* @param resultSetMapList
|
||||||
|
* @param keyName
|
||||||
|
* @param valueName
|
||||||
|
* @param <E>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static <E> Map<String, List> convertToOneToManyResult(List<Map<String, E>> resultSetMapList, String keyName, String valueName){
|
||||||
// 合并list为map
|
// 合并list为map
|
||||||
Map<String, List> resultMap = new HashMap<>();
|
Map<String, List> resultMap = new HashMap<>();
|
||||||
if(V.notEmpty(resultSetMapList)){
|
if(V.notEmpty(resultSetMapList)){
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class V {
|
||||||
/**
|
/**
|
||||||
* hibernate注解验证
|
* hibernate注解验证
|
||||||
*/
|
*/
|
||||||
private static Validator VALIDATOR = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator();
|
private static Validator VALIDATOR = null;
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* 对象是否为空
|
* 对象是否为空
|
||||||
|
@ -319,7 +319,7 @@ public class V {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
//TODO 无法识别的格式
|
// 无法识别的格式
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 返回校验不通过的结果
|
// 返回校验不通过的结果
|
||||||
|
@ -376,6 +376,23 @@ public class V {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if(source instanceof Map){
|
||||||
|
Map sourceMap = (Map)source, targetMap = (Map)target;
|
||||||
|
if(V.isEmpty(sourceMap) && V.isEmpty(targetMap)){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(sourceMap.size() != targetMap.size()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for(Object key : sourceMap.keySet()){
|
||||||
|
Object value = sourceMap.get(key);
|
||||||
|
Object targetValue = targetMap.get(key);
|
||||||
|
if(notEquals(value, targetValue)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
else{
|
else{
|
||||||
log.warn("暂未实现类型 "+ source.getClass().getSimpleName() + "-"+ target.getClass().getSimpleName() + " 的比对!");
|
log.warn("暂未实现类型 "+ source.getClass().getSimpleName() + "-"+ target.getClass().getSimpleName() + " 的比对!");
|
||||||
return false;
|
return false;
|
||||||
|
@ -433,6 +450,9 @@ public class V {
|
||||||
* @param obj
|
* @param obj
|
||||||
*/
|
*/
|
||||||
public static <T> String validateBean(T obj) {
|
public static <T> String validateBean(T obj) {
|
||||||
|
if(VALIDATOR == null){
|
||||||
|
VALIDATOR = Validation.byProvider(HibernateValidator.class).configure().failFast(false).buildValidatorFactory().getValidator();
|
||||||
|
}
|
||||||
// 校验
|
// 校验
|
||||||
Set<ConstraintViolation<T>> errors = VALIDATOR.validate(obj);
|
Set<ConstraintViolation<T>> errors = VALIDATOR.validate(obj);
|
||||||
if(errors == null || errors.size() == 0){
|
if(errors == null || errors.size() == 0){
|
||||||
|
@ -445,4 +465,23 @@ public class V {
|
||||||
return S.join(allErrors);
|
return S.join(allErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查当前sql中是否包含某列的条件
|
||||||
|
* @param normalSql
|
||||||
|
* @param column
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean checkHasColumn(String normalSql, String column){
|
||||||
|
normalSql = S.removeDuplicateBlank(normalSql);
|
||||||
|
int index = S.indexOfIgnoreCase(normalSql, column);
|
||||||
|
while(index >= 0){
|
||||||
|
normalSql = S.substring(normalSql, index);
|
||||||
|
}
|
||||||
|
if(S.containsIgnoreCase(normalSql, column)){
|
||||||
|
log.warn("注意:附加数据访问条件失效,因查询条件已包含列: " + column);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -16,7 +16,6 @@
|
||||||
package com.diboot.core.vo;
|
package com.diboot.core.vo;
|
||||||
|
|
||||||
import com.alibaba.fastjson.annotation.JSONField;
|
import com.alibaba.fastjson.annotation.JSONField;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.diboot.core.config.BaseConfig;
|
import com.diboot.core.config.BaseConfig;
|
||||||
|
@ -61,6 +60,7 @@ public class Pagination implements Serializable {
|
||||||
* 默认排序
|
* 默认排序
|
||||||
*/
|
*/
|
||||||
private static final String DEFAULT_ORDER_BY = Cons.FieldName.id.name()+":"+Cons.ORDER_DESC;
|
private static final String DEFAULT_ORDER_BY = Cons.FieldName.id.name()+":"+Cons.ORDER_DESC;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 排序
|
* 排序
|
||||||
*/
|
*/
|
||||||
|
@ -119,7 +119,7 @@ public class Pagination implements Serializable {
|
||||||
* @param <T>
|
* @param <T>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public <T> IPage<T> toIPage(){
|
public <T> Page<T> toPage(){
|
||||||
List<OrderItem> orderItemList = null;
|
List<OrderItem> orderItemList = null;
|
||||||
// 解析排序
|
// 解析排序
|
||||||
if(V.notEmpty(this.orderBy)){
|
if(V.notEmpty(this.orderBy)){
|
||||||
|
@ -142,14 +142,23 @@ public class Pagination implements Serializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IPage<T> page = new Page<T>()
|
Page<T> page = new Page<T>()
|
||||||
.setCurrent(getPageIndex())
|
.setCurrent(getPageIndex())
|
||||||
.setSize(getPageSize())
|
.setSize(getPageSize())
|
||||||
// 如果前端传递过来了缓存的总数,则本次不再count统计
|
// 如果前端传递过来了缓存的总数,则本次不再count统计
|
||||||
.setTotal(getTotalCount() > 0? -1 : getTotalCount());
|
.setTotal(getTotalCount() > 0? -1 : getTotalCount());
|
||||||
if(orderItemList != null){
|
if(orderItemList != null){
|
||||||
((Page<T>) page).addOrder(orderItemList);
|
page.addOrder(orderItemList);
|
||||||
}
|
}
|
||||||
return page;
|
return page;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当id不是主键的时候,默认使用创建时间排序
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String setDefaultCreateTimeOrderBy() {
|
||||||
|
return this.orderBy = Cons.FieldName.createTime.name()+":"+Cons.ORDER_DESC;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
package diboot.core.test.binder;
|
package diboot.core.test.binder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.BeanUtils;
|
||||||
import com.diboot.core.util.JSON;
|
import com.diboot.core.util.JSON;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
|
@ -54,9 +54,9 @@ public class TestEntityBinder {
|
||||||
// 加载测试数据
|
// 加载测试数据
|
||||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||||
List<User> userList = userService.list(queryWrapper);
|
List<User> userList = userService.getEntityList(queryWrapper);
|
||||||
// 自动绑定
|
// 自动绑定
|
||||||
List<EntityBinderVO> voList = RelationsBinder.convertAndBind(userList, EntityBinderVO.class);
|
List<EntityBinderVO> voList = Binder.convertAndBindRelations(userList, EntityBinderVO.class);
|
||||||
// 验证绑定结果
|
// 验证绑定结果
|
||||||
Assert.assertTrue(V.notEmpty(voList));
|
Assert.assertTrue(V.notEmpty(voList));
|
||||||
for(EntityBinderVO vo : voList){
|
for(EntityBinderVO vo : voList){
|
||||||
|
@ -70,7 +70,7 @@ public class TestEntityBinder {
|
||||||
}
|
}
|
||||||
// 单个entity接口测试
|
// 单个entity接口测试
|
||||||
EntityBinderVO singleVO = BeanUtils.convert(userList.get(0), EntityBinderVO.class);
|
EntityBinderVO singleVO = BeanUtils.convert(userList.get(0), EntityBinderVO.class);
|
||||||
RelationsBinder.bind(singleVO);
|
Binder.bindRelations(singleVO);
|
||||||
// 验证直接关联和通过中间表间接关联的绑定
|
// 验证直接关联和通过中间表间接关联的绑定
|
||||||
Assert.assertEquals(singleVO.getDepartmentId(), singleVO.getDepartment().getId());
|
Assert.assertEquals(singleVO.getDepartmentId(), singleVO.getDepartment().getId());
|
||||||
Assert.assertNotNull(singleVO.getDepartment().getOrgId());
|
Assert.assertNotNull(singleVO.getDepartment().getOrgId());
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package diboot.core.test.binder;
|
package diboot.core.test.binder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.entity.Dictionary;
|
||||||
import com.diboot.core.service.DictionaryService;
|
import com.diboot.core.service.DictionaryService;
|
||||||
import com.diboot.core.util.JSON;
|
import com.diboot.core.util.JSON;
|
||||||
|
@ -71,7 +71,7 @@ public class TestEntityListBinder {
|
||||||
queryWrapper.eq(Department::getId, 10001L);
|
queryWrapper.eq(Department::getId, 10001L);
|
||||||
List<Department> entityList = departmentService.getEntityList(queryWrapper);
|
List<Department> entityList = departmentService.getEntityList(queryWrapper);
|
||||||
// 自动绑定
|
// 自动绑定
|
||||||
List<EntityListSimpleBinderVO> voList = RelationsBinder.convertAndBind(entityList, EntityListSimpleBinderVO.class);
|
List<EntityListSimpleBinderVO> voList = Binder.convertAndBindRelations(entityList, EntityListSimpleBinderVO.class);
|
||||||
// 验证绑定结果
|
// 验证绑定结果
|
||||||
Assert.assertTrue(V.notEmpty(voList));
|
Assert.assertTrue(V.notEmpty(voList));
|
||||||
for(EntityListSimpleBinderVO vo : voList){
|
for(EntityListSimpleBinderVO vo : voList){
|
||||||
|
@ -95,9 +95,9 @@ public class TestEntityListBinder {
|
||||||
// 加载测试数据
|
// 加载测试数据
|
||||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||||
List<User> userList = userService.list(queryWrapper);
|
List<User> userList = userService.getEntityList(queryWrapper);
|
||||||
// 自动绑定
|
// 自动绑定
|
||||||
List<EntityListComplexBinderVO> voList = RelationsBinder.convertAndBind(userList, EntityListComplexBinderVO.class);
|
List<EntityListComplexBinderVO> voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class);
|
||||||
// 验证绑定结果
|
// 验证绑定结果
|
||||||
Assert.assertTrue(V.notEmpty(voList));
|
Assert.assertTrue(V.notEmpty(voList));
|
||||||
for(EntityListComplexBinderVO vo : voList){
|
for(EntityListComplexBinderVO vo : voList){
|
||||||
|
@ -114,7 +114,7 @@ public class TestEntityListBinder {
|
||||||
queryWrapper.eq(Dictionary::getType, "GENDER");
|
queryWrapper.eq(Dictionary::getType, "GENDER");
|
||||||
|
|
||||||
Dictionary dictionary = dictionaryService.getSingleEntity(queryWrapper);
|
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);
|
Assert.assertTrue(vo.getChildren().size() > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package diboot.core.test.binder;
|
package diboot.core.test.binder;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
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.JSON;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
import diboot.core.test.StartupApplication;
|
import diboot.core.test.StartupApplication;
|
||||||
|
@ -57,16 +57,16 @@ public class TestFieldBinder {
|
||||||
// 加载测试数据
|
// 加载测试数据
|
||||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||||
List<User> userList = userService.list(queryWrapper);
|
List<User> userList = userService.getEntityList(queryWrapper);
|
||||||
// 自动绑定
|
// 自动绑定
|
||||||
List<FieldBinderVO> voList = RelationsBinder.convertAndBind(userList, FieldBinderVO.class);
|
List<FieldBinderVO> voList = Binder.convertAndBindRelations(userList, FieldBinderVO.class);
|
||||||
// 验证绑定结果
|
// 验证绑定结果
|
||||||
Assert.assertTrue(V.notEmpty(voList));
|
Assert.assertTrue(V.notEmpty(voList));
|
||||||
for(FieldBinderVO vo : voList){
|
for(FieldBinderVO vo : voList){
|
||||||
// 验证直接关联和通过中间表间接关联的绑定
|
// 验证直接关联和通过中间表间接关联的绑定
|
||||||
Assert.assertNotNull(vo.getDeptName());
|
Assert.assertNotNull(vo.getDeptName());
|
||||||
Assert.assertNotNull(vo.getOrgName());
|
Assert.assertNotNull(vo.getOrgName());
|
||||||
Assert.assertNotNull(vo.getOrgTelphone());
|
Assert.assertNotNull(vo.getOrgParentId());
|
||||||
// 验证枚举值已绑定
|
// 验证枚举值已绑定
|
||||||
Assert.assertNotNull(vo.getGenderLabel());
|
Assert.assertNotNull(vo.getGenderLabel());
|
||||||
|
|
||||||
|
@ -79,9 +79,9 @@ public class TestFieldBinder {
|
||||||
// 加载测试数据
|
// 加载测试数据
|
||||||
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
queryWrapper.in(User::getId, 1001L, 1002L);
|
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||||
List<User> userList = userService.list(queryWrapper);
|
List<User> userList = userService.getEntityList(queryWrapper);
|
||||||
// 自动绑定
|
// 自动绑定
|
||||||
List<UserVO> voList = RelationsBinder.convertAndBind(userList, UserVO.class);
|
List<UserVO> voList = Binder.convertAndBindRelations(userList, UserVO.class);
|
||||||
if(V.notEmpty(voList)){
|
if(V.notEmpty(voList)){
|
||||||
for(UserVO vo : voList){
|
for(UserVO vo : voList){
|
||||||
Assert.assertNotNull(vo.getDeptName());
|
Assert.assertNotNull(vo.getDeptName());
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.LambdaQueryWrapper;
|
||||||
|
import com.diboot.core.binding.Binder;
|
||||||
|
import com.diboot.core.util.JSON;
|
||||||
|
import com.diboot.core.util.V;
|
||||||
|
import diboot.core.test.StartupApplication;
|
||||||
|
import diboot.core.test.binder.entity.Department;
|
||||||
|
import diboot.core.test.binder.entity.User;
|
||||||
|
import diboot.core.test.binder.service.DepartmentService;
|
||||||
|
import diboot.core.test.binder.service.UserService;
|
||||||
|
import diboot.core.test.binder.vo.EntityListComplexBinderVO;
|
||||||
|
import diboot.core.test.binder.vo.EntityListSimpleBinderVO;
|
||||||
|
import diboot.core.test.config.SpringMvcConfig;
|
||||||
|
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.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试字段绑定
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2019/06/22
|
||||||
|
*/
|
||||||
|
@RunWith(SpringRunner.class)
|
||||||
|
@ContextConfiguration(classes = {SpringMvcConfig.class})
|
||||||
|
@SpringBootTest(classes = {StartupApplication.class})
|
||||||
|
public class TestFieldListBinder {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
UserService userService;
|
||||||
|
@Autowired
|
||||||
|
DepartmentService departmentService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证直接关联的绑定
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSimpleBinder(){
|
||||||
|
// 加载测试数据
|
||||||
|
LambdaQueryWrapper<Department> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(Department::getId, 10001L);
|
||||||
|
List<Department> entityList = departmentService.getEntityList(queryWrapper);
|
||||||
|
// 自动绑定
|
||||||
|
List<EntityListSimpleBinderVO> voList = Binder.convertAndBindRelations(entityList, EntityListSimpleBinderVO.class);
|
||||||
|
// 验证绑定结果
|
||||||
|
Assert.assertTrue(V.notEmpty(voList));
|
||||||
|
for(EntityListSimpleBinderVO vo : voList){
|
||||||
|
// 验证直接关联的绑定
|
||||||
|
Assert.assertTrue(V.notEmpty(vo.getChildrenIds()));
|
||||||
|
System.out.println(JSON.stringify(vo.getChildrenIds()));
|
||||||
|
// 验证直接关联的绑定
|
||||||
|
Assert.assertTrue(V.notEmpty(vo.getChildrenNames()));
|
||||||
|
System.out.println(JSON.stringify(vo.getChildrenNames()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证通过中间表间接关联的绑定
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testComplexBinder(){
|
||||||
|
// 加载测试数据
|
||||||
|
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.in(User::getId, 1001L, 1002L);
|
||||||
|
List<User> userList = userService.getEntityList(queryWrapper);
|
||||||
|
// 自动绑定
|
||||||
|
List<EntityListComplexBinderVO> voList = Binder.convertAndBindRelations(userList, EntityListComplexBinderVO.class);
|
||||||
|
// 验证绑定结果
|
||||||
|
Assert.assertTrue(V.notEmpty(voList));
|
||||||
|
for(EntityListComplexBinderVO vo : voList){
|
||||||
|
// 验证通过中间表间接关联的绑定
|
||||||
|
Assert.assertTrue(V.notEmpty(vo.getRoleCodes()));
|
||||||
|
Assert.assertTrue(V.notEmpty(vo.getRoleCreateDates()));
|
||||||
|
|
||||||
|
System.out.println(JSON.stringify(vo.getRoleCodes()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.dto.UserDTO;
|
||||||
|
import diboot.core.test.binder.entity.Department;
|
||||||
|
import diboot.core.test.binder.entity.User;
|
||||||
|
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 testDateCompaire(){
|
||||||
|
Department example = departmentService.getSingleEntity(null);
|
||||||
|
DepartmentDTO departmentDTO = new DepartmentDTO();
|
||||||
|
departmentDTO.setCreateTime(example.getCreateTime());
|
||||||
|
QueryWrapper<Department> queryWrapper = QueryBuilder.toQueryWrapper(departmentDTO);
|
||||||
|
List<Department> list = departmentService.getEntityList(queryWrapper);
|
||||||
|
Assert.assertTrue(list.size() >= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleTableQuery(){
|
||||||
|
Department entity = new Department();
|
||||||
|
entity.setParentId(10001L);
|
||||||
|
entity.setName("测试组");
|
||||||
|
entity.setOrgId(100001L);
|
||||||
|
|
||||||
|
QueryWrapper<Department> queryWrapper = QueryBuilder.toQueryWrapper(entity);
|
||||||
|
System.out.println(queryWrapper.getExpression());
|
||||||
|
List<Department> 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<String> 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<DepartmentDTO> queryWrapper = QueryBuilder.toQueryWrapper(dto);
|
||||||
|
List<Department> departments = departmentService.getEntityList(queryWrapper);
|
||||||
|
Assert.assertTrue(departments.size() == 3);
|
||||||
|
|
||||||
|
// builder直接查询,不分页 3条结果
|
||||||
|
List<Department> 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<String> 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<Department> 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<DepartmentVO> 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试有中间表的动态sql join
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDynamicSqlQueryWithMiddleTable() {
|
||||||
|
// 初始化DTO,测试不涉及关联的情况
|
||||||
|
UserDTO dto = new UserDTO();
|
||||||
|
dto.setDeptName("研发组");
|
||||||
|
dto.setDeptId(10002L);
|
||||||
|
|
||||||
|
// builder直接查询,不分页 3条结果
|
||||||
|
List<User> builderResultList = QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(User.class);
|
||||||
|
Assert.assertTrue(builderResultList.size() == 2);
|
||||||
|
|
||||||
|
dto.setOrgName("苏州帝博");
|
||||||
|
builderResultList = QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(User.class);
|
||||||
|
Assert.assertTrue(builderResultList.size() == 2);
|
||||||
|
|
||||||
|
dto.setRoleCode("ADMIN");
|
||||||
|
builderResultList = QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(User.class);
|
||||||
|
Assert.assertTrue(builderResultList.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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.data.CheckpointType;
|
||||||
|
import com.diboot.core.binding.data.DataAccessCheckpoint;
|
||||||
|
import com.diboot.core.binding.query.BindQuery;
|
||||||
|
import com.diboot.core.binding.query.Comparison;
|
||||||
|
import com.diboot.core.util.D;
|
||||||
|
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.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Department DTO
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2018/12/27
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class DepartmentDTO extends Department {
|
||||||
|
private static final long serialVersionUID = 8670003133709715087L;
|
||||||
|
|
||||||
|
//private Long parentId;
|
||||||
|
|
||||||
|
//private Long orgId;
|
||||||
|
|
||||||
|
@BindQuery(comparison = Comparison.CONTAINS)
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
// 绑定join查询
|
||||||
|
@BindQuery(comparison = Comparison.STARTSWITH, entity = Organization.class, field = "name", condition = "this.org_id=id")
|
||||||
|
private String orgName;
|
||||||
|
|
||||||
|
// 绑定join查询
|
||||||
|
@BindQuery(entity = Department.class, field = "name", condition = "this.parent_id=id")
|
||||||
|
private String parentName;
|
||||||
|
|
||||||
|
// 数据权限检查点
|
||||||
|
@DataAccessCheckpoint(type = CheckpointType.ORG)
|
||||||
|
private Long orgId;
|
||||||
|
|
||||||
|
// 查询单个日期
|
||||||
|
@BindQuery(comparison = Comparison.GE, field = "createTime")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@BindQuery(comparison = Comparison.LT, field = "createTime")
|
||||||
|
private Date createTimeEnd;
|
||||||
|
|
||||||
|
private Date getCreateTimeEnd(){
|
||||||
|
return D.nextDay(createTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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 diboot.core.test.binder.entity.Role;
|
||||||
|
import diboot.core.test.binder.entity.User;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User DTO
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2018/12/27
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class UserDTO extends User {
|
||||||
|
|
||||||
|
// 字段关联
|
||||||
|
@BindQuery(entity= Department.class, field = "name", condition="this.department_id=id") // AND parent_id >= 0
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
|
// 字段关联
|
||||||
|
@BindQuery(entity= Department.class, field = "id", condition="this.department_id=id") // AND parent_id >= 0
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
// 通过中间表关联Entity
|
||||||
|
@BindQuery(comparison = Comparison.CONTAINS, entity = Organization.class, field = "name",
|
||||||
|
condition = "this.department_id=department.id AND department.org_id=id AND parent_id=0")
|
||||||
|
private String orgName;
|
||||||
|
// LEFT JOIN department r2m ON self.department_id = r2m.id
|
||||||
|
// LEFT JOIN organization r1 ON r2m.org_id=r2.id
|
||||||
|
|
||||||
|
@BindQuery(entity = Role.class, field = "code", condition = "this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
|
private String roleCode;
|
||||||
|
// LEFT JOIN user_role r3m ON self.id = r3m.user_id
|
||||||
|
// LEFT JOIN role r3 ON r3m.role_id = r3.id
|
||||||
|
|
||||||
|
}
|
|
@ -16,13 +16,15 @@
|
||||||
package diboot.core.test.binder.entity;
|
package diboot.core.test.binder.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
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 com.diboot.core.entity.BaseEntity;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时任务
|
* Department
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2018/12/27
|
* @date 2018/12/27
|
||||||
|
@ -39,6 +41,7 @@ public class Department extends BaseEntity {
|
||||||
@TableField
|
@TableField
|
||||||
private Long orgId;
|
private Long orgId;
|
||||||
|
|
||||||
|
@BindQuery(comparison = Comparison.CONTAINS)
|
||||||
@TableField
|
@TableField
|
||||||
private String name;
|
private String name;
|
||||||
}
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
package diboot.core.test.binder.entity;
|
package diboot.core.test.binder.entity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.diboot.core.binding.copy.Accept;
|
||||||
import com.diboot.core.entity.BaseEntity;
|
import com.diboot.core.entity.BaseEntity;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -38,6 +39,7 @@ public class User extends BaseEntity {
|
||||||
@TableField
|
@TableField
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
@Accept(name = "itemName")
|
||||||
@TableField
|
@TableField
|
||||||
private String gender;
|
private String gender;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.diboot.core.entity.BaseEntity;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2019/1/30
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class UserRole extends BaseEntity {
|
||||||
|
private static final long serialVersionUID = 3030761344045195972L;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@TableField
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
@TableField
|
||||||
|
private Long roleId;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private boolean deleted;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.mapper;
|
||||||
|
|
||||||
|
import com.diboot.core.mapper.BaseCrudMapper;
|
||||||
|
import diboot.core.test.binder.entity.UserRole;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户角色Mapper
|
||||||
|
* @author mazc@dibo.ltd
|
||||||
|
* @version 2018/12/22
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface UserRoleMapper extends BaseCrudMapper<UserRole> {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -15,9 +15,14 @@
|
||||||
*/
|
*/
|
||||||
package diboot.core.test.binder.service;
|
package diboot.core.test.binder.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.diboot.core.service.BaseService;
|
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 diboot.core.test.binder.entity.Department;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部门相关Service
|
* 部门相关Service
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
|
@ -26,4 +31,5 @@ import diboot.core.test.binder.entity.Department;
|
||||||
*/
|
*/
|
||||||
public interface DepartmentService extends BaseService<Department> {
|
public interface DepartmentService extends BaseService<Department> {
|
||||||
|
|
||||||
|
List<Department> getDepartmentSqlList(QueryWrapper<DepartmentDTO> queryWrapper, Pagination pagination);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package diboot.core.test.binder.service;
|
package diboot.core.test.binder.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.diboot.core.service.BaseService;
|
||||||
import diboot.core.test.binder.entity.User;
|
import diboot.core.test.binder.entity.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,6 +25,6 @@ import diboot.core.test.binder.entity.User;
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/1/5
|
* @date 2019/1/5
|
||||||
*/
|
*/
|
||||||
public interface UserService extends IService<User> {
|
public interface UserService extends BaseService<User> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,39 @@
|
||||||
*/
|
*/
|
||||||
package diboot.core.test.binder.service.impl;
|
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.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.entity.Department;
|
||||||
import diboot.core.test.binder.mapper.DepartmentMapper;
|
import diboot.core.test.binder.mapper.DepartmentMapper;
|
||||||
import diboot.core.test.binder.service.DepartmentService;
|
import diboot.core.test.binder.service.DepartmentService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 部门相关Service实现
|
* 部门相关Service实现
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2019/1/30
|
* @date 2019/1/30
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class DepartmentServiceImpl extends BaseServiceImpl<DepartmentMapper, Department> implements DepartmentService {
|
public class DepartmentServiceImpl extends BaseServiceImpl<DepartmentMapper, Department> implements DepartmentService {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Department> getDepartmentSqlList(QueryWrapper<DepartmentDTO> queryWrapper, Pagination pagination) {
|
||||||
|
// 如果是单表,return super.
|
||||||
|
boolean isSingleTableQuery = false;
|
||||||
|
if(isSingleTableQuery){
|
||||||
|
return super.getEntityList(queryWrapper, pagination);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package diboot.core.test.binder.service.impl;
|
package diboot.core.test.binder.service.impl;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.diboot.core.service.impl.BaseServiceImpl;
|
||||||
import diboot.core.test.binder.entity.User;
|
import diboot.core.test.binder.entity.User;
|
||||||
import diboot.core.test.binder.mapper.UserMapper;
|
import diboot.core.test.binder.mapper.UserMapper;
|
||||||
import diboot.core.test.binder.service.UserService;
|
import diboot.core.test.binder.service.UserService;
|
||||||
|
@ -27,6 +28,6 @@ import org.springframework.stereotype.Service;
|
||||||
* @version 2018/12/23
|
* @version 2018/12/23
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
|
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,15 @@
|
||||||
package diboot.core.test.binder.vo;
|
package diboot.core.test.binder.vo;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
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.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 定时任务
|
* Department VO
|
||||||
* @author mazc@dibo.ltd
|
* @author mazc@dibo.ltd
|
||||||
* @version v2.0
|
* @version v2.0
|
||||||
* @date 2018/12/27
|
* @date 2018/12/27
|
||||||
|
@ -38,4 +41,15 @@ public class DepartmentVO {
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@TableField
|
||||||
|
private Long orgId;
|
||||||
|
|
||||||
|
// 关联Entity
|
||||||
|
@BindEntity(entity = Department.class, condition = "this.parent_id=id") // AND ...
|
||||||
|
private Department department;
|
||||||
|
|
||||||
|
// 关联Entity,赋值给VO
|
||||||
|
@BindEntity(entity = Organization.class, condition = "this.org_id=id") // AND ...
|
||||||
|
private OrganizationVO organizationVO;
|
||||||
|
|
||||||
}
|
}
|
|
@ -16,12 +16,14 @@
|
||||||
package diboot.core.test.binder.vo;
|
package diboot.core.test.binder.vo;
|
||||||
|
|
||||||
import com.diboot.core.binding.annotation.BindEntityList;
|
import com.diboot.core.binding.annotation.BindEntityList;
|
||||||
|
import com.diboot.core.binding.annotation.BindFieldList;
|
||||||
import diboot.core.test.binder.entity.Role;
|
import diboot.core.test.binder.entity.Role;
|
||||||
import diboot.core.test.binder.entity.User;
|
import diboot.core.test.binder.entity.User;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,7 +41,16 @@ public class EntityListComplexBinderVO extends User {
|
||||||
private String userType = "OrgUser";
|
private String userType = "OrgUser";
|
||||||
|
|
||||||
// 支持通过中间表的多-多Entity实体关联
|
// 支持通过中间表的多-多Entity实体关联
|
||||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id AND user_role.user_id>1")
|
||||||
private List<Role> roleList;
|
private List<Role> roleList;
|
||||||
|
|
||||||
|
// 支持通过中间表的多-多Entity的单个属性集
|
||||||
|
@BindFieldList(entity = Role.class, field = "code", condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
|
private List<String> roleCodes;
|
||||||
|
|
||||||
|
// 支持通过中间表的多-多Entity的单个属性集
|
||||||
|
@BindFieldList(entity = Role.class, field = "createTime", condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
|
private List<Date> roleCreateDates;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package diboot.core.test.binder.vo;
|
package diboot.core.test.binder.vo;
|
||||||
|
|
||||||
import com.diboot.core.binding.annotation.BindEntityList;
|
import com.diboot.core.binding.annotation.BindEntityList;
|
||||||
|
import com.diboot.core.binding.annotation.BindFieldList;
|
||||||
import diboot.core.test.binder.entity.Department;
|
import diboot.core.test.binder.entity.Department;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -38,4 +39,12 @@ public class EntityListSimpleBinderVO extends Department {
|
||||||
@BindEntityList(entity = Department.class, condition = "this.id=parent_id")
|
@BindEntityList(entity = Department.class, condition = "this.id=parent_id")
|
||||||
private List<DepartmentVO> children;
|
private List<DepartmentVO> children;
|
||||||
|
|
||||||
|
// 1-n 关联,取单个属性
|
||||||
|
@BindFieldList(entity = Department.class, field = "parentId", condition = "this.id=parent_id")
|
||||||
|
private List<Long> childrenIds;
|
||||||
|
|
||||||
|
// 1-n 关联,取单个属性
|
||||||
|
@BindFieldList(entity = Department.class, field = "name", condition = "this.id=parent_id")
|
||||||
|
private List<String> childrenNames;
|
||||||
|
|
||||||
}
|
}
|
|
@ -44,8 +44,8 @@ public class FieldBinderVO extends User{
|
||||||
// 支持级联字段关联,相同条件的entity+condition将合并为一条SQL查询
|
// 支持级联字段关联,相同条件的entity+condition将合并为一条SQL查询
|
||||||
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
||||||
private String orgName;
|
private String orgName;
|
||||||
@BindField(entity = Organization.class, field="telphone", condition="this.department_id=department.id AND department.org_id=id")
|
@BindField(entity = Organization.class, field="parentId", condition="this.department_id=department.id AND department.org_id=id")
|
||||||
private String orgTelphone;
|
private Long orgParentId;
|
||||||
|
|
||||||
// 绑定数据字典枚举
|
// 绑定数据字典枚举
|
||||||
@BindDict(type="GENDER", field = "gender")
|
@BindDict(type="GENDER", field = "gender")
|
||||||
|
|
|
@ -44,7 +44,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
@TestConfiguration
|
@TestConfiguration
|
||||||
@ComponentScan(basePackages={"com.diboot", "diboot.core"})
|
@ComponentScan(basePackages={"com.diboot", "diboot.core"})
|
||||||
@MapperScan({"com.diboot.**.mapper", "diboot.**.mapper"})
|
@MapperScan({"com.diboot.core.mapper", "diboot.core.**.mapper"})
|
||||||
public class SpringMvcConfig implements WebMvcConfigurer{
|
public class SpringMvcConfig implements WebMvcConfigurer{
|
||||||
private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class);
|
private static final Logger log = LoggerFactory.getLogger(SpringMvcConfig.class);
|
||||||
|
|
||||||
|
@ -63,7 +63,6 @@ public class SpringMvcConfig implements WebMvcConfigurer{
|
||||||
// 设置fastjson的序列化参数:禁用循环依赖检测,数据兼容浏览器端(避免JS端Long精度丢失问题)
|
// 设置fastjson的序列化参数:禁用循环依赖检测,数据兼容浏览器端(避免JS端Long精度丢失问题)
|
||||||
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
|
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
|
||||||
SerializerFeature.BrowserCompatible);
|
SerializerFeature.BrowserCompatible);
|
||||||
fastJsonConfig.setDateFormat(D.FORMAT_DATETIME_Y4MDHM);
|
|
||||||
converter.setFastJsonConfig(fastJsonConfig);
|
converter.setFastJsonConfig(fastJsonConfig);
|
||||||
|
|
||||||
HttpMessageConverter<?> httpMsgConverter = converter;
|
HttpMessageConverter<?> httpMsgConverter = converter;
|
||||||
|
|
|
@ -16,23 +16,27 @@
|
||||||
package diboot.core.test.service;
|
package diboot.core.test.service;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.diboot.core.binding.QueryBuilder;
|
||||||
import com.diboot.core.config.BaseConfig;
|
import com.diboot.core.config.BaseConfig;
|
||||||
import com.diboot.core.entity.Dictionary;
|
import com.diboot.core.entity.Dictionary;
|
||||||
import com.diboot.core.service.impl.DictionaryServiceImpl;
|
import com.diboot.core.service.impl.DictionaryServiceImpl;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.util.ContextHelper;
|
import com.diboot.core.util.ContextHelper;
|
||||||
|
import com.diboot.core.util.JSON;
|
||||||
import com.diboot.core.util.V;
|
import com.diboot.core.util.V;
|
||||||
import com.diboot.core.vo.*;
|
import com.diboot.core.vo.*;
|
||||||
import diboot.core.test.StartupApplication;
|
import diboot.core.test.StartupApplication;
|
||||||
|
import diboot.core.test.binder.entity.UserRole;
|
||||||
|
import diboot.core.test.binder.service.UserService;
|
||||||
import diboot.core.test.config.SpringMvcConfig;
|
import diboot.core.test.config.SpringMvcConfig;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
|
@ -52,9 +56,11 @@ import java.util.*;
|
||||||
public class BaseServiceTest {
|
public class BaseServiceTest {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier("dictionaryService")
|
|
||||||
DictionaryServiceImpl dictionaryService;
|
DictionaryServiceImpl dictionaryService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
UserService userService;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGet(){
|
public void testGet(){
|
||||||
// 查询总数
|
// 查询总数
|
||||||
|
@ -87,6 +93,11 @@ public class BaseServiceTest {
|
||||||
List<Long> ids = BeanUtils.collectIdToList(dictionaryList);
|
List<Long> ids = BeanUtils.collectIdToList(dictionaryList);
|
||||||
dictionaryList = dictionaryService.getEntityListByIds(ids);
|
dictionaryList = dictionaryService.getEntityListByIds(ids);
|
||||||
Assert.assertTrue(V.notEmpty(dictionaryList));
|
Assert.assertTrue(V.notEmpty(dictionaryList));
|
||||||
|
|
||||||
|
// 获取map
|
||||||
|
List<Map<String, Object>> mapList = dictionaryService.getMapList(null, new Pagination());
|
||||||
|
Assert.assertTrue(mapList.size() > 0 && mapList.size() <= BaseConfig.getPageSize());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -144,6 +155,7 @@ public class BaseServiceTest {
|
||||||
dictionaryList.get(2).setItemValue("HZ2");
|
dictionaryList.get(2).setItemValue("HZ2");
|
||||||
dictionaryService.updateEntity(dictionaryList.get(2));
|
dictionaryService.updateEntity(dictionaryList.get(2));
|
||||||
Assert.assertTrue(success);
|
Assert.assertTrue(success);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -227,7 +239,6 @@ public class BaseServiceTest {
|
||||||
Assert.assertTrue(success);
|
Assert.assertTrue(success);
|
||||||
dictionaryList2 = dictionaryService.getEntityList(queryWrapper);
|
dictionaryList2 = dictionaryService.getEntityList(queryWrapper);
|
||||||
Assert.assertTrue(V.isEmpty(dictionaryList2));
|
Assert.assertTrue(V.isEmpty(dictionaryList2));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -257,4 +268,82 @@ public class BaseServiceTest {
|
||||||
System.out.println(database);
|
System.out.println(database);
|
||||||
Assert.assertTrue(database.equals("mysql") || database.equals("oracle"));
|
Assert.assertTrue(database.equals("mysql") || database.equals("oracle"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetValuesOfField(){
|
||||||
|
QueryWrapper<Dictionary> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("type", "GENDER");
|
||||||
|
List<Long> ids = dictionaryService.getValuesOfField(queryWrapper, Dictionary::getId);
|
||||||
|
Assert.assertTrue(ids.size() > 0);
|
||||||
|
|
||||||
|
LambdaQueryWrapper<Dictionary> wrapper = new QueryWrapper<Dictionary>().lambda()
|
||||||
|
.eq(Dictionary::getType, "GENDER");
|
||||||
|
List<String> itemValues = dictionaryService.getValuesOfField(wrapper, Dictionary::getItemValue);
|
||||||
|
Assert.assertTrue(itemValues.size() > 0);
|
||||||
|
System.out.println(JSON.stringify(ids) + " : " + JSON.stringify(itemValues));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetLimit(){
|
||||||
|
QueryWrapper<Dictionary> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("type", "GENDER");
|
||||||
|
queryWrapper.gt("parent_id", 0);
|
||||||
|
|
||||||
|
Dictionary dictionary = dictionaryService.getSingleEntity(queryWrapper);
|
||||||
|
Assert.assertTrue(dictionary != null);
|
||||||
|
|
||||||
|
List<Dictionary> ids = dictionaryService.getEntityListLimit(queryWrapper, 5);
|
||||||
|
Assert.assertTrue(ids.size() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPagination(){
|
||||||
|
Dictionary dict = new Dictionary();
|
||||||
|
dict.setParentId(1L);
|
||||||
|
dict.setType("GENDER");
|
||||||
|
dict.setEditable(true);
|
||||||
|
|
||||||
|
QueryWrapper<Dictionary> queryWrapper = QueryBuilder.toQueryWrapper(dict);
|
||||||
|
|
||||||
|
// 查询当前页的数据
|
||||||
|
Pagination pagination = new Pagination();
|
||||||
|
pagination.setPageSize(1);
|
||||||
|
|
||||||
|
List<DictionaryVO> voList = dictionaryService.getViewObjectList(queryWrapper, pagination, DictionaryVO.class);
|
||||||
|
Assert.assertTrue(voList.size() == 1);
|
||||||
|
Assert.assertTrue(pagination.getTotalPage() >= 2);
|
||||||
|
|
||||||
|
pagination.setPageIndex(2);
|
||||||
|
voList = dictionaryService.getViewObjectList(queryWrapper, pagination, DictionaryVO.class);
|
||||||
|
Assert.assertTrue(voList.size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试n-n的批量新建/更新
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Transactional
|
||||||
|
public void testCreateUpdateN2NRelations(){
|
||||||
|
Long userId = 10001L;
|
||||||
|
LambdaQueryWrapper<UserRole> queryWrapper = new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId);
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
List<Long> roleIdList = Arrays.asList(10L, 11L, 12L);
|
||||||
|
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
|
||||||
|
List<UserRole> list = ContextHelper.getBaseMapperByEntity(UserRole.class).selectList(queryWrapper);
|
||||||
|
Assert.assertTrue(list.size() == roleIdList.size());
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
roleIdList = Arrays.asList(13L);
|
||||||
|
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
|
||||||
|
list = ContextHelper.getBaseMapperByEntity(UserRole.class).selectList(queryWrapper);
|
||||||
|
Assert.assertTrue(list.size() == 1);
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
roleIdList = null;
|
||||||
|
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
|
||||||
|
list = ContextHelper.getBaseMapperByEntity(UserRole.class).selectList(queryWrapper);
|
||||||
|
Assert.assertTrue(list.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2015-2020, www.dibo.ltd (service@dibo.ltd).
|
||||||
|
* <p>
|
||||||
|
* 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
|
||||||
|
* <p>
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* <p>
|
||||||
|
* 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.service;
|
||||||
|
|
||||||
|
import com.diboot.core.binding.data.CheckpointType;
|
||||||
|
import com.diboot.core.binding.data.DataAccessCheckInterface;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据访问控制测试
|
||||||
|
* @author Mazc@dibo.ltd
|
||||||
|
* @version v2.0
|
||||||
|
* @date 2020/04/24
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DataAccessCheckImpl implements DataAccessCheckInterface {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Long> getAccessibleIds(CheckpointType type) {
|
||||||
|
return Arrays.asList(100001L);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.diboot.core.entity.Dictionary;
|
import com.diboot.core.entity.Dictionary;
|
||||||
import com.diboot.core.util.BeanUtils;
|
import com.diboot.core.util.BeanUtils;
|
||||||
import com.diboot.core.vo.DictionaryVO;
|
import com.diboot.core.vo.DictionaryVO;
|
||||||
|
import diboot.core.test.binder.entity.User;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -57,6 +58,14 @@ public class BeanUtilsTest {
|
||||||
Assert.assertTrue(dictionary3.getItemName().equals(itemName));
|
Assert.assertTrue(dictionary3.getItemName().equals(itemName));
|
||||||
Assert.assertTrue(dictionary3.isEditable() == true);
|
Assert.assertTrue(dictionary3.isEditable() == true);
|
||||||
Assert.assertTrue(dictionary3.getCreateTime() != null);
|
Assert.assertTrue(dictionary3.getCreateTime() != null);
|
||||||
|
|
||||||
|
// Accept注解拷贝
|
||||||
|
User user = new User();
|
||||||
|
BeanUtils.copyProperties(dictionary3, user);
|
||||||
|
Assert.assertTrue(user.getGender().equals(dictionary3.getItemName()));
|
||||||
|
user.setGender("123");
|
||||||
|
BeanUtils.copyProperties(dictionary3, user);
|
||||||
|
Assert.assertTrue(user.getGender().equals("123"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -51,11 +51,13 @@ public class DTest {
|
||||||
JsonResult j2 = new JsonResult(token, "申请token成功");
|
JsonResult j2 = new JsonResult(token, "申请token成功");
|
||||||
JsonResult j3 = new JsonResult(Status.OK, token);
|
JsonResult j3 = new JsonResult(Status.OK, token);
|
||||||
JsonResult j4 = new JsonResult(Status.OK, token, "申请token成功");
|
JsonResult j4 = new JsonResult(Status.OK, token, "申请token成功");
|
||||||
JsonResult j5 = new JsonResult(Status.OK);
|
JsonResult j5 = JsonResult.OK();
|
||||||
|
JsonResult j6 = JsonResult.FAIL_VALIDATION("xxx验证错误");
|
||||||
System.out.println(j1.getData());
|
System.out.println(j1.getData());
|
||||||
System.out.println(j2.getData());
|
System.out.println(j2.getData());
|
||||||
System.out.println(j3.getData());
|
System.out.println(j3.getData());
|
||||||
System.out.println(j4.getData());
|
System.out.println(j4.getData());
|
||||||
System.out.println(j5.getData());
|
System.out.println(j5.getData());
|
||||||
|
Assert.assertTrue(j6.getMsg().contains("xxx验证错误"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ create table user_role
|
||||||
|
|
||||||
-- 初始化样例数据
|
-- 初始化样例数据
|
||||||
INSERT INTO department (id, parent_id, org_id, name)
|
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)
|
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);
|
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);
|
||||||
|
|
|
@ -16,13 +16,14 @@ module.exports = {
|
||||||
collapsable: true,
|
collapsable: true,
|
||||||
sidebarDepth: 2,
|
sidebarDepth: 2,
|
||||||
children: [
|
children: [
|
||||||
['/guide/diboot-core/安装', '安装'],
|
['/guide/diboot-core/设计理念', '设计理念'],
|
||||||
|
['/guide/diboot-core/简介', '简介'],
|
||||||
['/guide/diboot-core/实体Entity', 'Entity相关'],
|
['/guide/diboot-core/实体Entity', 'Entity相关'],
|
||||||
['/guide/diboot-core/Service与实现', 'Service相关'],
|
['/guide/diboot-core/Service接口', 'Service相关'],
|
||||||
['/guide/diboot-core/Mapper及自定义', 'Mapper相关'],
|
['/guide/diboot-core/Mapper及自定义', 'Mapper相关'],
|
||||||
['/guide/diboot-core/Controller接口', 'Controller相关'],
|
['/guide/diboot-core/Controller接口', 'Controller相关'],
|
||||||
['/guide/diboot-core/无SQL关联', '无SQL关联绑定'],
|
['/guide/diboot-core/无SQL关联绑定', '无SQL关联绑定'],
|
||||||
['/guide/diboot-core/查询条件DTO', '查询条件DTO'],
|
['/guide/diboot-core/无SQL跨表查询', '无SQL跨表查询'],
|
||||||
['/guide/diboot-core/常用工具类', '常用工具类']
|
['/guide/diboot-core/常用工具类', '常用工具类']
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -62,6 +63,7 @@ module.exports = {
|
||||||
['/guide/diboot-antd-admin/添加页面', '添加页面'],
|
['/guide/diboot-antd-admin/添加页面', '添加页面'],
|
||||||
['/guide/diboot-antd-admin/权限控制', '权限控制'],
|
['/guide/diboot-antd-admin/权限控制', '权限控制'],
|
||||||
['/guide/diboot-antd-admin/接口请求', '接口请求'],
|
['/guide/diboot-antd-admin/接口请求', '接口请求'],
|
||||||
|
['/guide/diboot-antd-admin/组件', '组件'],
|
||||||
['/guide/diboot-antd-admin/CRUD快速集成', 'CRUD快速集成'],
|
['/guide/diboot-antd-admin/CRUD快速集成', 'CRUD快速集成'],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -76,7 +78,8 @@ module.exports = {
|
||||||
['/guide/diboot-element-admin/开始使用', '开始使用'],
|
['/guide/diboot-element-admin/开始使用', '开始使用'],
|
||||||
['/guide/diboot-element-admin/添加页面', '添加页面'],
|
['/guide/diboot-element-admin/添加页面', '添加页面'],
|
||||||
['/guide/diboot-element-admin/权限控制', '权限控制'],
|
['/guide/diboot-element-admin/权限控制', '权限控制'],
|
||||||
['/guide/diboot-antd-admin/接口请求', '接口请求'],
|
['/guide/diboot-element-admin/接口请求', '接口请求'],
|
||||||
|
['/guide/diboot-element-admin/组件', '组件'],
|
||||||
['/guide/diboot-element-admin/CRUD快速集成', 'CRUD快速集成'],
|
['/guide/diboot-element-admin/CRUD快速集成', 'CRUD快速集成'],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -90,18 +93,28 @@ module.exports = {
|
||||||
['/guide/diboot-devtools/介绍', '介绍'],
|
['/guide/diboot-devtools/介绍', '介绍'],
|
||||||
['/guide/diboot-devtools/开始使用', '开始使用'],
|
['/guide/diboot-devtools/开始使用', '开始使用'],
|
||||||
['/guide/diboot-devtools/数据表管理', '数据表管理'],
|
['/guide/diboot-devtools/数据表管理', '数据表管理'],
|
||||||
['/guide/diboot-devtools/代码生成与更新', '代码生成与更新']
|
['/guide/diboot-devtools/后端代码生成与更新', '后端代码生成与更新'],
|
||||||
|
['/guide/diboot-devtools/前端功能生成', '前端功能生成']
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'/guide/faq/': [
|
'/guide/notes/faq': [
|
||||||
{
|
{
|
||||||
title: 'F&Q',
|
title: 'FAQ',
|
||||||
collapsable: true,
|
collapsable: true,
|
||||||
sidebarDepth: 2,
|
sidebarDepth: 2,
|
||||||
children: [
|
children: [
|
||||||
['/guide/faq/devtools', 'devtools开发助理'],
|
['/guide/notes/faq/main', 'FAQ'],
|
||||||
['/guide/faq/iam', 'IAM 组件']
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'/guide/notes/upgrade': [
|
||||||
|
{
|
||||||
|
title: '版本升级指南',
|
||||||
|
collapsable: true,
|
||||||
|
sidebarDepth: 2,
|
||||||
|
children: [
|
||||||
|
['/guide/notes/upgrade/2_0_x升级至2_1_x', '2.0.x升级至2.1.x'],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -111,10 +124,11 @@ module.exports = {
|
||||||
}, {
|
}, {
|
||||||
text: '基础组件 指南',
|
text: '基础组件 指南',
|
||||||
items: [
|
items: [
|
||||||
{ text: 'F&Q', link: '/guide/faq/devtools' },
|
{ text: 'core基础内核', link: '/guide/diboot-core/设计理念' },
|
||||||
{ text: 'core基础内核', link: '/guide/diboot-core/安装' },
|
|
||||||
{ text: 'IAM身份认证', link: '/guide/diboot-iam/介绍' },
|
{ text: 'IAM身份认证', link: '/guide/diboot-iam/介绍' },
|
||||||
{ text: 'File文件组件', link: '/guide/diboot-file/介绍' }
|
{ text: 'File文件组件', link: '/guide/diboot-file/介绍' },
|
||||||
|
{ text: '版本升级指南', link: '/guide/notes/upgrade/2_0_x升级至2_1_x' },
|
||||||
|
{ text: 'F&Q', link: '/guide/notes/faq/main' },
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
text: '前端项目 指南',
|
text: '前端项目 指南',
|
||||||
|
@ -149,11 +163,9 @@ module.exports = {
|
||||||
link: 'https://github.com/dibo-software/diboot-v2'
|
link: 'https://github.com/dibo-software/diboot-v2'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}, {
|
}/*, {
|
||||||
text: '优秀案例',
|
text: '优秀案例',
|
||||||
link: '/other/excellentExample'
|
link: '/other/excellentExample'
|
||||||
},{
|
}*/]
|
||||||
text: '1.x旧版', link: 'https://www.diboot.com/diboot-v1/'
|
|
||||||
}]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Binary file not shown.
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 70 KiB |
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
## 开始之前
|
## 开始之前
|
||||||
|
|
||||||
* 在diboot-antd-admin中,我们对CRUD等常用功能进行了一些抽象,将常用的列表、详情、新建、更新、删除等功能需要的相关属性与方法都抽象成了vue的mixins文件,这些文件在**src/components/diboot/mixins**文件夹下,强烈建议您先阅览以下他们。
|
* 在diboot-antd-admin中,我们对CRUD等常用功能进行了一些抽象,将常用的列表、详情、新建、更新、删除等功能需要的相关属性与方法都抽象成了vue的mixins文件,这些文件在**src/components/diboot/mixins**文件夹下。
|
||||||
* 也可以对已有的一些页面组件代码进行阅读,比如**src/views/system/iamUser**文件夹下的相关组件代码。
|
* 也可以对已有的一些页面组件代码进行阅读,比如**src/views/system/iamUser**文件夹下的相关组件代码。
|
||||||
|
::: tip
|
||||||
|
在阅读文档之前,建议您准备好[diboot-antd-admin 最新版的环境](https://github.com/dibo-software/diboot-antd-admin/releases)源码环境,搭配代码使用更佳。
|
||||||
|
:::
|
||||||
## 列表页
|
## 列表页
|
||||||
|
|
||||||
1. 引入列表的mixins文件,如下:
|
1. 引入列表的mixins文件,如下:
|
||||||
|
@ -41,23 +43,51 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
5. 删除数据:直接在删除按钮上调用remove函数即可,传入需要删除的当前id。
|
5. 属性配置
|
||||||
6. 导出数据:直接在导出按钮上调用exportData函数即可,将会传入当前查询参数,并异步下载文件。
|
|
||||||
7. 自定义查询参数处理方法:重写rebuildQuery方法,接收mixins中已经定义的customQueryParam与queryParam的合并值,返回处理后的值,该方法将会在获取列表数据前或导出数据前被调用。
|
| 属性 | 说明 | 类型 |默认值 | 版本|
|
||||||
8. 钩子函数:afterLoadList,在列表加载完毕将会执行该操作,另外,删除函数返回的是 Promise对象,所以可以使用.then在删除完毕时执行某些操作。
|
| ------------- |:-------------| -----| -----| -----|
|
||||||
9. 相关配置:
|
| baseApi | 请求接口基础路径(必须配置)|String | / | - |
|
||||||
* customQueryParam: 不会被搜索栏改变的初始查询参数对象,一般用于页面固定的查询参数
|
| listApi | 列表数据接口| String | /list |- |
|
||||||
* queryParam:与查询条件绑定的查询参数对象
|
| deleteApiPrefix | 删除接口前缀 | String | / | - |
|
||||||
* advanced:用于构建可收缩/展开的查询框的状态参数
|
| exportApi | 导出接口| String |~~/export~~ /excel/export|~~2.0.5~~ **2.1.0**|
|
||||||
* more:当前对象的关联数据对象
|
| customQueryParam| 自定义参数(不被查询表单重置和改变的参数) | object | {} | - |
|
||||||
* getMore:是否在该页面初始化时加载当前对象的关联数据对象,加载的关联数据对象存储在more中,默认为false
|
| queryParam | 与查询条件绑定的参数(会被查询表单重置和改变的参数) | object | {} | - |
|
||||||
* getListFromMixin:是否在页面初始化时自动加载列表数据,默认为true
|
| dateRangeQuery | 日期区间选择配置 <br/>**时间区间字段请放在这个对象中,会自动构建参与查询** | object | {} | **2.1.0** |
|
||||||
* loadingData:标记页面加载数据状态
|
| advanced | 高级搜索 展开/关闭 | boolean | false | **2.1.0** |
|
||||||
* pagination:分页配置
|
| data | 存储列表数据 | array | [] |-|
|
||||||
* baseApi:接口前缀(必须配置)
|
| getMore | 是否使mixin在当前业务的attachMore接口中自动获取关联数据<br/>**<a href="#业务对象关联详解">:point_right:业务对象关联详解</a>**| boolean | false | - |
|
||||||
* listApi:列表数据接口,默认为 /list
|
| attachMoreList | 获取关联数据列表的配置列表<br/>**<a href="#业务对象关联详解">:point_right:业务对象关联详解</a>** | array | [] | **2.1.0** |
|
||||||
* deleteApiPrefix:删除接口前缀,默认为 /
|
| more | 存储当前对象的关联数据对象<br/>**<a href="#业务对象关联详解">:point_right:业务对象关联详解</a>**| object | {} | - |
|
||||||
* exportApi: 导出接口,默认为 /export
|
| getListFromMixin| 是否在页面初始化时自动加载列表数据 | boolean | true | - |
|
||||||
|
| loadingData | 标记页面加载数据状态 | boolean | false | - |
|
||||||
|
| pagination | 分页配置 | object | {pageSize: 10,current: 1,total: 0,showSizeChanger: true,pageSizeOptions: ['10', '20', '30', '50', '100'],showTotal: (total, range) => `当前显示 ${range[0]} - ${range[1]} 条/共 ${total} 条` } | - |
|
||||||
|
|
||||||
|
6. 功能函数
|
||||||
|
|
||||||
|
| 名称 | 说明 | 参数 | 版本|
|
||||||
|
| ------------- |:-------------| -----| -----|
|
||||||
|
| handleTableChange | 分页、排序、筛选变化时触发 |function(pagination, filters, sorter) | - |
|
||||||
|
| appendSorterParam | 构建排序 handleTableChange调用|function(sorter) | **2.1.0** |
|
||||||
|
| toggleAdvanced | 切换展示更多搜索框(绑定**advanced**属性)| - | **2.1.0** |
|
||||||
|
| onSearch | 搜索,查询第一页(默认查询按钮触发)| - | - |
|
||||||
|
| postList | post请求的获取列表(可以传递更长、更复杂参数) | - | - |
|
||||||
|
| getList | get请求获取列表 | - | - |
|
||||||
|
| attachMore | 加载当前页面关联的对象或者字典,参考属性:getMore、attachMoreList、more | - | - |
|
||||||
|
| reset | 重置查询 | - | - |
|
||||||
|
| remove | 根据id删除 | function(id) | - |
|
||||||
|
| exportData | 导出数据至excel | - | **2.1.0** |
|
||||||
|
| downloadFile | 下载文件 | function(res) | **2.1.0** |
|
||||||
|
| getPopupContainer | 解决带有下拉框组件在滚动时下拉框不随之滚动的问题 | function(trigger) |-|
|
||||||
|
| contentTransform | 处理查询参数中的moment数据 默认转化为YYYY-MM-DD | function(content, transform = {}) | **2.1.0** |
|
||||||
|
| dateRange2queryParam | 构建区间查询参数,(转化dateRangeQuery属性内容) | - | **2.1.0** |
|
||||||
|
|
||||||
|
7. 钩子函数
|
||||||
|
|
||||||
|
| 名称 | 说明 | 参数 | 版本|
|
||||||
|
| ------------- |:-------------| -----| -----|
|
||||||
|
| afterLoadList | 加载数据之后操作 |function(list) | - |
|
||||||
|
| rebuildQuery | 重新构建查询条件 (接收已经定义的customQueryParam与queryParam的合并值)|function(query) | - |
|
||||||
|
|
||||||
## 新建与更新
|
## 新建与更新
|
||||||
|
|
||||||
|
@ -69,36 +99,52 @@ export default {
|
||||||
mixins: [form]
|
mixins: [form]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
2. 相同业务对象的新建与更新使用同一个表单组件,默认以抽屉形式打开,可在引入地方,直接调用该组件的open方法进行打开。
|
2. 属性配置:
|
||||||
3. 使用以上引入列表mixins的方式引入form的mixins文件,将会具有mixins中的所有能力。
|
|
||||||
4. 已有功能:
|
| 属性 | 说明 | 类型 |默认值 | 版本|
|
||||||
* open函数,可直接调用打开该表单
|
| ------------- |:-------------| -----| -----| -----|
|
||||||
* 提供根据open中参数情况自动切换新建与更新操作
|
| primaryKey | 主键字段名 | string | id | **2.1.0** |
|
||||||
* 提供打开与关闭完成的钩子函数
|
| baseApi | 请求接口基础路径(必须配置)|String | / | - |
|
||||||
* 提供表单提交函数 onSubmit
|
| createApi | 新建接口,自动拼接在*baseApi*之后| String | / |- |
|
||||||
* 对form下校验规则的自动校验
|
| updateApiPrefix | 更新接口前缀,自动拼接在*baseApi*之后 | String | / | - |
|
||||||
* 提供对校验的自定义操作
|
| labelCol | label 默认布局样式 | object |{xs: {span: 24}, sm: {span: 5}}|-|
|
||||||
* 提供校验完成后的enhance钩子函数,可对需要提交的数据进行再处理
|
| wrapperCol | form控件默认布局样式 | object |{xs: {span: 24}, sm: {span: 16}}|-|
|
||||||
* 可以对新建数据或更新数据的方法重写
|
| model| 存放form数据 | object | {} | - |
|
||||||
* 提供提交成功与提交失败的钩子函数
|
| title | 标题 | String |新建/更新|-|
|
||||||
* 提供表单默认布局参数
|
| more | 存储当前对象的关联数据对象<br/>**<a href="#业务对象关联详解">:point_right:业务对象关联详解</a>** | object | {} | - |
|
||||||
* 提供对关联数据的自动加载
|
| attachMoreList | 获取关联数据列表的配置列表<br/>**<a href="#业务对象关联详解">:point_right:业务对象关联详解</a>** | array | [] | **2.1.0** |
|
||||||
* 提供各项配置支持自定义接口等
|
| getMore | 是否使mixin在当前业务的attachMore接口中自动获取关联数据<br/>**<a href="#业务对象关联详解">:point_right:业务对象关联详解</a>**| boolean | false | - |
|
||||||
5. 相关配置:
|
| state | 当前组件状态对象 | object | {visible: false, confirmSubmit: false} | - |
|
||||||
* baseApi:与列表页相同
|
| isUpload | 当前form是否包含上传<br/>**<a href="#文件上传详解">:point_right:文件上传详解</a>** | boolean | false | **2.1.0** |
|
||||||
* createApi:新建接口,将自动拼接在baseApi之后,默认为 /
|
| fileWrapper | 文件包装容器 <br/>**<a href="#文件上传详解">:point_right:文件上传详解</a>** | object | {} | **2.1.0** |
|
||||||
* updateApiPrefix: 更新接口前缀,将自动拼接在baseApi之后,默认为 /
|
| fileUuidList | 文件存储服务器后返回的唯一标识集合<br/>**<a href="#文件上传详解">:point_right:文件上传详解</a>** | array | [] | **2.1.0** |
|
||||||
* model: 更新时加载的原数据
|
|
||||||
* title:页面标题,默认为新建或更新
|
3. 功能函数
|
||||||
* reloadMore:加载的关联数据对象,默认为{},如果list页面以及由more数据,且不与自身关联,可通过传参方式直接使用list中的more参数
|
|
||||||
* getMore:初始化时是否加载reloadMore数据,默认为false
|
| 名称 | 说明 | 参数 | 版本|
|
||||||
* state:当前组件状态对象,默认为:{visible: false, confirmSubmit: false}
|
| ------------- |:-------------| -----| -----|
|
||||||
6. 钩子函数:
|
| moment | moment时间相关操作 |- | - |
|
||||||
* afterOpen:在组件打开后,或者更新时数据加载完毕后,执行该函数
|
| open | 打开表单 (根据参数id存在与否,设置为更新or新建操作) |function(id) | - |
|
||||||
* afterClose: 在组件关闭后,执行该函数
|
| close | 关闭表单 |- | - |
|
||||||
* enhance:在校验完成后,对提交数据进行处理的函数
|
| validate | 提交前的验证流程 |- | - |
|
||||||
* submitSuccess: 提交成功后,执行该函数,默认关闭该组件,并发送complete和changeKey事件
|
| add | 新建记录的提交 |function(values) | - |
|
||||||
* submitFailed: 提交失败后,执行该函数,默认提示错误消息
|
| update | 更新记录的提交 |function(values) | - |
|
||||||
|
| onSubmit | 表单提交事件 |- | - |
|
||||||
|
| getPopupContainer | 解决带有下拉框组件在滚动时下拉框不随之滚动的问题 |function(trigger) | - |
|
||||||
|
| attachMore | 加载当前页面关联的对象或者字典,参考属性:getMore、attachMoreList、more |- | - |
|
||||||
|
| filterOption | select选择框启用search功能后的过滤器 |- | - |
|
||||||
|
| clearForm | 清除form内容(关闭的时候自动调用) |- | - |
|
||||||
|
| __setFileUuidList__ | 设置文件uuid,参考属性:isUpload、fileWrapper、fileUuidList |- | - |
|
||||||
|
| __defaultFileWrapperKeys__ | 初始化fileWrapper(关闭时候自动调用) |- | - |
|
||||||
|
4. 钩子函数
|
||||||
|
|
||||||
|
| 名称 | 说明 | 参数 | 版本|
|
||||||
|
| ------------- |:-------------| -----| -----|
|
||||||
|
| afterOpen | 在组件打开后,或者更新时数据加载完毕后,执行该函数 |function(id) | - |
|
||||||
|
| afterClose | 在组件关闭后,执行该函数 |- | - |
|
||||||
|
| enhance | 在校验完成后,对提交数据进行处理的函数 |function(values) | - |
|
||||||
|
| submitSuccess | 提交成功后,执行该函数,默认关闭该组件,并发送complete和changeKey事件 |function(result) | - |
|
||||||
|
| submitFailed | 提交失败后,执行该函数,默认提示错误消息 |function(result) | - |
|
||||||
|
|
||||||
## 查看详情
|
## 查看详情
|
||||||
|
|
||||||
|
@ -110,16 +156,125 @@ export default {
|
||||||
mixins: [detail]
|
mixins: [detail]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
2. 已有功能:
|
|
||||||
* 自动加载当前记录数据
|
|
||||||
* 关闭弹窗或者抽屉
|
|
||||||
* 可通过父组件传入width参数,控制抽屉的宽度
|
|
||||||
3. 相关配置:
|
|
||||||
* baseApi:与列表页相同
|
|
||||||
* visible:当前组件显示状态,默认为false
|
|
||||||
* model:当前详情框详情数据
|
|
||||||
* title:当前详情框标题
|
|
||||||
4. 钩子函数:
|
|
||||||
* afterOpen: 打开详情之后执行的函数
|
|
||||||
* afterClose:关闭之后执行的函数
|
|
||||||
|
|
||||||
|
2. 属性配置
|
||||||
|
|
||||||
|
| 属性 | 说明 | 类型 |默认值 | 版本|
|
||||||
|
| ------------- |:-------------| -----| -----| -----|
|
||||||
|
| baseApi | 请求接口基础路径(必须配置)|String | / | - |
|
||||||
|
| visible | 当前组件显示状态 | String | / | - |
|
||||||
|
| model | 当前详情框详情数据 | object |{}|-|
|
||||||
|
| title | 标题 | String |详情|-|
|
||||||
|
| spinning | loading状态 | boolean | false | - |
|
||||||
|
|
||||||
|
3.功能函数
|
||||||
|
|
||||||
|
| 名称 | 说明 | 参数 | 版本|
|
||||||
|
| ------------- |:-------------| -----| -----|
|
||||||
|
| open | 打开详情(加载服务端数据)|function(id) | - |
|
||||||
|
| close | 关闭详情 | - | - |
|
||||||
|
| downloadFile | 下载文件(传入接口地址)| function(path) | **2.1.0** |
|
||||||
|
|
||||||
|
4. 钩子函数
|
||||||
|
|
||||||
|
| 属性 | 说明 | 参数 |
|
||||||
|
| ------------- |:-------------| -----|
|
||||||
|
| afterOpen | 打开之后的操作|function(id) |
|
||||||
|
| afterClose| 关闭之后操作 | - |
|
||||||
|
|
||||||
|
## 详解
|
||||||
|
- <a id="业务对象关联详解">业务对象关联详解</a>
|
||||||
|
- more: 值来源于*getMore*或*attachMoreList* 配置请求接口后返回的结果;
|
||||||
|
- getMore: 开启关联数据会从当前业务的/attachMore接口中读取,开启后优于attachMoreList使用;
|
||||||
|
- attachMoreList: **2.1.0 新增** 实现关联数据从/common/attachMore接口统一读取,配置如下:
|
||||||
|
```javascript
|
||||||
|
// type:D(字典数据)/T(关联业务对象)
|
||||||
|
attattachMoreList: [
|
||||||
|
{
|
||||||
|
type: 'D', // 查询字典
|
||||||
|
target: 'GENDER' // 指向字典的 type = GENDER字段值
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'D', //查询字典
|
||||||
|
target: 'USER_STATUS' // 指向字典的 type = USER_STATUS字段值
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'T', // 查询对象
|
||||||
|
target: 'iamRole', // 指向IamRole对象
|
||||||
|
key: 'name', // 指向IamRole#name字段,需要查询作为key的字段名称
|
||||||
|
value: 'id' // 指向IamRole#id字段,需要查询作为value的字段名称
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
- attachMoreList 返回值会自动绑定至more属性中,上述配置样例返回值为(⚠️data的key规则是上述target的小驼峰命名 + KvList):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code":0,
|
||||||
|
"data":{
|
||||||
|
"userStatusKvList":[
|
||||||
|
{
|
||||||
|
"k":"有效",
|
||||||
|
"v":"A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"iamRoleKvList":[
|
||||||
|
{
|
||||||
|
"k":"超级管理员",
|
||||||
|
"v":10000
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"genderKvList":[
|
||||||
|
{
|
||||||
|
"k":"女",
|
||||||
|
"v":"F"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"k":"男",
|
||||||
|
"v":"M"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"msg":"操作成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- 如非特殊,建议使用attachMoreList配置用以简化代码
|
||||||
|
- <a id="文件上传详解">文件上传详解 (2.1.0新增)</a>
|
||||||
|
|
||||||
|
**以下属性讲解,基于Upload组件** :point_right: [Upload.vue组件概述](/guide/diboot-antd-admin/组件.html#upload组件)
|
||||||
|
- isUpload: 标记当前form表单中是否包含上传属性,使用如:图片、文件,默认不包含,如果引入组件,请手动开启:
|
||||||
|
```javascript
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isUpload: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- fileWrapper: 所有文件的集合都放置与fileWrapper对象中,提交的时候会自动遍历,然后提交至服务端进行数据处理:
|
||||||
|
- template内容
|
||||||
|
```html{5}
|
||||||
|
<upload
|
||||||
|
v-if="state.visible"
|
||||||
|
:prefix="filePrefix"
|
||||||
|
:action="fileAction"
|
||||||
|
:file-list="fileWrapper.slideshowImgsList"
|
||||||
|
:rel-obj-type="relObjType"
|
||||||
|
rel-obj-field="slideshowImgs"
|
||||||
|
:limit-count="9"
|
||||||
|
:is-image="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
v-model="form.slideshowImgs"
|
||||||
|
></upload>
|
||||||
|
```
|
||||||
|
- script内容
|
||||||
|
```javascript
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 包含属性
|
||||||
|
fileWrapper: {
|
||||||
|
//存储form.slideshowImgs属性对应的文件集合
|
||||||
|
slideshowImgsList: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- fileUuidList: 如果包含上传,那么会自动构建文件的提交数据用于绑定当前对象
|
|
@ -28,4 +28,5 @@ diboot-antd-admin前端基础项目,是一个与diboot其他后端组件构成
|
||||||
* 登录人员管理界面;
|
* 登录人员管理界面;
|
||||||
* 角色与权限管理功能;
|
* 角色与权限管理功能;
|
||||||
* 权限管理功能;
|
* 权限管理功能;
|
||||||
* 登录日志管理功能。
|
* 登录日志管理功能;
|
||||||
|
* 预置上传、富文本、导入组件 **(2.1.0 新增)**
|
||||||
|
|
|
@ -7,11 +7,25 @@
|
||||||
* 在**src/views**文件夹下创建页面对应的文件夹以及对应的页面组件文件
|
* 在**src/views**文件夹下创建页面对应的文件夹以及对应的页面组件文件
|
||||||
|
|
||||||
## 添加路由配置
|
## 添加路由配置
|
||||||
|
### 自动添加路由配置
|
||||||
|
:point_right: [devtools 2.1.0 自动化生成您的前端页面](/guide/diboot-devtools/介绍)
|
||||||
|
:::tip
|
||||||
|
devtools 2.1.0为您提供了前端页面和后端接口的快速构建,赶快点击体验吧!!!
|
||||||
|
:::
|
||||||
|
|
||||||
* 后台菜单是根据路由配置自动生成的,具体可参考[路由与菜单](https://pro.loacg.com/docs/router-and-nav)
|
### 手动添加路由配置
|
||||||
|
* 后台菜单是根据路由配置自动生成的,具体可参考[路由与菜单](https://pro.antdv.com/docs/router-and-nav)
|
||||||
* 在**src/config/router.config.js**文件中,可以配置需要新增页面的路由。
|
* 在**src/config/router.config.js**文件中,可以配置需要新增页面的路由。
|
||||||
* 对于需要进行权限控制的菜单,需要放到asyncRouterMap中进行配置,其他不需要进行权限控制或所有人可用的菜单,可以放到constantRouterMap中。
|
* 对于需要进行权限控制的菜单,~~需要放到asyncRouterMap中进行配置~~,其他不需要进行权限控制或所有人可用的菜单,可以放到constantRouterMap中。
|
||||||
* 路由配置方式,可参考已有配置,如下:
|
> 2.1.0版本之后devtools具有前端生成能力,因此手动添加路由不建议直接放在asyncRoutes路由中,而是交给**generateRouterMap**处理,由devtools生成的路由会自动写入generateRouterMap集合
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
export const asyncRouterMap = []
|
||||||
|
// 在router.config.js 末尾添加如下内容
|
||||||
|
const generateRouterMap = []
|
||||||
|
asyncRouterMap[0].children.splice(1, 0, ...generateRouterMap)
|
||||||
|
````
|
||||||
|
* 手动路由配置方式,可参考已有配置,如下:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 系统管理
|
// 系统管理
|
||||||
|
|
|
@ -0,0 +1,276 @@
|
||||||
|
# 组件
|
||||||
|
> diboot组件基于Ant Design Vue进行二次封装和业务调整,搭配devtools后端接口服务,**一键生成交互代码**,减少您的学习成本。
|
||||||
|
::: tip
|
||||||
|
以下内容为组件概述,实际开发时,建议直接使用devtools生成前端代码。
|
||||||
|
:::
|
||||||
|
## import组件
|
||||||
|
> 路径:src/components/diboot/components/import/**.vue
|
||||||
|
>
|
||||||
|
> 功能:提供excel导入、示例下载、数据预览等功能
|
||||||
|
|
||||||
|
- 属性
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 |默认值 | 必填|
|
||||||
|
| ------------- |:-------------| -----| -----| -----|
|
||||||
|
| exampleUrl |示例文件地址| string| -| Y|
|
||||||
|
| uploadUrl |上传地址| string| -| Y|
|
||||||
|
| previewUrl |预览地址| string| -| Y|
|
||||||
|
| previewSaveUrl |预览后保存地址| string| -| Y|
|
||||||
|
| fieldsRequired |提交时候必须的参数| object| {}| N|
|
||||||
|
- 事件
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
| ------------- |:-------------|
|
||||||
|
| finishedUpload |数据上传至数据库成功后触发|
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
```vue
|
||||||
|
<a-drawer
|
||||||
|
title="数据上传"
|
||||||
|
:width="720"
|
||||||
|
@close="close"
|
||||||
|
:visible="visible"
|
||||||
|
:body-style="{ paddingBottom: '80px' }"
|
||||||
|
>
|
||||||
|
<excel-import
|
||||||
|
v-if="visible"
|
||||||
|
:example-url="`${baseApi}/downloadExample/room-example.xlsx`"
|
||||||
|
:upload-url="`${baseApi}/upload`"
|
||||||
|
:preview-url="`${baseApi}/preview`"
|
||||||
|
:preview-save-url="`${baseApi}/previewSave`"
|
||||||
|
@finishedUpload="handleFinishedUpload"
|
||||||
|
></excel-import>
|
||||||
|
<div class="drawer-footer">
|
||||||
|
<a-button @click="close">关闭</a-button>
|
||||||
|
</div>
|
||||||
|
</a-drawer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ExcelImport from '@/components/diboot/components/import/ExcelImport'
|
||||||
|
export default {
|
||||||
|
name: 'ImportExample',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
baseApi: '/importExample/excel',
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.visible = true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 刷新数据
|
||||||
|
*/
|
||||||
|
handleFinishedUpload() {
|
||||||
|
//触发上传完成,告知list组件
|
||||||
|
this.$emit('complete')
|
||||||
|
this.visible = false
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.visible = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ExcelImport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## richText组件
|
||||||
|
> 概述:富文本组件基于quill封装,提供简单的富文本功能
|
||||||
|
>
|
||||||
|
> 路径:src/components/diboot/components/richText/**.vue
|
||||||
|
>
|
||||||
|
> 功能:**QuillEditor.vue(富文本组件)** 和 **QuillHtmlRender.vue(渲染富文本组件)**
|
||||||
|
|
||||||
|
- QuillEditor.vue属性
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 |
|
||||||
|
| ------------- |:-------------| -----|
|
||||||
|
| value |v-decorator绑定的值| string|
|
||||||
|
| placeholder |富文本框的提示| string|
|
||||||
|
|
||||||
|
- QuillEditor.vue事件
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
| ------------- |:-------------|
|
||||||
|
| change |直接使用v-decorator绑定属性值即可|
|
||||||
|
|
||||||
|
- QuillEditor.vue示例
|
||||||
|
```html
|
||||||
|
<!--v-if主要为了强制刷新富文本子组件,否则在打开更新的时候不会更新字段值-->
|
||||||
|
<quill-editor
|
||||||
|
v-if="state.visible"
|
||||||
|
placeholder="请输入富文本编辑"
|
||||||
|
v-decorator="[
|
||||||
|
'richText',
|
||||||
|
{
|
||||||
|
initialValue: model.richText
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
></quill-editor>
|
||||||
|
```
|
||||||
|
- QuillHtmlRender.vue属性
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 |
|
||||||
|
| ------------- |:-------------| -----|
|
||||||
|
| content |富文本内容| string|
|
||||||
|
|
||||||
|
- QuillEditor.vue示例
|
||||||
|
```html
|
||||||
|
<quill-html-render :content="model.richText"></quill-html-render>
|
||||||
|
```
|
||||||
|
## upload组件
|
||||||
|
> 概述:基于Ant Design Vue的upload组件封装,更加贴合diboot接口服务
|
||||||
|
>
|
||||||
|
> 路径:src/components/diboot/components/upload/Upload.vue
|
||||||
|
>
|
||||||
|
> 功能:增加图片form校验、增强与后端接口交互,简化代码流程
|
||||||
|
|
||||||
|
- 属性
|
||||||
|
|
||||||
|
| 名称 | 说明 | 类型 |默认值 | 必填|
|
||||||
|
| ------------- |:-------------| -----| -----| -----|
|
||||||
|
| prefix |地址前缀(axios的baseUrl),用于图片回显| string| -| N|
|
||||||
|
| action |向后端发送的请求地址| string| -| Y|
|
||||||
|
| relObjType |绑定的业务对象类名| string| -| Y|
|
||||||
|
| relObjField |绑定业务对象的属性| string| -| Y|
|
||||||
|
| fileList |文件存储位置| array| -| Y|
|
||||||
|
| listType |上传列表的内建样式,支持text/picture-card| string| text(isImage为true时,使用picture-card)| N|
|
||||||
|
| limitCount |上传数量限制)| number| 1| N|
|
||||||
|
| limitType |上传类型限制,不传默认所有文件,限制多个使用','分割| string| -| N|
|
||||||
|
| limitSize |单个文件上传大小(M)| number| 2| N|
|
||||||
|
| isImage |是否是图片,默认不是图片类型(主要用户上传后构建值)| boolean| false| N|
|
||||||
|
| uploadText |上传框里面的文本| string| 上传| N|
|
||||||
|
| value |v-decorator绑定的值| string| -| Y|
|
||||||
|
|
||||||
|
|
||||||
|
- 事件
|
||||||
|
|
||||||
|
| 名称 | 说明 |
|
||||||
|
| ------------- |:-------------|
|
||||||
|
| change |直接使用v-decorator绑定属性值即可|
|
||||||
|
|
||||||
|
- 示例
|
||||||
|
```vue
|
||||||
|
<el-form-item label="轮播图" >
|
||||||
|
<upload
|
||||||
|
v-if="state.visible"
|
||||||
|
:prefix="filePrefix"
|
||||||
|
:action="fileAction"
|
||||||
|
:file-list="fileWrapper.slideshowImgsList"
|
||||||
|
:rel-obj-type="relObjType"
|
||||||
|
rel-obj-field="slideshowImgs"
|
||||||
|
:limit-count="9"
|
||||||
|
:is-image="true"
|
||||||
|
list-type="picture-card"
|
||||||
|
v-decorator="[
|
||||||
|
'slideshowImgs',
|
||||||
|
{
|
||||||
|
initialValue: model.slideshowImgs
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
></upload>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="附件" >
|
||||||
|
<upload
|
||||||
|
v-if="state.visible"
|
||||||
|
:prefix="filePrefix"
|
||||||
|
:action="fileAction"
|
||||||
|
:file-list="fileWrapper.attachmentList"
|
||||||
|
:rel-obj-type="relObjType"
|
||||||
|
rel-obj-field="attachment"
|
||||||
|
:limit-count="1"
|
||||||
|
v-decorator="[
|
||||||
|
'attachment',
|
||||||
|
{
|
||||||
|
initialValue: model.attachment
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
></upload>
|
||||||
|
</el-form-item>
|
||||||
|
<script>
|
||||||
|
import form from '@/components/diboot/mixins/form'
|
||||||
|
import Upload from '@/components/diboot/components/upload/Upload'
|
||||||
|
import { dibootApi } from '@/utils/request'
|
||||||
|
export default {
|
||||||
|
name: 'UploadExampleForm',
|
||||||
|
components: {
|
||||||
|
Upload
|
||||||
|
},
|
||||||
|
mixins: [form],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
baseApi: '/uploadExample',
|
||||||
|
filePrefix: '/api',
|
||||||
|
fileAction: '/uploadFile/upload/dto',
|
||||||
|
//当前业务对象类名
|
||||||
|
relObjType: 'UploadExample',
|
||||||
|
fileWrapper: {
|
||||||
|
//轮播图存放位置
|
||||||
|
slideshowImgsList: [],
|
||||||
|
//附件存放位置
|
||||||
|
attachmentList: [],
|
||||||
|
},
|
||||||
|
isUpload: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
enhance(values) {
|
||||||
|
// 设置文件uuid
|
||||||
|
this.__setFileUuidList__(values)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 打开表单之后的操作, 加载
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
afterOpen(id) {
|
||||||
|
if (id) {
|
||||||
|
// 更新的时候加载上传的轮播图
|
||||||
|
dibootApi.get(`/uploadFile/getList/${id}/${this.relObjType}/slideshowImgs`).then(res => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
if (res.data && res.data.length > 0) {
|
||||||
|
res.data.forEach(data => {
|
||||||
|
this.fileWrapper.slideshowImgsList.push(this.fileFormatter(data, true))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 更新的时候加载上传 附件
|
||||||
|
dibootApi.get(`/uploadFile/getList/${id}/${this.relObjType}/attachment`).then(res => {
|
||||||
|
if (res.code === 0) {
|
||||||
|
if (res.data && res.data.length > 0) {
|
||||||
|
res.data.forEach(data => {
|
||||||
|
this.fileWrapper.attachmentList.push(this.fileFormatter(data))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 数据转化
|
||||||
|
*/
|
||||||
|
fileFormatter (data, isImage) {
|
||||||
|
const file = {
|
||||||
|
uid: data.uuid, // 文件唯一标识,建议设置为负数,防止和内部产生的 id 冲突
|
||||||
|
name: data.fileName || ' ', // 文件名
|
||||||
|
status: 'done', // 状态有:uploading done error removed
|
||||||
|
response: '{"status": "success"}', // 服务端响应内容
|
||||||
|
filePath: data.accessUrl
|
||||||
|
}
|
||||||
|
if (isImage) {
|
||||||
|
Object.assign(file, {
|
||||||
|
url: `${this.filePrefix}${data.accessUrl}/image`,
|
||||||
|
thumbUrl: `${this.filePrefix}${data.accessUrl}/image`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return file
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -6,118 +6,64 @@
|
||||||
|
|
||||||
## BaseCrudRestController
|
## BaseCrudRestController
|
||||||
|
|
||||||
> 增删改查通用controller,以后的controller都可以继承该类,减少代码量。
|
> 增删改查通用controller, Entity对应的controller都可以继承该类,减少代码量。通用方法:
|
||||||
接下来会对BaseCrudRestController中的一些通用方法进行介绍。
|
|
||||||
|
|
||||||
* getService 抽象方法
|
* protected BaseService getService() 方法
|
||||||
```java
|
该方法是用来获取当前Entity中相关的Service实例。
|
||||||
/**
|
|
||||||
* 获取service实例
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected abstract BaseService getService();
|
|
||||||
```
|
|
||||||
该方法是用来获取当前类中相关业务Service实例。
|
|
||||||
所有继承了BaseCrudRestController的实体类都要实现该方法,如下:
|
|
||||||
* getService 方法实现
|
|
||||||
```java
|
|
||||||
@Autowired
|
|
||||||
private DictionaryService dictionaryService;
|
|
||||||
|
|
||||||
@Override
|
* getViewObject 获取详情页VO
|
||||||
protected BaseService getService() {
|
|
||||||
return dictionaryService;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* getEntityList 方法
|
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
protected JsonResult getEntityList(Wrapper queryWrapper) {...}
|
JsonResult getViewObject(Serializable id, Class<VO> voClass)
|
||||||
//方法调用示例
|
//子类调用示例
|
||||||
JsonResult jsonResult = super.getEntityList(queryWrapper);
|
JsonResult jsonResult = super.getViewObject(id, UserDetailVO.class);
|
||||||
System.out.println(jsonResult.getCode()==0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
该方法用于获取数据集合,入参为查询条件(queryWrapper),
|
该方法用于获取单个对象的详情VO视图对象。
|
||||||
调用该方法成功后会返回所有符合查询条件的数据集合,该方法无分页功能。
|
|
||||||
|
|
||||||
* getEntityListWithPaging 方法
|
* getVOListWithPaging 获取列表页VO(带分页)
|
||||||
```java
|
|
||||||
//方法定义
|
|
||||||
protected JsonResult getEntityListWithPaging(Wrapper queryWrapper, Pagination pagination) {...}
|
|
||||||
//方法调用示例
|
|
||||||
JsonResult jsonResult = super.getEntityListWithPaging(queryWrapper,pagination);
|
|
||||||
System.out.println(jsonResult.getCode()==0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
|
||||||
该方法用于获取数据集合,入参为查询条件(queryWrapper)、分页条件(pagination),
|
|
||||||
调用该方法成功后会返回符合查询条件的当前页数据集合,该方法有分页功能。
|
|
||||||
|
|
||||||
* getVOListWithPaging 方法
|
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
protected <T> JsonResult getVOListWithPaging(Wrapper queryWrapper, Pagination pagination, Class<T> clazz) {...}
|
protected <T> JsonResult getVOListWithPaging(Wrapper queryWrapper, Pagination pagination, Class<T> clazz) {...}
|
||||||
//方法调用示例
|
//调用示例
|
||||||
JsonResult jsonResult = super.getVOListWithPaging(queryWrapper,pagination,Organization.class);
|
JsonResult jsonResult = super.getVOListWithPaging(queryWrapper, pagination, UserListVO.class);
|
||||||
System.out.println(jsonResult.getCode()==0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
该方法用来获取数据VO集合,入参为查询条件(queryWrapper)、分页条件(pagination)、类类型(clazz),
|
列表页查询与分页的url参数示例: /${bindURL}?pageSize=20&pageIndex=1&orderBy=itemValue&type=GENDAR
|
||||||
调用该方法成功后会返回符合查询条件的当前页数据VO集合,该方法有分页功能。
|
orderBy排序: 格式为“”“”"字段:排序方式",如"id:DESC",多个以,分隔
|
||||||
|
|
||||||
* createEntity 方法
|
* getEntityList(queryWrapper),getEntityListWithPaging(queryWrapper, pagination)
|
||||||
|
返回entity对象的集合,供子类需要时调用
|
||||||
|
|
||||||
|
* createEntity 新建保存Entity
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
protected JsonResult createEntity(BaseEntity entity, BindingResult result) {...}
|
protected JsonResult createEntity(E entity)
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
JsonResult jsonResult = super.createEntity(entity,result);
|
JsonResult jsonResult = super.createEntity(entity);
|
||||||
System.out.println(jsonResult.getCode()==0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
该方法用来新建数据,入参为数据实体(entity)、绑定结果(result),调用该方法成功后会在相关表中插入一条数据。
|
|
||||||
|
|
||||||
* updateEntity 方法
|
* updateEntity 根据ID更新Entity
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
protected JsonResult updateEntity(BaseEntity entity, BindingResult result) {...}
|
protected JsonResult updateEntity(Serializable id, E entity)
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
JsonResult jsonResult = super.updateEntity(entity,result);
|
JsonResult jsonResult = super.updateEntity(id, entity);
|
||||||
System.out.println(jsonResult.getCode()==0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
该方法用来更新数据,入参为数据实体(entity)、绑定结果(result),调用该方法成功后会更新相关表中的数据。
|
|
||||||
|
|
||||||
* deleteEntity 方法
|
* deleteEntity 根据ID删除Entity
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
protected JsonResult deleteEntity(Serializable id) {...}
|
protected JsonResult deleteEntity(Serializable id) {...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
JsonResult jsonResult = super.deleteEntity(id);
|
JsonResult jsonResult = super.deleteEntity(id);
|
||||||
System.out.println(jsonResult.getCode()==0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
该方法用来删除数据,入参为数据ID(id),调用该方法成功后会删除相关表中的数据。
|
|
||||||
|
|
||||||
* convertToVoAndBindRelations 方法
|
* batchDeleteEntities 批量删除Entities
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
protected <VO> List<VO> convertToVoAndBindRelations(List entityList, Class<VO> voClass) {...}
|
protected JsonResult batchDeleteEntities(Collection<? extends Serializable> ids)
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
List<OrganizationVO> orgVOList = super.convertToVoAndBindRelations(entityList, OrganizationVO.class);
|
JsonResult jsonResult = super.batchDeleteEntities(ids);
|
||||||
System.out.println(orgVOList.size()>0);
|
|
||||||
//执行结果
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
该方法用来将数据实体集合转化为数据实体VO集合,入参为实体集合(entityList)、类类型(voClass),
|
|
||||||
调用该方法成功后返回数据实体VO集合。
|
|
||||||
|
|
||||||
* beforeCreate 方法
|
* beforeCreate 方法
|
||||||
```java
|
```java
|
||||||
|
@ -126,7 +72,7 @@ protected String beforeCreate(BaseEntity entity){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = this.beforeCreate(entity);
|
String str = this.beforeCreate(entity);
|
||||||
```
|
```
|
||||||
该方法用来处理新建数据之前的逻辑,如数据校验等,需要子类继承BaseCrudRestController时重写并实现具体处理逻辑。
|
该方法用来处理新建数据之前的检查/预处理逻辑,如数据校验等,供子类重写实现。
|
||||||
|
|
||||||
* afterCreated 方法
|
* afterCreated 方法
|
||||||
```java
|
```java
|
||||||
|
@ -135,7 +81,8 @@ String str = this.beforeCreate(entity);
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = this.afterCreated(entity);
|
String str = this.afterCreated(entity);
|
||||||
```
|
```
|
||||||
该方法用来处理新建数据之后的逻辑,需要子类继承BaseCrudRestController时重写并实现具体处理逻辑。
|
该方法用来处理新建数据之后的逻辑,如日志记录等,供子类重写实现。
|
||||||
|
注意:该接口在create entity创建完成之后调用,请勿用于事务性处理。
|
||||||
|
|
||||||
* beforeUpdate 方法
|
* beforeUpdate 方法
|
||||||
```java
|
```java
|
||||||
|
@ -144,7 +91,7 @@ protected String beforeUpdate(BaseEntity entity){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = this.beforeUpdate(entity);
|
String str = this.beforeUpdate(entity);
|
||||||
```
|
```
|
||||||
该方法用来处理更新数据之前的逻辑,需要子类继承BaseCrudRestController时重写并实现具体处理逻辑。
|
该方法用来处理更新数据之前的逻辑,供子类重写实现。
|
||||||
|
|
||||||
* afterUpdated 方法
|
* afterUpdated 方法
|
||||||
```java
|
```java
|
||||||
|
@ -153,7 +100,8 @@ protected String afterUpdated(BaseEntity entity){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = this.afterUpdated(entity);
|
String str = this.afterUpdated(entity);
|
||||||
```
|
```
|
||||||
该方法用来处理更新数据之后的逻辑,需要子类继承BaseCrudRestController时重写并实现具体处理逻辑。
|
该方法用来处理更新数据之后的逻辑,供子类重写实现。
|
||||||
|
注意:该接口在create entity创建完成之后调用,请勿用于事务性处理。
|
||||||
|
|
||||||
* beforeDelete 方法
|
* beforeDelete 方法
|
||||||
```java
|
```java
|
||||||
|
@ -162,7 +110,18 @@ protected String beforeDelete(BaseEntity entity){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = this.beforeDelete(entity);
|
String str = this.beforeDelete(entity);
|
||||||
```
|
```
|
||||||
该方法主要用来处理删除数据之前的逻辑,如检验是否具有删除权限等,需要子类继承BaseCrudRestController时重写并实现具体处理逻辑。
|
该方法主要用来处理删除数据之前的逻辑,如检验是否具有删除权限等,供子类重写实现。
|
||||||
|
|
||||||
|
## 关于 /common/attachMore 接口
|
||||||
|
attachMore是用于为前端select下拉框提供初始数据的通用接口,默认会初始化生成在DictionaryController中。
|
||||||
|
当前端展现形式为Select下拉等数据量不大的场景下可调用该接口进行初始化。
|
||||||
|
当数据量较大,不适合用Select展现等场景,请通过自定义接口实现。
|
||||||
|
~~~java
|
||||||
|
@PostMapping("/common/attachMore")
|
||||||
|
public JsonResult attachMore(@Valid @RequestBody ValidList<AttachMoreDTO> attachMoreDTOList) {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
## 数据校验
|
## 数据校验
|
||||||
|
|
||||||
|
@ -181,7 +140,7 @@ private String name;
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@PostMapping("/")
|
@PostMapping("/")
|
||||||
public JsonResult createEntity(@Valid Demo entity, BindingResult result, HttpServletRequest request)
|
public JsonResult createEntity(@Valid Demo entity, BindingResult result)
|
||||||
throws Exception{
|
throws Exception{
|
||||||
return super.createEntity(entity, result);
|
return super.createEntity(entity, result);
|
||||||
}
|
}
|
||||||
|
@ -189,9 +148,70 @@ public JsonResult createEntity(@Valid Demo entity, BindingResult result, HttpSer
|
||||||
|
|
||||||
* 如果您使用**json格式**进行数据提交,那么可以在@RequestBody注解前添加@Valid注解,如下:
|
* 如果您使用**json格式**进行数据提交,那么可以在@RequestBody注解前添加@Valid注解,如下:
|
||||||
```java
|
```java
|
||||||
public JsonResult createEntity(@Valid @RequestBody Demo entity, BindingResult result, HttpServletRequest request)
|
public JsonResult createEntity(@Valid @RequestBody Demo entity, BindingResult result)
|
||||||
throws Exception{
|
throws Exception{
|
||||||
return super.createEntity(entity, result);
|
return super.createEntity(entity, result);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 统一异常处理
|
||||||
|
异常处理全局实现类为DefaultExceptionHandler,继承自该类并添加@ControllerAdvice注解即可自动支持:
|
||||||
|
* 兼容JSON请求和Html页面请求的Exception异常处理
|
||||||
|
* Entity绑定校验的统一处理(BindException.class, MethodArgumentNotValidException.class)
|
||||||
|
|
||||||
|
* 示例代码
|
||||||
|
~~~java
|
||||||
|
@ControllerAdvice
|
||||||
|
public class GeneralExceptionHandler extends DefaultExceptionHandler{
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
## JsonResult 格式
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
code: 0,
|
||||||
|
msg: "OK",
|
||||||
|
data: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
调用方式
|
||||||
|
```java
|
||||||
|
JsonResult okResult = JsonResult.OK();
|
||||||
|
JsonResult failResult = JsonResult.FAIL_VALIDATION("xxx验证错误");
|
||||||
|
```
|
||||||
|
Status状态码定义:
|
||||||
|
```java
|
||||||
|
//请求处理成功
|
||||||
|
OK(0, "操作成功"),
|
||||||
|
|
||||||
|
// 部分成功(一般用于批量处理场景,只处理筛选后的合法数据)
|
||||||
|
WARN_PARTIAL_SUCCESS(1001, "部分成功"),
|
||||||
|
|
||||||
|
//有潜在的性能问题
|
||||||
|
WARN_PERFORMANCE_ISSUE(1002, "潜在的性能问题"),
|
||||||
|
|
||||||
|
// 传入参数不对
|
||||||
|
FAIL_INVALID_PARAM(4000, "请求参数不匹配"),
|
||||||
|
|
||||||
|
// Token无效或已过期
|
||||||
|
FAIL_INVALID_TOKEN(4001, "Token无效或已过期"),
|
||||||
|
|
||||||
|
// 没有权限执行该操作
|
||||||
|
FAIL_NO_PERMISSION(4003, "无权执行该操作"),
|
||||||
|
|
||||||
|
// 请求资源不存在
|
||||||
|
FAIL_NOT_FOUND(4004, "请求资源不存在"),
|
||||||
|
|
||||||
|
// 数据校验不通过
|
||||||
|
FAIL_VALIDATION(4005, "数据校验不通过"),
|
||||||
|
|
||||||
|
// 操作执行失败
|
||||||
|
FAIL_OPERATION(4006, "操作执行失败"),
|
||||||
|
|
||||||
|
// 后台异常
|
||||||
|
FAIL_EXCEPTION(5000, "系统异常"),
|
||||||
|
|
||||||
|
// 缓存清空
|
||||||
|
MEMORY_EMPTY_LOST(9999, "缓存清空");
|
||||||
|
```
|
|
@ -1,7 +1,7 @@
|
||||||
# Mapper及自定义
|
# Mapper及自定义
|
||||||
|
> 如果您的mapper.xml文件放置于src下的mapper接口同目录下,需要配置编译包含该路径下的xml文件。具体参考如下:
|
||||||
|
|
||||||
## Gradle设置
|
* Gradle设置
|
||||||
> 如果您使用了gradle,并且您的Mapper.xml文件实在java代码目录下,那么除了MapperScan的注解之外,还需要对gradle进行相关设置,使其能够找到相对应的Mapper.xml文件,如:
|
|
||||||
```groovy
|
```groovy
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
|
@ -18,9 +18,7 @@ sourceSets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
* Maven设置
|
||||||
## Maven设置
|
|
||||||
> 如果您使用Maven作为您的构建工具,并且您的Mapper.xml文件是在java代码目录下,那么除了MapperScan的注解之外,还需要再项目中的pom.xml文件中的添加如下配置,使其能够找到您的Mapper.xml文件,如:
|
|
||||||
```xml
|
```xml
|
||||||
<build>
|
<build>
|
||||||
<resources>
|
<resources>
|
||||||
|
@ -39,15 +37,11 @@ sourceSets {
|
||||||
## Mapper类
|
## Mapper类
|
||||||
> Mapper类需要继承diboot-core中的BaseCrudMapper基础类,并传入相对应的实体类,如:
|
> Mapper类需要继承diboot-core中的BaseCrudMapper基础类,并传入相对应的实体类,如:
|
||||||
```java
|
```java
|
||||||
package com.example.demo.mapper;
|
|
||||||
|
|
||||||
import com.diboot.core.mapper.BaseCrudMapper;
|
import com.diboot.core.mapper.BaseCrudMapper;
|
||||||
import com.example.demo.entity.Demo;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface DemoMapper extends BaseCrudMapper<Demo> {
|
public interface DemoMapper extends BaseCrudMapper<Demo> {
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
* BaseCrudMapper类继承了mybatis-plus提供的BaseMapper类,对于BaseCrudMapper中已有的相关接口可以参考[mybatis-plus关于Mapper类的文档](https://mybatis.plus/guide/crud-interface.html#mapper-crud-%E6%8E%A5%E5%8F%A3)
|
* BaseCrudMapper类继承了mybatis-plus提供的BaseMapper类,对于BaseCrudMapper中已有的相关接口可以参考[mybatis-plus关于Mapper类的文档](https://mybatis.plus/guide/crud-interface.html#mapper-crud-%E6%8E%A5%E5%8F%A3)
|
||||||
|
@ -64,20 +58,15 @@ public interface DemoMapper extends BaseCrudMapper<Demo> {
|
||||||
|
|
||||||
## 自定义Mapper接口
|
## 自定义Mapper接口
|
||||||
|
|
||||||
> 自定义Mapper接口可以使用mybatis增加mapper接口的处理方案,如:
|
> 自定义Mapper接口使用Mybatis处理方式,增加mapper接口的处理方案,如:
|
||||||
```java
|
```java
|
||||||
package com.example.demo.mapper;
|
|
||||||
|
|
||||||
import com.diboot.core.mapper.BaseCrudMapper;
|
import com.diboot.core.mapper.BaseCrudMapper;
|
||||||
import com.example.demo.entity.Demo;
|
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface DemoMapper extends BaseCrudMapper<Demo> {
|
public interface DemoMapper extends BaseCrudMapper<Demo> {
|
||||||
|
|
||||||
int forceDeleteEntities(@Param("name") String name);
|
int forceDeleteEntities(@Param("name") String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,153 +1,31 @@
|
||||||
# Service与实现
|
# BaseService接口
|
||||||
|
|
||||||
## Service类
|
## BaseService类
|
||||||
|
|
||||||
> 对于一个自定义的entity,我们推荐您继承diboot-core中封装好的BaseService接口及BaseServiceImpl实现。
|
> 对于entity对应的service,推荐继承diboot-core中封装好的BaseService接口及BaseServiceImpl实现,以使用增强扩展。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
package com.example.demo.service;
|
|
||||||
|
|
||||||
import com.diboot.core.service.BaseService;
|
import com.diboot.core.service.BaseService;
|
||||||
import com.example.demo.entity.Demo;
|
|
||||||
|
|
||||||
public interface DemoService extends BaseService<Demo> {
|
public interface DemoService extends BaseService<Demo> {
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> 提示:BaseService类并没有继承mybatis-plus中的IService接口,如果需要使用mybatis-plus中的IService接口,需要单独继承IService类.
|
> 提示:BaseService类具备mybatis-plus中的IService接口大多数接口,但并没有继承IService,如果需要使用IService接口,可单独继承IService类.
|
||||||
|
|
||||||
## 相关接口
|
## 查询相关接口
|
||||||
|
|
||||||
### getEntity
|
### getEntity
|
||||||
```java
|
```java
|
||||||
T getEntity(Serializable id);
|
T getEntity(Serializable id);
|
||||||
```
|
```
|
||||||
> getEntity接口可以通过一个主键参数得到数据库中的一个实体,如:
|
> getEntity接口可以通过一个主键值得到数据库中的对应记录转换为Entity,如:
|
||||||
```java
|
```java
|
||||||
Demo demo = demoService.getEntity(id);
|
Demo demo = demoService.getEntity(id);
|
||||||
```
|
```
|
||||||
|
### getSingleEntity
|
||||||
### createEntity
|
> 获取符合条件的一个Entity实体
|
||||||
```java
|
```java
|
||||||
boolean createEntity(T entity);
|
T getSingleEntity(Wrapper queryWrapper);
|
||||||
```
|
|
||||||
> createEntity接口将一个entity添加到数据库中,返回成功与失败。通过该接口创建记录成功后,会将新建记录的主键自动设置到该entity中,如:
|
|
||||||
|
|
||||||
```java
|
|
||||||
boolean success = demoService.createEntity(demo);
|
|
||||||
System.out.println(demo.getId());
|
|
||||||
// 输出结果
|
|
||||||
===> 1001
|
|
||||||
```
|
|
||||||
|
|
||||||
### updateEntity
|
|
||||||
```java
|
|
||||||
boolean updateEntity(T entity);
|
|
||||||
boolean updateEntity(T entity, Wrapper updateCriteria);
|
|
||||||
boolean updateEntity(Wrapper updateWrapper);
|
|
||||||
```
|
|
||||||
* updateEntity接口可以根据该实体的主键值来更新整个entity的所有字段内容到数据库,返回成功与失败,如:
|
|
||||||
```java
|
|
||||||
boolean success = demoService.updateEntity(demo);
|
|
||||||
```
|
|
||||||
|
|
||||||
* 该接口也可以通过条件更新对应的字段(可以通过条件设置需要更新的字段,以及需要更新记录的条件限制),返回成功与失败,如:
|
|
||||||
```java
|
|
||||||
/*** 将demo中所有可用的记录的name都更新为“张三” */
|
|
||||||
Demo demo = new Demo();
|
|
||||||
demo.setName("张三");
|
|
||||||
// 设置更新条件
|
|
||||||
LambdaQueryWrapper<Demo> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
wrapper.eq(Demo::isDeleted, false);
|
|
||||||
// 执行更新操作
|
|
||||||
boolean success = demoService.updateEntity(demo, wrapper);
|
|
||||||
System.out.println(success);
|
|
||||||
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
|
||||||
|
|
||||||
* 该接口也可以通过更新条件来执行更新,返回成功与失败,对于更新条件可以参考[mybatis-plus的UpdateWrapper文档](https://mybatis.plus/guide/wrapper.html#updatewrapper)。
|
|
||||||
|
|
||||||
### createOrUpdateEntity
|
|
||||||
```java
|
|
||||||
boolean createOrUpdateEntity(T entity);
|
|
||||||
```
|
|
||||||
> 该接口新建或更新一个实体到数据库,如果该实体主键有值,则更新到数据库,若主键无值,则新建一条记录到数据库,如果主键有值,但数据库中未找到,则报错,如:
|
|
||||||
```java
|
|
||||||
boolean success = demoService.createOrUpdateEntity(demo);
|
|
||||||
System.out.println(success);
|
|
||||||
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
|
||||||
|
|
||||||
### createOrUpdateEntities
|
|
||||||
```java
|
|
||||||
boolean createOrUpdateEntities(Collection entityList);
|
|
||||||
```
|
|
||||||
> 该接口将对一个Collection类型的列表中的每一个实体进行新建或更新到数据库,如:
|
|
||||||
```java
|
|
||||||
boolean success = demoService.createOrUpdateEntities(demoList);
|
|
||||||
System.out.println(success);
|
|
||||||
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
|
||||||
|
|
||||||
### createEntityAndRelatedEntities
|
|
||||||
```java
|
|
||||||
/**
|
|
||||||
* 添加entity 及 其关联子项entities
|
|
||||||
* @param entity 主表entity
|
|
||||||
* @param relatedEntities 从表/关联表entities
|
|
||||||
* @param relatedEntitySetter 设置关联从表绑定id的setter,如Dictionary::setParentId
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
|
||||||
```
|
|
||||||
> 该接口将对一个1对多关联数据的设置关联id并批量保存,如:
|
|
||||||
```java
|
|
||||||
boolean success = dictionaryService.createEntityAndRelatedEntities(dictionary, dictionaryList, Dictionary::setParentId);
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
|
||||||
类似的还有1对多数据的批量更新与删除:
|
|
||||||
~~~java
|
|
||||||
boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
|
||||||
boolean deleteEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
|
||||||
~~~
|
|
||||||
|
|
||||||
### deleteEntity
|
|
||||||
```java
|
|
||||||
boolean deleteEntity(Serializable id);
|
|
||||||
```
|
|
||||||
> 该接口通过主键字段对实体进行删除操作,如:
|
|
||||||
```java
|
|
||||||
boolean success = demoService.deleteEntity(demo.getId());
|
|
||||||
System.out.println(success);
|
|
||||||
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
|
||||||
|
|
||||||
### deletedEntities
|
|
||||||
```java
|
|
||||||
boolean deleteEntities(Wrapper queryWrapper);
|
|
||||||
```
|
|
||||||
> 该接口通过查询条件对符合该查询条件的所有记录进行删除操作,如:
|
|
||||||
```java
|
|
||||||
/*** 删除所有名称为“张三”的记录 **/
|
|
||||||
// 设置查询条件
|
|
||||||
LambdaQueryWrapper<Demo> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
wrapper.eq(Demo::getName, "张三");
|
|
||||||
// 执行删除操作
|
|
||||||
boolean success = demoService.deleteEntities(wrapper);
|
|
||||||
System.out.println(success);
|
|
||||||
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### getEntityListCount
|
### getEntityListCount
|
||||||
|
@ -161,10 +39,7 @@ LambdaQueryWrapper<Demo> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.eq(Demo::getName, "张三");
|
wrapper.eq(Demo::getName, "张三");
|
||||||
// 获取总条数
|
// 获取总条数
|
||||||
int count = demoService.getEntityListCount(wrapper);
|
int count = demoService.getEntityListCount(wrapper);
|
||||||
System.out.println(count);
|
log.debug(count);
|
||||||
|
|
||||||
// 输出
|
|
||||||
===> true
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### getEntityList(Wrapper queryWrapper)
|
### getEntityList(Wrapper queryWrapper)
|
||||||
|
@ -190,6 +65,15 @@ List<T> getEntityListLimit(Wrapper queryWrapper, int limitCount);
|
||||||
List<T> getEntityListByIds(List ids);
|
List<T> getEntityListByIds(List ids);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### getValuesOfField
|
||||||
|
* since v2.1
|
||||||
|
> 获取指定条件的Entity ID或其他字段的集合,示例如:
|
||||||
|
```java
|
||||||
|
QueryWrapper<Dictionary> queryWrapper = new QueryWrapper<>().eq("type", "GENDER");
|
||||||
|
// 提取符合条件的id集合
|
||||||
|
List<Long> ids = dictionaryService.getValuesOfField(queryWrapper, Dictionary::getId);
|
||||||
|
```
|
||||||
|
|
||||||
### getMapList
|
### getMapList
|
||||||
> 该方法通过查询条件查询和分页参数出符合条件的Map列表,其中分页参数是可选参数,返回查询出的Map列表。
|
> 该方法通过查询条件查询和分页参数出符合条件的Map列表,其中分页参数是可选参数,返回查询出的Map列表。
|
||||||
```java
|
```java
|
||||||
|
@ -197,33 +81,145 @@ List<Map<String, Object>> getMapList(Wrapper queryWrapper);
|
||||||
List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination);
|
List<Map<String, Object>> getMapList(Wrapper queryWrapper, Pagination pagination);
|
||||||
```
|
```
|
||||||
|
|
||||||
### getKeyValueList
|
### getKeyValueList, getKeyValueMap
|
||||||
> 该方法通过查询条件查询出查询出符合条件的 KeyValue 列表,该KeyValue是一个键值对,所以再查询条件中需要指定需要查询的字段。
|
> 该方法通过查询条件查询出查询出符合条件的 KeyValue 列表/map,该KeyValue是一个键值对,所以再查询条件中需要指定需要查询的字段。
|
||||||
|
> 注意: KeyValue对象支持第三个扩展字段ext,默认从queryWrapper构建指定select的第三个字段中取值。如queryWrapper.select(name, value, ext)
|
||||||
```java
|
```java
|
||||||
List<KeyValue> getKeyValueList(Wrapper queryWrapper);
|
List<KeyValue> getKeyValueList(Wrapper queryWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
### getViewObject
|
### getViewObject
|
||||||
> 该方法通过主键,查询出该主键VO实例,返回一个VO实例。
|
> 通过Entity主键,查询出该主键VO实例,返回一个VO实例。
|
||||||
提示:如果该VO通过相应注解绑定了数据字典关联或者数据表关联,那么该方法也将查询出相对应的数据字典信息或者关联数据信息等。
|
提示:如果该VO通过相应注解绑定了数据字典关联或者数据表关联,那么该方法也将查询出相对应的数据字典信息或者关联数据信息等。
|
||||||
```java
|
```java
|
||||||
<VO> VO getViewObject(Serializable id, Class<VO> voClass);
|
<VO> VO getViewObject(Serializable id, Class<VO> voClass);
|
||||||
```
|
```
|
||||||
|
|
||||||
### getViewObjectList
|
### getViewObjectList
|
||||||
> 该方法通过查询条件,分页条件,查询出符合该查询条件的当页数据列表,返回一个VO实例列表。
|
> 通过查询条件,分页条件,查询出符合该查询条件的当页数据列表,返回一个VO实例列表。
|
||||||
提示:如果该VO通过相应注解绑定了数据字典关联或数据表关联,那么该方法查询出的VO列表中,每一个VO元素也将有对应的数据字典信息或关联表信息等。
|
提示:如果该VO通过相应注解绑定了数据字典关联或数据表关联,那么该方法查询出的VO列表中,每一个VO元素也将有对应的数据字典信息或关联表信息等。
|
||||||
```java
|
```java
|
||||||
<VO> List<VO> getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class<VO> voClass);
|
<VO> List<VO> getViewObjectList(Wrapper queryWrapper, Pagination pagination, Class<VO> voClass);
|
||||||
```
|
```
|
||||||
### bindingFieldTo
|
|
||||||
|
|
||||||
|
### exists 是否存在匹配记录
|
||||||
```java
|
```java
|
||||||
FieldBinder<T> bindingFieldTo(List voList);
|
/**
|
||||||
EntityBinder<T> bindingEntityTo(List voList);
|
* 是否存在符合条件的记录
|
||||||
|
* @param getterFn entity的getter方法
|
||||||
|
* @param value 需要检查的值
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
boolean exists(IGetter<T> getterFn, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否存在符合条件的记录
|
||||||
|
* @param queryWrapper
|
||||||
|
* @return true/false
|
||||||
|
*/
|
||||||
|
boolean exists(Wrapper queryWrapper);
|
||||||
```
|
```
|
||||||
|
|
||||||
### bindingEntityListTo
|
## 更新相关接口
|
||||||
|
|
||||||
|
### createEntity
|
||||||
```java
|
```java
|
||||||
EntityListBinder<T> bindingEntityListTo(List voList);
|
boolean createEntity(T entity);
|
||||||
```
|
```
|
||||||
|
> createEntity接口将一个entity添加到数据库中,返回成功与失败。创建成功后,会将该记录的主键自动设置到该entity中,如:
|
||||||
|
|
||||||
|
```java
|
||||||
|
boolean success = demoService.createEntity(demo);
|
||||||
|
log.debug(demo.getId());
|
||||||
|
```
|
||||||
|
|
||||||
|
### updateEntity
|
||||||
|
```java
|
||||||
|
boolean updateEntity(T entity);
|
||||||
|
boolean updateEntity(T entity, Wrapper updateCriteria);
|
||||||
|
boolean updateEntity(Wrapper updateWrapper);
|
||||||
|
```
|
||||||
|
* updateEntity接口可以根据该实体的主键值来更新整个entity的所有非空字段值到数据库,返回成功与失败,如:
|
||||||
|
```java
|
||||||
|
boolean success = demoService.updateEntity(demo);
|
||||||
|
```
|
||||||
|
* 该接口也可以通过更新条件来执行更新,具体可参考[mybatis-plus的UpdateWrapper文档](https://mybatis.plus/guide/wrapper.html#updatewrapper)。
|
||||||
|
|
||||||
|
### createOrUpdateEntity
|
||||||
|
```java
|
||||||
|
boolean createOrUpdateEntity(T entity);
|
||||||
|
```
|
||||||
|
> 该接口新建或更新一个实体记录到数据库,如果该实体主键有值,则更新,无值,则新建。如主键有值,但数据库中未找到,则报错,如:
|
||||||
|
```java
|
||||||
|
boolean success = demoService.createOrUpdateEntity(demo);
|
||||||
|
log.debug(success);
|
||||||
|
```
|
||||||
|
|
||||||
|
### createOrUpdateEntities
|
||||||
|
```java
|
||||||
|
boolean createOrUpdateEntities(Collection entityList);
|
||||||
|
```
|
||||||
|
> 批量创建或更新实体集合,如:
|
||||||
|
```java
|
||||||
|
boolean success = demoService.createOrUpdateEntities(demoList);
|
||||||
|
log.debug(success);
|
||||||
|
```
|
||||||
|
|
||||||
|
### createEntityAndRelatedEntities
|
||||||
|
* since v2.0.5
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 添加entity 及 其关联子项entities
|
||||||
|
* @param entity 主表entity
|
||||||
|
* @param relatedEntities 从表/关联表entities
|
||||||
|
* @param relatedEntitySetter 设置关联从表绑定id的setter,如Dictionary::setParentId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean createEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
||||||
|
```
|
||||||
|
> 该接口将对一个1对多关联数据的设置关联id并批量保存,如:
|
||||||
|
```java
|
||||||
|
boolean success = dictionaryService.createEntityAndRelatedEntities(dictionary, dictionaryList, Dictionary::setParentId);
|
||||||
|
```
|
||||||
|
类似的还有1对多数据的批量更新与删除:
|
||||||
|
~~~java
|
||||||
|
boolean updateEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
||||||
|
boolean deleteEntityAndRelatedEntities(T entity, List<RE> relatedEntities, ISetter<RE, R> relatedEntitySetter);
|
||||||
|
~~~
|
||||||
|
|
||||||
|
### createOrUpdateN2NRelations 创建或更新n-n关联
|
||||||
|
* since v2.1.0
|
||||||
|
```java
|
||||||
|
/**
|
||||||
|
* 创建或更新n-n关联
|
||||||
|
* (在主动对象的service中调用,不要求中间表有service)
|
||||||
|
* @param driverIdGetter 驱动对象getter
|
||||||
|
* @param driverId 驱动对象ID
|
||||||
|
* @param followerIdGetter 从动对象getter
|
||||||
|
* @param followerIdList 从动对象id集合
|
||||||
|
*/
|
||||||
|
<R> boolean createOrUpdateN2NRelations(SFunction<R, ?> driverIdGetter, Object driverId, SFunction<R, ?> followerIdGetter, List<? extends Serializable> followerIdList);
|
||||||
|
```
|
||||||
|
使用示例:
|
||||||
|
~~~java
|
||||||
|
List<Long> roleIdList = Arrays.asList(10L, 11L, 12L);
|
||||||
|
// 新增/修改/删除(集合为空) 中间表关联关系
|
||||||
|
userService.createOrUpdateN2NRelations(UserRole::getUserId, userId, UserRole::getRoleId, roleIdList);
|
||||||
|
~~~
|
||||||
|
|
||||||
|
### deleteEntity
|
||||||
|
```java
|
||||||
|
boolean deleteEntity(Serializable id);
|
||||||
|
```
|
||||||
|
> 该接口通过主键字段对实体进行删除操作,如:
|
||||||
|
```java
|
||||||
|
boolean success = demoService.deleteEntity(demo.getId());
|
||||||
|
log.debug(success);
|
||||||
|
```
|
||||||
|
|
||||||
|
### deletedEntities
|
||||||
|
```java
|
||||||
|
boolean deleteEntities(Wrapper queryWrapper);
|
||||||
|
```
|
||||||
|
> 该接口通过查询条件对符合该查询条件的所有记录进行删除操作
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
# 安装
|
|
||||||
|
|
||||||
## diboot-core是什么?
|
|
||||||
|
|
||||||
> diboot-core 是 diboot 2.0版本的核心基础框架,基于Spring Boot、Mybatis-plus封装,实现基础代码的简化及高效。
|
|
||||||
> 使用diboot-core可以更加简单快捷地创建web后端应用,您之前的诸多代码将被极大简化,系统也更容易维护。同时搭档[devtools](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-devtools-example),你需要写的仅有业务逻辑代码。
|
|
||||||
|
|
||||||
* 集成了[mybatis-plus](https://mp.baomidou.com/),以实现单表CRUD无SQL。
|
|
||||||
* 扩展实现了多表关联查询的无SQL方案,只需要一个简单注解@Bind*,就可以实现关联对象(含字段、实体、实体集合等)的数据绑定,且实现方案是将关联查询拆解为单表查询,保障最佳性能。
|
|
||||||
* 支持@BindDict注解简单绑定数据字典,实现value-name转换。
|
|
||||||
* 实现了Entity/DTO自动转换为QueryWrapper(@BindQuery注解绑定字段参数对应的查询条件,无需再手动构建QueryWrapper查询条件)。
|
|
||||||
* 另外还提供其他常用开发场景的最佳实践封装。
|
|
||||||
* 我们还封装了[diboot-core-starter](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example),简化diboot-core的初始化配置(自动配置、自动创建数据字典表)
|
|
||||||
|
|
||||||
## 支持数据库
|
|
||||||
MySQL、MariaDB、ORACLE、SQLServer、PostgreSQL
|
|
||||||
|
|
||||||
## diboot-core使用
|
|
||||||
|
|
||||||
使用步骤请参考 [diboot-core README](https://github.com/dibo-software/diboot-v2/tree/master/diboot-core)
|
|
||||||
|
|
||||||
参考样例 [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example)
|
|
||||||
|
|
||||||
## 相关依赖
|
|
||||||
:::tip
|
|
||||||
以下依赖在引入diboot-core-starter依赖中,可以不再引入。
|
|
||||||
:::
|
|
||||||
* **javax.servlet-api**(javax.servlet:javax.servlet-api:4.0.1)
|
|
||||||
* **spring-boot-starter-web**(org.springframework.boot:spring-boot-starter-web:2.2.4.RELEASE)
|
|
||||||
* **mybatis-plus-boot-starter**(com.baomidou:mybatis-plus-boot-starter:3.2.0)
|
|
||||||
* **commons-io**(commons-io:commons-io:2.6)
|
|
||||||
* **commons-lang3**(org.apache.commons:commons-lang3:3.9)
|
|
||||||
* **fastjson**(com.alibaba:fastjson:1.2.60)
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
需要额外添加的jar
|
|
||||||
:::
|
|
||||||
* **数据库驱动包** (如 mysql:mysql-connector-java:8.0.18)
|
|
||||||
|
|
|
@ -2,72 +2,26 @@
|
||||||
|
|
||||||
> diboot-core中的实体Entity是与数据库表对应的映射对象,下文简称实体。
|
> diboot-core中的实体Entity是与数据库表对应的映射对象,下文简称实体。
|
||||||
|
|
||||||
所有实体统一存放在entity包名下,命名采用将表名转换为首字母大写的驼峰命名法命名,比如**sys_user**的实体名为**SysUser。**
|
所有实体命名约定采用将表名转换为首字母大写的驼峰命名法命名,比如**sys_user**的实体名为**SysUser**
|
||||||
|
|
||||||
## BaseEntity
|
## BaseEntity
|
||||||
|
|
||||||
> BaseEntity是diboot-core提供的基础实体类,提供了我们默认数据表结构的默认字段,比如id、is_deleted、create_time等,默认的方法如toMap等。
|
> BaseEntity是diboot-core提供的基础实体类,提供了我们默认数据表结构的默认字段,比如id、is_deleted、create_time等。
|
||||||
|
|
||||||
## BaseExtEntity
|
|
||||||
|
|
||||||
> BaseExtEntity是基于BaseEntity的扩展实体类,对数据表结构的扩展字段extdata添加了相关处理方法,extdata将以json字符串形式存储在数据库中。
|
|
||||||
|
|
||||||
* getFromExt方法
|
|
||||||
|
|
||||||
```java
|
|
||||||
/***
|
|
||||||
* 从extdata JSON中提取扩展属性值
|
|
||||||
* @param extAttrName
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public Object getFromExt(String extAttrName){
|
|
||||||
if(this.extdataMap == null){
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this.extdataMap.get(extAttrName);
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
该方法传入一个属性名称,返回该属性值
|
|
||||||
|
|
||||||
* addIntoExt
|
|
||||||
|
|
||||||
```java
|
|
||||||
/***
|
|
||||||
* 添加扩展属性和值到extdata JSON中
|
|
||||||
* @param extAttrName
|
|
||||||
* @param extAttrValue
|
|
||||||
*/
|
|
||||||
public void addIntoExt(String extAttrName, Object extAttrValue){
|
|
||||||
if(extAttrName == null && extAttrValue == null){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if(this.extdataMap == null){
|
|
||||||
this.extdataMap = new LinkedHashMap<>();
|
|
||||||
}
|
|
||||||
this.extdataMap.put(extAttrName, extAttrValue);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
该方法传入一个属性名与属性值,即可将该值设置到extdata的json字符串中。
|
|
||||||
|
|
||||||
## 数据校验
|
## 数据校验
|
||||||
|
|
||||||
> 我们在数据提交过程中可能会需要后端进行数据格式校验,我们默认是用validation来做后端数据校验,我们可以在实体中需要校验的字段上添加校验相关的注解,如下:
|
> 数据提交过程中一般需要后端进行数据格式校验,默认是用validation来做后端数据校验,字段上校验注解示例如下:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@NotNull(message = "上级ID不能为空,如无请设为0")
|
@NotNull(message = "上级ID不能为空,如无请设为0")
|
||||||
private Long parentId;
|
private Long parentId;
|
||||||
```
|
```
|
||||||
|
|
||||||
## @TableField注解
|
## 数据库表中不存在的列
|
||||||
|
|
||||||
> 如果mapper也继承我们core的BaseMapper来处理,那么实体中的所有字段都被认为在相对应的数据表中是存在的,如果某个字段在对应数据表中不存在,则会执行SQL时报错。
|
> Entity中的属性默认会自动映射为数据库列,如果某个字段在对应数据表中不存在,需要使用Mybatis-plus的 @TableField(exist = false) 注解告知Mybatis-plus忽略该字段。
|
||||||
|
|
||||||
所以,如果某个字段在相对应的数据表中不存在,而我们又需要使用到该字段,那么可以添加@Tablefield注解,并且设置@TableField(exist = false)中的exist参数为false即可,如下:
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@TableField(exist = false)
|
@TableField(exist = false)
|
||||||
private List<Long> userIdList;
|
private String ignoreMe;
|
||||||
```
|
```
|
|
@ -2,40 +2,37 @@
|
||||||
|
|
||||||
## BeanUtils(Bean)
|
## BeanUtils(Bean)
|
||||||
|
|
||||||
* copyProperties 方法
|
* copyProperties 对象拷贝
|
||||||
```java
|
```java
|
||||||
//方法定义
|
|
||||||
public static Object copyProperties(Object source, Object target){...}
|
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Object obj = BeanUtils.copyProperties(source, target);
|
Object obj = BeanUtils.copyProperties(source, target);
|
||||||
```
|
```
|
||||||
该方法用来复制一个对象的属性到另一个对象,入参为被复制对象(source)、作为返回值的目标对象(target)。
|
该方法用来复制source对象的值到另一个target对象。其在Spring的copyProperties之外扩展支持了@Accept注解指定的非同名字段拷贝,注解定义示例。
|
||||||
|
```java
|
||||||
|
// 指定接收来自源对象的genderLabel属性值
|
||||||
|
@Accept(name = "genderLabel")
|
||||||
|
private String gender;
|
||||||
|
```
|
||||||
|
|
||||||
* convert 方法
|
* convert 类型转换
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static <T> T convert(Object source, Class<T> clazz){...}
|
public static <T> T convert(Object source, Class<T> clazz){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Organization org = BeanUtils.convert(source, Organization.class);
|
Organization org = BeanUtils.convert(source, Organization.class);
|
||||||
System.out.println(org.getName());
|
|
||||||
//执行结果
|
|
||||||
===> xxx有限公司
|
|
||||||
```
|
```
|
||||||
该方法用来将一个对象转换为另外的对象实例,入参为被转化对象(source)、目标对象的类类型(clazz)。
|
该方法用来将一个对象转换为另外的对象实例,入参为被转化对象(source)、目标对象的类类型(clazz)。
|
||||||
|
|
||||||
* convertList 方法
|
* convertList 集合中的类型转换
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static <T> List<T> convertList(List sourceList, Class<T> clazz){...}
|
public static <T> List<T> convertList(List sourceList, Class<T> clazz){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
List<Organization> orgList = BeanUtils.convertList(sourceList, Organization.class);
|
List<Organization> orgList = BeanUtils.convertList(sourceList, Organization.class);
|
||||||
System.out.println(orgList.get(0).getName());
|
|
||||||
//执行结果
|
|
||||||
===> xxx有限公司
|
|
||||||
```
|
```
|
||||||
该方法用来将对象集合转换为另外的对象集合实例,入参为被转化对象集合(sourceList)、目标对象的类类型(clazz)。
|
该方法用来将对象集合转换为另外的对象集合实例,入参为被转化对象集合(sourceList)、目标对象的类类型(clazz)。
|
||||||
|
|
||||||
* bindProperties 方法
|
* bindProperties 绑定map属性到对象
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static void bindProperties(Object model, Map<String, Object> propMap){...}
|
public static void bindProperties(Object model, Map<String, Object> propMap){...}
|
||||||
|
@ -44,7 +41,7 @@ BeanUtils.bindProperties(model, propMap);
|
||||||
```
|
```
|
||||||
该方法用来绑定Map中的属性值到Model,入参为被绑定对象(model)、属性值Map(propMap)。
|
该方法用来绑定Map中的属性值到Model,入参为被绑定对象(model)、属性值Map(propMap)。
|
||||||
|
|
||||||
* getProperty 方法
|
* getProperty 获取属性值
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static Object getProperty(Object obj, String field){...}
|
public static Object getProperty(Object obj, String field){...}
|
||||||
|
@ -53,7 +50,7 @@ Object obj = BeanUtils.getProperty(obj, field);
|
||||||
```
|
```
|
||||||
该方法用来获取对象的属性值,入参为目标对象(obj)、对象字段名(field)。
|
该方法用来获取对象的属性值,入参为目标对象(obj)、对象字段名(field)。
|
||||||
|
|
||||||
* getStringProperty 方法
|
* getStringProperty 获取属性值并转为String
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static String getStringProperty(Object obj, String field){...}
|
public static String getStringProperty(Object obj, String field){...}
|
||||||
|
@ -62,7 +59,7 @@ String str = BeanUtils.getStringProperty(obj, field);
|
||||||
```
|
```
|
||||||
该方法用来获取对象的属性值并转换为字符串类型,入参为目标对象(obj)、字段名(field)。
|
该方法用来获取对象的属性值并转换为字符串类型,入参为目标对象(obj)、字段名(field)。
|
||||||
|
|
||||||
* setProperty 方法
|
* setProperty 设置属性值
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static void setProperty(Object obj, String field, Object value){...}
|
public static void setProperty(Object obj, String field, Object value){...}
|
||||||
|
@ -71,7 +68,7 @@ BeanUtils.setProperty(obj, field, value);
|
||||||
```
|
```
|
||||||
该方法用来设置对象属性值,入参为目标对象(obj)、字段名(field)、字段值(value)。
|
该方法用来设置对象属性值,入参为目标对象(obj)、字段名(field)、字段值(value)。
|
||||||
|
|
||||||
* convertToStringKeyObjectMap 方法
|
* convertToStringKeyObjectMap 转换集合为Key-Object的map,key为指定字段值
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static <T> Map<String, T> convertToStringKeyObjectMap(List<T> allLists, String... fields){...}
|
public static <T> Map<String, T> convertToStringKeyObjectMap(List<T> allLists, String... fields){...}
|
||||||
|
@ -80,7 +77,7 @@ Map map = BeanUtils.convertToStringKeyObjectMap(allLists, fields);
|
||||||
```
|
```
|
||||||
该方法用来将对象集合转化成键值对为String-Object的Map形式,入参为目标对象集合(allLists)、字段名(fields)。
|
该方法用来将对象集合转化成键值对为String-Object的Map形式,入参为目标对象集合(allLists)、字段名(fields)。
|
||||||
|
|
||||||
* convertToStringKeyObjectListMap 方法
|
* convertToStringKeyObjectListMap 转换集合为Key-Object的map,key为指定字段值的String
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static <T> Map<String, List<T>> convertToStringKeyObjectListMap(List<T> allLists, String... fields){...}
|
public static <T> Map<String, List<T>> convertToStringKeyObjectListMap(List<T> allLists, String... fields){...}
|
||||||
|
@ -89,7 +86,7 @@ Map map = BeanUtils.convertToStringKeyObjectListMap(allLists, fields);
|
||||||
```
|
```
|
||||||
该方法用来将对象集合转化成键值对为String-List的Map形式,入参为目标对象集合(allLists)、字段名(fields)。
|
该方法用来将对象集合转化成键值对为String-List的Map形式,入参为目标对象集合(allLists)、字段名(fields)。
|
||||||
|
|
||||||
* buildTree 方法
|
* buildTree 构建树形结构
|
||||||
```java
|
```java
|
||||||
//该方法用来构建支持无限层级的树形结构,默认顶层父级节点的parentId=0,入参为对象集合(allNodes)
|
//该方法用来构建支持无限层级的树形结构,默认顶层父级节点的parentId=0,入参为对象集合(allNodes)
|
||||||
//方法定义
|
//方法定义
|
||||||
|
@ -104,7 +101,7 @@ public static <T> List<T> buildTree(List<T> allNodes, Object rootNodeId){
|
||||||
List<Menu> menuTree = BeanUtils.buildTree(allNodes, 1L);
|
List<Menu> menuTree = BeanUtils.buildTree(allNodes, 1L);
|
||||||
```
|
```
|
||||||
|
|
||||||
* extractDiff 方法
|
* extractDiff 提取对象值变化
|
||||||
```java
|
```java
|
||||||
//该方法用来提取两个model的差异值,入参为两个实体对象oldModel、newModel
|
//该方法用来提取两个model的差异值,入参为两个实体对象oldModel、newModel
|
||||||
//方法定义
|
//方法定义
|
||||||
|
@ -119,7 +116,7 @@ public static String extractDiff(BaseEntity oldModel, BaseEntity newModel, Set<S
|
||||||
String str = BeanUtils.extractDiff(oldModel, newModel, fields);
|
String str = BeanUtils.extractDiff(oldModel, newModel, fields);
|
||||||
```
|
```
|
||||||
|
|
||||||
* collectToList 方法
|
* collectToList 收集某字段值到集合
|
||||||
```java
|
```java
|
||||||
//该方法用来从集合列表中提取指定属性值到新的集合,入参为对象集合(objectList)、IGetter对象(getterFn)
|
//该方法用来从集合列表中提取指定属性值到新的集合,入参为对象集合(objectList)、IGetter对象(getterFn)
|
||||||
//方法定义
|
//方法定义
|
||||||
|
@ -134,7 +131,7 @@ public static <E> List collectToList(List<E> objectList, String getterPropName){
|
||||||
List list = BeanUtils.collectToList(objectList, getterPropName);
|
List list = BeanUtils.collectToList(objectList, getterPropName);
|
||||||
```
|
```
|
||||||
|
|
||||||
* collectIdToList 方法
|
* collectIdToList 收集id字段值到集合
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
public static <E> List collectIdToList(List<E> objectList){...}
|
public static <E> List collectIdToList(List<E> objectList){...}
|
||||||
|
@ -143,22 +140,7 @@ List list = BeanUtils.collectIdToList(objectList);
|
||||||
```
|
```
|
||||||
该方法用来从集合列表中提取Id主键值到新的集合,入参为对象集合(objectList)。
|
该方法用来从集合列表中提取Id主键值到新的集合,入参为对象集合(objectList)。
|
||||||
|
|
||||||
* bindPropValueOfList 方法
|
* convertToFieldName 获取属性名
|
||||||
```java
|
|
||||||
//该方法用来绑定Map中的属性值到集合,入参为ISetter对象、目标集合(fromList)、IGetter对象、Map对象(valueMatchMap)
|
|
||||||
//方法定义
|
|
||||||
public static <T1,T2,R,E> void bindPropValueOfList(ISetter<T1,R> setFieldFn, List<E> fromList, IGetter<T2> getFieldFun, Map valueMatchMap){...}
|
|
||||||
//方法调用示例
|
|
||||||
BeanUtils.bindPropValueOfList(setFieldFn, fromList, getFieldFun, valueMatchMap);
|
|
||||||
|
|
||||||
//该方法用来绑定Map中的属性值到集合,入参为字段名(setterFieldName)、目标集合(fromList)、字段名(getterFieldName)、Map对象(valueMatchMap)
|
|
||||||
//方法定义
|
|
||||||
public static <E> void bindPropValueOfList(String setterFieldName, List<E> fromList, String getterFieldName, Map valueMatchMap){...}
|
|
||||||
//方法调用示例
|
|
||||||
BeanUtils.bindPropValueOfList(setterFieldName, fromList, getterFieldName, valueMatchMap);
|
|
||||||
```
|
|
||||||
|
|
||||||
* convertToFieldName 方法
|
|
||||||
```java
|
```java
|
||||||
//该方法用来转换方法引用为属性名,入参为IGetter对象。
|
//该方法用来转换方法引用为属性名,入参为IGetter对象。
|
||||||
//方法定义
|
//方法定义
|
||||||
|
@ -173,26 +155,30 @@ public static <T,R> String convertToFieldName(ISetter<T,R> fn){...}
|
||||||
String str = BeanUtils.convertToFieldName(fn);
|
String str = BeanUtils.convertToFieldName(fn);
|
||||||
```
|
```
|
||||||
|
|
||||||
* extractAllFields 方法
|
* extract*Fields 提取Field(包含父类中)
|
||||||
```java
|
```java
|
||||||
//方法定义
|
// 提取所有Field
|
||||||
public static List<Field> extractAllFields(Class clazz){...}
|
|
||||||
//方法调用示例
|
|
||||||
List<Field> list = BeanUtils.extractAllFields(Organization.class);
|
List<Field> list = BeanUtils.extractAllFields(Organization.class);
|
||||||
|
// 提取指定注解的Field
|
||||||
|
List<Field> list = BeanUtils.extractFields(Organization.class, MyAnnotation.class);
|
||||||
|
// 提取指定属性名的Field
|
||||||
|
Field field = extractField(Organization.class, "name");
|
||||||
|
```
|
||||||
|
|
||||||
|
* cloneBean 克隆对象
|
||||||
|
```java
|
||||||
|
//方法调用
|
||||||
|
User user2 = BeanUtils.cloneBean(user1);
|
||||||
|
```
|
||||||
|
|
||||||
|
* distinctByKey 基于某字段值去重
|
||||||
|
```java
|
||||||
|
//方法调用
|
||||||
|
List<Dictionary> list = BeanUtils.distinctByKey(dictionaryList, Dictionary::getId);
|
||||||
```
|
```
|
||||||
该方法用来获取类的所有属性(包含父类),入参为类类型(clazz)。
|
|
||||||
|
|
||||||
## ContextHelper(Spring上下文)
|
## ContextHelper(Spring上下文)
|
||||||
|
|
||||||
* setApplicationContext 方法
|
|
||||||
```java
|
|
||||||
//方法定义
|
|
||||||
public void setApplicationContext(ApplicationContext applicationContext){...}
|
|
||||||
//方法调用示例
|
|
||||||
ContextHelper.setApplicationContext(applicationContext);
|
|
||||||
```
|
|
||||||
该方法用来设置ApplicationContext上下文,入参为上下文对象(applicationContext)。
|
|
||||||
|
|
||||||
* getApplicationContext 方法
|
* getApplicationContext 方法
|
||||||
```java
|
```java
|
||||||
//方法定义
|
//方法定义
|
||||||
|
@ -258,7 +244,7 @@ public static final String FORMAT_TIME_HHmm = "HH:mm"; //例:12:30
|
||||||
public static final String FORMAT_TIME_HHmmss = "HH:mm:ss"; //例:12:30:30 表示 12点30分30秒
|
public static final String FORMAT_TIME_HHmmss = "HH:mm:ss"; //例:12:30:30 表示 12点30分30秒
|
||||||
public static final String FORMAT_DATETIME_Y4MDHM = "yyyy-MM-dd HH:mm"; //例:2019-08-16 12:30 表示 2019年8月16日 12点30分
|
public static final String FORMAT_DATETIME_Y4MDHM = "yyyy-MM-dd HH:mm"; //例:2019-08-16 12:30 表示 2019年8月16日 12点30分
|
||||||
public static final String FORMAT_DATETIME_Y4MDHMS = "yyyy-MM-dd HH:mm:ss";//例:2019-08-16 12:30:30 表示 2019年8月16日 12点30分30秒
|
public static final String FORMAT_DATETIME_Y4MDHMS = "yyyy-MM-dd HH:mm:ss";//例:2019-08-16 12:30:30 表示 2019年8月16日 12点30分30秒
|
||||||
protected static final String[] WEEK = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
|
public static final String[] WEEK = new String[]{"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
|
||||||
```
|
```
|
||||||
格式常量表示某种日期时间格式的一个字符串类型的常量,D类中已经内置了如下列表所示的格式常量,
|
格式常量表示某种日期时间格式的一个字符串类型的常量,D类中已经内置了如下列表所示的格式常量,
|
||||||
可以通过类似D.FORMAT_DATE_y2M这样的调用来获取这些常量的内容。
|
可以通过类似D.FORMAT_DATE_y2M这样的调用来获取这些常量的内容。
|
||||||
|
@ -269,7 +255,7 @@ protected static final String[] WEEK = new String[]{"星期日", "星期一", "
|
||||||
public static String now(String format){...}
|
public static String now(String format){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String nowDateStr = D.now("yyyy-MM-dd");
|
String nowDateStr = D.now("yyyy-MM-dd");
|
||||||
System.out.println(nowDateStr);
|
log.debug(nowDateStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 2019-08-20
|
===> 2019-08-20
|
||||||
```
|
```
|
||||||
|
@ -281,7 +267,7 @@ System.out.println(nowDateStr);
|
||||||
public static String toTimestamp(Date date){...}
|
public static String toTimestamp(Date date){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String dateStr = D.toTimestamp(date);
|
String dateStr = D.toTimestamp(date);
|
||||||
System.out.println(dateStr);
|
log.debug(dateStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 190820094202
|
===> 190820094202
|
||||||
```
|
```
|
||||||
|
@ -293,7 +279,7 @@ System.out.println(dateStr);
|
||||||
public static String getMonth(){...}
|
public static String getMonth(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String monthStr = D.getMonth();
|
String monthStr = D.getMonth();
|
||||||
System.out.println(monthStr);
|
log.debug(monthStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 1908
|
===> 1908
|
||||||
```
|
```
|
||||||
|
@ -305,7 +291,7 @@ System.out.println(monthStr);
|
||||||
public static String today(){...}
|
public static String today(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String todayStr = D.today();
|
String todayStr = D.today();
|
||||||
System.out.println(todayStr);
|
log.debug(todayStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 20190820
|
===> 20190820
|
||||||
```
|
```
|
||||||
|
@ -317,7 +303,7 @@ System.out.println(todayStr);
|
||||||
public static Date convert2FormatDate(String datetime, String fmt){...}
|
public static Date convert2FormatDate(String datetime, String fmt){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Date date = D.convert2FormatDate("2019-08-20", "yyyy-MM-dd");
|
Date date = D.convert2FormatDate("2019-08-20", "yyyy-MM-dd");
|
||||||
System.out.println(date);
|
log.debug(date);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Tue Aug 20 00:00:00 CST 2019
|
===> Tue Aug 20 00:00:00 CST 2019
|
||||||
```
|
```
|
||||||
|
@ -329,7 +315,7 @@ System.out.println(date);
|
||||||
public static String convert2FormatString(Date date, String fmt){...}
|
public static String convert2FormatString(Date date, String fmt){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String dateStr = D.convert2FormatString(new Date(), "yyyy-MM-dd");
|
String dateStr = D.convert2FormatString(new Date(), "yyyy-MM-dd");
|
||||||
System.out.println(dateStr);
|
log.debug(dateStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 2019-08-20
|
===> 2019-08-20
|
||||||
```
|
```
|
||||||
|
@ -341,7 +327,7 @@ System.out.println(dateStr);
|
||||||
public static String getDate(Date date, int... daysOffset){...}
|
public static String getDate(Date date, int... daysOffset){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String dateStr = D.getDate(new Date(), 0);
|
String dateStr = D.getDate(new Date(), 0);
|
||||||
System.out.println(dateStr);
|
log.debug(dateStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 2019-08-20
|
===> 2019-08-20
|
||||||
```
|
```
|
||||||
|
@ -353,7 +339,7 @@ System.out.println(dateStr);
|
||||||
public static String getDateTime(Date date, int... daysOffset){...}
|
public static String getDateTime(Date date, int... daysOffset){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String dateTimeStr = D.getDateTime(new Date(), 0);
|
String dateTimeStr = D.getDateTime(new Date(), 0);
|
||||||
System.out.println(dateTimeStr);
|
log.debug(dateTimeStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 2019-08-20 09:53
|
===> 2019-08-20 09:53
|
||||||
```
|
```
|
||||||
|
@ -365,7 +351,7 @@ System.out.println(dateTimeStr);
|
||||||
public static boolean isWorkingTime(){...}
|
public static boolean isWorkingTime(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
boolean isWorkingTime = D.isWorkingTime();
|
boolean isWorkingTime = D.isWorkingTime();
|
||||||
System.out.println(isWorkingTime);
|
log.debug(isWorkingTime);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> true
|
===> true
|
||||||
```
|
```
|
||||||
|
@ -377,7 +363,7 @@ System.out.println(isWorkingTime);
|
||||||
public static String getAmPm(){...}
|
public static String getAmPm(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String timeStr = D.getAmPm();
|
String timeStr = D.getAmPm();
|
||||||
System.out.println(timeStr);
|
log.debug(timeStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 早上
|
===> 早上
|
||||||
```
|
```
|
||||||
|
@ -389,7 +375,7 @@ System.out.println(timeStr);
|
||||||
public static String getYearMonth(){...}
|
public static String getYearMonth(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String yearMonthStr = D.getYearMonth();
|
String yearMonthStr = D.getYearMonth();
|
||||||
System.out.println(yearMonthStr);
|
log.debug(yearMonthStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 1908
|
===> 1908
|
||||||
```
|
```
|
||||||
|
@ -401,7 +387,7 @@ System.out.println(yearMonthStr);
|
||||||
public static String getYearMonthDay(){...}
|
public static String getYearMonthDay(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String yearMonthDayStr = D.getYearMonthDay();
|
String yearMonthDayStr = D.getYearMonthDay();
|
||||||
System.out.println(yearMonthDayStr);
|
log.debug(yearMonthDayStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 190820
|
===> 190820
|
||||||
```
|
```
|
||||||
|
@ -413,7 +399,7 @@ System.out.println(yearMonthDayStr);
|
||||||
public static int getDay(){...}
|
public static int getDay(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
int day = D.getDay();
|
int day = D.getDay();
|
||||||
System.out.println(day);
|
log.debug(day);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 20
|
===> 20
|
||||||
```
|
```
|
||||||
|
@ -425,7 +411,7 @@ System.out.println(day);
|
||||||
public static String getWeek(Date date){...}
|
public static String getWeek(Date date){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String week = D.getWeek(new Date());
|
String week = D.getWeek(new Date());
|
||||||
System.out.println(week);
|
log.debug(week);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 星期三
|
===> 星期三
|
||||||
```
|
```
|
||||||
|
@ -437,7 +423,7 @@ System.out.println(week);
|
||||||
public static Date timeMillis2Date(Long timeMillis){...}
|
public static Date timeMillis2Date(Long timeMillis){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Date date = D.timeMillis2Date(System.currentTimeMillis());
|
Date date = D.timeMillis2Date(System.currentTimeMillis());
|
||||||
System.out.println(date);
|
log.debug(date);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Tue Aug 20 10:06:12 CST 2019
|
===> Tue Aug 20 10:06:12 CST 2019
|
||||||
```
|
```
|
||||||
|
@ -449,7 +435,7 @@ System.out.println(date);
|
||||||
public static Date datetimeString2Date(String value){...}
|
public static Date datetimeString2Date(String value){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Date date = D.datetimeString2Date("2019-08-20 10:11:20");
|
Date date = D.datetimeString2Date("2019-08-20 10:11:20");
|
||||||
System.out.println(date);
|
log.debug(date);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Tue Aug 20 10:11:20 CST 2019
|
===> Tue Aug 20 10:11:20 CST 2019
|
||||||
```
|
```
|
||||||
|
@ -461,7 +447,7 @@ System.out.println(date);
|
||||||
public static Date convert2Date(String date){...}
|
public static Date convert2Date(String date){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Date date = D.convert2Date("2019-08-20");
|
Date date = D.convert2Date("2019-08-20");
|
||||||
System.out.println(date);
|
log.debug(date);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Tue Aug 20 00:00:00 CST 2019
|
===> Tue Aug 20 00:00:00 CST 2019
|
||||||
```
|
```
|
||||||
|
@ -473,7 +459,7 @@ System.out.println(date);
|
||||||
public static Date convert2DateTime(String dateTime, String... dateFormat){...}
|
public static Date convert2DateTime(String dateTime, String... dateFormat){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Date date = D.convert2DateTime("2019-08-20 10:14:20", "yyyy-MM-dd HH:mm:ss");
|
Date date = D.convert2DateTime("2019-08-20 10:14:20", "yyyy-MM-dd HH:mm:ss");
|
||||||
System.out.println(date);
|
log.debug(date);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Tue Aug 20 10:14:20 CST 2019
|
===> Tue Aug 20 10:14:20 CST 2019
|
||||||
```
|
```
|
||||||
|
@ -485,7 +471,7 @@ System.out.println(date);
|
||||||
public static Date fuzzyConvert(String dateString){...}
|
public static Date fuzzyConvert(String dateString){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Date date = D.fuzzyConvert("2019-08-20 10:14:20");
|
Date date = D.fuzzyConvert("2019-08-20 10:14:20");
|
||||||
System.out.println(date);
|
log.debug(date);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Tue Aug 20 10:14:20 CST 2019
|
===> Tue Aug 20 10:14:20 CST 2019
|
||||||
```
|
```
|
||||||
|
@ -499,7 +485,7 @@ System.out.println(date);
|
||||||
public static String encrypt(String input, String... key){...}
|
public static String encrypt(String input, String... key){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String encryptStr = Encryptor.encrypt("123456", "admin");
|
String encryptStr = Encryptor.encrypt("123456", "admin");
|
||||||
System.out.println(encryptStr);
|
log.debug(encryptStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> ZVmTuAFJIjD5PLwkURuvRw==
|
===> ZVmTuAFJIjD5PLwkURuvRw==
|
||||||
```
|
```
|
||||||
|
@ -511,13 +497,13 @@ System.out.println(encryptStr);
|
||||||
public static String decrypt(String input, String... key){...}
|
public static String decrypt(String input, String... key){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String decryptStr = Encryptor.decrypt("ZVmTuAFJIjD5PLwkURuvRw==", "admin");
|
String decryptStr = Encryptor.decrypt("ZVmTuAFJIjD5PLwkURuvRw==", "admin");
|
||||||
System.out.println(decryptStr);
|
log.debug(decryptStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 123456
|
===> 123456
|
||||||
```
|
```
|
||||||
该方法用来解密字符串,入参为需解密字符串(input)、解密秘钥(key)。
|
该方法用来解密字符串,入参为需解密字符串(input)、解密秘钥(key)。
|
||||||
|
|
||||||
## JSON
|
## JSON (扩展自fastjson)
|
||||||
|
|
||||||
* stringify 方法
|
* stringify 方法
|
||||||
```java
|
```java
|
||||||
|
@ -525,7 +511,7 @@ System.out.println(decryptStr);
|
||||||
public static String stringify(Object object){...}
|
public static String stringify(Object object){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = JSON.stringify(new Dictionary());
|
String str = JSON.stringify(new Dictionary());
|
||||||
System.out.println(str);
|
log.debug(str);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> {"editable":false,"parentId":0,"sortId":99,"system":true}
|
===> {"editable":false,"parentId":0,"sortId":99,"system":true}
|
||||||
```
|
```
|
||||||
|
@ -537,7 +523,7 @@ System.out.println(str);
|
||||||
public static Map toMap(String jsonStr){...}
|
public static Map toMap(String jsonStr){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Map map = JSON.toMap("{"editable":false,"parentId":0,"sortId":99,"system":true}");
|
Map map = JSON.toMap("{"editable":false,"parentId":0,"sortId":99,"system":true}");
|
||||||
System.out.println(map);
|
log.debug(map);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> {"system":true,"editable":false,"sortId":99,"parentId":0}
|
===> {"system":true,"editable":false,"sortId":99,"parentId":0}
|
||||||
```
|
```
|
||||||
|
@ -549,7 +535,7 @@ System.out.println(map);
|
||||||
public static LinkedHashMap toLinkedHashMap(String jsonStr){...}
|
public static LinkedHashMap toLinkedHashMap(String jsonStr){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
LinkedHashMap linkedMap = JSON.toLinkedHashMap("{"editable":false,"parentId":0,"sortId":99,"system":true}");
|
LinkedHashMap linkedMap = JSON.toLinkedHashMap("{"editable":false,"parentId":0,"sortId":99,"system":true}");
|
||||||
System.out.println(linkedMap);
|
log.debug(linkedMap);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> {editable=false, parentId=0, sortId=99, system=true}
|
===> {editable=false, parentId=0, sortId=99, system=true}
|
||||||
```
|
```
|
||||||
|
@ -575,7 +561,7 @@ System.out.pringtln(dictionary.getSystem());
|
||||||
public static String get(String key, String... propertiesFileName){...}
|
public static String get(String key, String... propertiesFileName){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String portStr = PropertiesUtils.get("database.port","system.properties");
|
String portStr = PropertiesUtils.get("database.port","system.properties");
|
||||||
System.out.println(portStr);
|
log.debug(portStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 3306
|
===> 3306
|
||||||
```
|
```
|
||||||
|
@ -587,7 +573,7 @@ System.out.println(portStr);
|
||||||
public static Integer getInteger(String key, String... propertiesFileName){...}
|
public static Integer getInteger(String key, String... propertiesFileName){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
Integer portInt = PropertiesUtils.getInteger("database.port","system.properties");
|
Integer portInt = PropertiesUtils.getInteger("database.port","system.properties");
|
||||||
System.out.println(portInt);
|
log.debug(portInt);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 3306
|
===> 3306
|
||||||
```
|
```
|
||||||
|
@ -599,7 +585,7 @@ System.out.println(portInt);
|
||||||
public static boolean getBoolean(String key, String... propertiesFileName){...}
|
public static boolean getBoolean(String key, String... propertiesFileName){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
boolean isOpen = PropertiesUtils.getBoolean("database.open","system.properties");
|
boolean isOpen = PropertiesUtils.getBoolean("database.open","system.properties");
|
||||||
System.out.println(isOpen);
|
log.debug(isOpen);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> true
|
===> true
|
||||||
```
|
```
|
||||||
|
@ -614,7 +600,7 @@ System.out.println(isOpen);
|
||||||
public static String cut(String input){...}
|
public static String cut(String input){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String cutStr = S.cut("ABCDE");
|
String cutStr = S.cut("ABCDE");
|
||||||
System.out.println(cutStr);
|
log.debug(cutStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> ABCDE
|
===> ABCDE
|
||||||
|
|
||||||
|
@ -623,7 +609,7 @@ System.out.println(cutStr);
|
||||||
public static String cut(String input, int cutLength){...}
|
public static String cut(String input, int cutLength){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String cutStr = S.cut("ABCDE", 1);
|
String cutStr = S.cut("ABCDE", 1);
|
||||||
System.out.println(cutStr);
|
log.debug(cutStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> A
|
===> A
|
||||||
```
|
```
|
||||||
|
@ -635,7 +621,7 @@ System.out.println(cutStr);
|
||||||
public static String join(List<String> stringList){...}
|
public static String join(List<String> stringList){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String joinStr = S.join(new ArrayList<String>(){{add("A");add("B");add("C");}});
|
String joinStr = S.join(new ArrayList<String>(){{add("A");add("B");add("C");}});
|
||||||
System.out.println(joinStr);
|
log.debug(joinStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> A,B,C
|
===> A,B,C
|
||||||
|
|
||||||
|
@ -644,7 +630,7 @@ System.out.println(joinStr);
|
||||||
public static String join(String[] stringArray){...}
|
public static String join(String[] stringArray){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String joinStr = S.join(new String[]{"A","B","C"});
|
String joinStr = S.join(new String[]{"A","B","C"});
|
||||||
System.out.println(joinStr);
|
log.debug(joinStr);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> A,B,C
|
===> A,B,C
|
||||||
```
|
```
|
||||||
|
@ -655,7 +641,7 @@ System.out.println(joinStr);
|
||||||
public static String[] split(String joinedStr){...}
|
public static String[] split(String joinedStr){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String[] strArray = S.split("A,B,C");
|
String[] strArray = S.split("A,B,C");
|
||||||
System.out.println(strArray[0]);
|
log.debug(strArray[0]);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> A
|
===> A
|
||||||
```
|
```
|
||||||
|
@ -676,7 +662,7 @@ String[] strArray = S.toStringArray(stringList);
|
||||||
public static String toSnakeCase(String camelCaseStr){...}
|
public static String toSnakeCase(String camelCaseStr){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String userName = S.toSnakeCase("userName");
|
String userName = S.toSnakeCase("userName");
|
||||||
System.out.println(userName);
|
log.debug(userName);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> user_name
|
===> user_name
|
||||||
```
|
```
|
||||||
|
@ -688,7 +674,7 @@ System.out.println(userName);
|
||||||
public static String toLowerCaseCamel(String snakeCaseStr){...}
|
public static String toLowerCaseCamel(String snakeCaseStr){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String userName = S.toLowerCaseCamel("user_name");
|
String userName = S.toLowerCaseCamel("user_name");
|
||||||
System.out.println(userName);
|
log.debug(userName);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> userName
|
===> userName
|
||||||
```
|
```
|
||||||
|
@ -718,7 +704,7 @@ Integer intValue = S.toInt("1");
|
||||||
public static boolean toBoolean(String strValue){...}
|
public static boolean toBoolean(String strValue){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
boolean isTrue = S.toBoolean("1");
|
boolean isTrue = S.toBoolean("1");
|
||||||
System.out.println(isTrue);
|
log.debug(isTrue);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> true
|
===> true
|
||||||
```
|
```
|
||||||
|
@ -730,7 +716,7 @@ System.out.println(isTrue);
|
||||||
public static String removeDuplicateBlank(String input){...}
|
public static String removeDuplicateBlank(String input){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = S.removeDuplicateBlank("A B");
|
String str = S.removeDuplicateBlank("A B");
|
||||||
System.out.println(str);
|
log.debug(str);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> A B
|
===> A B
|
||||||
```
|
```
|
||||||
|
@ -742,7 +728,7 @@ System.out.println(str);
|
||||||
public static String newUuid(){...}
|
public static String newUuid(){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String uuid = S.newUuid();
|
String uuid = S.newUuid();
|
||||||
System.out.println(uuid);
|
log.debug(uuid);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> c8b735798cfe4e0ba897a460d6107b8a
|
===> c8b735798cfe4e0ba897a460d6107b8a
|
||||||
```
|
```
|
||||||
|
@ -754,7 +740,7 @@ System.out.println(uuid);
|
||||||
public static String newRandomNum(int length){...}
|
public static String newRandomNum(int length){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String randomNum = S.newRandomNum(6);
|
String randomNum = S.newRandomNum(6);
|
||||||
System.out.println(randomNum);
|
log.debug(randomNum);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> 513987
|
===> 513987
|
||||||
```
|
```
|
||||||
|
@ -766,7 +752,7 @@ System.out.println(randomNum);
|
||||||
public static String uncapFirst(String input){...}
|
public static String uncapFirst(String input){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = S.uncapFirst("ABC");
|
String str = S.uncapFirst("ABC");
|
||||||
System.out.println(str);
|
log.debug(str);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> aBC
|
===> aBC
|
||||||
```
|
```
|
||||||
|
@ -778,50 +764,12 @@ System.out.println(str);
|
||||||
public static String capFirst(String input){...}
|
public static String capFirst(String input){...}
|
||||||
//方法调用示例
|
//方法调用示例
|
||||||
String str = S.capFirst("abc");
|
String str = S.capFirst("abc");
|
||||||
System.out.println(str);
|
log.debug(str);
|
||||||
//执行结果
|
//执行结果
|
||||||
===> Abc
|
===> Abc
|
||||||
```
|
```
|
||||||
该方法用来将首字母转为大写,入参为字符串(input)。
|
该方法用来将首字母转为大写,入参为字符串(input)。
|
||||||
|
|
||||||
## SqlExecutor(SQL执行)
|
|
||||||
|
|
||||||
* executeQuery 方法
|
|
||||||
```java
|
|
||||||
//方法定义
|
|
||||||
public static <E> List<Map<String,E>> executeQuery(String sql, List<E> params){...}
|
|
||||||
//方法调用示例
|
|
||||||
List list = SqlExecutor.executeQuery(sql, params);
|
|
||||||
```
|
|
||||||
该方法用来执行Select语句,入参为SQL语句(sql)、查询参数(params)。
|
|
||||||
|
|
||||||
* executeQueryAndMergeOneToOneResult 方法
|
|
||||||
```java
|
|
||||||
//方法定义
|
|
||||||
public static <E> Map<String, Object> executeQueryAndMergeOneToOneResult(String sql, List<E> params, String keyName, String valueName){...}
|
|
||||||
//方法调用示例
|
|
||||||
Map map = SqlExecutor.executeQueryAndMergeOneToOneResult(sql, params, keyName, valueName);
|
|
||||||
```
|
|
||||||
该方法用来执行一对一关联查询和合并结果并将结果Map的key转成String类型,入参为SQL语句(sql)、查询参数(params)、字段名(keyName)、字段名(valueName)。
|
|
||||||
|
|
||||||
* executeQueryAndMergeOneToManyResult 方法
|
|
||||||
```java
|
|
||||||
//方法定义
|
|
||||||
public static <E> Map<String, List> executeQueryAndMergeOneToManyResult(String sql, List<E> params, String keyName, String valueName){...}
|
|
||||||
//方法调用示例
|
|
||||||
Map map = SqlExecutor.executeQueryAndMergeOneToManyResult(sql, params, keyName, valueName);
|
|
||||||
```
|
|
||||||
该方法用来执行查询和合并结果并将结果Map的key转成String类型,入参为SQL语句(sql)、查询参数(params)、字段名(keyName)、字段名(valueName)。
|
|
||||||
|
|
||||||
* executeUpdate 方法
|
|
||||||
```java
|
|
||||||
//方法定义
|
|
||||||
public static boolean executeUpdate(String sql, List params){...}
|
|
||||||
//方法调用示例
|
|
||||||
boolean success = SqlExecutor.executeUpdate(sql, params);
|
|
||||||
```
|
|
||||||
该方法用来执行更新操作,入参为SQL语句(sql)、更新参数(params)。
|
|
||||||
|
|
||||||
## V(校验)
|
## V(校验)
|
||||||
|
|
||||||
* isEmpty 方法
|
* isEmpty 方法
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
# 异常处理
|
|
||||||
## DefaultExceptionHandler
|
|
||||||
|
|
||||||
* 默认异常处理,继承自该类并添加@ControllerAdvice注解即可自动支持:
|
|
||||||
* 兼容JSON请求和Html页面请求的Exception异常处理
|
|
||||||
* Entity绑定校验的统一处理(BindException.class, MethodArgumentNotValidException.class)
|
|
||||||
|
|
||||||
* 示例代码
|
|
||||||
~~~java
|
|
||||||
@ControllerAdvice
|
|
||||||
public class GeneralExceptionHandler extends DefaultExceptionHandler{
|
|
||||||
|
|
||||||
}
|
|
||||||
~~~
|
|
|
@ -1,147 +0,0 @@
|
||||||
# 无SQL关联
|
|
||||||
|
|
||||||
> diboot-core支持通过注解实现数据字典关联、单数据表关联、多数据表关联等功能。
|
|
||||||
|
|
||||||
## 如何开始
|
|
||||||
> 您可以创建完一个表相对应的实体类、service接口、mapper等代码后,创建一个VO类来尝试该功能,如:
|
|
||||||
```java
|
|
||||||
package com.example.demo.vo;
|
|
||||||
|
|
||||||
import com.diboot.core.binding.annotation.BindDict;
|
|
||||||
import com.diboot.core.binding.annotation.BindEntity;
|
|
||||||
import com.diboot.core.binding.annotation.BindEntityList;
|
|
||||||
import com.diboot.core.binding.annotation.BindField;
|
|
||||||
import com.example.demo.entity.Demo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DemoVO extends Demo {
|
|
||||||
private static final long serialVersionUID = 5476618743424368148L;
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
:::warning
|
|
||||||
注:@BindDict注解需要依赖dictionary表,初次启动时starter会自动安装。
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 处理方式
|
|
||||||
1. 通过在VO类中添加相关字段,以及对应的关联绑定注解,来定义我们的绑定类型和需要得到的结果以及额外的条件等信息;
|
|
||||||
2. 绑定完成后,我们需要调用**RelationsBinder**类中的相关方法类执行这个绑定关系,我们目前提供了两种方式可供处理:
|
|
||||||
### 自动绑定关联
|
|
||||||
> 该关联会自动将相关信息查询并设置到voList中,适用于对已有的voList做处理,如:
|
|
||||||
```java
|
|
||||||
//List<MyUserVO> voList = ...;
|
|
||||||
RelationsBinder.bind(voList);
|
|
||||||
```
|
|
||||||
### 自动转型并绑定关联
|
|
||||||
> 该关联会自动将vo所继承的父类的实体列表进行绑定并自动转型为voList,适用于对于非voList的实体列表等做处理,如:
|
|
||||||
```java
|
|
||||||
// 查询单表获取Entity集合
|
|
||||||
// List<User> entityList = userService.list(queryWrapper);
|
|
||||||
List<MyUserVO> voList = RelationsBinder.convertAndBind(userList, MyUserVO.class);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 数据字典关联绑定
|
|
||||||
:::tip
|
|
||||||
当表中的字段为数据字典类型的值时,可使用数据字典关联来绑定表字段与数据字典的关联关系。
|
|
||||||
<br>
|
|
||||||
在通过diboot-core的封装后,数据字典关联时开发者不再需要写大量java代码和SQL关联查询,只要使用相关注解即可快速转换值value为标签/名称name。
|
|
||||||
:::
|
|
||||||
* 当您需要将数据字典表中的某类数据映射到实体中的对应字段时,可以使用数据字典关联方式来对此建立关联关系。
|
|
||||||
* 我们提供了**BindDict**注解来进行数据字典关联绑定。
|
|
||||||
* 使用@BindDict注解时需传两个参数,分别是type和field。
|
|
||||||
* type表示关联的数据字典类型;
|
|
||||||
* field表示关联字段。
|
|
||||||
* 示例如下:
|
|
||||||
```java
|
|
||||||
@BindDict(type="USER_STATUS", field = "status")
|
|
||||||
private String statusLabel;
|
|
||||||
```
|
|
||||||
|
|
||||||
## 数据表关联绑定
|
|
||||||
* 数据表关联按照关联表的方式上可分为**单数据表直接关联**与**中间表关联**这两种,中间表关联是一种通过中间表进行多对多的关联处理方案。
|
|
||||||
* 按照得到结果的形式可分为**绑定关联表中对应字段**和**绑定关联表实体**这两种关联方式,前者得到关联表中的目标字段,后者得到关联表的整个实体。
|
|
||||||
* **绑定关联表实体**即支持绑定单个实体,也支持绑定实体列表。
|
|
||||||
* 支持对关联查询添加附加条件。
|
|
||||||
> 直接关联与中间表关联只是传入的参数不同,而绑定表字段与绑定表实体是使用的不同注解。
|
|
||||||
|
|
||||||
|
|
||||||
### 单表关联
|
|
||||||
:::tip
|
|
||||||
当两张表数据之间有直接的依赖关系时,如主外键关联关系,即可使用单表关联。
|
|
||||||
<br>
|
|
||||||
在通过diboot-core的封装后,
|
|
||||||
单表关联时开发者不再需要写大量java代码和SQL查询,只要使用相关注解即可快速绑定它们之间的关联关系。
|
|
||||||
<br>
|
|
||||||
同时关联的实现是拆解关联查询为单表查询,可以更加高效利用数据库缓存和索引,提高查询效率。
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 多表关联
|
|
||||||
:::tip
|
|
||||||
当两张表数据之间是通过第三张表来产生依赖关系时,即可使用多表关联。
|
|
||||||
<br>
|
|
||||||
在通过diboot-core的封装后,多表关联时开发者不再需要写大量java代码和SQL查询,只要使用相关注解即可快速绑定它们之间的关联关系。
|
|
||||||
<br>
|
|
||||||
同时关联的实现是拆解关联查询为单表查询,可以更加高效利用数据库缓存和索引,提高查询效率。
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 绑定目标字段
|
|
||||||
> 绑定字段使用**@BindField**注解进行处理,将得到关联表的目标字段的值。
|
|
||||||
|
|
||||||
* 使用@BindField注解时需传三个参数,分别是entity、field、condition。
|
|
||||||
* entity表示关联实体;
|
|
||||||
* field表示关联表字段名;
|
|
||||||
* condition表示关联条件。
|
|
||||||
|
|
||||||
* 单数据表直接关联 **(单表关联)** 获取目标字段值,用来将数据表中的某个字段映射到实体的对应字段,注解及参数示例如下:
|
|
||||||
```java
|
|
||||||
@BindField(entity=Department.class, field="name", condition="department_id=id AND parent_id>=0")
|
|
||||||
private String deptName;
|
|
||||||
```
|
|
||||||
|
|
||||||
* 通过中间表进行多表关联的级联关联 **(多表关联)** 绑定字段
|
|
||||||
```java
|
|
||||||
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
|
||||||
private String orgName;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 绑定单个实体
|
|
||||||
> 绑定单个实体使用**@BindEntity**注解进行处理,将得到关联表对应的单个实体。
|
|
||||||
> 如果属性类型为VO等非Entity对象类型,将自动转换为您指定的类型再绑定。
|
|
||||||
* 使用@BindEntity注解时需传两个参数,分别是entity和condition。
|
|
||||||
* entity表示关联实体类;
|
|
||||||
* condition表示关联条件。
|
|
||||||
* 单数据表直接关联 **(单表关联)** 获取目标单个实体,注解及参数示例如下:
|
|
||||||
```java
|
|
||||||
@BindEntity(entity = Department.class, condition="department_id=id")
|
|
||||||
private Department department;
|
|
||||||
```
|
|
||||||
* 通过中间表进行多表关联的级联关联 **(多表关联)** 绑定对应单个实体,示例如下:
|
|
||||||
```java
|
|
||||||
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id")
|
|
||||||
private Organization organization;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 绑定实体列表
|
|
||||||
> 绑定实体列表使用**@BindEntityList**注解进行处理,将得到关联表对应的实体列表。
|
|
||||||
> 如果待绑定List中的泛型参数类型为VO等非Entity对象,将自动转换为您指定的类型再绑定。
|
|
||||||
* 使用@BindEntityList注解时需传两个参数,分别是entity和condition。
|
|
||||||
* entity表示关联实体类;
|
|
||||||
* condition表示关联条件。
|
|
||||||
* 单数据表直接关联 **(单表关联)** 获取目标的实体列表,注解及参数示例如下:
|
|
||||||
```java
|
|
||||||
// 关联其他表
|
|
||||||
@BindEntity(entity = Department.class, condition="department_id=id")
|
|
||||||
private List<Department> departmentList;
|
|
||||||
|
|
||||||
// 关联自身,实现无限极分类等
|
|
||||||
@BindEntityList(entity = Department.class, condition = "id=parent_id")
|
|
||||||
private List<Department> children;
|
|
||||||
```
|
|
||||||
* 通过中间表进行多表关联的级联关联 **(多表关联)** 绑定对应的实体列表,示例如下:
|
|
||||||
```java
|
|
||||||
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
|
||||||
private List<Role> roleList;
|
|
||||||
```
|
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
# 无SQL关联绑定
|
||||||
|
|
||||||
|
> diboot-core支持通过注解实现数据字典关联、从表Entity(1-1)及Entity集合(1-n)关联,从表字段(1-1)及字段集合(1-n)关联等实现。
|
||||||
|
> 通过重构查询方式 (拆解关联查询,程序中Join) ,简化开发、提高性能
|
||||||
|
|
||||||
|
## 如何开始
|
||||||
|
> 自动绑定关联的前提是具备表相对应的Entity实体类、service接口、mapper等,您可以创建完之后建一个主表的VO类来尝试该功能,如:
|
||||||
|
```java
|
||||||
|
import com.diboot.core.binding.annotation.*;
|
||||||
|
import com.example.demo.entity.Demo;
|
||||||
|
|
||||||
|
public class DemoVO extends Demo {
|
||||||
|
private static final long serialVersionUID = 5476618743424368148L;
|
||||||
|
// @BindXX注解
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
注:@BindDict注解需要依赖dictionary表,初次启动时diboot-core-starter会自动安装。
|
||||||
|
:::
|
||||||
|
|
||||||
|
## 绑定调用方式
|
||||||
|
1. 通过在VO类中添加相关字段,以及对应的关联绑定注解,来定义我们的绑定类型和需要得到的结果以及额外的条件等信息;
|
||||||
|
2. 注解添加后,调用**Binder**类中的相关方法(*bindRelations)执行这个绑定关系,我们目前提供了两种方式可供处理:
|
||||||
|
### 自动绑定关联
|
||||||
|
> 该关联会自动将相关信息查询并设置到voList中,适用于对已有的voList做处理,如:
|
||||||
|
```java
|
||||||
|
//List<MyUserVO> voList = ...;
|
||||||
|
Binder.bindRelations(voList);
|
||||||
|
```
|
||||||
|
### 自动转型并绑定关联
|
||||||
|
> 该关联会自动将vo所继承的父类的实体列表进行绑定并自动转型为voList,适用于对于非voList的实体列表等做处理,如:
|
||||||
|
```java
|
||||||
|
// 查询单表获取Entity集合
|
||||||
|
// List<User> entityList = userService.list(queryWrapper);
|
||||||
|
// 转型并绑定关联
|
||||||
|
List<MyUserVO> voList = Binder.convertAndBindRelations(userList, MyUserVO.class);
|
||||||
|
```
|
||||||
|
### 通过BaseService接口实现绑定
|
||||||
|
```java
|
||||||
|
// 绑定单个VO对象
|
||||||
|
service.getViewObject()
|
||||||
|
// 绑定多个VO集合
|
||||||
|
service.getViewObjectList()
|
||||||
|
```
|
||||||
|
## 数据字典关联绑定
|
||||||
|
::: tip
|
||||||
|
当表中的字段为数据字典类型的值时,可使用数据字典关联来绑定表字段与数据字典的关联关系。
|
||||||
|
<br>
|
||||||
|
通过@BindDict注解,数据字典关联时无需写大量java代码和SQL关联查询,即可快速转换值字典value值为label。
|
||||||
|
:::
|
||||||
|
* 使用@BindDict注解时需传两个参数,分别是type和field。
|
||||||
|
* type表示关联的数据字典类型;
|
||||||
|
* field表示关联字段。
|
||||||
|
* 示例如下:
|
||||||
|
```java
|
||||||
|
@BindDict(type="USER_STATUS", field = "status")
|
||||||
|
private String statusLabel;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据表关联绑定
|
||||||
|
* 数据表关联按照关联表的方式上可分为**单数据表直接关联**与**中间表关联**这两种,中间表关联是一种通过中间表进行“"1-1"或"多-多"的关联处理方案。
|
||||||
|
* 按照得到结果的形式可分为**绑定关联表中对应字段(集合)**和**绑定关联表实体(集合)** 这两种关联方式,前者得到关联表中的目标字段(集合),后者得到关联表的整个实体(集合)。
|
||||||
|
* 支持对关联查询添加附加条件。
|
||||||
|
* diboot关联的实现是拆解关联查询为单表查询,可以更高效利用数据库缓存和索引,降低死锁概率,提高性能。
|
||||||
|
|
||||||
|
### 绑定从表Entity实体
|
||||||
|
> 绑定单个实体使用**@BindEntity**注解进行处理,将得到关联表对应的单个实体。
|
||||||
|
* 使用@BindEntity注解时需传两个参数,分别是entity和condition。
|
||||||
|
* entity表示关联实体类;
|
||||||
|
* condition表示关联条件。
|
||||||
|
* 主表1-1直接关联从表,获取从表Entity,注解示例如下:
|
||||||
|
```java
|
||||||
|
@BindEntity(entity = Department.class, condition="department_id=id")
|
||||||
|
private Department department;
|
||||||
|
```
|
||||||
|
* 主表1-1通过中间表间接关联从表,获取从表Entity,注解示例如下(condition不同):
|
||||||
|
```java
|
||||||
|
@BindEntity(entity = Organization.class, condition = "this.department_id=department.id AND department.org_id=id")
|
||||||
|
private Organization organization;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 绑定从表Entity实体列表
|
||||||
|
> 绑定实体列表使用**@BindEntityList**注解进行处理,将得到关联表对应的实体列表。
|
||||||
|
* @BindEntityList注解参数与@BindEntity相同。
|
||||||
|
* 主表1-n直接关联从表,获取从表的Entity列表,注解示例如下:
|
||||||
|
```java
|
||||||
|
// 关联其他表
|
||||||
|
@BindEntityList(entity = Department.class, condition="department_id=id")
|
||||||
|
private List<Department> departmentList;
|
||||||
|
|
||||||
|
// 关联自身,实现无限极分类等
|
||||||
|
@BindEntityList(entity = Department.class, condition = "id=parent_id")
|
||||||
|
private List<Department> children;
|
||||||
|
```
|
||||||
|
* 主表1-n通过中间表关联从表,绑定从表的Entity列表,示例如下:
|
||||||
|
```java
|
||||||
|
@BindEntityList(entity = Role.class, condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
|
private List<Role> roleList;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 绑定从表字段
|
||||||
|
> 绑定字段使用**@BindField**注解进行处理,将得到关联表的目标字段的值(集合)。
|
||||||
|
* @BindField 或 @BindFieldList 注解需三个参数,分别是entity、field、condition。
|
||||||
|
* entity表示关联实体;
|
||||||
|
* field表示关联表字段名;
|
||||||
|
* condition表示关联条件。
|
||||||
|
|
||||||
|
* 主表1-1直接关联从表,获取从表字段值,注解示例如下:
|
||||||
|
```java
|
||||||
|
@BindField(entity=Department.class, field="name", condition="department_id=id AND parent_id>=0")
|
||||||
|
private String deptName;
|
||||||
|
```
|
||||||
|
|
||||||
|
* 主表1-1通过中间表关联从表的级联关联,注解示例如下:
|
||||||
|
```java
|
||||||
|
@BindField(entity = Organization.class, field="name", condition="this.department_id=department.id AND department.org_id=id")
|
||||||
|
private String orgName;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 绑定从表字段列表
|
||||||
|
* since v2.1
|
||||||
|
> 绑定实体列表使用**@BindFieldList**注解进行处理,将得到关联表对应的实体列表。
|
||||||
|
* @BindFieldList注解参数同@BindField。
|
||||||
|
* 主表1-n直接关联从表字段集合,注解示例如下:
|
||||||
|
```java
|
||||||
|
// 关联其他表
|
||||||
|
@BindFieldList(entity = Department.class, field="id", condition="department_id=id")
|
||||||
|
private List<Long> departmentIds;
|
||||||
|
|
||||||
|
```
|
||||||
|
* 主表1-n通过中间表关联从表,绑定从表的Entity某字段的列表,示例如下:
|
||||||
|
```java
|
||||||
|
@BindFieldList(entity = Role.class, field="code", condition="this.id=user_role.user_id AND user_role.role_id=id")
|
||||||
|
private List<String> roleCodes;
|
||||||
|
```
|
|
@ -0,0 +1,93 @@
|
||||||
|
# 无SQL跨表查询
|
||||||
|
|
||||||
|
> 用户可自定义相关实体的查询DTO类,添加相应的BindQuery注解,diboot将自动构建QueryWrapper并查询。
|
||||||
|
> 对于字段条件跨表的查询将自动按需构建LEFT JOIN(无该字段条件时不JOIN),让无需手写SQL覆盖大多数场景。
|
||||||
|
|
||||||
|
## 使用方式
|
||||||
|
### 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自动构建QueryWrapper
|
||||||
|
> 构建方式有:
|
||||||
|
##### 方式1. controller中调用super.buildQueryWrapper(entityOrDto) 进行构建
|
||||||
|
~~~java
|
||||||
|
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto);
|
||||||
|
~~~
|
||||||
|
> 该方式基于url非空参数取值构建:
|
||||||
|
> url参数示例: /list?gender=M&realname=张
|
||||||
|
> 将构建为 queryWrapper.eq("gender", "M").like("realname", "张")
|
||||||
|
|
||||||
|
##### 方式2. 直接调用 QueryBuilder.toQueryWrapper(entityOrDto) 进行构建
|
||||||
|
~~~java
|
||||||
|
QueryWrapper<User> queryWrapper = super.buildQueryWrapper(userDto);
|
||||||
|
~~~
|
||||||
|
> 该方式基于dto对象非空值字段构建
|
||||||
|
|
||||||
|
##### 方式3. 明确构建为QueryBuilder.toDynamicJoinQueryWrapper 进行构建
|
||||||
|
~~~java
|
||||||
|
QueryBuilder.toDynamicJoinQueryWrapper(dto).xxx
|
||||||
|
~~~
|
||||||
|
> 该方式支持链式追加查询调用
|
||||||
|
|
||||||
|
|
||||||
|
### 3. 支持动态Join的跨表查询与结果绑定
|
||||||
|
> 动态查询的调用方式有以下两种:
|
||||||
|
##### 方式1. 通过调用BaseService.getEntityList接口
|
||||||
|
~~~java
|
||||||
|
// 调用join关联查询绑定
|
||||||
|
List<Entity> list = xxService.getEntityList(queryWrapper);
|
||||||
|
~~~
|
||||||
|
#### 方式2. 通过Binder调用joinQueryList查询
|
||||||
|
~~~java
|
||||||
|
// 调用join关联查询绑定
|
||||||
|
List<Entity> list = Binder.joinQueryList(queryWrapper, Department.class);
|
||||||
|
~~~
|
||||||
|
##### 方式3. 通过DynamicJoinQueryWrapper链式调用查询
|
||||||
|
~~~java
|
||||||
|
QueryBuilder.toDynamicJoinQueryWrapper(dto).queryList(Department.class);
|
||||||
|
~~~
|
||||||
|
|
||||||
|
|
||||||
|
**绑定调用将自动按需(有从表的查询字段时才Join)构建类似如下动态SQL并绑定结果:**
|
||||||
|
|
||||||
|
* 无跨表字段的查询:
|
||||||
|
> SELECT self.* FROM user self WHERE self.gender=? AND self.is_deleted=0
|
||||||
|
|
||||||
|
* 有跨表字段的查询:
|
||||||
|
> SELECT self.* FROM user self
|
||||||
|
LEFT OUTER JOIN organization r1 ON self.org_id=r1.id AND r1.is_deleted=0
|
||||||
|
WHERE self.gender=? AND (r1.name LIKE ?) AND self.is_deleted=0
|
||||||
|
|
||||||
|
基于最佳性能最少Join的原则,无SQL跨表查询仅查询主表数据。如果需要其他关联绑定对象,可调用Binder实现。
|
||||||
|
|
||||||
|
### 4. 数据权限控制
|
||||||
|
> 某些场景下搜索查询需要绑定一些强制条件,用于数据权限控制,如只能查询本部门的数据。
|
||||||
|
##### 1. 在需要控制的字段上添加@DataAccessCheckpoint注解,指定CheckpointType。
|
||||||
|
示例代码:
|
||||||
|
~~~java
|
||||||
|
// 数据权限检查点
|
||||||
|
@DataAccessCheckpoint(type = CheckpointType.ORG)
|
||||||
|
private Long orgId;
|
||||||
|
~~~
|
||||||
|
##### 2. 实现DataAccessCheckInterface接口,返回对应CheckpointType的合法ID集合
|
||||||
|
~~~java
|
||||||
|
public class DataAccessCheckImpl implements DataAccessCheckInterface {
|
||||||
|
@Override
|
||||||
|
public List<Long> getAccessibleIds(CheckpointType type) {
|
||||||
|
// 返回对应检查点的合法ID
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
通过QueryBuilder构建时,将自动追加IN(合法ID)条件。具体可参考: diboot-IAM组件。
|
|
@ -1,33 +0,0 @@
|
||||||
# 查询条件DTO
|
|
||||||
|
|
||||||
> 用户可自定义相关实体的DTO类,将其在Controller类的相关方法中,自动转换为QueryWrapper进行条件查询时使用:
|
|
||||||
|
|
||||||
## 创建DTO
|
|
||||||
```java
|
|
||||||
public class UserDTO{
|
|
||||||
// 无@BindQuery注解默认会映射为=条件
|
|
||||||
private Long gender;
|
|
||||||
|
|
||||||
// 有注解,映射为注解指定条件
|
|
||||||
@BindQuery(comparison = Comparison.LIKE)
|
|
||||||
private String realname;
|
|
||||||
|
|
||||||
//... getter, setter方法等
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 自动转换
|
|
||||||
> Controller类中对相关路由的查询条件可进行如下绑定:
|
|
||||||
```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<User> queryWrapper = super.buildQueryWrapper(userDto, request);
|
|
||||||
//... 查询list
|
|
||||||
return JsonResult.OK(list);
|
|
||||||
}
|
|
||||||
```
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
# 起步简介
|
||||||
|
|
||||||
|
## diboot-core 简介
|
||||||
|
|
||||||
|
> diboot-core 是 diboot 2.x版本的核心基础框架,基于Spring Boot、Mybatis-plus封装,实现基础代码的简化及高效开发。
|
||||||
|
> 使用diboot-core可以更加简单快捷地创建web应用,您之前的诸多代码将被极大简化,更易维护。
|
||||||
|
> 同时搭档[diboot-devtools](../diboot-devtools/介绍.md),让您彻底摆脱常规SQL与CRUD。
|
||||||
|
|
||||||
|
高效精简内核,重构查询方式,简化开发、提高性能,主要实现:
|
||||||
|
1. 单表CRUD无SQL
|
||||||
|
2. 关联查询绑定无SQL(注解自动绑定)
|
||||||
|
3. 数据字典无SQL(注解自动绑定)
|
||||||
|
4. 跨表Join查询无SQL(QueryWrapper自动构建与查询)
|
||||||
|
5. BaseService扩展增强,支持常规的单表及关联开发场景接口
|
||||||
|
6. 其他常用Service接口、工具类的最佳实践封装
|
||||||
|
7. 提供[diboot-core-starter](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example),简化diboot-core的初始化配置(自动配置、自动创建数据字典表)
|
||||||
|
|
||||||
|
## diboot-core 使用步骤
|
||||||
|
### 1. 引入依赖
|
||||||
|
Gradle:
|
||||||
|
~~~gradle
|
||||||
|
compile("com.diboot:diboot-core-spring-boot-starter:{latestVersion}")
|
||||||
|
~~~
|
||||||
|
或Maven
|
||||||
|
~~~xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.diboot</groupId>
|
||||||
|
<artifactId>diboot-core-spring-boot-starter</artifactId>
|
||||||
|
<version>{latestVersion}</version>
|
||||||
|
</dependency>
|
||||||
|
~~~
|
||||||
|
|
||||||
|
### 2. 配置参数(数据源)
|
||||||
|
* 以Mysql为例,配置数据源如下:
|
||||||
|
~~~properties
|
||||||
|
#datasource config
|
||||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/diboot_example?characterEncoding=utf8&serverTimezone=GMT%2B8
|
||||||
|
spring.datasource.username=diboot
|
||||||
|
spring.datasource.password=123456
|
||||||
|
spring.datasource.hikari.maximum-pool-size=5
|
||||||
|
spring.datasource.hikari.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
~~~
|
||||||
|
> 注:@BindDict注解需要依赖dictionary表,diboot-core starter 初次启动时会自动创建该表。
|
||||||
|
|
||||||
|
* diboot-core-spring-boot-starter的可选参数配置:
|
||||||
|
~~~properties
|
||||||
|
# 是否初始化sql,默认true,初始化之后可以改为false关闭
|
||||||
|
diboot.core.init-sql=false
|
||||||
|
~~~
|
||||||
|
|
||||||
|
### 3. 参考样例 - [diboot-core-example](https://github.com/dibo-software/diboot-v2-example/tree/master/diboot-core-example)
|
||||||
|
|
||||||
|
> diboot-core的最佳实践:建议自定义自己的Base类,避免直接继承core中的Base类,便于后期扩展。
|
||||||
|
启用devtools,可一键生成初始Base类代码到本地。
|
||||||
|
|
||||||
|
## 支持数据库
|
||||||
|
MySQL、MariaDB、PostgreSQL、ORACLE、SQLServer
|
||||||
|
|
||||||
|
## 相关依赖
|
||||||
|
::: tip
|
||||||
|
以下依赖在引入diboot-core-starter依赖中,可以不再引入。
|
||||||
|
:::
|
||||||
|
* **javax.servlet-api**(javax.servlet:javax.servlet-api:4.x)
|
||||||
|
* **spring-boot-starter-web**(org.springframework.boot:spring-boot-starter-web:2.3.x.RELEASE)
|
||||||
|
* **mybatis-plus-boot-starter**(com.baomidou:mybatis-plus-boot-starter:3.3.x)
|
||||||
|
* **commons-io**(commons-io:commons-io:2.6)
|
||||||
|
* **commons-lang3**(org.apache.commons:commons-lang3:3.10)
|
||||||
|
* **fastjson**(com.alibaba:fastjson:1.2.x)
|
||||||
|
|
||||||
|
::: tip
|
||||||
|
需要额外添加的jar
|
||||||
|
:::
|
||||||
|
* **数据库驱动包** (如 mysql:mysql-connector-java:8.0.18)
|
||||||
|
|
||||||
|
> 使用过程中遇到问题,可加群交流。
|
|
@ -0,0 +1,22 @@
|
||||||
|
# 设计理念
|
||||||
|
|
||||||
|
## diboot 的诞生背景
|
||||||
|
|
||||||
|
> 众多开发团队现状: 效率低、质量差、可维护性差,我们也常听到开发者抱怨每天CRUD、工作就是搬砖。
|
||||||
|
|
||||||
|
> 很多团队雇不起好的架构师,即使有也大概率不会给他机会去做打地基的事情。
|
||||||
|
|
||||||
|
> diboot正在尝试做些改变 :
|
||||||
|
|
||||||
|
## diboot 的设计理念
|
||||||
|
|
||||||
|
* Web系统开发需要一个普适的基础框架,把复杂的问题简单化,最好还能做到更佳性能,规避常见的坑
|
||||||
|
* 程序员很难被替代,但程序员应该聚焦于数据结构设计、业务实现、难点解决,重复CRUD没啥长进
|
||||||
|
* CRUD类重复性的工作是可以被自动化甚至被省掉的,包括单表及常规的关联场景相关场景
|
||||||
|
|
||||||
|
## diboot 主要技术栈
|
||||||
|
|
||||||
|
* 后端Java+关系数据库,跟紧Spring Boot
|
||||||
|
* ORM站队Mybatis,通用Mapper框架选择Mybatis-plus
|
||||||
|
* 后端权限使用Shiro+JWT
|
||||||
|
* 前后分离,前端选型Vue
|
Binary file not shown.
After Width: | Height: | Size: 106 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue