ibatis 使用文档 (下篇)

====================================================================================
第5章 使用高级查询技术

一:用已映射语句关联对象


问题:如果你用过Hibernate或JPA,会想到entity(实体对象 -- 数据库对应JavaBean)之间可能存在关联关系。如一对一、多对多等。伴随就出现了关联获取技术,我们iBATIS如何做到关联获取呢?

使用iBATIS可以根据相关联的对象来定义数据模型,并且让iBATIS立即加载到它们。
例如:
假设你有一个数据库,其中Account记录和Order记录存在关联,建立起这些关联关系之后,请求Account记录时,就会同时获得所有关联的Order对象。

使用起来非常简单,大体通过以下步骤:
第一步:在我们的JavaBean中建立对应关系
public class Order implements Serializable {
	private static final long serialVersionUID = -7307764485708148107L;


	private Integer orderId;
	private String orderAddress;
...


public class Account implements Serializable {
	private static final long serialVersionUID = 3337778507086494766L;


	private Integer accountId;
	private String username;
	//建立了一个包含关联关系
	private List<Order> orders = new LinkedList<Order>();
...



第二步:创建表(我用的MySQL)
CREATE TABLE `orders` (
  `order_id` int(11) NOT NULL AUTO_INCREMENT,
  `order_address` varchar(20) DEFAULT NULL,
  `account_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


CREATE TABLE `account` (
  `account_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`account_id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;


INSERT INTO `account` (`account_id`, `username`) VALUES 
  (1, 'hello'),
  (2, 'world');


INSERT INTO `orders` (`order_id`, `order_address`, `account_id`) VALUES 
  (1, 'hello1', 1),
  (2, 'hello2', 1);


第三步:在SqlMap中(Account.xml中)添加关联获取关系
主要在resultMap中做了手脚,实现方式很简单:
1.书写,我们关联获取时被调用的用于获取关联数据的<select>;被调取的用于获取关联数据的select中(也就是getOrderList中)参数使用#value#(固定格式)。
2.包含关联关系的resultMap标签中,除了添加自己本身表中对应的result,额外添加对应关联属性的result标签。
3.对应关联属性的result标签中,select属性值为被调取的用于获取关联数据的select标签的id。
4.对应关联属性的result标签中,column属性值为代码调取获取数据的<select>中“主表”的关联字段名称。
(<result property="orders" column="account_id" select="getOrderList" />)

<sqlMap namespace="Account">
	<typeAlias alias="account" type="com.partner4java.demo1.bean.Account" />
	<typeAlias alias="order" type="com.partner4java.demo1.bean.Order" />


	<!-- 简单关联方案 -->
	<resultMap class="account" id="accountMap">
		<result property="accountId" column="account_id" />
		<result property="username" column="username" />
		<!-- 通过select来获取的值放入orders;select需要一个关联参数,为本处column -->
		<result property="orders" column="account_id" select="getOrderList" />
	</resultMap>


	<resultMap class="order" id="orderMap">
		<result property="orderId" column="order_id" />
		<result property="orderAddress" column="order_address" />
	</resultMap>


	<select id="getAccount" resultMap="accountMap">
		select * from account
	</select>


	<select id="getOrderList" resultMap="orderMap">
		select * from orders where
		account_id = #value#
	</select>
</sqlMap>


第四步:执行代码
public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder
			.buildSqlMapClient(reader);
	List<Account> accounts = sqlMapClient.queryForList("getAccount", null);
	for(Account account:accounts){
		System.out.println(account);
	}
}
打印:
Account [accountId=1, username=hello, orders=[Order [orderId=1, orderAddress=hello1], Order [orderId=2, orderAddress=hello2]]]
Account [accountId=2, username=world, orders=[]]

延迟加载:
要使用延迟加载,需要编辑SqlMapConfig.xml文件,通过在<setting>元素中将lazyLoadingEnabled属性改为true来启动它。
如果想要使用延迟加载的cglib增强版,则必须下载并将其添加到应用程序的类路径中,同时也必须将<setting>元素中的enhancementEnabled属性改为true。
必须注意的是,这是一个全局设置,因此如果启用了这些特性,SQL映射中所有的已映射语句都将会使用延迟加载。

问题:你是否会揣摩到上面的查询方式会出现“N+1”问题呢,那么如何解决“N+1”问题?
避免N+1问题:
<!-- N + 1查询方案,注意每个resultMap都添加了一个groupBy -->
<resultMap class="order" id="orderJMap" groupBy="orders.order_id">
	<result property="orderId" column="order_id" />
	<result property="orderAddress" column="order_address" />
</resultMap>


<resultMap class="account" id="accountJMap" groupBy="account.account_id">
	<result property="accountId" column="account_id" />
	<result property="username" column="username" />
	<!-- 这里的resultMap为我们上面对order的定义 -->
	<result property="orders" resultMap="Account.orderJMap" />
</resultMap>


<select id="getAccountJ" resultMap="accountJMap">
	select
	account.account_id,account.username,orders.order_id,orders.order_address
	from account join orders on account.account_id = orders.account_id
	order
	by account.account_id,orders.order_id
</select>

public static void main(String[] args) throws Exception {
	Reader reader = Resources.getResourceAsReader("sql-map-config.xml");
	SqlMapClient sqlMapClient = SqlMapClientBuilder
			.buildSqlMapClient(reader);
	List<Account> accounts = sqlMapClient.queryForList("getAccountJ", null);
	for(Account account:accounts){
		System.out.println(account);
	}
}

但是这种方式会丢失不存在order的Account,可以使用left join。


问题:当我执行的不是一个简单insert、update时如何选择标签?
iBATIS框架的设计意图就是要灵活。当无法使用其他类型的已映射语句时,也许就可以使用<statement>已映射语句。
1.使用语句类型和DDL
<statement>类型的已映射语句有点奇怪,它和其他类型(例如<insert>)的已映射语句不同的地方就是,它没有对应的方法可以调用。
这就暗示了,我们不鼓励使用<statement>类型的已映射语句,只有在别无选择的情况下才可以考虑使用它。

<statement id="dropTableTest">
drop table test;
</statement>

sqlMapClient.update("dropTableTest", null);
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值