zoukankan      html  css  js  c++  java
  • 【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)。

  • 相关阅读:
    后端解决跨域
    常用JS验证函数
    Tomcat:IOException while loading persisted sessions: java.io.EOFException解决手记
    js常用小功能示例
    软件开发五个阶段
    子类异常、访问权限与父类的关系
    什么时候需要使用try-catch
    java中的容器组件和非容器组件
    main方法必须是public static void main(String[] args)
    创建线程的方法
  • 原文地址:https://www.cnblogs.com/549294286/p/2993631.html
Copyright © 2011-2022 走看看