您的位置:首页 > 编程语言 > Java开发

Java Web性能优化之一:减少DAO层的调用次数

2015-11-14 13:02 639 查看

前言

很简单的一个问题,一个业务方法,需要先查询一次得到结果(select),然后再根据查询的结果进行一次更新(update),通常情况下我们会在DAO层定义两个接口,一个接口实现查询,一个接口实现更新,在service层调用2次。当然还有一种解决方案,就是仅在DAO层定义一个接口,通过update set select 这种语法去做,这样调用一次就可以完成更新,那么实际情况我们选用哪一种高效呢?根据我的经验应该是第二种,但有人说尽量不要写子查询,那我就具体写个test
case测试一下效率问题。

Test

package zhsz_service;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.Log4jConfigurer;

import cn.zhsz.service.dao.evaluation.impl.EvaluationDaoImpl;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/spring/spring_servlet.xml",
"classpath*:/spring/spring_config.xml" })
@WebAppConfiguration
public class MainTest {

private static Log logger = LogFactory.getLog(MainTest.class);// 日志

@Resource(name="evaluationDao")
private EvaluationDaoImpl evaluationDao;

@Test
public void testOne() throws Exception {
long beforeCurrentTimeMillis = System.currentTimeMillis();
float score =this.evaluationDao.findScoreByOid(10);
boolean result = this.evaluationDao.updateOptionScoreByQid("123",5, score);
long afterCurrentTimeMillis = System.currentTimeMillis();
logger.error("testOne用时"+ (afterCurrentTimeMillis - beforeCurrentTimeMillis) + "毫秒!");
}

@Test
public void testTwo() throws Exception {
long beforeCurrentTimeMillis = System.currentTimeMillis();
boolean result = this.evaluationDao.updateOptionScoreByQid("123",5, 9);
long afterCurrentTimeMillis = System.currentTimeMillis();
logger.error("testTwo用时"+ (afterCurrentTimeMillis - beforeCurrentTimeMillis) + "毫秒!");
}

@Before
public void setUp() throws Exception {
Log4jConfigurer.initLogging("classpath:log4j.properties");
}

@After
public void tearDown() throws Exception {

}

}


可以看到,testOne调用了2次DAO层,分别是下面这两条sql:

<select id="findScoreByOid" parameterType="int" resultType="float">
select score from t_evaluation_options where id = #{oid}
</select>

<update id="updateOptionScoreByQid2" parameterType="Map">
update t_self_evaluation set score = #{score}
where stu_number = #{stuNum} and question_id = #{qid}
</update>


而testTwo只调用了1次DAO层,但sql是带子查询的update set select语法:

<update id="updateOptionScoreByQid" parameterType="Map">
update t_self_evaluation set score =
(select score from t_evaluation_options where id = #{oid})
where stu_number = #{stuNum} and question_id = #{qid}
</update>

现在分别运行这两个方法8次后得到8组测试接口,最后观察一下log4j输出在文件中的调试信息:

2015-11-14 12:25:41  [ main:2529 ] - [ ERROR ]  testOne用时578毫秒!

2015-11-14 12:25:51  [ main:1483 ] - [ ERROR ]  testTwo用时156毫秒!

2015-11-14 12:26:00  [ main:1469 ] - [ ERROR ]  testOne用时172毫秒!

2015-11-14 12:26:07  [ main:1452 ] - [ ERROR ]  testTwo用时172毫秒!

2015-11-14 12:26:17  [ main:1530 ] - [ ERROR ]  testOne用时235毫秒!

2015-11-14 12:26:25  [ main:1484 ] - [ ERROR ]  testTwo用时156毫秒!

2015-11-14 12:26:33  [ main:1531 ] - [ ERROR ]  testOne用时173毫秒!

2015-11-14 12:26:41  [ main:1454 ] - [ ERROR ]  testTwo用时171毫秒!

2015-11-14 12:26:49  [ main:1547 ] - [ ERROR ]  testOne用时204毫秒!

2015-11-14 12:26:57  [ main:1498 ] - [ ERROR ]  testTwo用时156毫秒!

2015-11-14 12:27:18  [ main:1468 ] - [ ERROR ]  testOne用时187毫秒!

2015-11-14 12:27:57  [ main:1469 ] - [ ERROR ]  testTwo用时156毫秒!

2015-11-14 12:28:52  [ main:1498 ] - [ ERROR ]  testOne用时202毫秒!

2015-11-14 12:28:59  [ main:1473 ] - [ ERROR ]  testTwo用时156毫秒!

2015-11-14 12:29:24  [ main:1531 ] - [ ERROR ]  testOne用时189毫秒!

2015-11-14 12:29:31  [ main:1469 ] - [ ERROR ]  testTwo用时156毫秒!

可以看到除了第二组用时一样,其它7组测试数据均是testOne的用时大于testTwo,也就是说即使testTwo的SQL中用了子查询,依旧比调用2次DAO层的效率要高一些。

下面再看一下最后一组测试的完整debug日志:



首先看一下图片中的红色标记,由于TestOne调用了两次DAO,那必然执行了2次SQL,也就是说会有2个PreparedStatement对象,这里我们用的druid连接池,也就是说会向池里put两次,最后再remove两次,再看一下绿色的标记,可以发现TestOne和TestTwo的效率差距也正是由于两次put中间的差值,而数据库的效率差距已经小到无法区分。

总结

所以当我们在写程序的时候如果遇到复杂的业务方法,可以考虑尽量直接在数据库层通过复杂sql、function或procedure去处理而避免多次调用DAO层的方法,这样在效率方面应该会有一些提升,如果有不对的地方欢迎批评指正,The End。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: