首页 > WEB开发 > 后台开发 > HQL和Hibernate性能优化
2014
10-18

HQL和Hibernate性能优化

三、性能问题

关于Hibernate的性能问题实际开发时很少使用,在面试时可能会问到。

1、1+N问题(☆)

在查找多对一(或一对多)“多”的一方时,默认会把“一”的一方查找出来(见《Hibernate关联关系映射及CRUD》一文)。
假设topic和category表中数据如下:

category-topic-msg-2

如果我们想查找所有的Topic,发送一条SQL:select * from Topic即可,如果是HQL则写作:From Topic,通过控制台打印的SQL可以发现,Hibernate一共发送了1+10条SQL语句(一条查询Topic的SQL,十条查询Category的SQL),这种现象称为1+N问题。

解决方法:join fetch

连接抓取(Join fetching):Hibernate 通过在 SELECT 语句使用 OUTER JOIN(外连接)来获得对象的关联实例或者关联集合。

如上面的HQL可以写成:from Topic t left join fetch t.category c,如下:

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());
}

这样,在查询所有Topic时只发送了一条SQL语句。

2、Query.list和Query.iterate的区别

面试时才需要抠下的东西,实际开发中不需要多考虑,直接用Query.list就能解决问题。这两者的区别有如下几点:

  • list一次性取出所有对象;iterate先取出Id,等用到的时候再根据Id取相应的对象。
  • 在同一次会话中(同一个session)list第二次发出仍会去数据库中查询。而第二次遍历iterate返回对象时,先取session缓存(一级缓存)中查找相应的对象,找不到再去数据库中查找。

3、一级缓存、二级缓存和查询缓存

1)一级缓存:session级的缓存,只有在同一个session中才能起作用。

Demo1:如下面的代码,执行了两次load请求同一个Category对象,但由于两次请求在同一个session中,因此只发出了一条sql语句。

Session session = sessionFactory.openSession();
session.beginTransaction();
Category c = (Category)session.load(Category.class, 1);
System.out.println(c.getName());

Category c2 = (Category)session.load(Category.class, 1);
System.out.println(c2.getName());
session.getTransaction().commit();
session.close();

Demo2:下面的代码发出了两条Sql语句,因为两次请求不在同一个session中:

Session session = sessionFactory.openSession();
session.beginTransaction();
Category c = (Category)session.load(Category.class, 1);
System.out.println(c.getName());

session.getTransaction().commit();
session.close();

Session session2 = sessionFactory.openSession();
session2.beginTransaction();
Category c2 = (Category)session2.load(Category.class, 1);
System.out.println(c2.getName());

session2.getTransaction().commit();
session2.close();

2)二级缓存:sessionFactory级的缓存,因此不同的session可以共享缓存数据

使用场景:经常被访问、不会经常改动、数量有限。如部门数据(Department)。

Hibernate中可以使用不同的二级缓存策略,见下图:

08. HQL和Hibernate性能优化6542

① 要想在Hibernate中开启二级缓存,首先需要在hibernate.cfg.xml中开启二级缓存,并指定缓存策略。这儿使用ehcache。

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

② 配置EhCache:ehcache.xml,该配置文件可以在project\etc目录下找到。 【 注:使用Ehcache二级缓存还需要导入ehcache.jar及commons-logging.jar 】

③ 在需要使用二级缓存的类上增加@Cache注解。这儿在Category类中添加@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)注解。

④ 再次测试上面的Demo2,只发出了一条SQL语句。表明Category对象被放入了二级缓存。

3)查询缓存 : 位于二级缓存中(从上面图片的最后一列也可以看出来)

session.load默认使用二级缓存;Query.iterate默认页使用二级缓存;Query.list默认往二级缓存加数据,但是查询的时候不使用,如果要Query.list使用二级缓存,需要打开查询缓存:

<property name="cache.use_query_cache">true</property>

调用Query.setCachable(true)方法指明使用二级缓存,如:

session.createQuery("from Category").setCacheable(true).list();

只有在两次发出的sql语句完全相同时查询缓存才能起作用(可以认为查询缓存使用的map的key为sql语句)

4)缓存算法(了解)

当缓存满了后,要将缓存中的对象清除掉一部分,关于清除部分的选择就是缓存算法要考虑的问题。

① LRU:least recently used

② LFU:least frequently used

③ FIFO:first in, first out

可以在ehcache.xml中指定( memoryStoreEvictionPolicy=”LRU” )

4、悲观锁、乐观锁

大多数情况下,数据库事务的隔离级别都设置成READ_COMMITED【效率问题】,此时如何处理不可重复读的问题?Hibernate给出了两种解决方式:悲观锁和乐观锁。(面试时了解,开发中很少用,用到再去看)


留下一个回复

你的email不会被公开。