【hibernate框架】性能优化之1+N问题

1+N问题/典型的面试题
a)Lazy
b)BatchSize
c)join fetch

什么是1+N:如果我在一个对象里面关联另外一个对象,同时fetch=FetchType.EAGER,
最典型的是@ManyToOne。本来我用一条Sql语句就可以解决的,结果发了1条外加N条sql语句。

1+N问题实例剖析
Category.java:

package com.bjsxt.hibernate;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Category {
	private int id;
	private String name;
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

Topic.java:

package com.bjsxt.hibernate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
public class Topic {
	private int id;
	private String title;
	private Category category;
	private Date createDate;
	private List<Msg> msgs = new ArrayList<Msg>();

	@OneToMany(mappedBy="topic")
	public List<Msg> getMsgs() {

		return msgs;
	}
	public void setMsgs(List<Msg> msgs) {
		this.msgs = msgs;
	}

	@Temporal(TemporalType.TIME)
	public Date getCreateDate() {
		return createDate;
	}
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}

	@ManyToOne
	public Category getCategory() {
		return category;
	}
	public void setCategory(Category category) {
		this.category = category;
	}

	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}

}

建表之后先填入记录:

@Test
	public void testSave() {
		Session session = sf.openSession();
		session.beginTransaction();

		//每一个版块ci下面有一个话题ti
		for(int i=0; i<10; i++) {
			Category c = new Category();
			c.setName("c" + i);
			Topic t=new Topic();
			t.setCategory(c);
			t.setTitle("t"+i);
			t.setCreateDate(new Date());
			session.save(c);
			session.save(t);
		}

		session.getTransaction().commit();
		session.close();
	}

//测试1加N问题

@Test
	public void testOneAddNProblem1(){
		Session session = sf.openSession();
		session.beginTransaction();
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic").list();
		for(Topic t:topics){
			System.out.println(t.getId()+"-"+t.getTitle());
		}
		session.getTransaction().commit();
		session.close();

	}

先取出Topic的所有信息,结果:
Hibernate: 
    select
        topic0_.id as id2_,
        topic0_.category_id as category4_2_,
        topic0_.createDate as createDate2_,
        topic0_.title as title2_ 
    from
        Topic topic0_
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
我只是想取出topic而已,为什么多了那么多category?因为在实体类上,topic类的category属性上面的@ManyToOne的fetch默认是EAGER,所以取出topic的时候,会顺带着把topic关联的category出取出来。取category的过程就是,每取一个topic的时候,根据topic中的category_id取category表中按照id号取出相应的category对象。

这就是1+N问题,就是本来你该发一条sql语句,结果发了N条sql语句。

解决方案1:
如果上面的@MangToOne改为@ManyToOne(fetch=FetchType.LAZY)
结果就变成了:
Hibernate: 
    select
        topic0_.id as id2_,
        topic0_.category_id as category4_2_,
        topic0_.createDate as createDate2_,
        topic0_.title as title2_ 
    from
        Topic topic0_
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
只有你取category的时候他才会发出sql语句(按需而发)

解决方案2:
使用QBC面向对象的查询语句:
List<Topic> topics=(List<Topic>)session.createCriteria(Topic.class).list();
这是因为Criteria默认的是用了"表连接"的方式来取数据,两张表做连接来取,而不是另外单独发sql语句来取。
测试结果:
Hibernate: 
    select
        this_.id as id2_0_,
        this_.category_id as category4_2_0_,
        this_.createDate as createDate2_0_,
        this_.title as title2_0_ 
    from
        Topic this_
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9

解决方案3:(这里@ManyToOne的fetch默认仍是EAGER)
@BatchSize(size=5)

首先在Category的类上面加@BatchSize(size=5):

package com.bjsxt.hibernate;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

import org.hibernate.annotations.BatchSize;

