【hibernate】【转】Hibernate的一些使用技巧

http://blog.csdn.net/chjttony/article/details/6042138

1.Hibernate是如今最流行的开源对象关系映射(ORM)持久化框架,SSH框架组合是很多JavaEE工程的首选,java持久化框架(JPA)的设计师是Hibernate的作者,因此对于Hibernate的一些基本知识在JPA学习笔记总结中具体总结。本篇只总结一些Hibernate使用中的一些小技巧。

2.实体对象的3中状态:

实体对象的生命周期是Hibernate中的一个关键概念,实体对象生命周期中有以下3种状态:

(1).Transient(自由状态):有人也叫透明状态,即在内存中自由存在的对象,与数据库中的记录无关。

(2).Persisent(持久态):也叫受管态,即实体对象处于由Hibernate持久化框架所管理的状态,这种状态下实体对象的引用被Hibernate实体容器管理,处于该状态的对象,其变更将被Hibernate持久化到数据库中。

(3).Datached(游离态):处于持久态的实体对象,其对应的Session实例关闭之后该对象就处于游离态。处于游离态的对象可以通过update再次变为持久态。

注意:自由状态和游离态的区别在于:自由状态在数据库中没有对于该对象的记录,而游离态对象在数据库中有对应的记录,只是脱离了Hibernate持久化框架的管理,其状态变化无法更新到数据库表中对应的记录。

3.值对象(VO)、持久化对象(PO)和数据传输对象(DTO):

(1). 值对象(VO):顾名思义是表示值的对象,是相对独立的对象,处于非管理状态。

(2). 持久化对象(PO):是由持久化框架所管理的对象,代表了与数据库中某条记录对应的实体,其变化在事务提交时将被更新到实际数据库中。

(3). 数据传输对象(DTO):持久化和业务逻辑层或者视图层交互时,持久化对象(PO)会首先通过构造变成一个值对象(VO),然后在将这个值对象(VO)传递给所需的层,这个充当数据传递媒介的值对象(VO)此时就被称为数据传输对象(DTO)。

4.Hibernate中get和load的区别:

在Hibernate中获取对象有两种方法:get方法和load方法,但是这两个方法有一些区别,初学者可能不太熟悉,有可能使用不当会出错,它们的区别如下:

(1).get方法是首先从内部缓存中查找,如果没有查找到则从数据库中直接取数据,若数据库中无相应的值时,get方法会返回null。

(2).load方法如果从内部缓存查找不到时,不是直接从数据库中取数据,而是还将查询二级缓存,可能返回对象代理,若数据库中无相应值时,load方法会ObjectNotFoundException抛异常。

5.Hibernate中save和persist方法的区别:

在Hibernate中对象持久化也有两种方法:save和persist,这两个方法是将游离态的对象保存到数据库中变为受管态的对象,但是这两个方法也有如下区别:

(1).若无事务时,persist方法不执行。

(2).若无事务时,save方法先在数据库中插入记录,然后又回滚数据库。

6.Hibernate的查询方式:

Hibernate支持两种查询方式:Hibernate查询语言(HQL)和Criteria查询。

(1).HQL:是Hibernate提供的面向对象查询语言,主要通过Query接口完成,和SQL写法类似,只是在HQL中不再对表和表中的列进行操作,而是对对象和对象的属性进行操作。如查询给定name的Person对象的HQL写法为:Query.createQuery(“Select p from  Person p where p.name = :name”).setParameter(“name”,name);
(2).Criteria:一种比HQL更加面向对象的查询方式,号称是完全面向对象方式的查询方式,和SQL写法相差比较大。和(1)中的例子一样,Criteria的写法如下:

Criteria criteria = Session.createCriteria(Person.class);
//添加查询条件
criteria.add(Restrictions.eq(“name”,name));

对于HQL和Criteria仁者见仁,智者见智,根据个人的习惯选择使用,它们在运行时,Hibernate都会将它们转换成对应的本地的SQL语句。

7.Hibernate延迟加载问题:

