Sunday, June 27, 2010
hibernate instance state machine

Hibernate的状态
hibernate的各种保存方式的区 (save,persist,update,saveOrUpdte,merge,flush,lock)及 对象的三种状态
hibernate的保存
hibernate对于对象的保存提供了太多的方法,他们之间有很多不同,这里细说一下,以便区别。
一、预备知识
在所有之前,说明一下,对于hibernate,它的对象有三种状态,transient、persistent、detached
下边是常见的翻译办法:
transient:瞬态或者自由态
(new DeptPo(1,”行政部”,20,”行政相关”),该po的实例和session没有关联,该po的实例处于transient)
persistent:持久化状态
(和数据库中记录想影射的Po实例,它的状态是persistent, 通过get和load等得到的对象都是persistent)
detached:脱管状态或者游离态
(1)当通过get或load方法得到的po对象它们都处于persistent,但如果执行delete(po)时(但不能执行事务),该 po状态就处于detached, (表示和session脱离关联),因delete而变成游离态可以通过save或saveOrUpdate()变成持久态
(2)当把session关闭时,session缓存中的persistent的po对象也变成detached
因关闭session而变成游离态的可以通过lock、save、update变成持久态
持久态实例可以通过调用 delete()变成脱管状态。
通过get()或load()方法得到的实例都是持久化状态的。
脱管状态的实例可以通过调用lock()或者replicate()进行持久化。
save()和persist()将会引发SQL的INSERT,delete()会引发SQLDELETE,
而update()或merge()会引发SQL UPDATE。对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQL UPDATE。
saveOrUpdate()或者replicate()会引发SQLINSERT或者UPDATE
二、save 和update区别
把这一对放在第一位的原因是因为这一对是最常用的。
save的作用是把一个新的对象保存
update是把一个脱管状态的对象或自由态对象(一定要和一个记录对应)更新到数据库
三、update 和saveOrUpdate区别
这个是比较好理解的,顾名思义,saveOrUpdate基本上就是合成了save和update,而update只是update;引用 hibernate reference中的一段话来解释他们的使用场合和区别
通常下面的场景会使用update()或saveOrUpdate():
程序在第一个session中加载对象,接着把session关闭
该对象被传递到表现层
对象发生了一些改动
该对象被返回到业务逻辑层最终到持久层
程序创建第二session调用第二个session的update()方法持久这些改动
saveOrUpdate(po)做下面的事:
如果该po对象已经在本session中持久化了,在本session中执行saveOrUpdate不做任何事
如果savaOrUpdate(新po)与另一个与本session关联的po对象拥有相同的持久化标识(identifier),抛出一个异 常
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [org.itfuture.www.po.Xtyhb#5]
saveOrUpdate如果对象没有持久化标识(identifier)属性,对其调用save() ,否则update() 这个对象
四、persist和save区别
这个是最迷离的一对,表面上看起来使用哪个都行,在hibernate reference文档中也没有明确的区分他们.
这里给出一个明确的区分。(可以跟进src看一下,虽然实现步骤类似,但是还是有细微的差别)
主要内容区别:
1,persist把一个瞬态的实例持久化,但是并"不保证"标识符(identifier主键对应的属性)被立刻填入到持久化实例中,标识符 的填入可能被推迟到flush的时候。
2,save, 把一个瞬态的实例持久化标识符,及时的产生,它要返回标识符,所以它会立即执行Sql insert
五、saveOrUpdate,merge和update区别
比较update和merge
update的作用上边说了,这里说一下merge的
如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象覆盖session已有的持久实例
(1)当我们使用update的时候,执行完成后,会抛出异常
(2)但当我们使用merge的时候,把处理自由态的po对象A的属性copy到session当中处于持久态的po的属性中,执行完成后原来 是持久状态还是持久态,而我们提供的A还是自由态
六、flush和update区别
这两个的区别好理解
update操作的是在自由态或脱管状态(因session的关闭而处于脱管状态)的对象//updateSQL
而flush是操作的在持久状态的对象。
默认情况下,一个持久状态的对象的改动(包含set容器)是不需要update的,只要你更改了对象的值,等待hibernate flush就自动更新或保存到数据库了。hibernate flush发生在以下几种情况中:
1, 调用某些查询的和手动flush(),session的关闭、SessionFactory关闭结合
get()一个对象,把对象的属性进行改变,把资源关闭。
2,transaction commit的时候(包含了flush)
七、lock和update区别
update是把一个已经更改过的脱管状态的对象变成持久状态
lock是把一个没有更改过的脱管状态的对象变成持久状态(针对的是因Session的关闭而处于脱管状态的po对象(2),不能针对因 delete而处于脱管状态的po对象)
对应更改一个记录的内容,两个的操作不同:
update的操作步骤是:
(1)属性改动后的脱管的对象的修改->调用update
lock的操作步骤是:
(2)调用lock把未修改的对象从脱管状态变成持久状态-->更改持久状态的对象的内容-->等待flush或者手动flush
八、clear和evcit的区别
clear完整的清除session缓存
evcit(obj)把某个持久化对象从session的缓存中清空。
session.lock(xtyhb,LockMode.NONE);//表示直接到缓存中去找变成持久态的对象
session.lock(xtyhb,LockMode.READ);//先通过ID读数据库该记录的ID看是否有该记录,如果有接着到缓存 中去找变成持久态的对象
(mine)Hibernate中cascade与inverse属性详解
cascade :
1. only be translated to "insert" and "delete"
inverse:
1. only be translated to "update"
Hibernate中cascade与inverse属性详解
http://hi.baidu.com/matrix286/blog/item/d3da6e5977d98689800a18ad.html
关于Hibernate中 cascade 与 inverse 的理解。
您买的Hibernate书是哪一本呢? 孙卫琴的精通Hibernate,还是 深入浅出Hibernate还是那本。。。
我是两本都买了,总体来说还可以,但是,有的地方讲的比较书面化,比如inverse这属性。
在学习Hibernate的过程中最不好理解的就是这两个属性了。
(我当初学习Hibernate的时候,发现网上介绍这两个属性的文章倒是不少,但是,居然有好多都是转帖。。。还有的就是 照书搬~~-_-!!!)。。。
据个例子:书上说 inverse=false时,由主控方维持关系。。。
由于我也是初学者。。。再加上语文水平偏低。。。不理解“维持关系是啥意思”囧~
提示:
(1)如果:您不了解Hibernate的one-to-many或many-to-one的概念。
(2)如果:你不了解 Hibernate的“自由态”“持久态”“游离态”的概念。
(3)如果:您不了解 Hibernate中的“脏数据”的概念。
(4)如果:您对 Hibernate中Session缓存,没有初步了解的话。
(在Hibernate中调用save进行存储数据的时候,并不是马上就对数据库进行insert操作,而是会将其“数据对象(vo)”纳入 Hibernate的Session缓存。)
在上面的4条提示中,如果您对其中的某一条,不是很清楚的话。希望请先了解有关知识。
否则,可能您将 “无法或很难”理解 cascade 或 inverse 这2个属性。
首相,cascade 与 inverse 这两个属性,其实是完全不同的两个东西,想要了解他们各自的“用途与区别”,详见如下介绍:
这里有两个表:
(1)class (班级表)
相应字段:
cid varchar(32) 主键 not-null (班级id)
cname varchar(16) not-null (班级名称)
(2)student (学生表)
相应字段:
sid varchar(32) 主键 not-null (学生id)
sname varchar(16) not-null (学生姓名)
class_id varchar(32) not-null (学生所属班级)
一个班级(class)对应多个学生(student),所以班级表(class)就是“one-to-many”端
反之student就是many-to-one
//--------Class类的代码--------
public class Class implements.....
{
private cId = "";
private cName = "";
private students = java.util.HashMap();
// 省略对应的 geter setter
}
//--------Class.hbm.xml--------
//--------Student 类的代码;*******
public class Student implements.....
{
private sId = "";
private sName = "";
private Class class = null;
// 省略对应的 geter setter
}
// Student.hbm.xml
(一) cascade 的介绍:
当Hibernate持久化一个“临时对象(也叫自由态对象)”时,在默认的情况下(即:没有设置cascade属性或cascade=none 时),Hibernate不会自动“持久化他所关联”的其他临时对象。
上面这些话是什么意思呢? 什么叫不会自动 “持久化”关联的临时对象呢?
看如下代码:
// 创建一个 临时对象(也叫自由态对象)
// 也就是说这个 class 没有被Hibernate纳入Session缓存管理。
Class class = new Class();
//class.id 为自动生成
class.setName("一年级1班");
Student stu = new Student();
//student.id 为自动生成
stu.setName("小白兔");
stu.setClass(class);
// 关键就是这里。。。
class.getStudents().add(stu);
session.save(class);
// 提交
// 注意: Class.hbm.xml文件中,cascade="save-update"并且也没有设置inverse属性,也就是说 inverse=false;
// 此时如果你开启了Hibernate的显示HQL语句功能,那么控制台将会显示如下3条HQL:
//----------------------------------------********
insert into demo.class (cid, cname) values (66666666666666666666666666666666, 一年级1班)
insert into demo.student (sid,sname,class_id) values (8888888888888888811cb2e04c888888, 小白兔, 66666666666666666666666666666666)
update demo.student set class_id=66666666666666666666666666666666 where sid=8888888888888888811cb2e04c888888
//----------------------------------------********
那么为什么会出现,这3条HQL语句呢,我们来一一分析一下:
第1条HQL语句:
其实第一条HQL比较好理解,
当我们调用 session.save(class) 后,在Hibernate进行提交的时候,
会发现“有”一条“新”的数据要插入(insert),所以就往class表中,插入了这条新的class记录。
第2条HQL语句:
注意问题就在这里:
这里为什么又出现了一条insert语句呢?而且还是向student表中插入数据。
我们在上面的代码中,并没有编写类似“session.save(student)”这样的语句啊。
这是为什么呢?
其实原因,是这么回事:因为我们在class端,设置了"级联更新"(即:cascade="save-update"),
也就是说,当Hibernate在向class表中插入“新”对象记录时,会检查“Class对象”所关联的属性(就是
进行操作。
上面讲的这句话到底是什么意思呢?
用你们“人”话说,就是:
因为调用了 class.getStudents().add(stu);
所以,在Hibernate在进行插入 class对象的时候,发现class对象,所关联的集合中,有一条
“自由态”的对象,而又因为class端设置了“级联属性cascade”,所以,在插入这条 “新class对象”时,也一同把他内部的那些,还属于“自由态”的其他对象,也一同插入到,他们所对应的表中去了。
还是不明白的话。。。可以看看。孙卫琴的《精通Hibernate》,在书上的第149页有。
但是关于inverse的介绍。。。写的就有些书面化了,如果语文不好的话。。。就难懂咯~
第3条HQL语句:
第三条HQL语句是一条update语句,是不是觉得,很莫名其妙。。。。
Hibernate大脑进水了吧,怎么吃饱了撑得,重复更新记录啊啊啊啊啊
假如:我们把 class端的配置文档中的 invser属性设置为true(即:inverse=true)
在执行上面的程序,发现,就变成2条insert语句啦。。。。。(update没啦。。。)
看来第三条的update语句和inverse有着密切的关系(他两有一腿~)。
所以我们下边,就来介绍一下 inverse属性:
当调用 Class.getStudents().add(stu)方法,进行添加操作时,
(即:向 "这个Class对象"所属的“集合 (也就是调用getStudents方法所返回的那个Set集合)”中添加一个Student(即 add(stu)),也就是说,这个“新”添加的Student对象(stu),
他的Student.class_id字段“必须”,要等于“被添加方Class”的主键(即:Class.cid)。
从“数据库”层面来讲,也就是说,这个“新”添加的“Student”的class_id字段,必须要与“Class”的cid字段,存在"主外键关联"。)
正因为如此:所以 Hibernate“怕” 在进行 "Class.getStudents().add(stu)" 这样的操作时,
出现意外情况(如: stu.getClass=null,即:stu没有所属班级),
即“添加方”(Student)与“被添加方”(Class),存在“外键”不一致的情况发生。
所以就出现了 那条多余的update语句。即:one-to-many(Class端)主动去维护Child.Class_id
所以就是说,Hibernate怕出错,就给你多执行一次无用的更新语句,以保证 add 到 Class“集合”中的所有Student
都是要与Class有外键关联的。
用普通话说就是:
一年1班.getStudents().add(小白兔);
一年1班.getStudents().add(大白兔);
也就是说现在不管是 小白兔 还是 大白兔
如果他们,目前还没有自己的班级的话,
一年1班的班主任就会主动邀请他们成为一年1班的同学啦~。
也就是说 一年1班的班主任 主动邀请 同学,而不是 同学自己来~~~ 所以效率也降低了。。。。
所以我们一般把 一对多端 invser设置为true,即:不让主控端去维护主键关联,
(即:让同学自己去找班级)
说白了,就是,one-to-many端不用去管理 “新添加对象” 的主外键约束问题。
把one-to-many端(即:class端)的invser设置为true
(即:每次向class.getStudents这个集合中添加 student时,不去主动update对应的外键),
而是在student端去手动设置
例如:
student.setClass(class);
session.save(student);
这样手动设置 student与class关联啦。。。。
所以上面的程序“最好”还是写成这样:
Class class = new Class();
class.setName("一年级1班");
session.save(class);
Student stu = new Student();
stu.setName("小白兔");
stu.setClass(class);
session.save(class);
/*
此时向class集合add内容,不会进行数据库操作(update)。
“更新”的只是session缓存中,数据镜像。
这样做的好处是:不仅减少了update语句,
而且,同时也更新了session缓存。
------------------------
而在原来:
one-to-many端inverse=false时,虽然也更新seesion缓存中的class集合,
但是有却又多余update
*/
class.getStudents().add(stu);
// 提交
总结:
当inverse=false 并且向one-to-many端的关联集合,添加“新对象(即: 自由态对象)” 时,
Hibernate就会自动,去update那“个刚刚到来的” “自由态对象”的外键。
(如果你向,one-to-many端添的集合中,add一个“已经持久化了的对象”,那就不会出现update了(因为已经持久化过了),除非,你去 更改“那个持久化对象”所对应的外键。。。那样的话。。。呵呵呵~~~
你可以试一试,应该不会报错,你可以当做练习去做一下,加深cascade和inverse这两个属性的理解)
// 如果看懂了上面的内容。来看一下,下面的东西。
假如,将one-to-many端(即:Class端)的 hbm.xml 文档中的cascade移除掉 或把cascade="none"。
那么上面的代码会出现什么情况呢。
结果会出现2条HQL,和一堆Exception
insert into demo.class (cid, cname) values (66666666666666666666666666666666, 一年级1班)
update demo.student set class_id=66666666666666666666666666666666 where sid=8888888888888888811cb2e04c888888
Hibernate Exceptinon......................................
相比较cascade被设置"save-update"的时候,缺少了1条 insert语句,而且也多了一些Exception。
那么,到底是少了哪1条 insert语句呢?
就是这条:
insert into demo.student (sid,sname,class_id) values (8888888888888888811cb2e04c888888, 小白兔, 66666666666666666666666666666666)
之所以会出现,这样的现象,想必您已经早就看出来了。
因为,我没有设置Class端的Cascade,所以在save(class)的时候,并没有自动将其所关联的“自由态对象”进行持久化操作。
然而,又因为 Class端的inverse=false,所以,Class会自动去维持,那个 “新来的student” 的外键。
所以会出现,没有insert就要update啦。。。。
然后在就是Exception了
Saturday, June 26, 2010
Why do you need ORM tools like hibernate? (summaried by me)
The main advantage of ORM like hibernate is that it shields developers from messy SQL. Apart from this, ORM provides following benefits:
Improved productivity
- High-level object-oriented API
- Less Java code to write
- No SQL to write
- Sophisticated caching
- Lazy loading
- Eager loading
- A lot less code to write
- Easy to immigrate between databases, dialect
- ORM framework generates database-specific SQL for you
- To use the ORM framework, developer need to design and code the model and business layer more reasonably, they need to separate the model and business classes after serious consideration. It isn't like the traditional JDBC programming, software engineer write a very long SQL to cover too many tables and views in one class, it will lead to the performance (bottle neck) problem, and hard to maintain.
My favourite hibernate interview questions with answers?
Q. How will you configure Hibernate?
Answer:
The configuration files hibernate.cfg.xml (or hibernate.properties) and mapping files *.hbm.xml are used by the Configuration class to create (i.e. configure and bootstrap hibernate) the SessionFactory, which in turn creates the Session instances. Session instances are the primary interface for the persistence service.
" hibernate.cfg.xml (alternatively can use hibernate.properties): These two files are used to configure the hibernate sevice (connection driver class, connection URL, connection username, connection password, dialect etc). If both files are present in the classpath then hibernate.cfg.xml file overrides the settings found in the hibernate.properties file.
" Mapping files (*.hbm.xml): These files are used to map persistent objects to a relational database. It is the best practice to store each object in an individual mapping file (i.e mapping file per class) because storing large number of persistent classes into one mapping file can be difficult to manage and maintain. The naming convention is to use the same name as the persistent (POJO) class name. For example Account.class will have a mapping file named Account.hbm.xml. Alternatively hibernate annotations can be used as part of your persistent class code instead of the *.hbm.xml files.
Q. What is a SessionFactory? Is it a thread-safe object?
Answer:
SessionFactory is Hibernate s concept of a single datastore and is threadsafe so that many threads can access it concurrently and request for sessions and immutable cache of compiled mappings for a single database. A SessionFactory is usually only built once at startup. SessionFactory should be wrapped in some kind of singleton so that it can be easily accessed in an application code.
SessionFactory sessionFactory = new Configuration().configure().buildSessionfactory();
Q. What is a Session? Can you share a session object between different theads?
Answer:
Session is a light weight and a non-threadsafe object (No, you cannot share it between threads) that represents a single unit-of-work with the database. Sessions are opened by a SessionFactory and then are closed when all work is complete. Session is the primary interface for the persistence service. A session obtains a database connection lazily (i.e. only when required). To avoid creating too many sessions ThreadLocal class can be used as shown below to get the current session no matter how many times you make call to the currentSession() method.
&
public class HibernateUtil {
&
public static final ThreadLocal local = new ThreadLocal();
public static Session currentSession() throws HibernateException {
Session session = (Session) local.get();
//open a new session if this thread has no session
if(session == null) {
session = sessionFactory.openSession();
local.set(session);
}
return session;
}
}
It is also vital that you close your session after your unit of work completes. Note: Keep your Hibernate Session API handy.
Q. What are the benefits of detached objects?
Answer:
Detached objects can be passed across layers all the way up to the presentation layer without having to use any DTOs (Data Transfer Objects). You can later on re-attach the detached objects to another session.
Q. What are the pros and cons of detached objects?
Answer:
Pros:
" When long transactions are required due to user think-time, it is the best practice to break the long transaction up into two or more transactions. You can use detached objects from the first transaction to carry data all the way up to the presentation layer. These detached objects get modified outside a transaction and later on re-attached to a new transaction via another session.
Cons
" In general, working with detached objects is quite cumbersome, and better to not clutter up the session with them if possible. It is better to discard them and re-fetch them on subsequent requests. This approach is not only more portable but also more efficient because - the objects hang around in Hibernate's cache anyway.
" Also from pure rich domain driven design perspective it is recommended to use DTOs (DataTransferObjects) and DOs (DomainObjects) to maintain the separation between Service and UI tiers.
Q. How does Hibernate distinguish between transient (i.e. newly instantiated) and detached objects?
Answer
" Hibernate uses the version property, if there is one.
" If not uses the identifier value. No identifier value means a new object. This does work only for Hibernate managed surrogate keys. Does not work for natural keys and assigned (i.e. not managed by Hibernate) surrogate keys.
" Write your own strategy with Interceptor.isUnsaved().
Q. What is the difference between the session.get() method and the session.load() method?
Both the session.get(..) and session.load() methods create a persistent object by loading the required object from the database. But if there was not such object in the database then the method session.load(..) throws an exception whereas session.get(&) returns null.
Q. What is the difference between the session.update() method and the session.lock() method?
Both of these methods and saveOrUpdate() method are intended for reattaching a detached object. The session.lock() method simply reattaches the object to the session without checking or updating the database on the assumption that the database in sync with the detached object. It is the best practice to use either session.update(..) or session.saveOrUpdate(). Use session.lock() only if you are absolutely sure that the detached object is in sync with your detached object or if it does not matter because you will be overwriting all the columns that would have changed later on within the same transaction.
Note: When you reattach detached objects you need to make sure that the dependent objects are reatched as well.
Q. How would you reatach detached objects to a session when the same object has already been loaded into the session?
You can use the session.merge() method call.
Q. What are the general considerations or best practices for defining your Hibernate persistent classes?
1.You must have a default no-argument constructor for your persistent classes and there should be getXXX() (i.e accessor/getter) and setXXX( i.e. mutator/setter) methods for all your persistable instance variables.
2.You should implement the equals() and hashCode() methods based on your business key and it is important not to use the id field in your equals() and hashCode() definition if the id field is a surrogate key (i.e. Hibernate managed identifier). This is because the Hibernate only generates and sets the field when saving the object.
3. It is recommended to implement the Serializable interface. This is potentially useful if you want to migrate around a multi-processor cluster.
4.The persistent class should not be final because if it is final then lazy loading cannot be used by creating proxy objects.
5.Use XDoclet tags for generating your *.hbm.xml files or Annotations (JDK 1.5 onwards), which are less verbose than *.hbm.xml files.


