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>

4.@Cache有三个参数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;
	}
	
	
}
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值