前不久的项目时间紧张,为了尽快完成原型开发,写了一段效率相当低的代码。

最近几天闲下来,主动把之前的代码优化了一下:)

标签:Java、Mybatis、MySQL
概况:本地系统从另外一个系统得到实体类集合List<UserEvent>,但是实体中只有eventId信息,其他属性值均为空。
需要从数据库中查询数据,完善List<UserEvent>的信息并返回。
相关业务表以及对应的实体类,如下图。(为了回避项目信息,相关业务内容均省略,以下表名、实体名、代码变量名等均用字母ABC代替。)

原处理

1.先来看代码,乍一看逻辑清晰,符合正常思维习惯。
但是仔细查看发现,for循环中的每步处理都和数据查询有关。假设有10次循环,每次循环中有4次数据库连接查询,一共需要连接数据库40次。
每次数据库连接都需要一定的开销,随着循环量不断增加,处理时间将成倍增长,造成资源浪费。
 1  String eventId = "";2  String aTime = "";3  for (UserEvent event : userEventList) {4       eventId = event.getEventId();5   6       // 获取业务B信息7       List<EntityB> listB = mapperB.selectBInfoByEventId(eventId);8       event.setListB(listB);9
10      // 获取业务C信息
11      List<EntityC> listC = mapperC.selectCInfoByEventId(eventId);
12      event.setListC(listC);
13
14      // 查看是否有业务B处理
15      EntityB entityB = mapperB.selectBInfoByPrimary(phone, eventId);
16      event.setIsActionB(null == entityB ? "false" : "true");
17
18      // 获取业务A和用户信息
19      User userInfo = mapperA.selectEventUserInfoByEventId(eventId);
20      if(null != userInfo){
21          aTime = userInfo.getTime();
22          event.setTime(aTime == null ? "" : sdfMd.format(sdfYmd.parse(aTime)));
23                      event.setUserInfo(userInfo);
24       }
25  }

2.再来看查询语句。
业务表A和业务表B没有复杂的查询。只有业务表C使用了一个子查询,来获取表内自身数据引用的信息。
各业务表数据都需要关联到用户表User。

 1   <select id="selectBInfoByEventId" parameterType="String" resultType="EntityA">2     SELECT3       B.phone     AS phone,4       B.time      AS time,5       U.name      AS userName6     FROM table_b B7     LEFT JOIN user U ON U.phone = B.phone8     WHERE B.event_id = #{eventId}9     ORDER BY B.time ASC
10   </select>
11   <select id="selectCInfoByEventId" parameterType="String" resultType="EntityC">
12     SELECT
13       C.cmtId,
14       C.referId,
15       C.time,
16       U.name   AS userName
17       ( SELECT TU.name FROM table_c TC
18         LEFT JOIN user TU ON TU.phone = TC.phone
19         WHERE TC.cmt_id = TC.refer_id
20        ) AS referName
21     FROM table_c C
22       LEFT JOIN user U ON C.phone = U.phone
23     WHERE C.event_id = #{eventId}
24     ORDER BY C.time ASC
25   <select id="selectEventUserInfoByEventId" parameterType="java.lang.String" resultType="User">
26       SELECT
27           U.name,
28           U.picId,
29           A.time
30        FROM table_a A
31        LEFT JOIN user U ON U.phone = A.phone
32       WHERE A.event_id = #{eventId}
33   </select>

优化分析

  1. 在代码结构上,要避免在for循环中作查询处理。考虑将查询参数evenId从for循环中提取出来,做批量查询,然后再将查询结果设定到对应的实体类中。
  2. 在业务上,对于每一个UserEvent中的eventId,业务表A中必定对应有一条记录,而在业务表B和业务表C中则未必有与这个eventId关联的数据。因此,可以将业务表A作为主表,通过eventId与另外几个表关联查询。查询次数也由原来的至少四次减少为一次查询。
  3. 对于联合查询的结果,以UserEvent作为查询结果的实体类,使用Mybatis中的collection、association来处理结果映射。
  4. 另外,各业务表的查询中都有与用户表User的关联,考虑将各业务信息的查询处理创建为视图。这样不仅能简化联合查询中SQL语句,也可以隔离基础表的数据。

优化后的代码

  int eventSize = userEventList.size();List<String> eventIds = new ArrayList<String>(); // 如果考虑去掉重复数据,可以使用集合Set,但是作为Mybatis的输入参数,最后还是需要将Set转化为List。   // 此处直接使用List,因为在业务上排除了重复数据的可能性。for (int i = 0; i < eventSize; i++) {eventIds.add(userEventList.get(i).getEventId());}Map<String, Object> paramsMap = new HashMap<String, Object>();paramsMap.put("eventIds", eventIds);paramsMap.put("phone", phone);List<UserEvent> eventInfoList = eventMapper.selectUserEventInfo(paramsMap);// 将查询结果转化为Map存储,方便调用Map<String, UserEvent> eventInfoMap = new HashMap<String, UserEvent>();for(UserEvent event : eventInfoList){eventInfoMap.put(event.getEventId(), event);}UserEvent newEvent = null;String aTime = null;for(UserEvent event : roadEventList){ // 从查询结果Map中取出补充信息,保存到原UserEvent对象中newEvent =eventInfoMap.get(event.getEventId());if(null != newEvent ){aTime = newEvent.getTime();event.setTime(aTime == null ? "" : sdfMd.format(sdfYmd.parse(aTime )));event.setIsActionB(newEvent.getIsActionB() == null ? "false" : newEvent.getIsActionB());event.setUserInfo(newEvent.getUserInfo());event.setListB(newEvent.getListB());event.setListC(newEvent.getListC());}}

    <resultMap id="UserMap" type="User"><result column="name" property="name" /><result column="picId" property="picId" /><result column="time" property="time" /></resultMap><resultMap id="BMap" type="EntityB"><id column="bPhone" property="phone" /><result column="bUserName" property="userName" /><result column="bTime" property="time" /></resultMap><resultMap id="CMap" type="EntityC"><id column="cmtId" property="cmtId" /><result column="referId" property="referId" /><result column="cUserName" property="userName" /><result column="referName" property="referName" /><result column="cTime" property="time" /></resultMap><resultMap id="EventResultMap" type="com.xxxx.bean.UserEvent"><id column="eventId" property="eventId" /><result column="time" property="time" /><result column="isActionB" property="isActionB" /><association property="userInfo" resultMap="UserMap" /><collection property="listB" resultMap="BMap" /><collection property="listC" resultMap="CMap" /></resultMap><select id="selectUserEventInfo" resultMap="EventResultMap">SELECTA.eventId,A.time,A.name,A.picId,CASE WHEN B.phone = #{phone} THEN "true" ELSE "false" END AS isActionB,B.phone          AS bPhone,B.userName       AS bUserName,B.time           AS bTime,C.cmtId,C.referId,C.userName       AS cUserName,C.referName      AS referName,C.time           AS cTimeFROM v_table_a ALEFT JOIN v_table_b B ON B.eventId = A.eventIdLEFT JOIN v_table_c C ON C.eventId = A.eventId<where>A.event_id in<foreach collection="eventIds" index="" item="eventId"             open="(" separator="," close=")">#{eventId}</foreach></where>;</select>

编码时需要注意的几个地方

1. 复杂对象的映射解析