@Entity
@BatchSize(size=5)//当你去加载Category的时候一次性加载5条
public class Category {
	private int id;
	private String name;
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

测试:

@Test
	public void testOneAddNProblem1(){
		Session session = sf.openSession();
		session.beginTransaction();
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic").list();
		for(Topic t:topics){
			System.out.println(t.getId()+"-"+t.getTitle());
		}
		session.getTransaction().commit();
		session.close();

	}

测试结果:
Hibernate: 
    select
        topic0_.id as id2_,
        topic0_.category_id as category4_2_,
        topic0_.createDate as createDate2_,
        topic0_.title as title2_ 
    from
        Topic topic0_
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id in (
            ?, ?, ?, ?, ?
        )
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id in (
            ?, ?, ?, ?, ?
        )
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
发现关于Category的查询语句变成了两条,每条查询五个记录。(取出了10条)
这样就没有必要一条一条的去取了。要设成10只要发一个就可以了。
BatchSize只是提高了效率,少发多条sql语句,其实并没有完全解决1+N问题。

解决方案4:join fetch

@Test
	public void testOneAddNProblem2(){
		Session session = sf.openSession();
		session.beginTransaction();
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic t left join fetch t.category c").list();
		for(Topic t:topics){
			System.out.println(t.getId()+"-"+t.getTitle());
		}
		session.getTransaction().commit();
		session.close();

	}

测试结果:
Hibernate: 
    select
        topic0_.id as id2_0_,
        category1_.id as id0_1_,
        topic0_.category_id as category4_2_0_,
        topic0_.createDate as createDate2_0_,
        topic0_.title as title2_0_,
        category1_.name as name0_1_ 
    from
        Topic topic0_ 
    left outer join
        Category category1_ 
            on topic0_.category_id=category1_.id
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9

只发出了一条sql语句,原因是用了"Topic t left join fetch",做了一个外连接,做了一个外连接的话就没有必要每拿出一条来单独再去取了。

转载请注明出处:http://blog.csdn.net/acmman/article/details/43937551

时间: 2024-08-30 09:18:30

【hibernate框架】性能优化之1+N问题的相关文章

Hibernate程序性能优化的考虑要点

程序|性能|优化 本文依照HIBERNATE帮助文档,一些网络书籍及项目经验整理而成,只提供要点和思路,具体做法可以留言探讨,或是找一些更详细更有针对性的资料. 初用HIBERNATE的人也许都遇到过性能问题,实现同一功能,用HIBERNATE与用JDBC性能相差十几倍很正常,如果不及早调整,很可能影响整个项目的进度. 大体上,对于HIBERNATE性能调优的主要考虑点如下: Ø 数据库设计调整 Ø HQL优化 Ø API的正确使用(如根据不同的业务类型选用不同的集合及查询API) Ø 主配置参

Hibernate 性能优化法则

Hibernate 是 Java EE 应用中流行的 JPA 框架,简单易用,但很多使用过 Hibernate 的开发者都普遍反映 Hibernate 性能低下.究其原因,还是使用者没有对 Hibernate 进行过深入理解,对 Hibernate 的应用也只是浮于表面.本文介绍了几种简单实现 Hibernate 性能优化的方法. 启用 Hibernate 数据统计策略 没有测量就没有优化.启用 Hibernate 数据统计策略,用来做优化前后的数据对比. 将 hibernate.generat

前端工程与性能优化之静态资源管理与模板框架

本系列文章从一个全新的视角来思考web性能优化与前端工程之间的关系,通过解读百度前端集成解 决方案小组(F.I.S)在打造高性能前端架构并统一百度40多条前端产品线的过程中所经历的技术尝试 ,揭示前端性能优化在前端架构及开发工具设计层面的实现思路. 在上一部分,我们介绍了静 态资源版本更新与缓存.今天的部分将会介绍静态资源管理与模板框架的用法. 静态资源管理与模板框架 让我们再来看看前面的优化原则表还剩些什么: 很不幸,剩下的优化原则都 不是使用工具就能很好实现的.或许有人会辩驳:"我用某某工具

.NET框架WPF中加载高质量大图慢的性能优化

最近的项目中,遇到一个关于WPF中同时加载多张图片时,内存占用非常高的问题. 问题背景: 在一个ListView中同时加载多张图片,注意:我们需要加载的图片分辨率非常高. 代码: XAML: <Grid>     <Grid.RowDefinitions>         <RowDefinition Height="Auto"/>         <RowDefinition Height="*"/>     <

Hibernate学习之------&amp;gt;Hibernate性能优化的几点建议

1.针对oracle数据库而言,Fetch Size 是设定JDBC的Statement读取数据的时候每次从数据库中取出的记录条数,一般设置为30.50.100.Oracle数据库的JDBC驱动默认的Fetch Size=15,设置Fetch Size设置为:30.50,性能会有明显提升,如果继续增大,超出100,性能提升不明显,反而会消耗内存. 即在hibernate配制文件中进行配制: 1 <property name="hibernateProperties"> 2

详解Java的Hibernate框架中的注解与缓存_java

注解Hibernate注解是一个没有使用XML文件来定义映射的最新方法.可以在除或替换的XML映射元数据使用注解. Hibernate的注解是强大的方式来提供元数据对象和关系表的映射.所有的元数据被杵到一起的代码POJO java文件这可以帮助用户在开发过程中同时要了解表的结构和POJO. 如果打算让应用程序移植到其他EJB3规范的ORM应用程序,必须使用注解来表示映射信息,但仍然如果想要更大的灵活性,那么应该使用基于XML的映射去. 环境设置Hibernate注释 首先,必须确保使用的是JDK

详解Java的Hibernate框架中的缓存与原生SQL语句的使用_java

Hibernate缓存缓存是所有关于应用程序的性能优化和它位于应用程序和数据库之间,以避免数据库访问多次,让性能关键型应用程序有更好的表现. 缓存对Hibernate很重要,它采用了多级缓存方案下文所述: 第一级缓存: 第一级缓存是Session的缓存,是一个强制性的缓存,通过它所有的请求都必须通过. Session对象不断自身的动力的对象,提交到数据库之前. 如果发出多个更新一个对象,Hibernate试图拖延尽可能长的时间做了更新,以减少发出的更新SQL语句的数量.如果您关闭会话,所有被缓存

丰趣海淘:跨境电商平台的前端性能优化实践

原文出自[听云技术博客]:http://blog.tingyun.com/web/article/detail/586 随着互联网的发展,尤其是在2000年之后浏览器技术渐渐成熟,Web产品也越来越丰富,这时我们被浏览器窗口内的丰富"内容"所吸引,关注HTML/CSS,深入研究Dom.Bom和浏览器的渲染机制等,接触JavaScript库,"前端"这个职业,由此而生. 前端技术在这10多年中飞速发展,到了今天,我们可能发现"内容"的美在视觉上是有

ASP.NET性能优化小结(ASP.NET&amp;amp;C#)

ASP.NET: 一.返回多个数据集 检查你的访问数据库的代码,看是否存在着要返回多次的请求.每次往返降低了你的应用程序的每秒能够响应请求的次数.通过在单个数据库请求中返回多个结果集,可以减少与数据库通信的时间,使你的系统具有扩展性,也可以减少数据库服务器响应请求的工作量. 如果用动态的SQL语句来返回多个数据集,那用存储过程来替代动态的SQL语句会更好些.是否把业务逻辑写到存储过程中,这个有点争议.但是我认为,把业务逻辑写到存储过程里面可以限制返回结果集的大小,减小网络数据的流量,在逻辑层也不

快”在细节 J2EE程序的性能优化技巧

j2ee|程序|技巧|性能|优化 应用J2EE平台开发的系统的性能是系统使用者和开发者都关注的问题,本文从服务器端编程时应注意的几个方面讨论代码对性能的影响,并总结一些解决的建议. 关键词:性能,Java,J2EE,EJB,Servlet,JDBC 一.概要 Java 2 Platform, Enterprise Edition (J2EE)是当前很多商业应用系统使用的开发平台,该技术提供了一个基于组件的方法来设计.开发.装配和部署企业级应用程序.J2EE平台提供了一个多层结构的分布式的应用程序