天天看點

hibernate3第六章之性能優化相關幾個問題_1

一、1+N問題,也叫N+1問題

1.問題描述如testQueryByNoLazy方法所示:

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

import org.hibernate.*;
import org.hibernate.cfg.AnnotationConfiguration; 
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class Modeltest {

	private static SessionFactory sf=null;
	@BeforeClass
	public static void beforeClass(){
		sf=new AnnotationConfiguration().configure().buildSessionFactory();
	}
	@Test
	public void testSave(){
		//先插入c再插入t保證每個t都關聯不同的c
		Session session=sf.getCurrentSession();
		session.beginTransaction();
		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();
	}

	@Test
	public void testQueryByNoLazy(){
		//預設Topic中是Eager,這樣取資料的話會發出11條sql語句(1條取所有t,10條取c)由于是eager的原因
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic").list();
		for(Topic  t :  topics){
			System.out.println(t.getTitle()+":"+t.getId());
		}
		session.getTransaction().commit();
	}
	@Test
	public void testQueryByLazy(){
		//将預設的fetch設定為Lazy,這樣隻會發出1條sql語句(隻取t),但是如果用到其關聯的c的話,用到誰的c會發1條sql語句取c。
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic").list();
		for(Topic  t :  topics){
			System.out.println(t.getTitle()+":"+t.getId()+":"+t.getCategory().getName());
		}
		session.getTransaction().commit();
	}
	@Test
	public void testQueryByCriteria(){
		//Criteria預設使用left join取資料  不會發那麼多sql語句
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Topic> topics=(List<Topic>)session.createCriteria(Topic.class).list();
		for(Topic  t :  topics){
			System.out.println(t.getTitle()+":"+t.getId()+":"+t.getCategory().getName());
		}
		session.getTransaction().commit();
	}
	@Test
	public void testQueryByJoinfetch(){
		//手動設定left join 也不會發出那麼多sql語句
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic t join fetch t.category c").list();
		for(Topic  t :  topics){
			System.out.println(t.getTitle()+":"+t.getId()+":"+t.getCategory().getName());
		}
		session.getTransaction().commit();
	}
	@Test
	public void testQueryByBatchSize(){
		//這個查詢和nolazy方法一樣  隻是在Category類上加了@BatchSize(size=5) 這樣的話會發出3條sql語句(1條取t,2條取c:每一條sql語句使用id in()的形式各取出5條c)
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic").list();
		for(Topic  t :  topics){
			System.out.println(t.getTitle()+":"+t.getId()+":"+t.getCategory().getName());
		}
		session.getTransaction().commit();
	}
	
	@Test
	public void testSchemaExport(){
		new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);
	}
	@AfterClass
	public static void afterClass(){
		sf.close();
	}
}
           
import org.hibernate.annotations.BatchSize;

@Entity
//@BatchSize(size=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;
	} 
}
           
import java.util.ArrayList;
import java.util.Date;
import java.util.List; 
import javax.persistence.*;
 
@Entity 
public class Topic {
	private int id;
	private String title;
	private Category category;
	private Date createDate; 
	@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;
	}//解決方法:<span style="font-family: Arial, Helvetica, sans-serif;">@ManyToOne(fetch=FetchType.LAZY)</span>

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

二、list和iterate效率  

1.list隻往緩存裡寫,不從緩存裡讀;iterate先去緩存裡讀,再去資料庫讀,會先讀出資料的ID,用到該資料的其他屬性後才發出sql語句查詢

2.load也是先查緩存;外連結不能和iterate一起使用,會報錯

