1+N問題的描述:舉例,一個帖子(Category)含有多個主題(Topic),多個主題(Topic)屬於一個帖子(Category),當只須要查詢Topic時不要查詢Category時,若是 @ManyToOne的屬性fetch=FetchType.EAGER,這時查詢全部Topic時,每查詢一個Topic就會多產生一個SQL語句查詢 相關的Category表的數據,這樣要是有N條Topic數據,就會產生1+N條SQL語句。一樣的在@OneToMany的狀況下,要是在Many方 設置fetch=FetchType.EAGER,一樣也會產生1+N的問題。 java
解決方案有三種:mysql
1. fetch=FetchType.LAZY,設爲懶加載sql
2. @BatchSize(size=5)表明一次取5條數據,這樣取5條數據只要發出一條SQL語句,注意是用在被關聯類上的(不建議用)session
3. 迫切左外鏈接檢索 join fetch(Criteria 查詢默認就是join fetchapp
Category類測試
1 package com.lbx.model; 2 3 import javax.persistence.Entity; 4 import javax.persistence.GeneratedValue; 5 import javax.persistence.Id; 6 7 import org.hibernate.annotations.BatchSize; 8 9 @Entity 10 @BatchSize(size=2) 11 public class Category { 12 13 private int id; 14 private String name; 15 16 @Id 17 @GeneratedValue 18 public int getId() { 19 return id; 20 } 21 public void setId(int id) { 22 this.id = id; 23 } 24 public String getName() { 25 return name; 26 } 27 public void setName(String name) { 28 this.name = name; 29 } 30 31 }
Topic類fetch
1 package com.lbx.model; 2 3 import javax.persistence.Entity; 4 import javax.persistence.FetchType; 5 import javax.persistence.GeneratedValue; 6 import javax.persistence.Id; 7 import javax.persistence.ManyToOne; 8 9 @Entity 10 public class Topic { 11 12 private int id; 13 private String title; 14 private Category category; 15 16 @Id 17 @GeneratedValue 18 public int getId() { 19 return id; 20 } 21 public void setId(int id) { 22 this.id = id; 23 } 24 public String getTitle() { 25 return title; 26 } 27 public void setTitle(String title) { 28 this.title = title; 29 } 30 31 //@ManyToOne(fetch=FetchType.LAZY) 32 @ManyToOne 33 public Category getCategory() { 34 return category; 35 } 36 public void setCategory(Category category) { 37 this.category = category; 38 } 39 40 }
hibernate.cfg.xml文件配置ui
1 <?xml version='1.0' encoding='utf-8'?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 5 6 <hibernate-configuration> 7 8 <session-factory> 9 10 <!-- Database connection settings --> 11 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 12 <property name="connection.url">jdbc:mysql://localhost:3306/testhib</property> 13 <property name="connection.username">root</property> 14 <property name="connection.password">root</property> 15 16 <!-- JDBC connection pool (use the built-in) --> 17 <!-- <property name="connection.pool_size">1</property> --> 18 19 <!-- SQL dialect --> 20 <property name="dialect">org.hibernate.dialect.MySQLDialect</property> 21 22 <!-- Enable Hibernate's automatic session context management --> 23 <!-- <property name="current_session_context_class">thread</property> --> 24 25 <!-- Echo all executed SQL to stdout --> 26 <property name="show_sql">true</property> 27 <property name="hibernate.format_sql">true</property> 28 29 <!-- Drop and re-create the database schema on startup --> 30 <property name="hbm2ddl.auto">update</property> 31 32 <mapping class="com.lbx.model.Category"/> 33 <mapping class="com.lbx.model.Topic"/> 34 <mapping class="com.lbx.model.Msg"/> 35 </session-factory> 36 37 </hibernate-configuration>
測試代碼this
1 package com.lbx.model.test; 2 3 import java.util.List; 4 5 import javax.persistence.FetchType; 6 import javax.persistence.ManyToOne; 7 8 import junit.framework.TestCase; 9 10 import org.hibernate.Query; 11 import org.hibernate.Session; 12 import org.hibernate.lucene.Text; 13 14 import com.lbx.hibernate.Util.HibUtil; 15 import com.lbx.model.Category; 16 import com.lbx.model.Msg; 17 import com.lbx.model.Topic; 18 19 public class Test extends TestCase { 20 21 @Text 22 public void save(){ 23 Session session = HibUtil.getSession(); 24 session.beginTransaction(); 25 26 for (int i = 0; i < 3; i++) { 27 Category c = new Category(); 28 c.setName("c" + i); 29 Topic t = new Topic(); 30 t.setTitle("t" + i); 31 t.setCategory(c); 32 session.save(c); 33 session.save(t); 34 } 35 36 session.beginTransaction().commit(); 37 session.close(); 38 } 39 40 //1+N問題(這裏我只要取出Topic就能夠了) 41 @Text 42 public void testHQL_01(){ 43 Session session = HibUtil.getSession(); 44 session.beginTransaction(); 45 46 //List<Topic> topics = (List<Topic>)session.createCriteria(Topic.class).list(); 47 /** 48 * 這裏要是不把Topic類中不設 @ManyToOne(fetch=FetchType.LAZY),這裏就要發不少SQL語句,關聯的表都會查 49 * 可是設了@ManyToOne(fetch=FetchType.LAZY) 以後就不會發出查詢相關表的查詢語句,用到的時候才發出 50 */ 51 Query q = session.createQuery("from Topic"); 52 List<Topic> topics = (List<Topic>)q.list(); 53 System.out.println(topics.size()); 54 for (int i = 0; i < topics.size(); i++) { 55 System.out.println(topics.get(i).getId() + " " + topics.get(i).getTitle()); 56 } 57 session.beginTransaction().commit(); 58 session.close(); 59 } 60 61 //用到被關聯表的信息 62 @Text 63 public void testHQL_02(){ 64 Session session = HibUtil.getSession(); 65 session.beginTransaction(); 66 67 //List<Topic> topics = (List<Topic>)session.createCriteria(Topic.class).list(); 68 /** 69 * 這裏要是不把Topic類中不設 @ManyToOne(fetch=FetchType.LAZY),這裏就要發不少SQL語句,關聯的表都會查 70 * 可是設了@ManyToOne(fetch=FetchType.LAZY) 以後就不會發出查詢相關表的查詢語句,用到的時候才發出 71 */ 72 Query q = session.createQuery("from Topic"); 73 List<Topic> topics = (List<Topic>)q.list(); 74 System.out.println(topics.size()); 75 for (int i = 0; i < topics.size(); i++) { 76 System.out.println(topics.get(i).getId() + " " + topics.get(i).getTitle()); 77 /** 78 * 注意,在這裏要用到Category類的信息,因此就會發出相關的查詢信息 79 */ 80 System.out.println(topics.get(i).getCategory().getId() + " " + 81 topics.get(i).getCategory().getName()); 82 } 83 session.beginTransaction().commit(); 84 session.close(); 85 } 86 87 //@BatchSize的使用,其屬性size=5就表明一次取5個 88 @Text 89 public void testHQL_03(){ 90 Session session = HibUtil.getSession(); 91 session.beginTransaction(); 92 93 //List<Topic> topics = (List<Topic>)session.createCriteria(Topic.class).list(); 94 /** 95 * 這裏要是不把Topic類中不設 @ManyToOne(fetch=FetchType.LAZY),這裏就要發不少SQL語句,關聯的表都會查 96 * 可是設了@ManyToOne(fetch=FetchType.LAZY) 以後就不會發出查詢相關表的查詢語句,用到的時候才發出 97 */ 98 Query q = session.createQuery("from Topic"); 99 List<Topic> topics = (List<Topic>)q.list(); 100 System.out.println(topics.size()); 101 for (int i = 0; i < topics.size(); i++) { 102 System.out.println(topics.get(i).getId() + " " + topics.get(i).getTitle()); 103 /** 104 * 注意,在這裏要用到Category類的信息,因此就會發出相關的查詢信息 105 */ 106 System.out.println(topics.get(i).getCategory().getId() + " " + 107 topics.get(i).getCategory().getName()); 108 } 109 session.beginTransaction().commit(); 110 session.close(); 111 } 112 113 // join fetch,迫切左外鏈接檢索 114 @Text 115 public void testHQL_04(){ 116 Session session = HibUtil.getSession(); 117 session.beginTransaction(); 118 //Criteria 查詢默認就是join fetch 119 //List<Topic> topics = (List<Topic>)session.createCriteria(Topic.class).list(); 120 /** 121 * 這裏要是不把Topic類中不設 @ManyToOne(fetch=FetchType.LAZY),這裏就要發不少SQL語句,關聯的表都會查 122 * 可是設了@ManyToOne(fetch=FetchType.LAZY) 以後就不會發出查詢相關表的查詢語句,用到的時候才發出 123 */ 124 Query q = session.createQuery("from Topic t left join fetch t.category c"); 125 List<Topic> topics = (List<Topic>)q.list(); 126 System.out.println(topics.size()); 127 for (int i = 0; i < topics.size(); i++) { 128 System.out.println(topics.get(i).getId() + " " + topics.get(i).getTitle()); 129 } 130 session.beginTransaction().commit(); 131 session.close(); 132 } 133 134 }