Hibernate对象状态详解

Hibernate是一个完整的ORM解决方法,它帮助开发者屏蔽了底层对于数据库访问的细节,而且提供了对象状态的概念。所以,对于性能要求不高的系统而言,若是使用Hibernate做为持久化的解决方案,开发者能够更多的去思考对象的状态,而不是底层SQL的执行。可是若是当开发者须要去提高系统性能的时候,咱们就须要去更多的关注底层SQL的执行。sql

本文内容旨在讨论Hibernate对象状态(内容彻底参考Hibernate4.3官方手册)数据库

 Hibernate object states:缓存

1.Transient :session

当一个对象被刚刚new出来的时候而且和Hibernate中的session没有关联,此时它就是一个Transitent状态。Transient状态对象的特色是在数据库中没有记录而且没有oid,同时没有session与其关联。若是该对象再也不有引用指向其时,那么该对象就会被GC所回收,从上面的图中能够清楚看到。app

 2.Persistent:dom

一个Persistent状态的对象会在数据库中存在1条记录而且此时该对象就有oid,而且存在一个session与该对象关联。ide

3.Detached:性能

Detached状态的对象是在数据库中存在记录而且具备oid,可是不存在与之关联的session。spa

 

Transitent------>Persistenthibernate

咱们可使用session的save或者saveOrUpdate或者persist方法来将瞬时状态对象变成持久化状态对象。

 1     public void testSave01() {
 2         Session session = null;
 3         try {
 4             session = HibernateUtil.openSession();
 5             session.beginTransaction();
 6             
 7             DomesticCat fritz  = new DomesticCat();
 8             fritz.setColor("Red");
 9             fritz.setSex('M');
10             fritz.setName("Fritz");
11             
12             Long id = (Long) session.save(fritz);
13             
14             System.out.println("Generated ID is:"+id);
15             
16             session.getTransaction().commit();
17         } catch (Exception e) {
18             if (session != null) {
19                 session.getTransaction().rollback();
20             }
21         } finally {
22             if (session != null) {
23                 session.close();
24             }
25         }
26     }

save方法存储的特色:当save方法执行完毕以后会当即产生1条insert语句,而且会将对象的oid做为返回值返回。与此同时,即便在事务未开启或者事务已经提交的状况下执行save方法一样会产生insert语句。

persist方法存储的特色:它不会当即为对象分配oid,也不会当即产生insert语句。而是会在session.flush()的时候完成这项操做,一般当提交事务的时候,Hibernate会自动完成session.flush()方法的调用。除此以外,若是当事务未开启或者事务已经提交的状况下,执行persist方法,也不会产生任何的sql语句。

除了使用以上方法能够将对象的状态变成持久化以外,还可使用load方法或者query方法获得一个persistent状态的对象。对于persistent状态的对象最大的特色就是当session.flush的时候(事务提交的时候),Hibernate会自动地去检测当前持久化对象与session中维护的对象的状态是否存在不一样,若是不一样,Hibernate会自动的完成更新操做,而不用开发者手动地去执行update语句。

    @Test
    public void persistent1(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            //此时对象的状态为持久化状态
            DomesticCat domesticCat =  (DomesticCat) session.load(DomesticCat.class, new Long(1));
            domesticCat.setName("WangWang");
            //此时不会发送任何的sql
            session.update(domesticCat);
            //由于此时该对象已是持久化状态的对象,因此也不会发送任何的sql语句
            session.save(domesticCat);
            //由于持久化对象的状态已经发生了改变,因此会生成update语句
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

使用load方法的注意事项:

load方法的注意事项:load方法查询一个不存在的对象时,首先会使用 GGLIB 去返回一个代理对象,此时Hibernate不会与数据库交互,当调用真实对象的具体方法时,Hibernate才会发送sql到数据库,若是此时没有数据,Hibernate则会抛出ObjectNotFoundException

使用load获取到代理对象以后,若是在sesion关闭以后才去调用对象的方法,那么因为返回的代理对象还未初始化,此时会引起LazyInitationlException。(后面会在Hibernate的延迟加载分析中结合源码来具体分析其实现)

若是使用get方法进行查询,那么Hibernate会当即发送select语句完成查询,而且不会在返回代理对象,而是返回真实对象,若是查询不到,则返回一个null。

 

除了使用load方法能够将detached状态转变成persistent状态,当咱们须要查询多个对象的时候,一般会采用Hiberante为咱们Query对象来完成列表的查询。而后采用HQL替代传统  的SQL语句。使用Query所查询  到的对象一样也是持久化对象。

    /**
     * 简单查询,查询列表
     */
    @SuppressWarnings("unchecked")
    @Test
    public void query1() {
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            // HQL是基于对象的查询方式,此时查询到全部对象都是持久化状态
            List<DomesticCat> cats = (List<DomesticCat>) session
                    .createQuery("from DomesticCat cat where cat.id > ?")
                    .setParameter(0, new Long(2)).list();
            for (DomesticCat cat : cats) {
                cat.setSex('M');
            }
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }
    }

