需求:
有时候我们在查询mapper层时,有时候可能由于入参数据过大或者查询的范围较大,导致查询性能较慢,此时 我们需要将原本的查询按照一定规则将查询范围进行切面,然后分片查询,最后将查询结果进行组装合并
1,自定义注解
import com.taia.yms.aop.reponse.inter.MapperRequestSlicesInterface;
import java.lang.annotation.*;
/**
* 主要针对 查询 mapper 时,使用一定规则进行切片后,按照指定并发个数进行mapper查询,然后再汇总结果
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MapperRequestSlices {
/**
* 指定执行规则的方法,默认方法为:asyncExecute
* @return
*/
String method() default "asyncRequestExecute";
Class<? extends MapperRequestSlicesInterface> operation();
}
2,定义切片规则接口
/**
* 主要针对mapper层查询,需要按照自定义规则 进行分片并发查询,提升效率
* @param <T>
*/
public interface MapperRequestSlicesInterface<R,T> {
R asyncRequestExecute(Object request);
}
3,编写核心切面处理类
import com.taia.yms.aop.reponse.inter.MapperRequestSlicesInterface;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Aspect
@Slf4j
public class MapperRequestSlicesAspect {
@Pointcut("execution(* com.taia.yms.mapper.*.*(..)) && @annotation(com.taia.yms.aop.reponse.MapperRequestSlices)")
private void pointCut() {
//方法为空,仅做签名
}
//对切点方法进行前置增强,就是在调用切点方法前进行做一些必要的操作,这就成为增强
@Around("pointCut()")
public Object getRes(ProceedingJoinPoint joinPoint) throws Throwable {
// 获取被拦截的方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取被拦截的方法
Method method = signature.getMethod();
Object[] objects = joinPoint.getArgs();
Object target = joinPoint.getTarget();
Class<?> returnType = method.getReturnType();
//针对 返回值是集合的场景,便于后期切片后汇总
if(!returnType.getTypeName().equalsIgnoreCase(List.class.getTypeName())){
return joinPoint.proceed();
}
MapperRequestSlices annotation = method.getAnnotation(MapperRequestSlices.class);
// 查找并获取注解
try{
// 读取注解的属性
Class<? extends MapperRequestSlicesInterface> operation = annotation.operation();
MapperRequestSlicesInterface operationInstance = operation.getDeclaredConstructor().newInstance();
String methoded = annotation.method();
Method operationMethod = operation.getDeclaredMethod(methoded, Object.class);
Object obj = operationMethod.invoke(operationInstance, objects);
//如果切分的结果只有一个,那么还是按照原有的查询来
if(!(obj instanceof List && obj != null)){
return joinPoint.proceed();
}
List<Object> result =((List<?>) obj).stream().flatMap(v -> {
try {
return ((List<Object>)(method.invoke(target, v))).stream();
} catch (Exception e) {
log.error("类[{}]的方法[{}]执行失败,报错:{}",target.getClass().getName(),method.getName(),e.getMessage());
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
return result;
}catch (Throwable e){
log.error("类[{}]的方法[{}]执行失败,报错:{}",annotation.operation().getName(),annotation.method(),e.getMessage());
return joinPoint.proceed();
}
}
}
4,编写切片规则实现类
import com.taia.yms.aop.reponse.inter.MapperRequestSlicesInterface;
import com.taia.yms.entity.po.QualityViewPo;
import com.taia.yms.entity.requestbody.SummarySearchRequestBody;
import com.taia.yms.entity.vo.DataTypeSlice;
import com.taia.yms.entity.vo.DataTypeVo;
import com.taia.yms.enums.RangeTypeEnum;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import java.util.List;
import java.util.stream.Collectors;
public class DataByTime2ReqSlice implements MapperRequestSlicesInterface<List<SummarySearchRequestBody>,List<QualityViewPo>> {
@Override
public List<SummarySearchRequestBody> asyncRequestExecute(Object request) {
if(request == null){
return null;
}
SummarySearchRequestBody requestBody = (SummarySearchRequestBody) request;
//按 天统计不考虑
if(RangeTypeEnum.DAY.getType().equalsIgnoreCase(requestBody.getRangeType())){
return null;
}
DataTypeVo dataTypeVo = requestBody.getDataTypeVo();
if(dataTypeVo == null || CollectionUtils.isEmpty(dataTypeVo.getDataTypeSliceList())){
return null;
}
List<DataTypeSlice> dataTypeSliceList = dataTypeVo.getDataTypeSliceList();
dataTypeSliceList.stream().sorted();
List<SummarySearchRequestBody> bodyList = dataTypeSliceList.stream().map(v -> {
SummarySearchRequestBody summarySearchRequestBody = new SummarySearchRequestBody();
BeanUtils.copyProperties(requestBody, summarySearchRequestBody);
summarySearchRequestBody.setStartDate(v.getStartTime());
summarySearchRequestBody.setEndDate(v.getEndTime());
return summarySearchRequestBody;
}).collect(Collectors.toList());
return bodyList;
}
}
5,编写相关对象类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @ClassName QualityViewPo
* Date 2023/4/25 11:47
* Version 1.0
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class QualityViewPo {
private Integer countNum;
private String dateTime;
private Integer successFilesNum;
private Integer failFilesNum;
private Integer loadingFilesNum;
private Integer qualityScore;
private String fabProductVersion;
private String station;
}
import com.taia.yms.entity.ExportPageReqBody;
import com.taia.yms.entity.vo.DataTypeVo;
import com.taia.yms.validgroups.summarysearch.DataProgressSummaryGroup;
import com.taia.yms.validgroups.summarysearch.TodayBoardGroup;
import com.taia.yms.validgroups.summarysearch.TrendsBoardGroup;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.sql.Timestamp;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class SummarySearchRequestBody extends ExportPageReqBody {
/**时间区间 day/week/month*/
@Valid
@NotEmpty(message = "时间范围为空", groups = {DataProgressSummaryGroup.class, TrendsBoardGroup.class})
@Pattern(regexp = "day|week|month|custom|year|all",message = "时间范围不符合正则", groups = {DataProgressSummaryGroup.class, TrendsBoardGroup.class})
private String rangeType;
/**开始时间*/
@NotNull(message = "开始时间为空", groups = {DataProgressSummaryGroup.class, TrendsBoardGroup.class})
private Timestamp startDate;
/**结束时间*/
@NotNull(message = "结束时间为空", groups = {DataProgressSummaryGroup.class, TrendsBoardGroup.class})
private Timestamp endDate;
/**数据类型 WAT/INLINE/CP-BIN/DEFECT*/
@Valid
@NotNull(message = "数据类型为空", groups = {DataProgressSummaryGroup.class, TodayBoardGroup.class, TrendsBoardGroup.class})
private List<@NotEmpty(message = "数据类型为空", groups = {DataProgressSummaryGroup.class, TodayBoardGroup.class, TrendsBoardGroup.class}) String> stations;
/**产品数据 */
//@NotNull(message = "产品数据为空", groups = {DataProgressSummaryGroup.class, TodayBoardGroup.class, TrendsBoardGroup.class})
private List<String> fabProductVersions;
private DataTypeVo dataTypeVo;
}
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @ClassName ExportPageReqBody
* 导出 和 分页数据
**/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class ExportPageReqBody {
/**页码*/
@ApiModelProperty(example = "1")
private Integer pageNum = 1;
/**页面大小*/
@ApiModelProperty(example = "10")
private Integer pageSize = 10;
/**1-导出excel, 0-导出CSV*/
private String isExcel;
/**1-只导出表头,0或空-导出表头和数据*/
private String isEmpty;
/**1-配置数据, 0或空-待添加配置数据*/
private String isConfig;
/**选择导出,有值时只导出选中的id*/
private List<Long> selectedIds;
/**当前登录用户的userId*/
private String userNo;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class DataTypeVo {
private List<String> timeTable;
private List<DataTypeSlice> dataTypeSliceList;
}
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.sql.Timestamp;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class DataTypeSlice {
private Timestamp startTime;
private Timestamp endTime;
}