Java Web性能优化之一:减少DAO层的调用次数
2015-11-14 13:02
639 查看
前言
很简单的一个问题,一个业务方法,需要先查询一次得到结果(select),然后再根据查询的结果进行一次更新(update),通常情况下我们会在DAO层定义两个接口,一个接口实现查询,一个接口实现更新,在service层调用2次。当然还有一种解决方案,就是仅在DAO层定义一个接口,通过update set select 这种语法去做,这样调用一次就可以完成更新,那么实际情况我们选用哪一种高效呢?根据我的经验应该是第二种,但有人说尽量不要写子查询,那我就具体写个testcase测试一下效率问题。
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。相关文章推荐
- java按值传递理解
- [CareerCup] 14.4 Templates Java模板
- Java入门之数据类型以及变量的定义
- Java与Mysql开发中的特殊字符(包括Emoji)
- java.lang.IllegalArgumentException: Service Intent must be explicit异常说明
- java日志组件介绍(common-logging,log4j,slf4j,logback)
- java中Random()详解
- 解决spring、springMVC重复扫描导致事务失效的问题
- JavaFX透明窗口
- 如何给Spring MVC的action加上事务
- 利用java反射动态调整数组长度
- Spring中ServletContextAware接口使用理解
- Spring实例化Bean的三种方式及Bean的类型
- 在Eclipse中查看JDK类库的源代码
- java.lang.IllegalStateException: commit already called
- 当Eclipse的关键字高亮显示不小心被关闭之后......
- Struts2系统学习(14)输入校验-基于XML配置方式实现校验
- Jdbc事务以及Spring事务解惑
- java keytool证书工具使用小结
- Spring中InitializingBean接口使用理解