论坛首页 Java企业应用论坛

记一次代码优化—大数组拆分查询

浏览 9174 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (2) :: 隐藏帖 (1)
作者 正文
   发表时间:2010-08-04   最后修改:2010-08-06


    需求:

     同一页面显示的内容需要到2个不同的数据库a,b中查询,a的结果中的id作为条件再到b中查询。
     如a的结果数据量大,需要分次到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/539465    

     2.不知道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();
	}

 

   发表时间:2010-08-04  
有点复杂,看不懂,晕。
0 请登录后投票
   发表时间:2010-08-04  
一条sql
0 请登录后投票
   发表时间:2010-08-04  
抛出异常的爱 写道
一条sql

在不同的数据库上,这2个数据库没在一台电脑上。。。
0 请登录后投票
   发表时间:2010-08-04   最后修改:2010-08-04
25707332 写道
抛出异常的爱 写道
一条sql

在不同的数据库上,这2个数据库没在一台电脑上。。。

没注意到.

PS:pagesize 与SELECT_IN_NUMBER =200什么关系 ?

  temp.clear();  这个不应该放到方法内部
会产生阅读中断
0 请登录后投票
   发表时间:2010-08-04  
抛出异常的爱 写道
25707332 写道
抛出异常的爱 写道
一条sql

在不同的数据库上,这2个数据库没在一台电脑上。。。

没注意到.

pagesize 与pag 200什么关系 ?


没啥关系。。。

200 根据a的id查询b时的条数上限  就是下面那个sql里面的in  最多允许200个参数  那个pagesiz可大可小  表示一次查多少条a
0 请登录后投票
   发表时间:2010-08-04  
太复杂了,没有看

不过Oracle有dblink这个东西,能支持两个数据库一起查。
0 请登录后投票
   发表时间:2010-08-04  
mathfox 写道
太复杂了,没有看

不过Oracle有dblink这个东西,能支持两个数据库一起查。


!
那我就不知道为什么项目经理叫我这样做了。。。

0 请登录后投票
   发表时间:2010-08-04  
25707332 写道
mathfox 写道
太复杂了,没有看

不过Oracle有dblink这个东西,能支持两个数据库一起查。


!
那我就不知道为什么项目经理叫我这样做了。。。


我也想说有dblink,只是不知道dblink效率怎么样,以前用来导些数据,感觉不是很好
0 请登录后投票
   发表时间:2010-08-04  
bDAO.queryCountByOwnerForList(temp) 这个里面是怎么查的,如果也是按每个id查一次,那每n条id查一次数据库和每个id查一次数据的差距有多少呢?那 temp 这个是不是也多余呢? 这点有点不明白
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics