博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Mybatis中的collection、association来处理结果映射
阅读量:7064 次
发布时间:2019-06-28

本文共 6235 字,大约阅读时间需要 20 分钟。

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

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

 
标签: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
listB = mapperB.selectBInfoByEventId(eventId); 8 event.setListB(listB); 9 10 // 获取业务C信息11 List
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   11   26       SELECT27           U.name,28           U.picId,29           A.time30        FROM table_a A31        LEFT JOIN user U ON U.phone = A.phone32       WHERE A.event_id = #{eventId}33   

 

优化分析

  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
eventIds = new ArrayList
(); // 如果考虑去掉重复数据,可以使用集合Set,但是作为Mybatis的输入参数,最后还是需要将Set转化为List。 // 此处直接使用List,因为在业务上排除了重复数据的可能性。 for (int i = 0; i < eventSize; i++) { eventIds.add(userEventList.get(i).getEventId()); } Map
paramsMap = new HashMap
(); paramsMap.put("eventIds", eventIds); paramsMap.put("phone", phone); List
eventInfoList = eventMapper.selectUserEventInfo(paramsMap); // 将查询结果转化为Map存储,方便调用 Map
eventInfoMap = new HashMap
(); 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()); } }

 

 

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

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中使用。
    
   
    
    
   
        
        
       
       
   

 

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

 

你可能感兴趣的文章
精益企业中架构师的角色
查看>>
区块链技术精华:四十种智能合约支持平台(四)
查看>>
美团点评CTO罗道锋确认离职,新东家是快手?
查看>>
Kubernetes首爆严重安全漏洞,请升级你的Kubernetes
查看>>
Scrum丰田之道
查看>>
渔村小厂,如何成长为5G霸王
查看>>
GitHub推出更多课程
查看>>
InfoQ播客:Tal Weiss谈JVM的可观测性、插桩、以及字节码操作
查看>>
独家!支付宝小程序技术架构全解析
查看>>
1100名达摩院“扫地僧”加持,阿里云的下一个十年
查看>>
python学习笔记-类对象的信息
查看>>
Java多线程(4):使用线程池执行定时任务
查看>>
苹果推出开源医学研究框架ResearchKit
查看>>
Java 9的日期时间格式化趋近Unicode区域设置标准
查看>>
.NET Core中的去虚
查看>>
CLion 2016.1新增Python、Swift支持,并改进了C++支持
查看>>
2016 “Better Software East/DevOps East/Agile Dev East”三个会议上的发言
查看>>
为什么要选择Apache Pulsar(一)
查看>>
基于Gitflow分支模型自动化Java项目工作流
查看>>
Azure Stack 云安全概览
查看>>