我们现在设计有如下两张表(其中,学生表位班级表的子表)
班级表(sys_class):
属性:id name head_teacher ...
学生表(sys_student)
属性:id class_id name gender ...
此时,我们有某个需求,需要查询学生,并且需要学生的班级名称。
我们一般的做法有以下几种:
(1). 使用sql的join来实现
select t1.*,t2.name as class_name from sys_student t1 left join sys_class t2 on t1.class_id= t2.id
join的本质还是创建了虚拟表,对结果做笛卡儿积。这是非常恐怖的,笛卡儿积会导致数据成倍增长,占用大量的资源,导致大幅降低数据库的吞吐量。
(2). 使用程序来实现关联 做法是先查询全部学生,再查询全部班级,使用两层for循环或者是使用map缓存班级id和班级名称的对应关系。总之,该方法所有逻辑处理均在代码中完成,数据库只提供基础的查询功能。
数据库是维持系统稳定的重要基础设施,大多数情况下,此方法比(1)中的方法要更优秀的,只需做简单的数据库io,这样数据库系统就能用节省下来的资源去支持更多的并发。缺点也很明显,相比第一种方式,需要编写代码来实现逻辑,尤其是复杂的系统,代码量可能更大。
笔者使用的是第三种方法,现在大多数的ORM框架都默认实现了单表的查询,在此基础上,笔者选择方法2,但是基于aop来实现关系字段的转译。
代码如下
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DictS.class)
public @interface Dict {
String dicTable(); //字典表
String dicText(); //字典表中需要翻译到的text
String dicColumn(); //字典表的列
String targetColumnName() default ""; //新增的列名,默认为{code}加上_text
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DictS {
Dict[] value();
}
/**
* @Description 注解翻译字典
* @since 2020-12-25
*/
@Aspect
@Component
@Slf4j
public class TranslateDictAspect {
@Resource
CommonApiService commonApiService;
@Resource
ObjectMapper objectMapper;
// 定义切点Pointcut
@Pointcut("execution(public * com.trusted.software..*.*Controller.*(..))")
public void executeService() {
}
@Around("executeService()")
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
MethodSignature signature = (MethodSignature) pjp.getSignature();
UnUseDict unUseDict = signature.getMethod().getAnnotation(UnUseDict.class);
if(unUseDict!=null){
return pjp.proceed();
}
long time1=System.currentTimeMillis();
//执行controller
Object result = pjp.proceed();
long time2=System.currentTimeMillis();
log.debug("controller 耗时:"+(time2-time1)+"ms");
long start=System.currentTimeMillis();
//翻译controller返回结果
try {
this.translate(result);
} catch (JsonProcessingException | JSONException e) {
//同时捕获jackson,fastjson 转换异常
//处理失败,静默原样返回
log.debug("非json格式数据->",e);
}
long end=System.currentTimeMillis();
log.debug("翻译 耗时"+(end-start)+"ms");
return result;
}
/**
* TODO 批量转换
* @param result
* @throws IllegalAccessException
*/
private void translate(Object result) throws JsonProcessingException {
if (result instanceof R) {
Object data = ((R) result).getData();
//处理集合
if(data instanceof Collection) {
//这里处理一层集合
Collection collection = (Collection)data;
List<JSONObject> resultList = new ArrayList<>();
for (Object obj : collection) {
JSONObject item = handleItem(obj);
resultList.add(item);
}
//数据回填
((R)result).setData(resultList);
}else{
//分页数据
if(data instanceof IPage){
List records = ((IPage) data).getRecords();
List<JSONObject> resultList = new ArrayList<>();
for (Object obj : records) {
JSONObject item = handleItem(obj);
resultList.add(item);
}
//数据回填
((IPage) data).setRecords(resultList);
}else {
if(null!=data) {
//非分页单条数据
JSONObject item = handleItem(data);
//数据回填
((R) result).setData(item);
}
}
}
}
}
public JSONObject handleItem(Object obj) throws JsonProcessingException {
String json="{}";
//使用ObjectMapper处理一遍-时间格式化问题
json = objectMapper.writeValueAsString(obj);
JSONObject item = JSONObject.parseObject(json);
Class<?> clazz = obj.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.isAnnotationPresent(Dict.class)) {
String fieldName = declaredField.getName();
declaredField.setAccessible(true);
Object value = null;
try {
value = declaredField.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Dict dict = declaredField.getAnnotation(Dict.class);
dicItem(dict,value,item,fieldName);
}else if (declaredField.isAnnotationPresent(DictS.class)){
String fieldName = declaredField.getName();
DictS dictS = declaredField.getAnnotation(DictS.class);
Dict[] dicArr = dictS.value();
for (Dict dict:dicArr) {
declaredField.setAccessible(true);
Object value = null;
try {
value = declaredField.get(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
dicItem(dict,value,item,fieldName);
}
}
}
return item;
}
public void dicItem(Dict dict,Object value,JSONObject item,String fieldName){
String table = dict.dicTable();
String text = dict.dicText();
String column = dict.dicColumn();
String targetColumnName = dict.targetColumnName();
//此处做了reids缓存
DictModel dictModel = commonApiService.queryTableDictTextByKey(table, text, column, value);
if (dictModel!=null) {
String translatedText = dictModel.getText();
if(!StrUtil.isEmpty(targetColumnName)) {
item.put(targetColumnName, translatedText);
}else{
item.put(fieldName + "_text", translatedText);
}
}
}
}
这样,我们在业务系统中,就可以只通过注解@Dict的方式,减少大量的代码。配合上aop,就实现了外键的转译。在实际开发中,还可以结合sql,查询vo,对vo再进行转译,大量降低子查询,join查询,使用起来是比较灵活的。
本文由 转啊转 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2023/06/13 15:29
这个思路很好,点赞学习 已经用到项目里了👍