hibernate3第六章之性能优化相关几个问题_1

来源:互联网 发布:深圳冰川网络 张雄 编辑:程序博客网 时间:2024/06/08 01:17

一、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;@BeforeClasspublic static void beforeClass(){sf=new AnnotationConfiguration().configure().buildSessionFactory();}@Testpublic void testSave(){//先插入c再插入t保证每个t都关联不同的cSession 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();}@Testpublic 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();}@Testpublic 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();}@Testpublic 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();}@Testpublic 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();}@Testpublic 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();}@Testpublic void testSchemaExport(){new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);}@AfterClasspublic static void afterClass(){sf.close();}}


import org.hibernate.annotations.BatchSize;@Entity//@BatchSize(size=5)public class Category {private int id;private String name;@Id@GeneratedValuepublic 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@GeneratedValuepublic 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>@ManyToOnepublic 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;@BeforeClasspublic static void beforeClass(){sf=new AnnotationConfiguration().configure().buildSessionFactory();}@Testpublic void testSave(){//先插入c再插入t保证每个t都关联不同的cSession 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();}@Testpublic 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();} @Testpublic 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();} @Testpublic 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();} @Testpublic 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();} @Testpublic 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();} @Testpublic void testSchemaExport(){new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);}@AfterClasspublic 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@GeneratedValuepublic 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@GeneratedValuepublic 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;@BeforeClasspublic static void beforeClass(){sf=new AnnotationConfiguration().configure().buildSessionFactory();}@Testpublic void testSave(){//先插入c再插入t保证每个t都关联不同的cSession 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();}@Testpublic 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(); } @Testpublic 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(); } @Testpublic void testSchemaExport(){new SchemaExport(new AnnotationConfiguration().configure()).create(false, true);}@AfterClasspublic 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@GeneratedValuepublic 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@GeneratedValuepublic 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;}}
 


0 0