ORM with Hibernate - 对象状态剖析
ORM其实有两个方面,一个是静态的mapping,也就是结构化的关系。一个对象与一张或者多张表怎么样对应。
还有一个方面就是动态关系。在应用中创建,查询,修改一些对象的时候,这些变化如何同步到到DB里面去,反过来,
DB的数据如果变了,怎么样反映到应用中的对象中来。换句话说,是内存中的对象是如何与DB里面的数据同步的。
关于如何mapping,使用hibernate的人都应该懂得,只是另一个动态的方面,初学者不重视,也不在意。不过会经常遇到一些问题,
抱怨hibernate复杂。我今天就和大家分享一下我的学习和理解。
首先理解一个概念,持久化上下文(persistencecontext),也就是一种缓存。它会记住app对object的更改,
择机同步到DB里面去。还有一个概念是持久化管理器(persistencemanager),Session,通过它来操作对象,改变对象
与持久化上下文的关系。
持久化上下文(persistencecontext)
你可以把它看成是一级缓存,他缓存了所有的持久化对象。在Hibernate中每一个Session都有一个内部的持久化上下文。
你也许看不到它,你也不需要手动的开启它,实际上他默认而且必须被开启。但他帮你做了很多事情。
1.变更检查以及后写入。既然是缓存就会发生被缓存的对象snapshot和DB不一致。因为不是所有的对象都变过,Hibernate就有
一定的策略帮你找到变更过的对象,在适当的时候,通常是事务comment的时候更新到DB。见到数据库的压力。同样,在一个事务
中,你也许对某一个对象做了很多次改变,Hibernate会在事务结束的时候合并变化一次更新,这就是后写入。这样可以减少对DB
的lock时间。
2.提供一级缓存,这样,当你要查询一个对象的时候,它会先在cache里面找,有就返回。这样就不用hitDB了。好处你懂得。
3.保证java对象标识。在同一个context里面,如果你对一个row查两次,context保证你只有一个对象产生,这样就保证只有一个
对象与row对应。不会产生冲突,也节约内存。
4.Hibernate可以帮你把这个context扩展到一个用户会话。尤其是在web应用时,很有用。
■Hibernatecandoautomaticdirtycheckingandtransactionalwrite-behind.
■Hibernatecanusethepersistencecontextasafirst-levelcache.
■HibernatecanguaranteeascopeofJavaobjectidentity.
■Hibernatecanextendthepersistencecontexttospanawholeconversation.
尽管如此,你也得善待持久化上下文。既然是缓存,就耗费内存,如果你无所顾忌的在一个事务中load大量不用的对象,就会内存溢出。
持久化上下文和数据库同步也就是Flushing的时机如下
■WhenaTransactionontheHibernateAPIiscommitted
■Beforeaqueryisexecuted
■Whentheapplicationcallssession.flush()explicitly
第二点特别注意,如果你不注意,查一句,改一句,查一句,再改一句,hibernate会不停的flush。performance就会有问题。
下面看看再Hibernate的概念中,对象的不同状态:Transientobjects,Persistentobjects,Removedobjects还有Detachedobjects。
有了持久化上下文(context)的概念以后,就好理解这四种状态了。Transientobjects就是你刚刚创建(new),这时候context不知道有
她,当然也不会管它。一旦你save了它,Hibernate就把它放到context里面cache起来,也就是Persistentobjects了。这时候对他的修改会在
事务flush的时候同步到DB里面去。如果你从DB里面load一个对象出来,它也是Persistentobjects,但是如果你delete它,它就会被hibernate再flush的
时候从DB里面删除。在这之前是Removedobjects,这种对象你最好不要再用它,也不要有对他的引用,因为它就是一个即将被删除的对象。对于一个Persistentobject,
如果Session关闭了,自然也就没有人管了,他就是Detachedobjects。我感觉Detachedobjects和Transientobjects也没有什么本质的区别。表面上看大概Transientobjects
里面有dbid,但Transientobject里面没有。对于Removedobjects,当Session关闭后也就变成Transientobject,因为它和DB里面的数据没有关系。Detachedobjects虽然没有
人管,但是实际上它和DB里面的某个row关联。
关于Detachedobjects多说一点
因为人们经常会用这样的对象,比如你用DAO查出来以后返回到service或UI层。context关闭了,很正常也很必要。需要注意的是,如果你要对
这些对象执行euqals方法的时候,你要从写它的equals核和hashCode方法。尤其是你要用HashSet来存它的时候。因为如果你在一个新的context里面再load一个
和这个Detachedobject对应的同一条row的对象时,他们不是指向同一个对象引用,这样很危险,容易冲突。前面说了,只有再同一个context里面,hibernate才
帮你保证同一对象引用一个row。其实他是靠dbid来判断的。Detachedobject可不一定都有dbid,也许你后面修改了呢,db不知道。在实现equals方法的时候,也不要
用dbid来比,最好用bussinesskey,也就是业务上唯一的一组属性。
再说一下get和load方法的区别
1.load不会查询db,而是返回一个proxy。get会查询db。
2.如果查不到,load会报ObjectNotFoundException。get会返回null。
load方法很有用,有很多时候你指向将他和另外一个对象关联,添加外键,这时候load就够了。这类情况很能改进performance。hibernate只在用到对象id以外属性的时候,
采取查db。