关于多数据源(除自己数据库外,另一部分数据需通过接口调取第三方获取)的查询问题
同样,还是一次工作上遇到的问题的记录,当时也是搞了挺久才搞出来的,虽然不太完美,但是误差方面在我的项目里还算可以接受,哪位老哥有更好的办法还请指导下,哪怕评论里留个网页链接我去看也好,多谢了。
具体问题描述大致如下:
客户需要app能查询出所有订单,分页并且按时间倒序排列,现在所有的消费订单数据在我们自己的数据库里,而所有的充值订单数据其实在第三方那里(其实我们自己也记录了一笔,但是数据结果不可靠,因此还是要取用对方的),关于从第三方获取数据的参数概括是(用户唯一标识,每页显示行数,当前页数),即(userId,pageSize,pageNo),其中pageSize要求小于50。
经过思考,需要解决的问题主要在于:
1.如何从第三方获取我需要的数据
2.整合我们库里的数据,第三方获取的数据,进行时间倒序排列,并返回给app
实际上第二点很好解决,只要写一个排序类,对整合后的数据进行排序即可,最主要难点在于第一点,如何确定调取第三方接口的入参pageSize和pageNo,然后还要把之前已经展示过的数据排除掉。
具体解决方法如下:
1.首先,我用了cache,用于记录app已展示的(消费记录数)(充值记录数),即(我方库数据展示量)(第三方数据展示量),暂定参数为own和other,当app端传的页数为第一页时,将这两个数据都置为0.
2.查询接下来需要展示的数据中,我方的数据,这点没什么好说的,正常的查自己数据库即可,需要查出来的量为own<rownum<own+appPageSize,其中appPageSize是指app的入参,app端的页显示行数
3.然后查询需要展示的数据中,第三方的数据,关于确定调取第三方接口入参的方法如下:
1 /** 2 * 获取第三方订单查询接口所需要的参数 3 * @param start 起始数据量 4 * @param end 结束数据量 5 * @param size 展示数据量 6 * @return 7 * Map,包含两个参数,pagenum页数,pagesize页显示行数 8 */ 9 public static Map<String,Integer> getWdPara(int start,int end,int size){ 10 Map<String,Integer> map=new HashMap<>(); 11 if (start<size||start/size<1) { 12 map.put("pagenum", 1); 13 map.put("pagesize", 10*((end/10)+1)); 14 }else{ 15 if (start/size==end/size) { 16 map.put("pagenum", (end/size)+1); 17 map.put("pagesize", size); 18 }else { 19 map=getWdPara(start, end, size+1); 20 } 21 } 22 return map; 23 }
其中三个参数的关系是end=start+size,实际上start入参的值就是other,而size入参的值就是appPageSize。而出参的两个参数就分别是调取第三方时候的入参pageSize和pageNo了。关于如何确定这个方法可以获取想要的参数,说起来有点绕,我数学也不太好,当时打了挺久的草稿,参照如下,如果看不懂直接跳过就好。。。因为我自己也经常忘记什么意思:
默认app端展示数据量为10条,所以从第三方中获取的数据需要包含other+10条,因为根据时间倒序,这中间存在没有数据是消费数据,全是充值数据的情况,即所有展示数据均来自于第三方。
第三方数据的起始-截止数据量 pageSize参数,即s pageNo参数,即n 实际取到的数据量
8-18 20 1 1-20
18-28 15 2 15-30
28-38 20 2 20-40
。。。
一开始是这样草稿打下去的,后来找规律后发现,其实就和查自己数据库一样,保证查到的数据包含起始数据和终止数据即可,即
( 页数-1)*页显示行数>起始数据,页数*页显示行数>终止数据
然后进行几次举例验证后,就得出上述获取参数的方法了。
至此,最难的一步已经完成了,即已经获取到所有需要的数据了。
4.然后进行下一步,需要去除掉之前已经展示过了的数据,就如上面例子,我们获取到的数据量,其实是大于需要的数据量,有一部分数据可能之前已经展示了,因此在这里需要将之前展示了的数据去除掉,代码如下
1 /** 2 * 处理订单数据,排除之前显示过的数据 3 * @param dataList 待处理的list 4 * @param start 数据起始 5 * @param size 页显示行数 6 */ 7 public static void dealList(List<Map<String, String>> dataList,int start,int size){ 8 int removeNum=start%size; 9 for (int i = 0; i < removeNum; i++) { 10 dataList.remove(0); 11 } 12 }
即起始数据除以页显示行数,取余数,然后获取到的数据量从前往后删除这个余数量的数据即可,这个余数量即之前已经展示用过的数据量。和上面获取参数一样,这个计算方法,也是我自己写了好几个例子,然后找规律写出来的。。。数学不好,见谅,我也不知道逻辑上该怎么解释,反正根据例子找规律,发现这样写没问题。
5.再然后就没啥问题了,要展示的充值数据,消费数据都已经全部获取到了,只要将一些参数名称处理下,确保保持一致,放在一个list里面,然后写个排序方法,根据订单时间进行倒序,排完序以后,获取前appPageSize个数据返回给前端即可,排序代码如下:
1 /** 2 * 数据进行排序,按照时间倒序排序 3 * @param list 4 */ 5 @SuppressWarnings("unchecked") 6 public static void sortTimeMethod(List<Map<String,String>> list){ 7 Comparator comp= new Comparator<Map<String, String>>(){ 8 //返回值大于0时,两者交换顺序 9 @Override 10 public int compare(Map<String, String> p1,Map<String, String> p2){ 11 String p1Date=Tools.processNull(p1.get("order_date")); 12 String p2Date=Tools.processNull(p2.get("order_date")); 13 if ("".equals(p1Date) 14 ||"".equals(p2Date)) { 15 return 0; 16 }else { 17 //如果o1时间早于等于o2时间,则o1排在后面 18 if (!DateUtil.formatDate(p1Date).after(DateUtil.formatDate(p2Date))) { 19 return 1; 20 }else { 21 return -1; 22 } 23 } 24 } 25 }; 26 Collections.sort(list, comp); 27 }
6.当然,这时候也别忘记更新cache里面的数据,根据最终返回给前端的数据,判断有多少消费数据和充值数据,对own和other进行相应的累加。
以上就全部完成。不过就和我之前说的,有误差,很有局限性,跟我们使用场景有关,因此不是很具备参考性,相关说明如下:
1.我们是APP展示用的,场景中无法跳页,也就是用户只能选择先看第一页,再看第二页数据,这样依次下排,如果存在跳页,我这个方法应该不行。
2.当用户在看订单的同时,无法进行充值或消费操作,进行充值或消费等产生订单的操作后,重新来查询必定是从第一页开始,因此那种查了一会儿,突然增加一条订单了,这种情况是不会出现的,否则我这方法会存在问题,即cache里的值不准了,导致的结果就是查询的数据也不准了,有个订单会展示两次。
可能看上去条理不是很清晰,有点混乱。。。但是尽力了,其实一开始没打算写,后来隔了半个多月,突然发现自己这个代码看不懂了,然后重新理解了半天,想想这个当初也是自己搞了挺久才弄出来的,怕以后又忘记了,于是此时开始写这篇随笔,本身就是隔了半个月后再写,思路就不是很清晰,结果写了一小半,客户又来事情了,于是忙去了。。。然后一段时间没闲下来,再然后忙着忙着就给忘了。。。。直到今天,已经是半年后了,再次来续写的时候,脑子就更乱了,强行看着半篇内容加自己原先的代码,将其续写完成。主要用处估计也就是以后自己如果遇到相同问题,给自己一个提点,一个思路,防止到时候自己脑子又转不过来。