`
sarin
  • 浏览: 1765158 次
  • 性别: Icon_minigender_1
  • 来自: 大连
博客专栏
E3b14d1f-4cc5-37dd-b820-b6af951740bc
Spring数据库访问系列...
浏览量:174287
C2083dc5-6474-39e2-993e-263652d27795
Android学习笔记
浏览量:369569
5f40a095-b33c-3e8e-8891-606fcf3b8d27
iBatis开发详解
浏览量:189900
B272a31d-e7bd-3eff-8cc4-c0624ee75fee
Objective-C学习...
浏览量:100806
社区版块
存档分类
最新评论

iBatis查询复杂集合

阅读更多
    本文系iBatis开发详解系列文章之在iBatis查询复杂集合
    通常我们使用iBatis的select查询都是映射的简单对象,即便在一个查询中连接多个表也是如此,那么既然iBatis是SQL Mapper,也就是说它可以映射复杂集合,我们来看看如何让对象模型向数据模型(关系型数据模型)靠拢。
    假设在在线购物应用中,我们有用户表User,订单表Order和订单项表OrderItem,它们之间存在的关联是显而易见的。用户可以下订单,而订单中可以包含多个项。
    我们的数据库设计如下:
CREATE TABLE `user` (
`userId`  int(11) NOT NULL AUTO_INCREMENT ,
`userName`  varchar(50) NULL DEFAULT NULL ,
`password`  varchar(32) NULL DEFAULT NULL ,
`mobile`  varchar(11) NULL DEFAULT NULL ,
`email`  varchar(50) NULL DEFAULT NULL ,
`age`  int(3) NULL DEFAULT NULL ,
PRIMARY KEY (`userId`)
)

    订单表为:
CREATE TABLE `order` (
`orderId`  int(11) NOT NULL AUTO_INCREMENT ,
`orderName`  varchar(50) NULL DEFAULT NULL ,
`generateTime`  datetime NULL DEFAULT NULL ,
`userId`  int(11) NULL DEFAULT NULL ,
PRIMARY KEY (`orderId`)
)

    订单项表为:
CREATE TABLE `orderItem` (
`orderItemId`  int(11) NOT NULL AUTO_INCREMENT ,
`itemName`  varchar(50) NULL DEFAULT NULL ,
`quantity`  int(3) NULL DEFAULT NULL ,
`price`  float NULL DEFAULT NULL ,
`orderId`  int(11) NOT NULL ,
PRIMARY KEY (`orderItemId`)
)

    三个表之前通过userId和orderId进行关联,这里仅做示例性说明,并没有添加物理外键关联。
    下面我们设计POJO来描述这三个对象:
package ibatis.model;
public class OrderItem implements java.io.Serializable {
	private Integer orderItemId;
	private String itemName;
	private int quantity;
	private float price;
	private Integer orderId;
	public OrderItem() {
	}
	public OrderItem(Integer orderItemId, String itemName, int quantity,
			float price, Integer orderId) {
		super();
		this.orderItemId = orderItemId;
		this.itemName = itemName;
		this.quantity = quantity;
		this.price = price;
		this.orderId = orderId;
	}
	//Setters And Getters
	public String toString() {
		return "OrderItem [itemName=" + itemName + ", orderItemId=" + orderItemId+ ", orderId=" + orderId + ", price=" + price + ", quantity="
				+ quantity + "]";
	}
}

    我们使用各个属性连描述orderItem表的字段,下面是order对象:
package ibatis.model;
import java.util.Arrays;
public class OrderInfo {
	private Order order;
	private OrderItem[] orderItemList;
	public Order getOrder() {
		return order;
	}
	public void setOrder(Order order) {
		this.order = order;
	}
	public OrderItem[] getOrderItemList() {
		return orderItemList;
	}
	public void setOrderItemList(OrderItem[] orderItemList) {
		this.orderItemList = orderItemList;
	}
	public String toString() {
		return "OrderInfo [order=" + order + ", orderItemList="
				+ Arrays.toString(orderItemList) + "]";
	}
}

    使用数组来存放每个订单的订单项,分别给出setter和getter方法。下面是user对象:
package ibatis.model;
import java.util.Arrays;
public class UserInfo {
	private User user;
	private OrderInfo[] orderList;
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
	public OrderInfo[] getOrderList() {
		return orderList;
	}
	public void setOrderList(OrderInfo[] orderList) {
		this.orderList = orderList;
	}
	public String toString() {
		return "UserInfo [orderList=" + Arrays.toString(orderList) + ", user="+ user + "]";
	}
}

    和OrderInfo类似,我们也使用数组来存放用户所下的订单。
    那么实体对象和数据表我们就都有了,下面来编写iBatis的sqlMap文件来描述它们之间的关系,我们自定义resultMap来说明:
	<resultMap class="ibatis.model.OrderItem" id="ResultOrderItemMap">
		<result property="orderId" column="orderId" />
		<result property="orderItemId" column="orderItemId" />
		<result property="itemName" column="itemName" />
		<result property="quantity" column="quantity" />
		<result property="price" column="price" />
	</resultMap>

    描述OrderItem则是最简单的了,就是刻画各个属性即可。
	<resultMap class="ibatis.model.OrderInfo" id="ResultOrderInfoMap">
		<result property="order.orderId" column="orderId" />
		<result property="order.orderName" column="orderName" />
		<result property="order.generateTime" column="generateTime" />
		<result property="orderItemList" select="pioneer.getOrderItemList"
			column="orderId" />
	</resultMap>
	<select id="getOrderItemList" resultMap="ResultOrderItemMap">
		select
			orderId,
			orderItemId,
			itemName,
			quantity,
			price
		from
			orderItem
		where
			orderId = #orderId#
	</select>

    在刻画OrderInfo时,我们加入了一个select查询,就是查到该订单下的所有订单项,并使用数组保存起来,那么上面这段XML代码就好理解了。
	<resultMap class="ibatis.model.UserInfo" id="ResultUserInfoMap">
		<result property="user.userId" column="userId" />
		<result property="user.userName" column="userName"/>
		<result property="orderList" select="pioneer.getOrderInfoList"
			column="userId" />
	</resultMap>
	<select id="getOrderInfoList" resultMap="ResultOrderInfoMap">
		select
			orderId,
			orderName,
			generateTime
		from
			test.order
		where
			userId = #userId#
	</select>

    和上面的订单项是类似的,我们在查询用户时,也把用户所下的订单都给查出来。最后我们是要获取内系统的所有用户,那么使用下面的这个SQL:
	<select id="getUserInfoList" resultMap="ResultUserInfoMap">
		select
			userId,
			userName
		from
			user
	</select>

    综上的XML表述了这么一个需求,就是查询系统的内的所有用户,以及它们所下的所有订单,并还要查出每个订单项。
    那么示例程序就很简单了:
package ibatis;
import ibatis.model.UserInfo;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
public class UserInfoDemo {
	private static String config = "ibatis/SqlMapConfig.xml";
	private static Reader reader;
	private static SqlMapClient sqlMap;
	static {
		try {
			reader = Resources.getResourceAsReader(config);
		} catch (IOException e) {
			e.printStackTrace();
		}
		sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
	}
	public static void main(String[] args) throws Exception {
		long start = System.currentTimeMillis();
		ArrayList<UserInfo> userInfoList = (ArrayList<UserInfo>) sqlMap
				.queryForList("pioneer.getUserInfoList");
		long end = System.currentTimeMillis();
		System.out.println(userInfoList);
		System.out.println((end - start) + " ms");
	}}

    运行程序,我们可以得到如下输出:

    查询得到的内容为:
    [UserInfo [orderList=[OrderInfo [order=Order [generateTime=Mon Aug 27 21:35:27 CST 2012, orderId=1, orderItems=null, orderName=Mobile Phone], orderItemList=[OrderItem [itemName=Moto MB525, orderItemId=1, orderId=1, price=1000.0, quantity=1], OrderItem [itemName=Moto MB526, orderItemId=2, orderId=1, price=1200.0, quantity=1]]], OrderInfo [order=Order [generateTime=Mon Aug 27 22:28:38 CST 2012, orderId=2, orderItems=null, orderName=Laptop], orderItemList=[OrderItem [itemName=Lenovo X201, orderItemId=3, orderId=2, price=5000.0, quantity=1], OrderItem [itemName=Lenovo X220, orderItemId=4, orderId=2, price=7000.0, quantity=1]]]], user=User [age=0, email=null, mobile=null, password=null, userId=1, userName=Sarin]]]
    他们都是以对象的形式来描述的,因为我们命没有查询user的email,mobile等信息,所以它们是null。
    程序编写完了,但是问题随之而来。先看这么一大堆的输出,我们仅仅有1个用户的两个订单,每个订单仅包含2个项。如果我们的系统内有10000个用户,每个用户下了100个订单,每个订单有5项,那么一次查询结果就会生成500万个对象,显然在数据不是很多时,就已经带来了问题。首先这是一个数据库I/O的问题,大量数据库I/O和内存对象,降低了性能,消耗了资源。其次是N+1查询问题。
    N+1问题也好理解,在本例中,我们要查询一个用户的N个订单,还要查询这N个订单中每个订单的N个订单项,就产生了N+1查询问题。
    iBatis提供了针对每个问题的解决方案,但是却不能同时解决这两个问题。
    针对数据库I/O,我们很容易想到延迟加载的特性,就是对于所有数据并不是一次全部查出,在需要的时候继续进行查询,那么就会大幅度减少数据库的I/O,开启iBatis的延迟加载特性非常简单,只需修改如下XML设置即可:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig      
    PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"      
    "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
	......
	<settings useStatementNamespaces="true" lazyLoadingEnabled="true" />
	......
</sqlMapConfig>

    也就是在settings中设置了lazyLoadingEnabled为true,如果想开启cglib的增强版,需要在类路径下添加相关jar包,并设置enhancementEnabled为true即可。但是要清楚一点,这些设置都是全局的设置,也就是说项目中的其它iBatis查询都将应用延迟加载特性。下面我们来测试一下代码:

    可以看到,在仅有的几条数据时,所消耗的时间也大幅减少,说明延迟加载特性已经启用。
    下面我们来看针对N+1查询问题的解决方案,那就是使用iBatis在resultMap中为我们提供的groupBy属性。我们将上面的XML文件修改如下:
	<resultMap class="ibatis.model.UserInfo" id="ResultUserInfoMapN" groupBy="user.userId">
		<result property="user.userId" column="userId" />
		<result property="user.userName" column="userName"/>
		<result property="orderList" resultMap="pioneer.ResultOrderInfoMapN" />
	</resultMap>
	<resultMap class="ibatis.model.OrderInfo" id="ResultOrderInfoMapN" groupBy="order.orderId">
		<result property="order.orderId" column="orderId" />
		<result property="order.orderName" column="orderName" />
		<result property="order.generateTime" column="generateTime" />
		<result property="orderItemList" resultMap="pioneer.ResultOrderItemMapN" />
	</resultMap>
	<resultMap class="ibatis.model.OrderItem" id="ResultOrderItemMapN">
		<result property="orderId" column="orderId" />
		<result property="orderItemId" column="orderItemId" />
		<result property="itemName" column="itemName" />
		<result property="quantity" column="quantity" />
		<result property="price" column="price" />
	</resultMap>
	<select id="getUserInfoListN" resultMap="ResultUserInfoMapN">
		select
			user.userId as userId,
			user.userName as userName,
			test.order.orderId as orderId,
			test.order.orderName as orderName,
			test.order.generateTime as generateTime,
			orderItem.*
		from
			user join test.order on user.userId=test.order.userId join orderItem on test.order.orderId=orderItem.orderId
		order by
			userId,test.order.orderId,orderItemId
	</select>

    resultMap的配置使用了groupBy属性,其余和上面配置类似,而这次我们的查询语句就合并成一个连接查询,测试时,需要修改POJO中的数组变量为单对象变量,那么执行程序后,我们得到如下效果:

    也可以看到查询效率的显著提升。
    综上所述,延迟加载适用于大型数据集合,但是并非其中的每条记录都会被用到。此方法前期的性能大幅度提高,但是后期仍需加载所需数据。而N+1查询解决方案适用于小型数据集合或者是所有数据都肯定会被用到的数据集合,使得整体性能得到提高。
  • 大小: 35 KB
  • 大小: 36.4 KB
  • 大小: 35.7 KB
12
0
分享到:
评论
2 楼 SunJK000 2012-10-09  
hellostory 写道
用mybatis3的人越来越多,如果有就更好

支持
1 楼 hellostory 2012-09-05  
用mybatis3的人越来越多,如果有就更好

相关推荐

    IMG_20250415_160847.jpg

    IMG_20250415_160847.jpg

    big_dripleaf_stem.png

    big_dripleaf_stem

    计算机求职面试内容与技巧分享-针对应届毕业生的华为、腾讯技术岗位准备指南

    内容概要:本文详细介绍了针对国内顶级科技公司(如华为、腾讯)的计算机求职面试内容与技巧。文章首先概述了技术能力考察的重点领域,包括数据结构与算法、操作系统、计算机网络、数据库以及特定编程语言的深入知识点。接着阐述了项目经验和系统设计方面的考察标准,强调了STAR法则的应用和具体的设计案例。此外,还分别描述了两家公司在面试流程上的不同之处,提供了具体的面试技巧,如代码编写的注意事项、项目回答的数据支持方法、系统设计的关键考量因素以及反问面试官的有效问题。最后,给出了避坑指南和资源推荐,帮助求职者更好地准备面试。 适合人群:即将或计划进入华为、腾讯等大型科技企业工作的应届毕业生和技术人员。 使用场景及目标:①帮助求职者了解并准备好技术面试所需的知识点;②指导求职者如何有效地展示自己的项目经验;③提供系统设计题目的解答思路;④传授面试过程中需要注意的行为规范和沟通技巧。 阅读建议:由于文中涉及大量专业知识和技术细节,建议读者在阅读时结合自身背景有选择地进行重点复习,并利用提供的资源链接进一步深化理解。同时,在准备过程中要注意将理论知识与实际操作相结合,多做练习以增强信心。

    基于SpringBoot的课程设计选题管理系统(源码+数据库+万字文档+ppt)525

    基于SpringBoot的课程设计选题管理系统,系统包含三种角色:管理员、用户,教师主要功能如下。 【用户功能】 系统首页:浏览课程设计选题管理系统的信息。 个人中心:管理个人信息,查看选题进展和历史记录。 课题信息管理:浏览已有的课题信息。 选题信息管理:查看已选择的选题信息。 自拟课题管理:提出和管理个人自拟的课题,。 系统管理:修改个人密码。 【管理员功能】 系统首页:查看系统整体概况。 个人中心:管理个人信息。 学生管理:审核和管理注册学生用户的信息。 教师管理:审核和管理注册教师用户的信息。 课题信息管理:监管和管理系统中的课题信息,包括发布、编辑、删除等。 课题分类管理:管理课题的分类信息。 选题信息管理:查看学生已选题目的情况,包括审批和管理选题流程。 自拟课题管理:审批和管理学生提出的自拟课题。 系统管理:管理系统的基本设置。 【教师功能】 系统首页:查看系统。 个人中心:管理个人信息。 课题信息管理:浏览已有的课题信息。 课题分类管理:管理课题的分类信息。 选题信息管理:查看学生已选题目的情况。 自拟课题管理:提出和管理个人自拟的课题。 系统管理:校园资讯管理。

    橡胶履带牵引车辆改进设计(无极自动变速器方案设计).rar

    橡胶履带牵引车辆改进设计(无极自动变速器方案设计).rar

    1-剑桥大学发布GVAR数据集-社科数据.rar

    剑桥大学发布的GVAR(Global Vector Autoregressive)数据集是用于全球宏观经济分析的重要社科数据资源。该数据集基于GVAR模型开发,旨在量化宏观经济发展对金融机构的影响,并分析全球经济互动。GVAR模型通过处理高维系统中的相互作用,解决了“维度诅咒”问题,适用于国家、地区、行业等多层次的经济分析。数据集包含1979-2016年33个国家的季度数据,可用于冲击情景分析、预测及政策评估。配套的GVAR工具箱(GVAR Toolbox)提供了用户友好的界面,支持MATLAB和Excel操作,便于研究人员开展实际应用。该数据集为经济学、金融学及相关领域的学术研究和政策制定提供了有力支持。

    某汽车联合车间工艺布置图.zip

    某汽车联合车间工艺布置图.zip

    在stm32f407zgt上通过标准库实现w5500tcpserver和client

    在stm32f407zgt上通过标准库实现w5500tcpserver和client,可以ping通速率不快

    基于Python的微信跳一跳游戏程序.zip

    基于Python的微信跳一跳游戏程序

    white_concrete.png

    j

    ElectLines.py

    ElectLines.py

    【Python测试开发】算法能力测试卷:涵盖选择题、填空题、编程题及综合题的全面考核

    内容概要:本文档是一份针对Python测试开发工程师的算法能力测试卷,涵盖选择题、填空题、编程题和综合题四个部分。选择题考察Python基础知识、数据结构与算法、HTTP协议等;填空题涉及递归、排序、设计模式、HTTP请求方法、测试框架等具体知识点;编程题要求完成字符串反转、链表环检测、二叉树最大深度、两数之和及单元测试类的编写;综合题则包括设计自动化测试框架和实现测试报告生成器,旨在评估考生对Python编程和测试开发的全面掌握程度。 适合人群:具备Python编程基础,从事或计划从事测试开发工作的工程师。 使用场景及目标:①作为招聘流程中的技术考核工具;②帮助工程师自测和提升Python测试开发技能;③为企业内部培训提供标准化的评测标准。 阅读建议:此测试卷不仅考察语法和算法,更注重实际编程能力和解决问题的思路。建议考生在准备过程中多动手实践,熟悉常见的算法和数据结构,并掌握常用的测试框架和工具,如pytest、coverage等。同时,理解每个题目背后的设计意图,有助于更好地应对实际工作中的挑战。

    一级减速器成套CAD图【22CAD】.rar

    一级减速器成套CAD图【22CAD】.rar

    beetroots_stage2.png

    beetroots_stage2

    IMG_20250415_104619.jpg

    IMG_20250415_104619.jpg

    吴萌2262040206.zip

    吴萌2262040206.zip

    Android开发banner效果demo源码

    Android开发banner效果,用的是youthbanner的库,你们也可以去找原库demo

    h5py-3.13.0-cp310-cp310-win_amd64.whl

    该资源为h5py-3.13.0-cp310-cp310-win_amd64.whl,欢迎下载使用哦!

    Python实现经典贪吃蛇游戏代码

    内容概要:本文档提供了一个基于Python的贪吃蛇游戏完整代码示例。代码主要使用了Pygame库来创建游戏窗口、处理图形渲染与事件响应。游戏规则简单明了:玩家控制一条绿色的小蛇在黑色背景的游戏区域内移动,通过键盘方向键改变小蛇行进的方向,目的是吃到红色的食物方块使自身变长。当小蛇碰到边界或者自己的身体时,则判定为游戏失败并提示玩家选择是否重新开始或退出游戏。此外,还设置了帧率限制确保游戏流畅度。 适合人群:有一定Python编程基础的学习者,特别是对Pygame库感兴趣的开发者。 使用场景及目标:①作为初学者练习项目,帮助理解Pygame的基本用法;②可用于教学演示,讲解面向对象编程思想以及事件驱动机制;③为后续开发更复杂的游戏打下良好基础。 阅读建议:建议先熟悉Python语言特性及基本语法,再逐步深入研究本代码中的各个函数功能及其调用关系。同时可以尝试修改参数值(如窗口尺寸、颜色配置等),观察不同设置下的效果变化,从而加深对整个程序的理解。

    birch_planks.png

    birch_planks

Global site tag (gtag.js) - Google Analytics