- 浏览: 1756998 次
- 性别:
- 来自: 大连
博客专栏
-
Spring数据库访问系列...
浏览量:173669
-
Android学习笔记
浏览量:368140
-
iBatis开发详解
浏览量:189271
-
Objective-C学习...
浏览量:99843
最新评论
-
iLidy:
引用[/c
Hibernate持久化对象的生命周期详解 -
fengzigogo:
您好,有这个项目架构的源码下载地址吗?
一种Java Web应用开发框架的构建(基于Struts2+Spring+FreeMarker)之一 -
spring_springmvc:
可以参考最新的文档:如何在eclipse jee中检出项目并转 ...
用Maven构建Java Web开发环境(Jetty容器)之二 -
springdata_springmvc:
spring mvc demo教程源代码下载,地址:http: ...
Spring 3之MVC & Security简单整合开发(二) -
赵庆辉:
看帖回复是美德,楼主讲的很清晰明了,看了豁然开朗.
Java String对象的经典问题(new String())
本文系iBatis开发详解系列文章之在iBatis查询复杂集合
通常我们使用iBatis的select查询都是映射的简单对象,即便在一个查询中连接多个表也是如此,那么既然iBatis是SQL Mapper,也就是说它可以映射复杂集合,我们来看看如何让对象模型向数据模型(关系型数据模型)靠拢。
假设在在线购物应用中,我们有用户表User,订单表Order和订单项表OrderItem,它们之间存在的关联是显而易见的。用户可以下订单,而订单中可以包含多个项。
我们的数据库设计如下:
订单表为:
订单项表为:
三个表之前通过userId和orderId进行关联,这里仅做示例性说明,并没有添加物理外键关联。
下面我们设计POJO来描述这三个对象:
我们使用各个属性连描述orderItem表的字段,下面是order对象:
使用数组来存放每个订单的订单项,分别给出setter和getter方法。下面是user对象:
和OrderInfo类似,我们也使用数组来存放用户所下的订单。
那么实体对象和数据表我们就都有了,下面来编写iBatis的sqlMap文件来描述它们之间的关系,我们自定义resultMap来说明:
描述OrderItem则是最简单的了,就是刻画各个属性即可。
在刻画OrderInfo时,我们加入了一个select查询,就是查到该订单下的所有订单项,并使用数组保存起来,那么上面这段XML代码就好理解了。
和上面的订单项是类似的,我们在查询用户时,也把用户所下的订单都给查出来。最后我们是要获取内系统的所有用户,那么使用下面的这个SQL:
综上的XML表述了这么一个需求,就是查询系统的内的所有用户,以及它们所下的所有订单,并还要查出每个订单项。
那么示例程序就很简单了:
运行程序,我们可以得到如下输出:
查询得到的内容为:
[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设置即可:
也就是在settings中设置了lazyLoadingEnabled为true,如果想开启cglib的增强版,需要在类路径下添加相关jar包,并设置enhancementEnabled为true即可。但是要清楚一点,这些设置都是全局的设置,也就是说项目中的其它iBatis查询都将应用延迟加载特性。下面我们来测试一下代码:
可以看到,在仅有的几条数据时,所消耗的时间也大幅减少,说明延迟加载特性已经启用。
下面我们来看针对N+1查询问题的解决方案,那就是使用iBatis在resultMap中为我们提供的groupBy属性。我们将上面的XML文件修改如下:
resultMap的配置使用了groupBy属性,其余和上面配置类似,而这次我们的查询语句就合并成一个连接查询,测试时,需要修改POJO中的数组变量为单对象变量,那么执行程序后,我们得到如下效果:
也可以看到查询效率的显著提升。
综上所述,延迟加载适用于大型数据集合,但是并非其中的每条记录都会被用到。此方法前期的性能大幅度提高,但是后期仍需加载所需数据。而N+1查询解决方案适用于小型数据集合或者是所有数据都肯定会被用到的数据集合,使得整体性能得到提高。
支持
通常我们使用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查询解决方案适用于小型数据集合或者是所有数据都肯定会被用到的数据集合,使得整体性能得到提高。
评论
2 楼
SunJK000
2012-10-09
hellostory 写道
用mybatis3的人越来越多,如果有就更好
支持
1 楼
hellostory
2012-09-05
用mybatis3的人越来越多,如果有就更好
发表评论
-
iBatis操作DDL和映射继承
2012-09-09 21:46 9266本文系iBatis开发详解系列文章之iBatis操作D ... -
iBatis中使用XML
2012-08-29 19:57 7699本文系iBatis开发详解系列文章之在iBatis中使 ... -
iBatis执行非查询语句(CRUD,函数和过程)
2012-08-26 21:40 9324CRUD操作中除了查询操作,其他都统一称为更新操作,因 ... -
Spring数据库访问之iBatis(二)
2012-06-10 13:56 5795接上文,我们继续来研究Spring和iBatis的整合 ... -
Spring数据库访问之iBatis(一)
2012-01-02 18:45 15988为了丰富博客专栏【Spring数据库访问系列】的内容, ... -
我的视频教程《中小企业OA系统》
2011-07-29 22:27 7749经过5个月的制作,和华章合作的《中小企业OA系统》Ja ... -
iBatis分页(基于Struts2和Freemarker)
2011-05-02 10:05 13112之前介绍过基于Hibernate分页的原理和设计,这里 ... -
Spring数据库访问之ORM(三)
2011-03-02 20:35 17618本文接上一篇继续研究。 之前我们使用的是Hib ... -
Spring数据库访问之ORM(二)
2011-02-16 13:19 23383本文接上一篇继续来研究Spring的ORM模块。 ... -
Spring数据库访问之ORM(一)
2011-01-27 10:54 30806Spring数据库访问中另外一大模块就是ORM,ORM ... -
Spring数据库访问之异常处理
2011-01-19 10:29 27509使用JDBC API时,很 ... -
Spring数据库访问(HSQL)(四)
2011-01-16 21:49 14566本文接上一篇继续研究Spring的JDBC模板。 ... -
Spring数据库访问(HSQL)(三)
2011-01-13 10:07 13439本文接上一篇继续研究JDBC模板。 之前说的都 ... -
Spring数据库访问(HSQL)(二)
2011-01-11 11:27 10730上一篇我们介绍了 ... -
Spring数据库访问(HSQL)(一)
2011-01-09 23:34 13835本部分主要介绍Spring的JDBC模板,JDBC模板 ... -
Spring 3之MVC & Security简单整合开发(三)
2010-12-03 19:04 22067本文接上一篇继续深入研究Security框架。 ... -
Spring 3之MVC & Security简单整合开发(二)
2010-12-01 20:29 60089本文接上一篇继续 ... -
Spring 3之MVC & Security简单整合开发(一)
2010-11-30 22:00 42625Spring的MVC模块是一种简洁的Web应用框架,实 ... -
iBatis查询select详解
2010-08-07 12:19 40529<select>是iBatis已经映射的语 ... -
iBatis查询API
2010-07-31 13:04 17691先说点基础的内容 ...
相关推荐
文档"Ibatis复杂查询语句.doc"所展示的查询语句就是一个很好的例子,展示了Ibatis如何处理复杂的数据库操作。接下来,我们将详细解析这个查询语句中的关键知识点。 1. **动态SQL**: - `<dynamic>`标签用于动态...
iBATIS是一个开源的Java库,它允许程序员将SQL数据库查询与应用程序代码分离,从而简化了数据访问层的实现。...无论你是初学者还是有经验的开发者,这个文档集合都会是你深入理解和使用iBATIS的重要资源。
在“iBATIS-SqlMaps中文教程集合”中,你将找到四本深入浅出的教程,这些教程涵盖了iBATIS的基础使用、高级特性和实战案例,帮助开发者快速掌握并熟练运用iBATIS解决项目中的实际问题。 1. **基础篇** - iBATIS...
在《iBATIS In Action》一书中,作者详细介绍了如何使用iBATIS进行高级查询技术,包括处理复杂的集合属性、映射关系的继承、语句类型和DDL(Data Definition Language),以及如何处理大规模数据集。 1. **使用...
1. 动态SQL:iBATIS的动态SQL功能强大,可以在XML映射文件中进行条件判断、循环等复杂的逻辑操作,生成动态的SQL语句。 2. 映射器与DAO模式:通过映射器接口,iBATIS实现了DAO(Data Access Object)模式,使得业务...
总结起来,Ibatis的动态查询语句配置提供了强大的灵活性,使得我们可以根据实际业务需求构建各种复杂的SQL语句,而无需硬编码。通过熟练掌握和运用这些动态SQL标签,开发者可以提高代码的可读性和维护性,同时减少...
iBatis 动态查询条件详解 iBatis 是一个基于 Java 的持久层框架,它提供了动态查询...iBatis 的动态查询条件提供了强大的功能,可以根据不同的参数生成不同的 SQL 语句,使得开发者可以更方便地实现复杂的查询逻辑。
本文将深入探讨如何在iBATIS中进行主子表查询,以及涉及到的相关技术如一对多关系、日志管理库log4j等。 首先,主子表查询是数据库设计中常见的场景,通常涉及到一个“父”表(主表)和一个或多个“子”表(从表)...
举个例子,假设我们有一个用户ID的列表,我们需要查询这些ID对应的所有用户信息,可以这样配置iBatis的映射文件: ```xml SELECT * FROM users WHERE id IN (" property="ids" open="," close=")"> #{ids[index...
这个入门级别的教程将引导你了解 Ibatis 的基本概念和使用方法,包括增删改查操作、多条件查询以及单对象和数据集合查询。 1. **安装与配置** 在开始使用 Ibatis 之前,你需要在项目中引入 Ibatis 的依赖。如果是 ...
例如,你可能看到如何使用iBatis进行增删改查操作,以及如何处理复杂查询,如联接查询、子查询等。 "iBatis开发指南"则是更全面的官方文档,它包含了iBatis的所有特性和使用方法。从中,你可以学习到动态SQL的使用...
iBATIS提供了集合映射和关联映射的方式来处理这些关系,使我们能够在Java对象中方便地操作这些复杂的数据结构。 其次,属性设置详解涉及到的是如何在iBATIS中设置和获取Java对象的属性,以及如何将这些属性与数据库...
5. **结果集映射**: 映射查询结果到对象,可以是单个对象或集合。 **四、Person实体类与映射文件** 在示例中,我们看到一个名为`Person`的文件,这可能是一个表示人员信息的类。通常,这个类会包含属性,如`Id`、`...
这个开发文档集合包含了"iBATIS SQL Maps 开发指南"、"iBATIS SQL Maps入门教程"以及"ibatis 开发指南"等资源,旨在帮助开发者深入理解和有效地使用iBATIS.NET。 iBATIS的核心理念是将SQL语句与应用程序代码分离,...
对于查询,返回对象或对象集合。 基础知识点包括: 1. SqlMapClient:它是iBATIS的主要接口,负责执行SQL映射和批处理操作。提供了如queryForObject、queryForMap等方法来执行查询或更新操作。 2. sqlmapconfig.xml...
ibatis 是一个基于 Java 的开源持久层框架,它提供了 SQL 映射功能,使得开发者可以通过 XML 文件或注解来定义 SQL 查询语句、参数类型以及结果映射规则,从而极大地简化了 JDBC 编程的复杂性。与 Hibernate 等全 ...
`resultMap`是ibatis中一个重要的概念,用于描述SQL查询结果与Java对象之间的映射关系。通过`resultMap`可以实现复杂的对象关系映射,如一对一、一对多等关联关系。 ### 动态SQL ibatis还支持动态SQL语句,可以...
综上所述,IBATIS(MyBatis)的动态查询功能不仅极大地增强了SQL语句的灵活性,还通过预编译SQL语句的方式提高了应用的安全性和执行效率,是处理复杂查询场景的理想选择。通过深入理解和掌握IBATIS动态查询的技巧,...
7. **结果映射**:用于将查询结果自动映射到Java对象的机制,包括基本类型、复杂对象和集合的映射。 8. **缓存**:Ibatis提供了本地缓存和二级缓存功能,这个jar包可能包含了缓存相关的类和接口。 9. **事务管理**...
7. **结果集映射**:详细说明如何将数据库查询结果映射到Java对象,包括基本类型、复杂类型、集合和自定义映射。 8. **动态SQL**:iBatis允许在SQL语句中使用条件判断,使得SQL更加灵活,减少了大量重复的模板代码...