`
xiaoye4188
  • 浏览: 30947 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

ibatis N+1问题

阅读更多

我们先来看一个问题:一个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的解决方法是一次性取出的,适合数据量较少的情况

 

 

分享到:
评论
1 楼 baggio7095586 2013-02-18  
好,学习了!

相关推荐

    ibatis n+1选择问题 的几种解决方案

    N+1问题发生在当我们执行一系列单独的SQL查询来获取关联数据,而不是一次性加载所有所需数据。这可能导致大量的数据库交互,从而降低系统性能。下面将详细介绍几种解决iBATIS中的N+1选择问题的方法。 1. **批处理...

    ibatis解决多对一n+1问题(更新上传例子(mybatis)代码)

    "ibatis解决多对一n+1问题"这个主题聚焦于MyBatis框架中如何高效地处理多对一关联查询,避免出现性能瓶颈的“n+1”问题。这个问题通常发生在查询一对多关系时,如果不对查询进行优化,会导致大量的额外数据库访问,...

    maven3+struts2+spring+ibatis

    maven3+struts2+spring+ibatis,本来是用maven3+struts2+spring+hibernate但考虑到hibernate...2.struts2出现乱码的问题,解决方法:在struts.xml中加入 &lt;constant name="struts.i18n.encoding" value="gb2312" /&gt;即可

    ibatis应对批量update

    为了解决这个问题,ibatis(现已更名为MyBatis)提供了一种支持批量更新的机制,能够显著提高数据处理的速度。 #### 批量Update背景与问题 在实际应用中,经常会遇到需要批量更新数据库中的数据的情况,例如:商品...

    iBATIS2.3及其说明文档

    7. 性能优化:通过合理设计SQL,避免N+1查询问题,使用缓存提高性能。 总之,iBATIS作为一个轻量级的持久层框架,通过提供SQL Maps和Mapped Statements,降低了数据库操作的复杂性,使得开发者可以专注于业务逻辑而...

    01_ibatis教程_准备ibatis环境.zip

    10. **最佳实践**:了解如何优化Ibatis的使用,如合理设计SQL,避免N+1问题,使用动态SQL等。 这个教程将引导初学者一步步完成Ibatis环境的搭建,理解其基本工作原理,并通过实际操作掌握Ibatis的使用。通过这些...

    ibatis分页技术

    为了解决N+1问题,即由于懒加载机制导致的多次数据库访问,通常会关闭load-lazy属性,并采用以下策略: - **多结果集映射**:通过定义多个`resultMap`,可以区分不同的数据加载需求。例如,`result`用于快速获取...

    ibatis高级特性

    - **N+1 Select 问题**: 在一对多关联的情况下,如果每个用户的地址都需要单独查询,则会导致 N+1 次数据库查询,这可能会导致性能问题。 - **优化方案**: 可以通过批处理查询或者使用缓存机制来优化。 ##### 2. 一...

    ibatis-sqlMap

    - **Avoiding N+1 Selects (1:1)**:避免N+1次查询问题,提高性能。 - **Complex Collection Properties**:处理集合类型的属性。 - **Avoiding N+1 Selects (1:M and M:N)**:处理一对多或多对多关系时的查询...

    ibatis学习锦集

    6. **最佳实践**:学习如何优化SQL,避免N+1查询问题,以及如何利用缓存提高性能。 7. **实战项目**:通过参与实际项目或复刻JpetStore_4,提升对iBatis的运用能力。 总之,iBatis是一个强大且灵活的持久层框架,...

    iBATIS资料汇总

    在这份资料中,你可能会学习到如何编写高效的iBATIS SQL语句,避免N+1问题,以及如何优化查询性能等实用技巧。 8. **案例分析** 文件可能还包含了一些实际案例,展示如何在不同场景下使用iBATIS,如单表操作、多...

    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 ...

    ibatis 开发指南.

    10. **最佳实践**:如何有效地设计Mapper接口和XML文件,避免N+1查询,以及如何利用iBATIS进行性能优化。 学习iBATIS不仅需要理解上述知识点,还需要通过实际项目实践来巩固和深化理解。通过分析提供的源码,我们...

    一个iBatis的demo

    10. **最佳实践**:分享一些使用 iBatis 的最佳实践,如避免N+1查询问题,提高性能的技巧等。 通过学习这个 iBatis 的 demo,开发者可以更好地理解和掌握 iBatis 在实际开发中的运用,提升数据访问层的构建能力。...

    IBATIS.NET 学习文档

    这部分内容涉及构造函数、结果元素、自定义类型处理器、继承映射、原始结果以及避免在结果集中多次查询的N+1问题等。 ***支持的类型和数据库类型在相应的章节中有所介绍,同时文档还提供了关于缓存模型的详细信息。...

    ibatis spring

    支持代码生成工具,避免N+1查询问题。 - **缺点**:相较于Hibernate等框架来说,知名度和社区支持相对较低。 ### 3. 在具体项目中如何选择合适的技术栈 在实际项目开发过程中,选择合适的数据库访问技术是至关重要...

Global site tag (gtag.js) - Google Analytics