# 表格列排序
# 概述
在表格分页查询中,如果需要按指定列进行排序,则需要后端进行代码支持,实现真分页排序。
# 实现
前端发起表格查询请求时,传递需要进行正序或倒序的字段。后端进行接收处理,排序功能主要分为:注解排序和自定义排序.
# 注解排序
注解排序使用Mybatis-Plus自带的分页Page对象中的orders属性来实现,所以service排序方法中参数必须有Page对象和有实现了PageEnity或者BaseEntity的实体入参。
# Order 注解
Order注解作用于需要排序的方法上,注解详情如下:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Order {
# TableOrderProperty 注解
TableOrderProperty注解作用于查询dto实体类上,用于添加字段前缀,添加实体映射,注解详情如下:
/**
* @Description: TODO
* @author: whh
* @Date: 2023-05-17 09:56
*/
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface TableOrderProperty {
/**
*@Description:查询sql时,所查询字段的前缀。
* @return: java.lang.String
*@auth:whh
*@date 2023/5/17
**/
String prefix()default "";
/**
*@Description:映射的实体,配置以后可以根据实体去匹配,有些dto中不一定存在排序字段,可以通过实体映射去匹配
* @return: java.lang.Class
*@auth:whh
*@date 2023/5/19
**/
Class entity() default String.class;
}
# OrderProperty 注解
OrderProperty 最小粒度的注解,作用于需要排序的字段上,用于添加字段前缀,匹配数据库表名称等,注解详情如下:
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface OrderProperty {
/**
*@Description:对应数据库表中的字段名称例如:person_code
* @return: java.lang.String
*@auth:whh
*@date 2023/5/17
**/
String value() default "";
/**
*@Description:查询sql时,自定义数据表的别名,如果在字段中设置了前缀,TableOrderProperty中设置的前缀对于当前字段无效
* @return: java.lang.String
*@auth:whh
*@date 2023/5/17
**/
String prefix()default "";
/**
*@Description:是否中文排序
* @return: null
*@auth:whh
*@date 2023/5/22
**/
boolean isChineseSort() default false;
}
实现示例:实现部门表格排序。
实现了PageEntity的dto中添加注解如下:
@Data
@TableOrderProperty(prefix = "dept",entity = HosDepartment.class)
public class HosDepartmentDTO extends PageEntity {
@ApiModelProperty(value = "部门代码")
@OrderProperty("code")
private String code;
@ApiModelProperty(value = "部门名称")
@OrderProperty(value = "name",isChineseSort = true)
private String name;
@ApiModelProperty(hidden = true)
@OrderProperty(value = "name",prefix = "orgac",isChineseSort = true)
private String orgacName;
......
}
service方法添加Order注解
@Override
@Order
public IPage<HosDepartmentVO> selectAllPage(Page<HosDepartment> page, HosDepartmentDTO hosDepartmentDTO) {
}
字段匹配逻辑规则:
1.通过Order注解在执行目标方法之前,找到实现了 PageEntity或者 BaseEntity的入参;
2.在当前实体中查找标记了OrderProperty注解的字段,如果存在标记了OrderProperty注解的字段,取注解value,判断是否存在前缀如果存在前缀将前缀拼上,判断是否是中文排序,如果是中文排序将中文排序拼上
3.如果未设前缀且实体添加tableOrderProperty注解,判断TableOrderProperty中是否存在前缀,如果存在前缀将前缀拼上.
4.如果当前实体中没有标记了OrderProperty注解的字段,查找是否存在标记了TableField注解的字段,如果存在取 注解value;
5.重复3步骤
6.如果当前实体字段中没有OrderProperty和TableField注解,且实体存在TableOrderProperty注解,TableOrderProperty映射了实体去映射实体中查找,
重复执行类似步骤2,3,4,5的方法,需注意的是映射实体中使用前缀时,会去目标实体中查找TableOrderProperty注解上的前缀
7.如果不存在映射实体或者映射实体中未找到标注OrderProperty和TableField注解的字段,则在目标类中查找是否存在匹配的数据字段,如果存在取实体字段名称重复步骤3
8.如果步骤7也未找到排序字段,则去映射实体中去查找,映射实体字段中去查找,如果存在取映射实体字段名称重复步骤3
9.将匹配处理好的字段放入到page对象中。
# 自定义排序
# 完全自定义
实现示例:查询系统参数,支持配置编码排序。
分页mapper接口及实现sql如下:
IPage<Config> selectPage(Page<Config> page, @Param("config") ConfigDTO config);
select * from hos_sys_config hsc where 1=1
<if test="config.name!=null and config.name!=''">
and hsc.name like CONCAT('%',CONCAT(#{config.name},'%'))
</if>
<if test="config.code!=null and config.code!=''">
and hsc.code like CONCAT('%',CONCAT(#{config.code},'%'))
</if>
order by hsc.create_time asc
- 将查询入参
ConfigDTO
继承PageEntity
分页参数实体。
public class ConfigDTO extends PageEntity {
@ApiModelProperty(value="配置编码")
private String code;
@ApiModelProperty(value="配置名称")
private String name;
}
@Data
public class PageEntity {
@ApiModelProperty(value="页数")
private int current;
@ApiModelProperty(value="每页条数")
private int size;
@ApiModelProperty(value="hisui页数")
private int page;
@ApiModelProperty(value="hisui每页条数")
private int rows;
@ApiModelProperty(value="排序字段")
private String sort;
@ApiModelProperty(value="排序类型")
private String order;
/**
* 兼容HisUI入参
* @return int
*/
public int getCurrent() {
if (this.current == 0 && this.page > 0) {
return this.page;
}
return this.current;
}
/**
* 兼容HisUI入参
* @return int
*/
public int getSize() {
if (this.size == 0 && this.rows > 0) {
return this.rows;
}
return this.size;
}
}
- 处理排序字段,放入分页page对象中。
public class ConfigServiceImpl extends BaseServiceImpl<ConfigMapper, Config> implements ConfigService {
@Override
public IPage<Config> selectPage(Page<Config> page, ConfigDTO dto) {
// 排序字段
String sort = dto.getSort();
// 排序类型
String order = dto.getOrder();
List<OrderItem> orderItemList = new ArrayList<>();
if (StringUtil.isNotBlank(sort) && StringUtil.isNotBlank(order)) {
// 入参与sql字段不一致需手动处理
sort = "hsc."+sort;
if (CommonUtils.equals("asc", order)) {
orderItemList.add(new OrderItem(sort, true));
}
if (CommonUtils.equals("desc", order)) {
orderItemList.add(new OrderItem(sort, false));
}
if (orderItemList.size() > 0) {
page.addOrder(orderItemList);
}
}
return this.baseMapper.selectPage(page, dto);
}
}
原sql中如果已有order by,加入的排序字段会插入到最前方。示例如下(按名称name倒序):
select * from hos_sys_config hsc where 1=1 order by hsc.name desc,hsc.create_time asc
注意:当存在传入字段与sql表字段不一致时,需要在代码中对字段进行转换。
1、传入字段存在驼峰。例如:userName ---> user_name
2、查询sql中使用了表别名。例如:code ---> hsc.code
# 通过工具类处理
实现示例:菜单管理排序。
实体类
@Data
@TableOrderProperty(prefix = "tr",entity = Resource.class)
public class ResourceDTO extends PageEntity {
/**
* 父资源id
*/
private String parentId;
/**
* 资源编码
*/
@OrderProperty("code")
private String code;
/**
* 资源名称
*/
@OrderProperty(value = "name",isChineseSort = true)
private String name;
@ApiModelProperty(hidden = true)
private String orderSql;
}
service 方法
@Override
public List<ResourceVO> listResources(ResourceDTO resourceDTO) {
// 全部资源树。
String sql = SqlOrderUtil.orderSql(resourceDTO, driverClassName, "tr");
resourceDTO.setOrderSql(sql);
}
工具类
/**
*@Description:设置排序
* @param data:查询实体
* @param driverClassName:yml配置的driverClass
* @param prefix:前缀
* @return: 处理好的sql
*@auth:whh
*@date 2023/5/17
**/
public static <E> String orderSql(E data,String driverClassName,String prefix){
....
}
字段匹配逻辑规则与注解处理直接基本一致,需要注意的是,使用工具类时,TableOrderProperty上的前缀无效,需手动传入前缀。