hibernate性能优化

1.使用dynamic-insert和dynamic-update
当需要更新或是插入部分数据时,hibernate默认户更新全部字段,若是目标表的字段特别多,将会对性能产生影响,因此此时可以设置dynamic-insert和dynamic-update
实例:
<class name="com.example.A" table="a" dynamic-update="true"></class>
2.延迟加载
当面对一对多等关系时,若是需要在查询1方时查询出关联的多方,则这种关联很方便,若是只是要使用1方的部分属性,那么查询时查询出关联到多方就会带来不必要的性能开销。此时使用延迟加载使程序的性能得到优化。延迟加载只有在session没有关闭的情况下,去获取多对应的数据集合时才会去动态的加载。所以在hibernate3中默认使用的是延迟加载。
如,若是没有使用延迟加载。
session的load方法会读取关联的数据。
session的load方法可以利用持久化对象实现延迟加载。但是get方法不能。
3.0网上的版本有3种形式实现延迟加载:
1)、通过<class>标签的lazy属性设置为true(默认),激活持久化对象的延迟加载。否则关闭延迟加载。
如:
<hibernate-mapping>
   <class name="" table="" lazy="true">
    </class>
</hibernate-mapping>
这时
session.load(Category.class,new Integer(12));
tx.commit();这时不会去读取数据库,只用使用对象的属性时才会去查询数据库(在session内)
使用session.load的方法获取的并不是一个真正的持久化对象,而是一个代理对象。
代理对象包含目标对象的属性和方法。真正的对象包含在代理对象的handler.target属性中。
为了能生成代理对象,hibernate要求所有持久化对象都要有公开的构造方法。该类不能声明为final类型的。且不能有声明final的方法。

代理对象在仅仅需要得到某个对象的引用(不需要获取该对象的所有属性)时非常有用。如
Category category = (Category)session.load(Category.class.new Integer(1));
Product product = new Product();
product.setCategory(category);
session.save(product);
tx.commit();

此时load方法并没有查询数据库,所以上面非常有用,若是load换成get方法,则先会根据主键查询出对象。
product保存时并不需要知道category的其他属性,只需要知道主键即可。
集合对象的延迟加载
在面对一对多或是多对多等关联时,对Set或是List不必要查询时,可以设置延迟加载。
如:
<class name="com.Category" table="category" lazy="true">//此处的lazy表示该对象使用延迟加载
   ...
  <set name="products" cascade="save-update" inverse="true" lazy="true">
  <key column="category_id"></key>
  <one-to-many class="com.Product"/>
   </set>
</class>
//对上面进行解释
cascade表示当对Category对象进行添加或是更改时,同时对关联的Product集合进行操作。
inverse=true表示,将控制权交给多方进行控制。即插入时先插入1方,再插入多方大的数据,数据库的插入就会避免update语句的产生了。
lazy=true表示在查询category时,不会查询set集合。
若是关闭set处的延迟加载
sql语句会先查询category,然后再通过category_id查询product集合。
设置lazy="extra"
这样即使调用集合的size(),isEmpty()等方法也不会去查询集合元素。
属性的延迟加载
属性的延迟加载使用于当某些持久化类包含BLOB,CLOB等字段时,而查询该持久化类又不需要使用该字段。此时可以考虑使用属性的持久化。
使用方式为<property name="" type="" lazy="true">
注意:属性持久化对性能提升可能不是很大,所以开发中很少使用。
解决LazyInitalizationException。
方法一,在关闭session前,调用Hibernate.initialize(category)//该方法会强制category加载关联的懒加载对象。
如:
Hibernate.initalize(category);
tx.commit();
category.getProduct()是可以调用的。
方法二,使用OpenSessionInview的设计模式。写一个Servlet的过滤器即可实现。
集合对象的抓取策略
适用于1对多或是多对多的关联查询中。
设置抓取策略可以通过持久化对象的映射文件中,或是HQL,或是Criteria。
Hibernate提供了4种抓取策略:
1)、查询抓取:hibernate底层先通过SQL语句查询持久话对象,然后再通过SQL语句查询关联的持久化对象集合。如果没有显示禁止懒加载,会在调用关联结合的时候再去查询。这也形成了N+1的查询。
2)、子查询,先通过SQL查询持久化对象,然后然后通过另一个子查询sql语句查询关联的对象。就是select中还有select。
3)、链接抓取、使用外联结抓取当前对象与关联的集合对象。
4)、批量抓取、首先获取主键或是外键的列表。然后根据列表获取一批对象或是集合
通过抓取策略结合延迟加载可以设置查询语句的执行时机。如果未启动延迟加载,会再加载持久化对象时立即加载关联的持久化对象和集合。即执行抓取策略相关的查询语句。
若是启动了延迟加载,会分布查询所需要查询的数据。
查询抓取
查询抓取首先查询当前的持久化对象,然后根据延迟加载设置判断立即查询还是延迟查询被关联的对象。
Hibernate通过fetch设置抓取策略

<set name="products" cascade="save-update" inverse="true" fetch="select">
     <key column = "category_id"></key>
     <one-to-many class="Product"/>
</set>
执行时,先查询当前对象,然后只有再调用set集合时才会执行select的进一步查询。
是2条SQL语句。
如果上面设置lazy="false",则也会执行2条sql语句,只是会立即执行。
使用映射文件设置抓取策略后仍然可以使用HQL或是Criteria重置抓取策略
如:
Query q = session.createQuery("select c from Category c where inner join fetch c.products where c.id=?");
此时sql语句会
select 类别的字段,产品的字段 from category,product where category.id = producct.category_id
子查询抓取
设置子查询时需要在持久对象的映射文件中配置。如
<set name="products" cascade="save-update" inverse="true" fetch="subselect">
     <key column = "category_id"></key>
     <one-to-many class="Product"/>
</set>
则查询时先查询category对象,然后使用
select product字段属性 from product表 where product.category_id in (select id from category)
链接抓取(只有这个对HQL语句无效)
设置链接抓取后在hibernate底层不会使用多长查询,而只需要通过一条sql语句就可以将对象与关联的对集合对象查询出来。设置join链接后不会对使用HQL生效。只对使用session.get,criteria.list方式生效。
设置链接抓取时,请使用fetch="join"。
sql语句会表现成:
select 类别字段,产品字段 from 类别表,产品表 where 类别表.id=产品表.类别ID and 类别id=?
批量抓取:
使用batch-size="5"进行设置,这样做hibernate会分批进行抓取。若是一共一百条记录会进行抓取20次。
sql语句如下:
select 类别字段 from 类别表
select 产品表字段 from 产品表 where 产品.类别ID in(?,?,?)会先获取5个对象。

Hibernate的N+1问题
当使用hibernate的iterator方法时,若是返回N个持久话对象会需要N+1次查询。
因为hibernate使用了缓存的机制,
所以首次查询持久化对象时,如iterator方法时,会先查询出所有持久化对象的ID,然后再通过ID去数据库里查询对应的持久化数据信息。若是第二次查询时就可以命中对象。
如果通过主键去map里查询没有命中,则会去数据库里查询。
如果iterator总是不能命中数据,那么会打打的影响hiberante的性能,此时可以考虑list方法。
如果大多数情况下命中缓存则会大大提升hiberante的性能。
所以要用辩证的眼光看待这个问题。

你可能感兴趣的:(hibernate性能优化)