首页 > 代码库 > Hibernate性能优化
Hibernate性能优化
性能优化
1、Session.clear()的应用
不断分页循环的时候,使用clear()方法。
Java中的内存泄漏:
如果使用java打开某一个资源后,一定要关闭,否则就有可能导致内存泄漏。比如:使用java打开一个文件,实际上是java调用了C,C调用了windows底层的API,如果不手动将资源关闭,C语言必须手动回收内存,这就导致了有java引起的内存泄漏。
2、1+N问题
(1)使用@ManyToOne的时候,默认FetchType=EAGER,此时会自动取出One这一方的信息。
(2)解决方法:
a) 将FetchType设置为Lazy。这种方式在One这一方被使用到的时候,才会发出SQL语句
b) 在对应的类上加上@BatchSize,这样会在发出SQL语句,以下取出N条One这一方的信息。(其实没有解决,只是减少了SQL语句的条数)
c) 在HQL语句中,使用join fetch 进行查询。或者使用QBC语句进行查询
实验:
(1)默认的ManyToOne的FetchType为EAGER
// N+1
@Test
public void testQuery1() {
Session session = sf.openSession();
session.beginTransaction();
List<Topic> topics = (List<Topic>) session.createQuery("from Topic")
.list();// 如果此时不设置FetchType=LAZY,则会默认取出Category中的id属性
for(Topic t : topics) {
System.out.println(t.getId() + "-" + t.getTitle());
}
session.getTransaction().commit();
session.close();
}
查看生成的SQL语句如下:
Hibernate:
select
topic0_.id as id1_2_,
topic0_.category_id as category4_2_,
topic0_.createDate as createDa2_2_,
topic0_.title as title3_2_
from
Topic topic0_
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
结论:
默认的FetchType会导致自动获取One一方的属性。
(2)解决方法1:
将FetchType设置为LAZY
@ManyToOne(fetch=FetchType.LAZY)
再次观察生成的结果SQL语句:
Hibernate:
select
topic0_.id as id1_2_,
topic0_.category_id as category4_2_,
topic0_.createDate as createDa2_2_,
topic0_.title as title3_2_
from
Topic topic0_
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9Hibernate:
select
topic0_.id as id1_2_,
topic0_.category_id as category4_2_,
topic0_.createDate as createDa2_2_,
topic0_.title as title3_2_
from
Topic topic0_
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
结论:
可以实现预期结果,但是,当FetchType为LAZY的时候,并不是不会发出查询One这一边的信息的SQL语句,而是在用到One这一方信息的时候,才会发出SQL语句。
(3)解决方法2:
使用@BatchSize(size=n)的形式控制SQL语句执行的次数。
使用如上形式,可以一次性取出n条one这一方的数据,这样就减少了每条数据都会查询One这一方的信息的SQL查询,
测试:
在Category也就是One这一方加入@BatchSize(size=5)
再次运行以上测试程序:
Hibernate:
select
topic0_.id as id1_2_,
topic0_.category_id as category4_2_,
topic0_.createDate as createDa2_2_,
topic0_.title as title3_2_
from
Topic topic0_
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id in (
?, ?, ?, ?, ?
)
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id in (
?, ?, ?, ?, ?
)
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
观察结果:此时关于One这一方的查询操作只做了两次,因为总共有10条数据,每次查询5条,2次就查询完了。
结论:
以上做法只是减少了SQL语句的发出,并没有从根本上解决1+N问题。
(4)解决方法3:
在QL中使用join fetch
测试方法改成如下形式:
//join fetch
@Test
public void testQuery4() {
Session session = sf.openSession();
session.beginTransaction();
List<Topic> topics =
(List<Topic>)session.createQuery("from Topic t left join fetch t.category c").list();
for(Topic t : topics) {
System.out.println(t.getId() + "-" + t.getTitle());
// System.out.println(t.getCategory().getName());
}
session.getTransaction().commit();
session.close();
}
查看执行的SQL语句:
Hibernate:
select
topic0_.id as id1_2_0_,
category1_.id as id1_0_1_,
topic0_.category_id as category4_2_0_,
topic0_.createDate as createDa2_2_0_,
topic0_.title as title3_2_0_,
category1_.name as name2_0_1_
from
Topic topic0_
left outer join
Category category1_
on topic0_.category_id=category1_.id
1-t0
2-t1
3-t2
4-t3
5-t4
6-t5
7-t6
8-t7
9-t8
10-t9
结论:
此方法可以达到预期的效果,在使用到One一方的数据的时候,会发出SQL语句进行查询操作。
3、list_iterate
(1)list()方法直接取出所需要的对象。在同一个Session中,如果有两个list()方法,将会指定两次list操作,每条数据会发出两条SQL语句。list()方法不会使用session级别的缓存
(2)iterate()方法会取出对应的id值,当使用到该对象的属性的时候,才会根据id值去查找所需要的值。iterate()方法会使用session中的缓存,当再次查询的时,就可以直接从缓存中查找,当session中没有的时候,会再次查询并发出SQL语句。
(3)当使用的时候,可以先使用list()方法,再次使用的时候,可以选择iterator()进行查询
测试:
package com.zgy.hibernate.model;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
public class HibernateQLTest {
private static SessionFactory sf;
@BeforeClass
public static void beforeClass() {
sf = HibernateUtil.getSessionFactory();
}
@AfterClass
public static void afterClass() {
sf.close();
}
@Test
public void testSchemaExport() {
new SchemaExport(new Configuration().configure()).create(true,true);
}
@Test
public void testSave() {
Session session = sf.openSession();
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();
session.close();
}
//join fetch
@Test
public void testQueryList() {
Session session = sf.openSession();
session.beginTransaction();
//List<Topic> topics = (List<Topic>)session.createCriteria(Topic.class).list();
List<Category> categories = (List<Category>)session.createQuery("from Category").list();//同一个session中的第一个list()
for(Category c : categories) {
System.out.println(c.getName());
}
List<Category> categories2 = (List<Category>)session.createQuery("from Category").list();//同一个session中的第二个list()
for(Category c : categories2) {
System.out.println(c.getName());
}
session.getTransaction().commit();
session.close();
}
//join fetch
@Test
public void testQueryIterate() {
Session session = sf.openSession();
session.beginTransaction();
//List<Topic> topics = (List<Topic>)session.createCriteria(Topic.class).list();
Iterator<Category> categories = (Iterator<Category>)session.createQuery("from Category").iterate();//同一个Session中的第一个iterate
while(categories.hasNext()) {
Category c = categories.next();
System.out.println(c.getName());
}
Iterator<Category> categories2 = (Iterator<Category>)session.createQuery("from Category").iterate();//同一个session中的第二个iterate()
while(categories2.hasNext()) {
Category c = categories2.next();
System.out.println(c.getName());
}
session.getTransaction().commit();
session.close();
}
public static void main(String[] args) {
beforeClass();
}
}
观察运行结果:
testList()方法测试完后产生的SQL语句如下:
Hibernate:
select
category0_.id as id1_0_,
category0_.name as name2_0_
from
Category category0_
c0
c1
c2
c3
c4
c5
c6
c7
c8
c9
Hibernate:
select
category0_.id as id1_0_,
category0_.name as name2_0_
from
Category category0_
c0
c1
c2
c3
c4
c5
c6
c7
c8
c9
testIterate()方法测试完后产生的SQL语句:
Hibernate:
select
category0_.id as col_0_0_
from
Category category0_
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c0
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c1
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c2
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c3
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c4
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c5
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c6
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c7
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c8
Hibernate:
select
category0_.id as id1_0_0_,
category0_.name as name2_0_0_
from
Category category0_
where
category0_.id=?
c9
Hibernate:
select
category0_.id as col_0_0_
from
Category category0_
c0
c1
c2
c3
c4
c5
c6
c7
c8
c9
Hibernate性能优化