import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.*;
import org.hibernate.cfg.AnnotationConfiguration; 
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class Modeltest {

	private static SessionFactory sf=null;
	@BeforeClass
	public static void beforeClass(){
		sf=new AnnotationConfiguration().configure().buildSessionFactory();
	}
	@Test
	public void testSave(){
		//先插入c再插入t保證每個t都關聯不同的c
		Session session=sf.getCurrentSession();
		session.beginTransaction();
		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();
	}

	@Test
	public void testQueryByLazy(){
		//将預設的fetch設定為Lazy,這樣隻會發出1條sql語句(隻取t),但是如果用到其關聯的c的話,用到誰的c會發1條sql語句取c。
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
//		List<Topic> topics=(List<Topic>)session.createCriteria(Topic.class).list();
		List<Topic> topics=(List<Topic>)session.createQuery("from Topic").list();
		for(Topic  t :  topics){
			System.out.println(t.getTitle()+":"+t.getId()+":"+t.getCategory().getName());
		}
		session.getTransaction().commit();
	} 
	@Test
	public void testQueryByIterate(){
		/*如果用left join+iterate的話會報錯:org.hibernate.QueryException: fetch may not be used with scroll() or iterate() [from com.wzy.model.Topic t join fetch t.category c]
		at org.hibernate.hql.ast.tree.FromElement.setFetch(FromElement.java:429)
		at org.hibernate.hql.ast.tree.FromElementFactory.createEntityJoin(FromElementFactory.java:263)
		at org.hibernate.hql.ast.tree.DotNode.dereferenceEntityJoin(DotNode.java:454)
		at org.hibernate.hql.ast.tree.DotNode.dereferenceEntity(DotNode.java:373)
		at org.hibernate.hql.ast.tree.DotNode.resolve(DotNode.java:232)
		at org.hibernate.hql.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:117)
		at org.hibernate.hql.ast.HqlSqlWalker.createFromJoinElement(HqlSqlWalker.java:369)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.joinElement(HqlSqlBaseWalker.java:3452)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElement(HqlSqlBaseWalker.java:3239)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromElementList(HqlSqlBaseWalker.java:3112)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.fromClause(HqlSqlBaseWalker.java:720)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:571)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:288)
		at org.hibernate.hql.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:231)
		at org.hibernate.hql.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:254)
		at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:185)
		at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:136)
		at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:101)
		at org.hibernate.engine.query.HQLQueryPlan.<init>(HQLQueryPlan.java:80)
		at org.hibernate.engine.query.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:94)
		at org.hibernate.impl.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:156)
		at org.hibernate.impl.SessionImpl.iterate(SessionImpl.java:1215)
		at org.hibernate.impl.QueryImpl.iterate(QueryImpl.java:69)
		at com.wzy.model.Modeltest.testQueryByIterate(Modeltest.java:59)
		at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
		at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
		at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
		at java.lang.reflect.Method.invoke(Unknown Source)
		at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
		at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
		at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
		at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
		at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
		at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
		at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
		at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
		at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
		at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
		at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
		at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
		at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
		at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
		at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
		at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
		at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
		at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
		at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
		at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)*/

		//iterate隻會取出資料的Id 程式中用到其他屬性值的時候會再發出sql語句取資料庫中取 ,因為iterate會利用緩存list則不會
		//下面我取的是多的一方,由于1+N問題,會發出多條sql語句将1的一方也取出來
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		//Iterator<Topic> topics=(Iterator<Topic>)session.createQuery("from Topic t join fetch t.category c").iterate();
		Iterator<Topic> topics=(Iterator<Topic>)session.createQuery("from Topic").iterate();
 		while(topics.hasNext()){
			Topic t=topics.next();
			System.out.println(t.getTitle()+":"+t.getId()+":"+t.getCategory().getName());
		}
		//下面我取的是一的一方,這樣就不考慮1+N問題了
		/*Iterator<Category> category=(Iterator<Category>)session.createQuery("from Category").iterate();

		while(category.hasNext()){
			Category c=category.next();
			System.out.println(c.getName());
		}*/
		session.getTransaction().commit();
	} 
	
	@Test
	public void testQueryByList(){
		//聽說如果用left join+iterate的話會報錯 list實作iterater的接口
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Category> categorys=(List<Category>)session.createQuery("from Category").list();
		for(Category  c :  categorys){
			System.out.println(c.getName());
		}
		session.getTransaction().commit();
	} 
	
	@Test
	public void testQueryByIterate2(){
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		//取兩遍Category  sql隻發一遍,從緩存中讀取
		Iterator<Category> category=(Iterator<Category>)session.createQuery("from Category").iterate();
		while(category.hasNext()){
			Category c=category.next();
			System.out.println(c.getName());
		}
		
		Iterator<Category> category2=(Iterator<Category>)session.createQuery("from Category").iterate();
		while(category2.hasNext()){
			Category c2=category.next();
			System.out.println(c2.getName());
		}
		session.getTransaction().commit();
	} 
	
	@Test
	public void testQueryByList2(){
		//取兩遍Category  sql發2遍  不讀緩存,重新整理緩存
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Category> categorys=(List<Category>)session.createQuery("from Category").list();
		for(Category  c :  categorys){
			System.out.println(c.getName());
		}
		List<Category> categorys2=(List<Category>)session.createQuery("from Category").list();
		for(Category  c2 :  categorys){
			System.out.println(c2.getName());
		}
		session.getTransaction().commit();
	} 
	
	@Test
	public void testSchemaExport(){
		new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);
	}
	@AfterClass
	public static void afterClass(){
		sf.close();
	}
}
           
import java.util.ArrayList;
import java.util.Date;
import java.util.List; 
import javax.persistence.*;
 
@Entity 
public class Topic {
	private int id;
	private String title;
	private Category category;
	private Date createDate;
	@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)
	public Category getCategory() {
		return category;
	}
	public void setCategory(Category category) {
		this.category = category;
	}
	public Date getCreateDate() {
		return createDate;
	}
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}
	
	
}
           
import javax.persistence.*;
 

@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;
	} 
}
           

三、緩存

1.一級緩存:session級别緩存,跨session讀不到,不用更改配置(相當局部變量)

