电子商务网站建设人才调研,电商网站建设实训要求,上海网站怎么备案,最火的营销方式以下是一个比较常见通用的一个查询并且为单表查询#xff0c;在开发初期#xff0c;或者项目不是很复杂的时候#xff0c;或者一开始项目框架就规划好的情况下#xff0c;通常我们都会封装。
在我们的项目中#xff0c;这部分代码其实是自动生成的#xff0c;足以满足大…以下是一个比较常见通用的一个查询并且为单表查询在开发初期或者项目不是很复杂的时候或者一开始项目框架就规划好的情况下通常我们都会封装。
在我们的项目中这部分代码其实是自动生成的足以满足大部分的简单系统尤其是你很喜欢单表查询的时候个人写代码比较排斥多表联合查询性能太低具体相关的怎么实现这里不多说可以参考以往的文章。 SpringBoot集成OpenAPI(Swagger3)和Mybatis-plus代码生成器 https://blog.csdn.net/m0_37892044/article/details/126154714 下面这是我们freemarker自动生成代码的框架代码
ApiOperation(value 按照条件进行分页查询${table.comment})
PostMapping(/info/page)
public RIPage${entity} page${entity}Info(RequestBody PageQuery pageQuery) {IPage${entity} page queryService.queryByPage(${table.serviceName ? substring(1) ? uncap_first}, pageQuery);return R.ok(page);
}以下是我们项目中自动生成的代码
ApiOperation(value 6按照条件进行分页查询数据服务)
PostMapping(/info/page)
ApiOperationSupport(order 6)
public RIPageDataService pageDataServiceInfo(RequestBody PageQuery pageQuery) {IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);return R.ok(page);
}以下是自动生成的接口返回的分页查询结果。可以发现其实最基本的分页查询功能已经实现了。但是现在有个问题当数据在前端展示的时候可能需要我们进行进一步转换如下所示上面部分是框架自动给我们实现的接口并且能用但是距离我们想要的有一点点差距。下面是我们实际想要的接口。 差距主要体现自动生成的接口完全按照数据库来例如type数据库存储300050001数字但是用户在页面肯定是希望看到Mysql这个字符串同理请求参数和响应参数示例在数据库都是一个字符串并且接口返回的数据是经过转码以后得字符串但是用户实际上想看到的是Json对象。
相信大家经常都会遇到诸如此类利用自己项目搭建的框架或者分页框架使用了一些框架自带的查询但是对于查询结果还需要做进一步转换才能返回给前端或者第三方。这里我就说一下我的使用感受也算是寻求下有没有其他的好的解决方案。
这个问题我也一直很烦恼有时候仅需要小改造就能复用自动生成的代码有时候为了复用自动生成的代码结果代码改了一大堆最后实在忍不了干脆自己直接实现来的方便和好维护。
方案1同对象同属性直接修改原来的属性值
适用场景就是你分页接口返回的属性和你要进行转换的数据属性是完全一样的。 例如这里我接口返回分页对象是DataService转换后也是DataService
修改方式1直接去修改原来的属性值。例如下面dataViewId是字符串该ID所对应的name也是字符串那么直接把name的值赋值到id上即可。简单粗暴就是代码可读性差属性字段叫做ID结果里面是name。
IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);
page.getRecords().forEach(temp-{temp.setDataViewId(dataServiceService.getById(temp.getDataViewId()).getName();)
});方案2同对象不同属性新增属性
修改方式2为了不引起歧义或者不方便修改原来的值就从新增加一个字段叫做dataViewName把name属性赋值给dataViewName如下所示此外下图中数据服务的类型type这个字段明显是不能直接在原来的属性上修改因为它是Integer 类型只能赋值正数因此我们也是新增一个字段typeName并且均使用TableField(exist false)标明这个不是数据库字段如果不想返回原来的字段给前端用JsonIgnore屏蔽即可。
ApiModelProperty(数据服务类型)
TableField(type)
DicCheck(dicGroup DicGroupEnum.数据服务类型)
JsonIgnore
private Integer type;TableField(exist false)
private String typeName;ApiModelProperty(所属视图)
TableField(data_view_id)
private String dataViewId;TableField(exist false)
private String dataViewName;此事下面代码这样转换。
IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);
page.getRecords().forEach(temp-{temp.setDataViewName(dataServiceService.getById(temp.getDataViewId()).getName())temp.setTypeName(dicTypeService.getById(temp.getType()).getName())
});疑问方案1、2大数据量怎么优化
可以发现我们其实上面的两个例子中都存在一个问题那就是在for循环中去转换正常分页一般也就10页15页循环个10次15次的数据库操作也不至于有啥大问题就怕万一有人操作每页5000条然后你循环个5000次查询数据库那样肯定不行的。解决问题也简单要么限制分页查询的数量的大小比如最多每页20不然就得想办法解决掉for循环查数据库。 以下是一种很常见的方式
优化1实时查询的本地缓存
其思路就是根据原始的结果遍历一遍找出需要转换的ID然后在根据ID查询对应的名称在将名称存入Hash一般需要3次循环两次数据库操作假设只有一个字段转换。对比优化前是1次循环以及一次循环的总数1次的数据库操作。 比如数据总量1万优化前是循环10001次的数据库操作以及10000次的set操作。优化后是2次数据库操作以及10000的add操作10000次的put操作10000次的set操作。而三次for循环其实加起来也就3万次3万次的这种简单的add,set,put操作和1万次的操作时间上几乎不会有差异但是1万次的数据库操作其差距是会尤其特别大大到你怀疑人生。
public RIPageDataService pageDataServiceInfo(RequestBody PageQuery pageQuery) {//查询结果中某个字段为ID但是在页面上需要显示此ID对应的名称IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);//从page结果中获取所有需要转换的dataViewIdsSetString dataViewIds new HashSet();page.getRecords().forEach(temp - dataViewIds.add(temp.getDataViewId()));//查出dataViewIds对应的名称存入本地缓存MapString,String idToName new HashMap();ListDataService list dataServiceService.list(new QueryWrapperDataService().in(ID, dataViewIds));list.forEach(temp - idToName.put(temp.getId(), temp.getName()));//将page结果中的dataViewId值从新设置成名称page.getRecords().forEach(temp-{temp.setDataViewId(idToName.get(temp.getDataViewId()));return R.ok(page);
}优化2分布式缓存替换本地缓存
上述的优化1中我们每次都是去数据库查询适用于数据库经常存在数据变化的情况假设数据不常变化我们可以考虑使用诸如redis这样的缓存 相对比与上面其实就是减少了每次去实时查数据库然后在将查询的结果转成hash使用缓存数据库以后可以直接将上述hash直接存到redis。伪代码如下所示。
public RIPageDataService pageDataServiceInfo(RequestBody PageQuery pageQuery) {//查询结果中某个字段为ID但是在页面上需要显示此ID对应的名称IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);MapString,String idToName redisService.getStrMap(data_view_id);//将page结果中的dataViewId值从新设置成名称page.getRecords().forEach(temp-{temp.setDataViewId(idToName.get(temp.getDataViewId()));return R.ok(page);
}优化3枚举替换分布式/本地缓存
这种情况适用于数据两较少并且万年不变的这种例如性别状态类型这种字典类型的数据。
public RIPageDataService pageDataServiceInfo(RequestBody PageQuery pageQuery) {//查询结果中某个字段为ID但是在页面上需要显示此ID对应的名称IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);page.getRecords().forEach(temp-{temp.setTypeName(DbTypeEnum.getNameById(temp.getType()));return R.ok(page);
}上面的情况都是基于IPage转IPage的问题接下来说IPage转IPage的问题
方案3不同对象取出泛型在拷贝
不同对象也就是说我们自带的框架查询出来的对象和我们要返回给前端的对象就不一样。 我不知道有没有人尝试写过下面的代码。利用框架查询的分页对象是数据库实体类DataService而我们要返回给前端的是DataServiceRes他两可能大部分属性一样也可能DataServiceRes的属性是DataService的子集不方便暴露DataService的所有数据当然也可能是有字段不一样需要转换于是我们想能否使用copyProperties方法将IPage拷贝给IPage然后在修改IPage
public RIPageDataServiceRes pageDataServiceInfo(RequestBody PageQuery pageQuery) {IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);IPageDataServiceRes pageVO new Page();BeanUtils.copyProperties(page, pageVO);pageVO.getRecords().forEach(temp-{temp.setTypeName(DbTypeEnum.getNameById(temp.getType()));});return R.ok(pageVO);
}如果你这样做了你将得到一个错误
java.lang.ClassCastException:DataService cannot be cast to DataServiceRes当然你要是把for循环注释掉你会发现他不报错了。debug看一下就会发现虽然我们new的page是DataServiceRes但是发现拷贝过来以后是DataService 因为IPage本身是泛型的我们使用的 org.springframework.beans.BeanUtils.copyProperties是没办法办法进行泛型的拷贝因为是通过反射进行赋值的。以如下的源码所示因此换个思路。既然无法拷贝泛型。那我们先取出泛型在拷贝。
所以方法就是从新创建一个对象把原来对象的数据在复制过去复制的时候要避免泛型的出现如果某个参数是泛型那就先把这个参数取出来创建一个新的参数去替换这个泛型参数。
public RIPageDataServiceRes pageDataServiceInfo(RequestBody PageQuery pageQuery) {IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);//先通过属性拷贝把page中的非泛型的分页参数拷贝到pageVOIPageDataServiceRes pageVO new Page();BeanUtils.copyProperties(page, pageVO);//通过page.getRecords()取出泛型数据ListDataService records page.getRecords();//将ListDataService records拷贝到ListDataServiceRes listRes此时没有泛型了ListDataServiceRes listRes CopyUtils.copyList(records, DataServiceRes.class);//修改转义需要的字段listRes.forEach(temp- {temp.setDataViewId(大王叫我来巡山);});//替换pageVO中的RecordspageVO.setRecords(listRes);return R.ok(pageVO);
}CopyUtils.copyList也是用的BeanUtils.copyProperties
public static T ListT copyList(List? listSource,ClassT targetClazz){if (listSource null){return new ArrayList();}ListT listTarget new ArrayList(listSource.size());listSource.forEach(temp -{T newInstance;try {newInstance targetClazz.newInstance();BeanUtils.copyProperties(temp, newInstance);listTarget.add(newInstance);} catch (Exception e) {e.printStackTrace();} });return listTarget;
}结果如下
方案4Mybatis自带的convert
如下所示Mybatis给我们提供了convert方法我们可以在convert方法中返回转换后的对象
public RIPageDataServiceRes pageDataServiceInfo(RequestBody PageQuery pageQuery) {IPageDataService page queryService.queryByPage(dataServiceService, pageQuery);IPageDataServiceRes pageVO page.convert(t-{/*DataServiceRes s new DataServiceRes();BeanUtils.copyProperties(t, s);*/DataServiceRes s CopyUtils.copyBean(t, DataServiceRes.class);s.setReqExam(JsonUtils.strToJavaBean(t.getReqExam(),Object.class));s.setResExam(JsonUtils.strToJavaBean(t.getResExam(),Object.class));return s;});return R.ok(pageVO);
}方案5重构大法
改不下去了干脆自己最后自己去从新写自己特定业务的分页查询了