以上对N个对象修改了状态以后,当事务提交的时候,就会产生N条update语句。

 

Detached ---->Persistent

还能够将detached状态的对象修改为persitent状态:这里咱们借助文档中的例子给出一个典型的场景应用

the application loads an object in the first session(应用第一次使用一个session加载完成对象,session关闭)

the object is passed up to the UI tier (对象返回给视图层)

some modifications are made to the object (用户操做,对象状态被修改)

the object is passed back down to the business logic tier(对象被传递到业务逻辑层,调用持久化层)

the application persists these modifications by calling update() in a second session(此时会使用一个新的session完调用update方法完成对象状态的改变,同时会当即发送1条sql语句到数据库)

使用update方法注意事项:

1.更新一个瞬时状态的对象

@Test
    public void update1(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            //直接update一个瞬时对象,Hibernate会抛出异常
            session.update(cat);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

此时会产生org.hibernate.TransientObjectException: The given object has a null identifier: org.plx.hibernate.domain.DomesticCat

因而可知:Hibernateupdate的时候会去检测当前对象的oid是否存在,若是不存在,Hibernate就认为这个对象是瞬时状态的对象,因此会对外抛出异常。

2.更新一个数据库中没有的对象,可是该对象具备oid

@Test
    public void update2(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setId(new Long(100));
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            /*
             * 此时数据库里其实并无id为100的记录,可是会发送sql语句
             * Hibernate会根据返回的结果去作出判断,若是返回的结果是0
             * 即表示不存在这条记录,Hiberante一样会抛出异常
             */
            session.update(cat);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

org.hibernate.StaleStateException:Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1   

底层确定是经过使用Jdbc中的executeUpdate()方法来获得影响数据库中的记录数,此时为0,Hibernate也会给咱们抛出异常。

3.在session中存在两个相同标识的对象

    @Test
    public void update3(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            //此时会产生一条sql语句
            session.saveOrUpdate(cat);
            Long id = cat.getId();
            //建立一个与session中具备相同id的对象
            DomesticCat cat2 = new DomesticCat();
            cat2.setId(id);
            /*
             * 执行时session会去检测该对象的id,
             * 当发现该对象的id与session中的对象的id值相同,
             * 此时Hiberante也会抛出异常
             */
            session.update(cat2);
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }
    

可是若是Session中已经一个持久化对象,若是此时再次建立一个新的对象,调用update方法的时候就会抛出异常,Hibernate不容许在session中存在两个具备相同id的同类型的对象。

所以对于saveOrUpdate判断对象状态的本质就是看对象的oid是否存在。若是存在,就调用update方法,若是不存在就调用save方法。

Hibernate中还为咱们提供了一个merge方法:

    @Test
    public void merge1(){
        Session session = null;
        try {
            session = HibernateUtil.openSession();
            session.beginTransaction();
            DomesticCat cat = new DomesticCat();
            cat.setName("haha");
            cat.setSex('M');
            cat.setColor("Black");
            //此时会产生一条sql语句
            session.save(cat);
            Long id = cat.getId();
            //建立一个与session中具备相同id的对象
            DomesticCat cat2 = new DomesticCat();
            cat2.setId(id);
            cat2.setName("mimi");
            /*
             * 此时会将cat2的状态拷贝到cat对象中
             */
            session.merge(cat2);
            //此时cat的name已经变成了mimi,再也不是haha
            System.out.println(cat.getName());
            session.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
            if(session != null){
                session.getTransaction().rollback();
            }
        }finally{
            if(session != null){
                session.close();
            }
        }
    }

session调用merge方法首先会去判断session中是否存在与给定对象相同id的对象,若是存在,就将给定对象的状态拷贝至与给定对象相同id的持久化对象,可是给定对象此时依旧是游离状态,它的状态不会发生任何的改变。若是session中不存在与给定对象就有相同id值的对象,那么Hibernate会去数据库中加载或者从新建立出一个新的对象。

Persistent ---->Detached

当session被关闭或者session的一级缓存被清空时(即调用clear()方法),那么对象状态就会从persist转变成detached。注意的是:session.flush()方法只是将持久化对象的状态与数据库中的数据同步而已,所以会发送sql语句,可是session中依旧会保存中数据,不会被清空,所以不会去改变持久化对象的状态。

Persistent ---->Transitent

当调用session.delete方法以后,一个持久化对象就会变成瞬时对象,此时就会产生1条delete的sql语句,而且对象也不会被session所管理。

相关文章
相关标签/搜索