2.二級緩存:sessionFatory級别緩存,同一個sessionFatory裡誇session共享資料,誇sessionFatory讀不到(相當全局變量),需要在hibernate配置檔案裡加上打開二級緩存的代碼,并在需要緩存的實體entity上加@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)标簽:

(1)二級緩存是有很多類型的:

HashTable:隻是用來測試的,不能投入使用,隻能用在記憶體裡,不用于叢集環境,支援三級緩存,代碼為org.hibernate.cache.HashtableCacheProvider

EhCache:hibernate3.2預設Cache,記憶體不夠的時候可以存入硬碟,不用于叢集環境,支援三級緩存,代碼為org.hibernate.cache.EhCacheProvider

OSCache:記憶體不夠的時候可以存入硬碟,不用于叢集環境,支援三級緩存,代碼為org.hibernate.cache.OSCacheProvider

SwarmCahce:記憶體不夠的時候可以存入硬碟,支援叢集環境,不支援三級緩存,代碼為org.hibernate.cache.SwarmCacheProvider

JBossCahe1.x::記憶體不夠的時候可以存入硬碟,支援叢集環境,支援三級緩存,代碼為org.hibernate.cache.TreeCacheProvider

JBossCache2:記憶體不夠的時候可以存入硬碟,支援叢集環境,支援三級緩存,代碼為org.hibernate.cache.jbc2.JBossCacheRegionProvider

(2)二級緩存配置檔案代碼(使用EhCache):  先打開二級緩存,再設定緩存類型,再加入緩存配置檔案

<property name="cache.use_second_level_cache">true</property>
        <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
           

在根目錄下加入ehcache.xml

3.查詢緩存(三級緩存):打開query緩存,使用.setCacheable(true)

hibernate配置檔案要在“二級緩存”代碼後加上:<property name="cache.use_query_cache">true</property>

[email protected]有三個參數usage一般都是READ_WRITE,region指定緩存(在ehcache配置檔案中命名的cache):

@Cache(
    CacheConcurrencyStrategy usage();                 (1)
    String region() default "";                       (2)
    String include() default "all";                   (3)
)      
(1) usage: 給定緩存的并發政策(NONE, READ_ONLY, NONSTRICT_READ_WRITE, READ_WRITE, TRANSACTIONAL)
(2) region (可選的):緩存範圍(預設為類的全限定類名或是集合的全限定角色名)
(3) include (可選的):值為all時包括了所有的屬性(proterty), 為non-lazy時僅含非延遲屬性(預設值為all)
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.hibernate.*;
import org.hibernate.cfg.AnnotationConfiguration; 
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class Modeltest {

	private static SessionFactory sf=null;
	@BeforeClass
	public static void beforeClass(){
		sf=new AnnotationConfiguration().configure().buildSessionFactory();
	}
	@Test
	public void testSave(){
		//先插入c再插入t保證每個t都關聯不同的c
		Session session=sf.getCurrentSession();
		session.beginTransaction();
		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();
	}

	@Test
	public void testQueryCache(){ 
		//load,iterate會使用2級緩存,list會存入2級緩存,但不會從2級緩存中取值
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		Category c=(Category)session.load(Category.class, 1); 
		System.out.println(c.getName()); 
		session.getTransaction().commit();
		
		
		Session session2=sf.getCurrentSession();
		session2.beginTransaction();   
		Category c2=(Category)session2.load(Category.class, 1); 
		System.out.println(c2.getName()); 
		session2.getTransaction().commit(); 
		
	} 
	@Test
	public void testQueryCacheByList(){ 
		//load,iterate會使用2級緩存,list會存入2級緩存,但不會從2級緩存中取值
		Session session=sf.getCurrentSession();
		session.beginTransaction();   
		List<Category> categories=(List<Category>)session.createQuery("from Category").setCacheable(true).list(); 
		for(Category c:categories){
			System.out.println(c.getName());
		} 
		session.getTransaction().commit();
		
		
		Session session2=sf.getCurrentSession();
		session2.beginTransaction();   
		List<Category> categories1=(List<Category>)session2.createQuery("from Category").setCacheable(true).list(); 
		for(Category c:categories1){
			System.out.println(c.getName());
		} 
		session2.getTransaction().commit(); 
		
	} 
	
	@Test
	public void testSchemaExport(){
		new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);
	}
	@AfterClass
	public static void afterClass(){
		sf.close();
	}
}
           
import javax.persistence.*;

import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
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;
	}
	
	
	
	
}
           
import java.util.ArrayList;
import java.util.Date;
import java.util.List; 
import javax.persistence.*;
 
@Entity 
public class Topic {
	private int id;
	private String title;
	private Category category;
	private Date createDate; 
	@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)
	public Category getCategory() {
		return category;
	}
	public void setCategory(Category category) {
		this.category = category;
	}
	public Date getCreateDate() {
		return createDate;
	}
	public void setCreateDate(Date createDate) {
		this.createDate = createDate;
	}
	
	
}
           

繼續閱讀