1+N问题:
如果在一个对象里关联另一个对象,同时fetchType为eager,比如最典型的ManyToOne。当你要取many中的对象时,这些被关联对象都会单独再发1条sql,本来应该发1条sql就能解决的问题实际发了1+N条sql,形成1+N问题。
1+N问题重现:
package com.hibernate.demo.model; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; @Entity public class Msg { private int id; private String name; private Topic topic; @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; } @ManyToOne @JoinColumn(name="topicId") public Topic getTopic() { return topic; } public void setTopic(Topic topic) { this.topic = topic; } }
package com.hibernate.demo.model; import java.util.HashSet; import java.util.Set; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; @Entity public class Topic { private int id; private String name; private Set<Msg> msgs = new HashSet<Msg>(); @OneToMany(mappedBy="topic") public Set<Msg> getMsgs() { return msgs; } public void setMsgs(Set<Msg> msgs) { this.msgs = msgs; } @Id 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; } }
以上代码映射如下表结构:
当我要查询所有的msg时,使用如下代码:
@Test public void testLoad(){ Session s = sf.getCurrentSession(); s.beginTransaction(); Query q = s.createQuery("from Msg"); for(Object obj : q.list()){ Msg m = (Msg)obj; System.out.println(m.getName()); } s.getTransaction().commit(); }
实际执行的查询语句如下:
如此,本来应该只执行第一句的,却附加了后面多余的N次查询,故叫做1+N问题。对于OneToMany,一旦设定fetch=FetchType.EAGER,也会出现该问题;
1+N问题的解决
解决方案1:设置fetch= FetchType.LAZY;
解决方案2:使用join fetch,即表连接查询
解决方案3:使用@BatchSize标签,减少不必要的查询次数,但不能从根本上解决;
最佳实践是在1,2两种方案中选择,具体视情况而定。