在查询一个对象时,如果该对象还引用了其他对象,在查询时会将其引用的所有对象也一同查询出来,这样在大批量数据查询时性能会非常差,为了解决这个问题Hibernate使用了延迟加载(Lazy),即默认只查询要查询的对象本身,该对象所引用的对象只有当真正使用到时再从数据库中查询,这样就可以做到按需查询,大大提高性能。但是延迟加载也会带来其他的问题,比如在一个页面上查询出一个对象,而在其他地方又需要使用该对象的引用对象时,就会出现因为Hibernate会话(Session)关闭对象丢失无法查找所需对象的异常。解决延迟加载的常用办法:

(1).有人提供了一个过滤器,针对Hibernate的叫做:OpenSessionInView的过滤器,针对JPA的叫做OpenEntityManagerInView的过滤器。该过滤器作用是当页面在打开时,Hibernate会话(Session)和JPA的实体管理器(EntityManager)保存打开,这样当真正需要引用对象值时再从数据库中查找就不会出现异常。

(2).查出一个对象后使用Hibernate.initialize(对象)将该对象初始化,这样做延迟加载的机制就不再起作用。

8.线程局部变量ThreadLocal:

ThreadLocal是java的JDK中提供的一个叫做线程局部变量的类,该类的作用是针对共享数据每个线程在其本身保存一份拷贝,避免线程间相互影响。其简单用法如下:

ThreadLocal session = new ThreadLocal();
public static getThreadLocal(){
       //以当前线程作为key来获取线程局部变量的值
       Session s  =  (Session)session.get();
       if(s == null){
       s = this.getSession();
       session.set(s);
}
}

线程局部变量(ThreadLocal)相当于一个Map,其Key是当前的线程,只在一个线程内有效。

在Hibernate中用线程局部变量(ThreadLocal)来管理Hibernate会话(Session),可以实现多个操作共享一个Hibernate会话(Session),从而可以避免反复获取同一个session。

9.Hibernate批量操作:

Hibernate中Session是线程不安全的,而SessionFactory是线程安全的。

Hibernate执行flush刷新方法时,会将一级缓存中的数据与数据库同步。

Hibernate操作大批量数据时可能会造成内存溢出,解决办法:

(1).清除Session中的数据,如:

for(int i = 0; i < 1000000; i++){
       session.save(obj);
       //每100条记录刷新一次session缓存
       if(i % 100 == 0){
       session.flush();
              session.clear();
    }
}

(2).使用无状态会话接口StatelessSession,不和一级、二级缓存交互,不触发事件,通过该接口的所有操作会立刻发送给数据库。

(3).使用Hibernate中的Query.executeUpdate()执行批量更新时,会清除相关的二级缓存。

10.Hibernate中创建动态离线查询:

DatachedCriteria dc = DatecheCriteria.forClass(要查询的类.class);
//添加查询条件
dc. add(Restrictions.eq(“name”,name));
使用动态离线查询:
Criteria criteria = dc.getExecutable(Session对象);

11.Hibernate中的SQL查询和命名查询(NamedQuery):

(1).SQL查询时在hibernate创建query对象时可以直接使用SQL语句,如下:

session.createSQLQuery(SQL语句);

SQL查询常用于一些对于性能要求比较高的地方,但是一般不推荐使用,因为涉及到SQL语句的兼容性,会影响到程序的可移植性。

(2)命名查询(NamedQuery)类似于jdbc中的prepareStatement对象,是一种预编译的查询,可以大大提高效率,创建方法如下:

session.createNamedQuery(HQL查询语句);

12.Hibernate中使用集合时的一个小问题:

在Hibernate和JPA中使用集合对象时,如果使用的是List,在操作时经常会报一个MultiBagException的异常,解决方法为:

(1).最简单最方便的方法:使用Set代替List。

(2).在使用List集合的字段上添加@Cloumn或者@OrderCloumn注解,即给集合指定一个列索引。

13.Hibernate的缓存:

Hibernate中维持了两级缓存:

(1).内部缓存,即一级缓存,属于事务级缓存,在Session对象生命周期中,每个session都维护了一个独立的缓存,为当前session实例所独享。内部缓存有Hibernate自动维护,如需手动干预可以使用以下两个方法:

