我们先来看一个问题:一个User用户对应多张信用卡Card
类User:
package com.demo.beans;
import java.util.List;
public class User {
private int id;
private String name;
private List<Card> cards;
set get省略...
}
类Card:
package com.demo.beans;
public class Card {
private int id;
private String cardName;
private double balance;
private int userId;
set get省略...
}
对应数据库中的表结构:
表user:
CREATE TABLE `user` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=1004 DEFAULT CHARSET=utf8;
表card:
CREATE TABLE `card` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`cardName` varchar(255) DEFAULT NULL,
`balance` double DEFAULT NULL,
`userId` int(11) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=2006 DEFAULT CHARSET=utf8;
我们考虑这样一个需求:从数据库中获得所有用户的信息(包括他所持有的信用卡信息)。
对应的UserCards.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="UserCards">
<typeAlias alias="Card" type="com.demo.beans.Card" />
<typeAlias alias="User" type="com.demo.beans.User"/>
<resultMap class="User" id="UserResult" >
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="cards" select="UserCards.getCardsByUserId" column="id"/>
</resultMap>
<resultMap id="CardResult" class="Card">
<result property="id" column="id" />
<result property="cardName" column="cardName" />
<result property="balance" column="balance" />
<result property="userId" column="userId" />
</resultMap>
<select id="getUserById" resultMap="UserResult" parameterClass="int">
select * from user where id = #value#
</select>
<select id="getCardsByUserId" resultMap="CardResult" parameterClass="int">
select * from card where userId = #value#
</select>
<select id="getAllUsers" resultMap="UserResult">
select * from user
</select>
<insert id="addCard" parameterClass="Card" >
<selectKey resultClass="int" type="post" keyProperty="id" >
select LAST_INSERT_ID() as value
</selectKey>
insert into card values(#id#,#cardName#,#balance#,#userId#)
</insert>
<insert id="addUser" parameterClass="User" >
<selectKey resultClass="int" type="post" keyProperty="id" >
select LAST_INSERT_ID() as value
</selectKey>
insert into user values(#id#,#name#)
</insert>
</sqlMap>
注意上面xml关键部分:
<result property="cards" select="UserCards.getCardsByUserId" column="id"/>
每查一个user,根据他的id来getCardsByUserId查卡信息
我们的单元测试部分:
public void testGetAll(){
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) factory.getBean("userDao");
long start = System.currentTimeMillis();
List<User> userList = userDao.getAllUser();
for(User user : userList){
System.out.println(user.getName());
for(Card card : user.getCards()){
System.out.println(card.getCardName()+"|"+card.getBalance());
}
System.out.println("------------------------");
}
long end = System.currentTimeMillis();
System.out.println(end-start);
}
在数据库中我们添加了1000个左右的用户和1000张卡信息,相互对应
控制台输出如下:
xiaoye994
2011-01-14 10:38:52,218 DEBUG - {conn-102994} Connection
2011-01-14 10:38:52,218 DEBUG - {conn-102994} Preparing Statement: select * from card where userId = ?
2011-01-14 10:38:52,218 DEBUG - {pstm-102995} Executing Statement: select * from card where userId = ?
2011-01-14 10:38:52,218 DEBUG - {pstm-102995} Parameters: [998]
2011-01-14 10:38:52,218 DEBUG - {pstm-102995} Types: [java.lang.Integer]
中信A00998|999.0
------------------------
xiaoye995
2011-01-14 10:38:52,218 DEBUG - {conn-102997} Connection
2011-01-14 10:38:52,218 DEBUG - {conn-102997} Preparing Statement: select * from card where userId = ?
2011-01-14 10:38:52,218 DEBUG - {pstm-102998} Executing Statement: select * from card where userId = ?
2011-01-14 10:38:52,218 DEBUG - {pstm-102998} Parameters: [999]
2011-01-14 10:38:52,218 DEBUG - {pstm-102998} Types: [java.lang.Integer]
中信A00999|1000.0
------------------------
2078
可以看到每次输出一个用户名如"xiaoye995",然后发出一条select * from card where userId = ?语句。
这就是N+1问题:我们有995个用户 那么就会发出995条上面的语句 另外一条则是查询用户的SQL语句select * from user,
这里的N即为995。可以看到,如果要查询所有用户及其卡信息,这样的效率是很差的,总共花了大约2000多毫秒的时间。
解决办法之一,使用懒加载 就是在需要用户卡信息的时候才触发sql调用
<settings lazyLoadingEnabled="true" useStatementNamespaces="true"/>
解决办法之二,ibatis中已经解决了N+1问题,具体看下面:
修改UserCards.xml文件,关键部分如下:
<resultMap class="User" id="UserResult" groupBy="id">
<result property="id" column="id" nullValue="0"/>
<result property="name" column="name"/>
<result property="cards" resultMap="UserCardsN1.CardResult" />
</resultMap>
<select id="getAllUsers" resultMap="UserResult">
select * from user a left join card b on a.id = b.userId
</select>
区别主要体现在groupBy="id"和<result property="cards" resultMap="UserCardsN1.CardResult" />及select用了表连接
控制台输出信息:
2011-01-14 11:10:55,156 DEBUG - {conn-100000} Connection
2011-01-14 11:10:55,171 DEBUG - {conn-100000} Preparing Statement: select * from user a left join card b on a.id = b.userId
2011-01-14 11:10:55,703 DEBUG - {pstm-100001} Executing Statement: select * from user a left join card b on a.id = b.userId
2011-01-14 11:10:55,703 DEBUG - {pstm-100001} Parameters: []
2011-01-14 11:10:55,703 DEBUG - {pstm-100001} Types: []
xiaoye
建行卡|4353.0
招行卡|23112.0
中信A001|2.0
------------------------
king
工行卡|1000.0
中信A002|3.0
------------------------
..........
.........
------------------------
766
可以看到只发出了一条sql语句 输出了所有的信息 大概用时766毫秒
结论:
1)懒加载适合不需要一次性取出用户信息及其关联信息的场景
2)N+1的解决方法是一次性取出的,适合数据量较少的情况
分享到:
相关推荐
N+1问题发生在当我们执行一系列单独的SQL查询来获取关联数据,而不是一次性加载所有所需数据。这可能导致大量的数据库交互,从而降低系统性能。下面将详细介绍几种解决iBATIS中的N+1选择问题的方法。 1. **批处理...
"ibatis解决多对一n+1问题"这个主题聚焦于MyBatis框架中如何高效地处理多对一关联查询,避免出现性能瓶颈的“n+1”问题。这个问题通常发生在查询一对多关系时,如果不对查询进行优化,会导致大量的额外数据库访问,...
maven3+struts2+spring+ibatis,本来是用maven3+struts2+spring+hibernate但考虑到hibernate...2.struts2出现乱码的问题,解决方法:在struts.xml中加入 <constant name="struts.i18n.encoding" value="gb2312" />即可
为了解决这个问题,ibatis(现已更名为MyBatis)提供了一种支持批量更新的机制,能够显著提高数据处理的速度。 #### 批量Update背景与问题 在实际应用中,经常会遇到需要批量更新数据库中的数据的情况,例如:商品...
7. 性能优化:通过合理设计SQL,避免N+1查询问题,使用缓存提高性能。 总之,iBATIS作为一个轻量级的持久层框架,通过提供SQL Maps和Mapped Statements,降低了数据库操作的复杂性,使得开发者可以专注于业务逻辑而...
10. **最佳实践**:了解如何优化Ibatis的使用,如合理设计SQL,避免N+1问题,使用动态SQL等。 这个教程将引导初学者一步步完成Ibatis环境的搭建,理解其基本工作原理,并通过实际操作掌握Ibatis的使用。通过这些...
为了解决N+1问题,即由于懒加载机制导致的多次数据库访问,通常会关闭load-lazy属性,并采用以下策略: - **多结果集映射**:通过定义多个`resultMap`,可以区分不同的数据加载需求。例如,`result`用于快速获取...
- **N+1 Select 问题**: 在一对多关联的情况下,如果每个用户的地址都需要单独查询,则会导致 N+1 次数据库查询,这可能会导致性能问题。 - **优化方案**: 可以通过批处理查询或者使用缓存机制来优化。 ##### 2. 一...
- **Avoiding N+1 Selects (1:1)**:避免N+1次查询问题,提高性能。 - **Complex Collection Properties**:处理集合类型的属性。 - **Avoiding N+1 Selects (1:M and M:N)**:处理一对多或多对多关系时的查询...
6. **最佳实践**:学习如何优化SQL,避免N+1查询问题,以及如何利用缓存提高性能。 7. **实战项目**:通过参与实际项目或复刻JpetStore_4,提升对iBatis的运用能力。 总之,iBatis是一个强大且灵活的持久层框架,...
在这份资料中,你可能会学习到如何编写高效的iBATIS SQL语句,避免N+1问题,以及如何优化查询性能等实用技巧。 8. **案例分析** 文件可能还包含了一些实际案例,展示如何在不同场景下使用iBATIS,如单表操作、多...
6.2.3 避免N+1查询问题 105 6.3 继承 107 6.4 其他用途 109 6.4.1 使用语句类型和DDL 109 6.4.2 处理超大型数据集 109 6.5 小结 115 第7章 事务 116 7.1 事务是什么 116 7.1.1 一个简单的银行转账示例 116 7.1.2 ...
10. **最佳实践**:如何有效地设计Mapper接口和XML文件,避免N+1查询,以及如何利用iBATIS进行性能优化。 学习iBATIS不仅需要理解上述知识点,还需要通过实际项目实践来巩固和深化理解。通过分析提供的源码,我们...
10. **最佳实践**:分享一些使用 iBatis 的最佳实践,如避免N+1查询问题,提高性能的技巧等。 通过学习这个 iBatis 的 demo,开发者可以更好地理解和掌握 iBatis 在实际开发中的运用,提升数据访问层的构建能力。...
这部分内容涉及构造函数、结果元素、自定义类型处理器、继承映射、原始结果以及避免在结果集中多次查询的N+1问题等。 ***支持的类型和数据库类型在相应的章节中有所介绍,同时文档还提供了关于缓存模型的详细信息。...
支持代码生成工具,避免N+1查询问题。 - **缺点**:相较于Hibernate等框架来说,知名度和社区支持相对较低。 ### 3. 在具体项目中如何选择合适的技术栈 在实际项目开发过程中,选择合适的数据库访问技术是至关重要...