采用resultMap嵌套。其中,collection标签表示映射一个集合,association标签表示映射一个实体类,
标签中的property属性值对应的是,该集合/实体在查询结果对象中的变量名。
对于各表中名称相同的字段,需要建立别名,否则解析时无法确定各属性与表字段的对应关系。
如:业务表B和业务表C中都有userName字段,在查询语句中为为字段别名加了前缀来区分。
B.userName AS bUserName, <result column="bUserName"property="userName"/>
C.userName AS cUserName, <result column="cUserName" property="userName" />
resultMap中type属性表示标签所包含内容对应映射的Java类。
该属性可以写类的全路径(如:<resultMap id="EventResultMap" type="com.xxxx.bean.UserEvent"> ),
也可以配置为简写的类名(如:<resultMap id="UserMap" type="User"> )。
简写的类名需要在xml配置文件中设置(如下),配好之后的简写类名可以在各个sql.xml中使用。
  <!-- spring-mybatis.xml文件 --><!-- 配置sqlSessionFactory --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" />    <!-- 将各Java类的简写别名单独放到文件mybatis.xml中,方便修改和管理 --><property name="configLocation" value="classpath:xml/mybatis.xml" />      <property name="mapperLocations" value="classpath:sql/*.xml" /></bean>

  <!-- mybatis.xml文件 --><configuration><typeAliases><typeAlias alias="EntityA" type="com.xxxx.model.EntityA" /><typeAlias alias="EntityB" type="com.xxxx.model.EntityB" /><typeAlias alias="EntityC" type="com.xxxx.model.EntityC" /><typeAlias alias="User" type="com.xxxx.model.User" /></typeAliases></configuration>

2. foreach标签的使用

如果查询接口只有一个参数,参数类型为list,则标签中的collection属性应该设定collection="list";参数类型为数组,则应设定为collection="array"
如果查询接口有多个参数,则最好通过Map来传递各参数。此时,foreach标签的collection属性应设置为,Map中表示集合参数的键。
如上面的代码中,表示集合参数是eventIds,它在Map中的键为"eventIds" ,所以collection="eventIds"

处理时间对比

各表数据量在200、300条左右,List<UserEvent>集合记录为13条。
虽然优化后的代码行数有所增加,查询结果解析略微复杂,但是十几条数据的查询已有2秒的差距。

http://www.cnblogs.com/quiet-snowy-day/p/6166340.html

Mybatis中的collection、association来处理结果映射相关推荐

  1. Mybatis中的collection和association一关系

    collection 一对多和association的多对一关系 学生和班级的一对多的例子 班级类: package com.glj.pojo;import java.io.Serializable; ...

  2. mybatis 中 foreach collection的三种用法

    oreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有 item,index,collection,open,separator,close. i ...

  3. mybatis中,collection配置后查询只显示一条记录

    描述一下问题: 已知有两个表,一个是user表,一个是address,一(user)对多(address)的关系,在user的实体类里面写属性: private List<Address> ...

  4. MyBatis中的collection使用方法

    demo: 实体:Position @TableName("tb_position") public class Position {private static final lo ...

  5. mybatis collection标签_一对多的关系,在MyBatis中如何映射?

    # 使用collection标签 需求:根据用户id查询用户信息的同时获取用户拥有的角色,一个用户可以拥有1个或多个角色. 一般情况下,不建议直接修改数据库表对应的实体类. 所以这里我们延用之前博客中 ...

  6. Mybatis中Collection集合标签的使用

    mybatis简单的CURD就不用多说了,网上相关博客文档一大堆.分析一下Mybatis里面的collection聚集查询.  假设一个班级有多名学生为例,通过班级号查询出该班级的信息,和班级里面的所 ...

  7. MyBatis中resultMap详解

    MyBatis 中 resultMap 详解 resultMap 是 Mybatis 最强大的元素之一,它可以将查询到的复杂数据(比如查询到几个表中数据)映射到一个结果集当中.如在实际应用中,有一个表 ...

  8. mybatis中的ResultMap使用

    mybatis中的ResultMap使用 文章目录 mybatis中的ResultMap使用 一.自动映射 二.手动映射 1.返回值类型为resultMap 2.编写resultMap,实现手动映射! ...

  9. MyBatis之输入与输出(resultType、resultMap)映射

    2019独角兽企业重金招聘Python工程师标准>>> 在MyBatis中,我们通过parameterType完成输入映射(指将值映射到sql语句的占位符中,值的类型与dao层响应方 ...

最新文章

  1. Word中的图片显示出不来的解决办法
  2. 计算机java语言答案_【计算机二级Java语言】卷019
  3. hisi mmz内存管理
  4. CF1594F-Ideal Farm【构造】
  5. Java使用者的延期执行
  6. 类支付宝微信密码输入框
  7. ORACLE--Connect By、Level、Start With的使用(Hierarchical query-层次查询)
  8. (转)MTK softkey流程 必看
  9. Flutter TextField设置默认值默认值和光标位置
  10. Matlab中的逻辑运算与,||与|的区别
  11. 红外测试操作步骤_红外分光测油仪操作步骤及注意事项
  12. 华为云学院-人人学loT学习笔记- 第四章 物联网关 汇聚回传
  13. 护眼软件Linux,四个 Linux 下的“护眼”软件解析
  14. 珍重了,我亲爱的朋友们
  15. MySQL设计一张学生表选择的数据类型保存学号,姓名,性别,出生日期,入学日期,家庭住址信息。
  16. 综合素质能力测试软件,儿童综合素质体检测评系统
  17. 北京航空航天大学计算机考研推免比例,2021考研报考人数公布!今年人数暴增了吗?...
  18. 爬虫抓取京东、苏宁、唯品会商品价格
  19. 一文读懂阿里云挑战 AWS 的底气 | 2018•大复盘
  20. 2017年总结-我的学习之路

热门文章

  1. python电影名称词云_python-词云
  2. PHP怎样防止小数点精度不丢失,javascript小数精度丢失的完美解决方法
  3. python3.6安装包多大_win10下Python3.6安装、配置以及pip安装包教程
  4. dict格式转字符串两种方法的区别
  5. git只添加指定类型的文件的.gitignore规则
  6. Java竞赛目的_ACM竞赛 Java编程小结
  7. 程序员送女朋友的礼物:域名和祝福视频
  8. 第11章:项目风险管理—章节重点
  9. ElementUI中的el-form怎样格式化显示1和0为是和否
  10. Android中使用getDrawable时提示:Call requires API level 21(current min is 15)