a.Session.evict:将某个特定的对象从内部缓存中清除。

b.Session.clear:清空内部缓存。

(2).二级缓存,由当前的SessionFactory创建的多个Session实例所共享,在Hibernate中二级缓存涵盖了应用级缓存和分布式缓存。

Hibernate中缓存在以下情况中发挥作用:

a.       通过Id(主键)加载数据时。

b.       延迟加载。

14.ibernate的二级缓存:

(1).引入Hibernate二级缓存的情况:

应用部署在集群环境中。

(2).使用二级缓存的条件:

a.数据不会被第三方修改。

b.数据大小在可接受范围之内。

c.数据的读取大于修改,即数据的更新频率比较低。

d.非关键的数据,可以容忍无效的数据。

(3).使用第三方提供的二级缓存实现:

Hibernate自身提供了二级缓存实现,但是功能比较有限,仅用于在开发调试时使用。Hibernate提供了面向第三方的二级缓存实现接口,常用的二级缓存如下:

a.JCS。  b. EHCache。 c. OSCache。 d. JBoss Cache。e. SwarmCache。

(4).缓存的同步策略:

缓存同步策略决定了数据对象在缓存中的存取规则,为了使得缓存调动遵循正确的应用级事务隔离机制,在使用缓存时必须为每个实体指定相应的缓存同步策略,Hibernate中提供了4中缓存同步策略:

a.       read-only:只读,针对不会发生改变的数据。

b.       nonstrict-read-write:非严格读写,针对于并非同步要求不是很严格,且数据更新频率很低的数据。

c.       read-write:严格可读性缓存,基于时间戳判断机制,实现了read committed事务隔离等级,用于对数据同步严格情况,但是该策略不支持分布式缓存,也是实际应用中使用最多的缓存策略。

d.       transactional:事务型缓存,必须运行在JTA环境,该策略实现了repeatable read事务隔离等级,适用于对关键性数据的缓存。

15.一个Hibernate使用二级缓存的小例子:

(1).在hibernate中启用二级缓存:

在hibernate的配置文件hibernate.cfg.xml中添加以下参数(以EHCache为例):

<hibernate-configuration>
       <session-factory>
              <!--指定缓存提供者-->
              <property name=”hibernate.cache.provide_class”>
                     net.sf.ehcache.hibernate.Provider
              </property>
              ……
       </session-factory>
       ……
</hibernate-configuration>

(2).对象EHCache实现本身进行配置:

默认在类路径下加入ehcache.xml文件,添加如下配置:

<ehcache>
       <diskStore path=”指定缓存存放目录”>
       <defaultCache
              mexElementsInMemory=”10000” //cache中最大允许保存的数据对象数量
              eternal=”false”                            //cache中数据是否为常量
              timeToIdleSeconds=”120”            //缓存数据钝化时间
              timeToLiveSeconds=”120”           //缓存数据的存活时间
              overflowToDisk=”true”                //内存不足时,是否启用硬盘缓存
/>
</ehcache>

(3).指定缓存同步策略:

在各个实体映射文件中指定缓存同步策略,添加如下:

<class name=”hibernate实体类全路径”>
       <cache usage=”read-write”/>
……
    <set name=”……”>.
              <cache usage=”read-only”/>
       </set>
</class>

注意:缓存同步策略既可用于实体类,也可以用于集合属性。

16.       Hibernate的适用和不适用场景:

由于像Hibernate这样的对象关系映射(ORM)在对象和关系型数据库之间操作时,需要将面向对象的操作转化为本地数据库的SQL语句,存在一些性能上的劣势,另外如果对Hibernate熟悉还可以会出现N+1查询和延迟加载的一些问题,所以Hibernate有其适合和不适合的场景。

(1).不适用场景:

a.联机分析处理(OLAP):以大量的查询为主。

b.海里数据,同时性能要求苛刻的系统。

(2).适用场景:

联机事务处理(OLTP)。

原文地址:https://www.cnblogs.com/549294286/p/2993631.html