Hibernate 1+N 問題

1+N問題的描述:舉例,多個主題(Topic)屬於一個帖子(Category),一個帖子含有多個主題。當只需要查詢Topic時不要查詢Category時,如果@ManyToOne的屬性fetch=FetchType.EAGER,這時查詢所有Topic時,每查詢一個Topic就會多產生一個SQL語句查詢相關的Category表的數據,這樣要是有N條Topic數據,就會產生1+N條SQL語句。同樣的在@OneToMany的情況下,要是在Many方設置fetch=FetchType.EAGER,同樣也會產生1+N的問題。

 

解決方案有三種:

  1. fetch=FetchType.LAZY,設爲懶加載

  2. @BatchSize(size=5)代表一次取5條數據,這樣取5條數據只要發出一條SQL語句,注意是用在被關聯類上的(不建議用)

  3. 迫切左外連接檢索 join fetch(Criteria 查詢默認就是join fetch)

下面請看代碼:

Category類

Java代碼 複製代碼 收藏代碼
  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. }  
package com.lbx.model;

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

import org.hibernate.annotations.BatchSize;

@Entity
@BatchSize(size=2)
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代碼 複製代碼 收藏代碼
  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. }  
package com.lbx.model;

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

@Entity
public class Topic {

	private int id;
	private String title;
	private 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;
	}
	
	//@ManyToOne(fetch=FetchType.LAZY)
	@ManyToOne
	public Category getCategory() {
		return category;
	}
	public void setCategory(Category category) {
		this.category = category;
	}
	
}

 

 

hibernate.cfg.xml文件配置

Xml代碼 複製代碼 收藏代碼
  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.            
  36.   
  37.     </session-factory>  
  38.   
  39. </hibernate-configuration>  
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/testhib</property>
        <property name="connection.username">root</property>
        <property name="connection.password">root</property>

        <!-- JDBC connection pool (use the built-in) -->
       <!-- <property name="connection.pool_size">1</property> --> 

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <!-- <property name="current_session_context_class">thread</property>  -->

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
        <property name="hibernate.format_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">update</property>

		<mapping class="com.lbx.model.Category"/>
		<mapping class="com.lbx.model.Topic"/>
		<mapping class="com.lbx.model.Msg"/>
		

    </session-factory>

</hibernate-configuration>

 

 

測試代碼

Java代碼 複製代碼 收藏代碼
  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.        
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章