锁定老帖子 主题:记一次代码优化—大数组拆分查询
精华帖 (0) :: 良好帖 (1) :: 新手帖 (2) :: 隐藏帖 (1)
|
|
---|---|
作者 | 正文 |
发表时间:2010-08-04
最后修改:2010-08-06
同一页面显示的内容需要到2个不同的数据库a,b中查询,a的结果中的id作为条件再到b中查询。
JDK:1.6 持久层框架:ibatis2.0
要注意的细节: 1.循环次数 2.内存占用
public PageResult queryForPageListNew(int pageSize, int pageNum,AForm queryForm) { // 初始化循环次数 int i = 0; // 初始化n int n = 200; // 获得a表单 PageResult pr = aDAO.queryForPageResult(queryForm .getParams(), pageSize, pageNum, A.class); List<A> aList = pr.getResultList(); // 初始化size int size = aList.size(); // 创建临时数组,用于存放a表单中的id List<Long> temp = new ArrayList<Long>(); // 初始化hashmap,size*2避免hashmap resize() Map<Long, Long> countMap = new HashMap<Long, Long>(size*2); for (A a : aList) { i++; temp.add(a.getId()); // 每n条数据查询一次数据库 if (((i + n) % n == 0 && i < size)||(i==size)) { // 由于使用某种特殊框架自动生成DAOImpl,目前只能返回List List<Map<String, Long>> countMapList = bDAO .queryCountByOwnerForList(temp); // 将查询结果放入countMap中 for (Map<String, Long> map : countMapList) { Long id = map.get("owner"); Long count = map.get("count"); countMap.put(id, count); } temp.clear(); } } temp = null; for (A a : aList) { a.setCount(countMap.get(a.getId())); } countMap.clear(); countMap = null; return pr; } b的ibatis SqlMap: <select id="queryCountByOwnerForList" resultMap="resultMap.B" parameterClass="List"> select t1.owner, count(t1.owner) as count from t_b t1 where ( t1.status= 4 or t1.status=5 or t1.status=6 ) and t1.owner in <iterate open= "(" close = ")" conjunction = ","> #[]# </iterate> group by t1.owner </select> HashMap countMap 使用说明: 两次的查询结果,需要比对id,进行组合。2次查询的结果集均未List,如直接组合,需要做for循环的嵌套,比对方式如下: for(A a: aList){ for(B b: countMapList){ if(a.id==b.id) { 组合... } } } 这样就要循环n的平方次,才能组合查询结果,使用HashMap只需要循环2n次
这里假定使用HashMap存取结果比List嵌套循环快(没经过测试)
改进思考: 1.hashmap初始化的值应为 大于 (int)(size/0.75)+1 且最小的2的n次方 参见:http://www.iteye.com/topic/5394652.不知道ibatis是否支持参数为List<Object>,并获得Object的属性。如果可以,能否省掉temp?
代码初评: 1.int n = 1000; 常量应该设置为fanal static
2.int size = aList.size(); 可以直接用aList.size(),没有必要再定义一个变量
3.List<Long> temp = new ArrayList<Long>(); 长度也可以初始化
4.if (((i + n) % n == 0 && i < size)||(i==size)) 太复杂,不容易看懂,尽量用面对对象的思想,而且i<size的条件也是多余的
5.没有考虑第一次查询aList为空的情况,程序仍在继续往下执行
初评过后的代码:
private final static int SELECT_IN_NUMBER=200; public PageResult queryForPageListNew(int pageSize, int pageNum, CorpClaimSetQueryForm queryForm) { // 获得a查询分页表单 PageResult pr = aDAO.queryForPageResult(queryForm .getParams(), pageSize, pageNum, A.class); List<A> aList = pr.getResultList(); // 如果cssList查询结果为空直接返回pr if (aList.size() <= 0) { return pr; } // 创建临时数组,用于存放a表单中的id List<Long> temp = new ArrayList<Long>(SELECT_IN_NUMBER); // 初始化hashmap,size*2避免hashmap resize() Map<Long, Long> countMap = new HashMap<Long, Long>(aList.size() * 2); for (A a : aList) { temp.add(a.getId()); // 每n条数据查询一次数据库 if (temp.size() == SELECT_IN_NUMBER) { List<Map<String, Long>> countMapList = bDAO .queryCountByOwnerForList(temp); // 将查询结果放入countMap中 for (Map<String, Long> map : countMapList) { Long id = map.get("owner"); Long count = map.get("count"); countMap.put(id, count); } //每查询1次,清空一次temp temp.clear(); } } //对temp中余留的数据进行查询 if (!temp.isEmpty()) { List<Map<String, Long>> countMapList = bDAO .queryCountByOwnerForList(temp); for (Map<String, Long> map : countMapList) { Long id = map.get("owner"); Long count = map.get("count"); countMap.put(id, count); } temp.clear(); temp = null; } //将统计结果加入到原有的查询结果中 for (A a : aList) { a.setCount(countMap.get(a.getId())); } countMap.clear(); countMap = null; return pr; }
再次评审: 1.集合调用clear()之后,不需要再设置为null(方法调用完后,内存会自动释放,这些操作都没必要?)
2.一段代码中用到相同的代码2次,这段相同的代码应该提炼出来,自成一个方法
最终代码:
private final static int SELECT_IN_NUMBER = 200; /** * 分页查询 在原有的基础上增加数量统计,并可进行批量统计,每n条统计一次。 * @param pageSize * @param pageNum * @param queryForm * @return PageResult */ @SuppressWarnings("unchecked") @Override public PageResult queryForPageListNew(int pageSize, int pageNum, CorpClaimSetQueryForm queryForm) { // 获得a查询分页表单 PageResult pr = aDAO.queryForPageResult(queryForm .getParams(), pageSize, pageNum,A.class); List<A> aList = pr.getResultList(); // 如果aList查询结果为空直接返回pr if (aList.size() <= 0) { return pr; } // 创建临时数组,用于存放a中的id List<Long> temp = new ArrayList<Long>(SELECT_IN_NUMBER); // 初始化hashmap,size*2避免hashmap resize() Map<Long, Long> countMap = new HashMap<Long, Long>(aList.size() * 2); for (A a : aList) { temp.add(a.getId()); // 每n条数据查询一次数据库 if (temp.size() == SELECT_IN_NUMBER) { queryAndWarpCountMap(temp, countMap); } } //对temp中余留的数据进行查询 if (!temp.isEmpty()) { queryAndWarpCountMap(temp, countMap); } //将统计结果加入到原有的查询结果中 for (A a : aList) { a.setCount(countMap.get(a.getId())); } return pr; } /** * 根据临时ID集合,查询统计数据,并且包装到统计map中。 * 最后清空临时ID集合 * @param temp * @param countMap */ private void queryAndWarpCountMap(List<Long> temp, Map<Long, Long> countMap) { List<Map<String, Long>> countMapList = bDAO .queryCountByOwnerForList(temp); for (Map<String, Long> map : countMapList) { Long id = map.get("owner"); Long count = map.get("count"); countMap.put(id, count); } //每查询1次,清空一次temp temp.clear(); }
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-08-04
有点复杂,看不懂,晕。
|
|
返回顶楼 | |
发表时间:2010-08-04
一条sql
|
|
返回顶楼 | |
发表时间:2010-08-04
抛出异常的爱 写道 一条sql 在不同的数据库上,这2个数据库没在一台电脑上。。。 |
|
返回顶楼 | |
发表时间:2010-08-04
最后修改:2010-08-04
25707332 写道 抛出异常的爱 写道 一条sql
在不同的数据库上,这2个数据库没在一台电脑上。。。 没注意到. PS:pagesize 与SELECT_IN_NUMBER =200什么关系 ? temp.clear(); 这个不应该放到方法内部 会产生阅读中断 |
|
返回顶楼 | |
发表时间:2010-08-04
抛出异常的爱 写道 25707332 写道 抛出异常的爱 写道 一条sql
在不同的数据库上,这2个数据库没在一台电脑上。。。 没注意到. pagesize 与pag 200什么关系 ? 没啥关系。。。 200 根据a的id查询b时的条数上限 就是下面那个sql里面的in 最多允许200个参数 那个pagesiz可大可小 表示一次查多少条a |
|
返回顶楼 | |
发表时间:2010-08-04
太复杂了,没有看
不过Oracle有dblink这个东西,能支持两个数据库一起查。 |
|
返回顶楼 | |
发表时间:2010-08-04
mathfox 写道 太复杂了,没有看
不过Oracle有dblink这个东西,能支持两个数据库一起查。 ! 那我就不知道为什么项目经理叫我这样做了。。。 |
|
返回顶楼 | |
发表时间:2010-08-04
25707332 写道 mathfox 写道 太复杂了,没有看
不过Oracle有dblink这个东西,能支持两个数据库一起查。 ! 那我就不知道为什么项目经理叫我这样做了。。。 我也想说有dblink,只是不知道dblink效率怎么样,以前用来导些数据,感觉不是很好 |
|
返回顶楼 | |
发表时间:2010-08-04
bDAO.queryCountByOwnerForList(temp) 这个里面是怎么查的,如果也是按每个id查一次,那每n条id查一次数据库和每个id查一次数据的差距有多少呢?那 temp 这个是不是也多余呢? 这点有点不明白
|
|
返回顶楼 | |