Hibernate数据保存操作方法的原理对比

Interface Session 
All Superinterfaces: 
Serializable 
All Known Subinterfaces: 
EventSource, Session 
All Known Implementing Classes: 
SessionImpl

public interface Session  extends Serializable

Java应用程序与Hibernate之间的主要运行时接口。它是抽象了持久化服务概念的核心抽象API类。

Session的生命周期绑定在一个物理的事务(tansaction)上面。(长的事务可能跨越多个数据库事物。)

引用
Session的主要功能是提供对映射的实体类实例的创建,读取和删除操作。 
实例可能以下面三种状态存在: 
瞬态或者*态(transient): 不曾进行持久化,未与任何Session相关联 
持久化状态(persistent): 仅与一个Session相关联 
脱管状态或者游离态(detached): 已经进行过持久化,但当前未与任何Session相关联

游离状态的实例可以通过调用save()、persist()或者saveOrUpdate()方法进行持久化。 
持久化实例可以通过调用 delete()变成游离状态。 
通过get()或load()方法得到的实例都是持久化状态的。 
游离状态的实例可以通过调用 update()、0saveOrUpdate()、lock()或者replicate()进行持久化。 
游离或者*状态下的实例可以通过调用merge()方法成为一个新的持久化实例。

save()和persist()将会引发SQL的INSERT,delete()会引发SQLDELETE,而update()或merge()会引发SQLUPDATE。 
对持久化(persistent)实例的修改在刷新提交的时候会被检测到,它也会引起SQLUPDATE。 
saveOrUpdate()或者replicate()会引发SQLINSERT或者UPDATE。 

其具体实现并不一定是线程安全的。每个线程/事务应该从一个SessionFactory获取自己的session实例。

如果其持久化对象类是可序列化的,则Session实例也是可序列化的。

一个典型的事务应该使用下面的形式:

  1. Session sess = factory.openSession();
  2. Transaction tx;
  3. try {
  4. tx = sess.beginTransaction();
  5. //do some work
  6. ...
  7. tx.commit();
  8. }
  9. catch (Exception e) {
  10. if (tx!=null) tx.rollback();
  11. throw e;
  12. }
  13. finally {
  14. sess.close();
  15. }

如果Session抛出了异常, 事务必须回滚而session会被废弃。在异常发生后Session的内部状态可能会与数据库失去同步。

引用
save方法 
public Serializable save(Object object) 
                  throws HibernateException

Persist the given transient instance, first assigning a generated identifier. 
(Or using the current value of the identifier property if the assigned generator is used.) 
This operation cascades to associated instances if the association is mapped with cascade="save-update". 
持久化指定的游离态对象,首先要分配一个自动生成的主键标示符。 
(或者如果主键生成器正在使用,使用当前主键标示符属性值) 
如果关联关系映射配置了cascade="save-update"属性,save操作对关联对象级联操作。 
Parameters: 
object - a transient instance of a persistent class 
一个要持久化类的*状态的实例对象 
Returns: 
the generated identifier 
返回生成的主键标示符 
Throws: 
HibernateException 

实例: 
Session.save()方法用于实体对象的持久化保存,也就是说当执行session.save()方法时会生成对应的insert SQL语句,完成数据的保存。 
如下面的代码:

  1. public static void insertUser()
  2. {
  3. Session sess = HibernateSessionFactory.getSession();
  4. User user = new User();
  5. Transaction tx = null;
  6. try
  7. {
  8. tx = sess.beginTransaction();
  9. // do some work
  10. user.setLogonName("关羽");
  11. user.setNickName("关云长");
  12. user.setPassword("1234");
  13. sess.save(user);
  14. tx.commit();
  15. }
  16. catch (Exception e)
  17. {
  18. if (tx != null)
  19. tx.rollback();
  20. }
  21. finally
  22. {
  23. sess.close();
  24. }
  25. }

当执行到session.save()方法时,Hibernate并不会马上生成insert SQL语句来进行数据的保存,而是当稍后清理session的缓存时才有可能执行insert SQL语句,那么session.save()方法到底会执行哪些步骤呢?

Hibernate3.2中save()方法实现的源代码:

引用
  1. /**
  2. * Concrete implementation of a Session, and also the central, organizing component
  3. * of Hibernate's internal implementation. As such, this class exposes two interfaces;
  4. * Session itself, to the application, and SessionImplementor, to other components
  5. * of Hibernate. This class is not threadsafe.
  6. *
  7. * @author Gavin King
  8. */
  9. public final class SessionImpl extends AbstractSessionImpl
  10. implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
  11. //省略部分代码
  12. // save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13. public void save(Object obj, Serializable id) throws HibernateException {
  14. save(null, obj, id);
  15. }
  16. public Serializable save(Object obj) throws HibernateException {
  17. return save(null, obj);
  18. }
  19. public Serializable save(String entityName, Object object) throws HibernateException {
  20. return fireSave( new SaveOrUpdateEvent(entityName, object, this) );
  21. }
  22. public void save(String entityName, Object object, Serializable id) throws HibernateException {
  23. fireSave( new SaveOrUpdateEvent(entityName, object, id, this) );
  24. }
  25. private Serializable fireSave(SaveOrUpdateEvent event) {
  26. errorIfClosed();
  27. checkTransactionSynchStatus();
  28. SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners();
  29. for ( int i = 0; i < saveEventListener.length; i++ ) {
  30. saveEventListener[i].onSaveOrUpdate(event);
  31. }
  32. return event.getResultId();
  33. }
  34. //省略部分代码
  35. }

持久化指定的游离态对象,首先要分配一个自动生成的主键标示符。(或者如果主键生成器正在使用,使用当前主键标示符属性值)

引用
请看进行了如下总结: 
一、在session的内部缓存中寻找保存对象,如果找到了,则认为此数据已经保存(曾经执行过insert操作),实体对象已经处于persistent状态,直接返回。此时即使数据相比之前的状态发生了变化,也将在事务提交时由脏数据检查来判定是否需要执行update操作。

二、如果实体对象实现了lifecycle接口,那么将执行待保存对象的onSave()方法。

三、如果实体对象实现了Validatable接口,那么将会执行相应的validate()方法。

四、如果存在拦截器对象,那么将会执行Interceptor.onSave()方法。

五、构造insert SQL语句完成数据保存。

六、数据保存成功后,设定实体对象的id为插入记录的id。

七、将保存后的实体对象纳入Hibernate的内部缓存(一级缓存)。注意Hibernate不会把保存后的实体对象纳入二级缓存,因为刚刚保存过的实体对象很可能在之后被修改,缓存的频繁更新以及带来的同步问题代价,超出了缓存该对象所带来的收益。

八、如果关联关系映射配置了cascade="save-update"属性,save操作对关联对象级联操作。

引用
update方法 
public void update(Object object) 
            throws HibernateException

Update the persistent instance with the identifier of the given detached instance. 
If there is a persistent instance with the same identifier, an exception is thrown. 
This operation cascades to associated instances if the association is mapped with cascade="save-update". 
使用给定的游离状态对象的主键标示符来更新持久化状态实例对象。 
如果存在相同主键标示符的持久化状态对象,抛出异常。 
如果关联关系映射配置了cascade="save-update"属性,update操作对关联对象级联操作。

Parameters: 
object - a detached instance containing updated state 
包含了要更新状态的游离状态对象 
Throws: 
HibernateException 

Hibernate3.2中update()方法实现的源代码:

引用
  1. /**
  2. * Concrete implementation of a Session, and also the central, organizing component
  3. * of Hibernate's internal implementation. As such, this class exposes two interfaces;
  4. * Session itself, to the application, and SessionImplementor, to other components
  5. * of Hibernate. This class is not threadsafe.
  6. *
  7. * @author Gavin King
  8. */
  9. public final class SessionImpl extends AbstractSessionImpl
  10. implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
  11. //省略部分代码
  12. // update() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13. public void update(Object obj) throws HibernateException {
  14. update(null, obj);
  15. }
  16. public void update(Object obj, Serializable id) throws HibernateException {
  17. update(null, obj, id);
  18. }
  19. public void update(String entityName, Object object) throws HibernateException {
  20. fireUpdate( new SaveOrUpdateEvent(entityName, object, this) );
  21. }
  22. public void update(String entityName, Object object, Serializable id) throws HibernateException {
  23. fireUpdate(new SaveOrUpdateEvent(entityName, object, id, this));
  24. }
  25. private void fireUpdate(SaveOrUpdateEvent event) {
  26. errorIfClosed();
  27. checkTransactionSynchStatus();
  28. SaveOrUpdateEventListener[] updateEventListener = listeners.getUpdateEventListeners();
  29. for ( int i = 0; i < updateEventListener.length; i++ ) {
  30. updateEventListener[i].onSaveOrUpdate(event);
  31. }
  32. }
  33. //省略部分代码
  34. }

实例: 
前面我在实体对象状态转化部分曾经讲过,session.update()方法能够将一个处于游离状态的对象,重新纳入Hibernate的内部缓存,变成持久化对象。 
如下面的代码:

  1. Configuration cfg = new Configuration();
  2. SessionFactory sf=cfg. configure().buildSessionFactory();
  3. Customer customer=new Customer("关羽",27);//customer对象处于*状态
  4. Session session=sf.openSession();
  5. // 开启事务
  6. Transaction tx=session.beginTransaction();
  7. //保存后customer对象处于持久化状态
  8. session.save(customer);
  9. //清空缓存后customer对象处于游离状态
  10. session.flush();
  11. // 提交事务
  12. tx.commit();
  13. //session关闭
  14. session.close();
  15. // 又开启一个新的session
  16. Session session2=sf.openSession();
  17. Transaction tx2=session2.beginTransaction();
  18. //通过调用update()方法将游离状态的customer对象,再次转化成持久化状态
  19. session2.update(customer);
  20. //调用delete()方法后,当清空缓存时,会将customer对象移出缓存,同时会在数据库中生成delete事务,来删除customer对象对应的数据记录
  21. session2.delete(customer);
  22. tx.commit();
  23. session.close();

那么update方法到底执行了哪些步骤呢?它会按照下面的步骤进行操作:

引用
控制台查看Hibernate生成的sql语句如下: 
Hibernate: select user0_.id as id0_0_, user0_.logon_name as logon2_0_0_, user0_.nick_name as nick3_0_0_, user0_.password as password0_0_ from logon_user user0_ where user0_.id=? 
Hibernate: update logon_user set logon_name=?, nick_name=?, password=? where id=? 

一、首先会在缓存中寻找需要更新的实体对象,如果找到就立刻返回,从这里我们可以看出如果对一个已经处于persistent的实体对象执行update()方法,将不会产生任何作用。 
二、然后当提交事务进行缓存清理时,将会通过脏数据检查,确定变化的属性,然后生成update SQL语句完成数据的更新。 
这里有一个问题我们要强调一下,那就是只要通过update()方法将一个游离对象与session相关联,那么不论这个游离的实体对象的属性是否发生改变,都会执行update SQL语句。 
如下面的代码:

  1. Transaction tx=session.beginTransaction();
  2. session.update(customer);
  3. tx.commit();
  4. session.close();

在这段代码中并没有修改customer对象的任何属性值,但是也会执行一个update SQL语句,如果你希望在没有改变实体对象属性值的情况下不去执行update SQL语句,那么你要开启实体对象元素的”select-before-update”属性,将其设置为”true”,这个属性默认为”false”。 
如下进行配置: 
如果启用了这个属性配置,那么在清理session缓存之前,会首先执行类似如下的一条SQL语句: 
Select * from logon_user where id='1'; 
查询处所有的customer实体在数据库中对应的属性值,然后逐条与缓存中属性值进行比较,如果发生了改变,那么将会生成update操作进行数据更新,如果没有发生改变那么将不会进行update操作。 
要跟据实际需求情况来决定是否开启这个选项,如果实体对象的属性不会经常发生改变,那么就应该开启这个选项,以免执行多余的update操作。如果实体对象的属性会经常发生改变,那么就没必要开启这个选项,以免在执行update操作前再执行多余的select语句。

注意事项: 
1.当执行对一个游离实体对象执行session.update()操作时,如果在数据库中不存在这个实体对应的纪录,那么这个操作将会抛出异常。 
2.当执行session.update()方法将一个游离对象与session关联时,如果此时在缓存中已经存在了与该实体对象具有相同OID的持久化对象,那么这个方法会抛出异常。 
如下面代码:

  1. Customer customer1=new Customer("刘备",25);
  2. Session session1=sf.openSession();
  3. Transaction tx=session1.beginTransaction();
  4. //保存后customer对象处于持久化状态
  5. session.save(customer1);
  6. //清空缓存后customer对象处于游离状态
  7. session.flush();
  8. tx.commit();
  9. session1.close();
  10. Session session2=sf.openSession();
  11. Transaction tx2=session2.beginTransaction();
  12. Customer othercustomer=(Customer)session2.load(Customer.class,"1");
  13. //通过调用update()方法将游离状态的customer1对象,再次转化成持久化状态
  14. session2.update(customer1)
  15. tx2.commit();
  16. session2.close();

当再次将游离对象customer1与session2关联时,此时因为load()操作,在缓存已经加载了一个和customer1具有相同OID的othercustomer对象,此时由于Hibernate缓存的对象缓存机制不允许把OID相同的对象缓存,所以会抛出异常。

引用
load方法 
public void load(Object object, 
                 Serializable id) 
          throws HibernateException

Read the persistent state associated with the given identifier into the given transient instance. 
读取和给定的主键标示符关联的持久化状态到给定的*状态的实例对象。 
Parameters: 
object - an "empty" instance of the persistent class 
id - a valid identifier of an existing persistent instance of the class 
object - 一个持久化类的空对象 
id - 一个已经存在的持久化实例对象的有效的主键标示符 
Throws: 
HibernateException 

引用
get和load方法的区别? 
get和load方式是根据id取得一个记录 
下边详细说一下get和load的不同,因为有些时候为了对比也会把find加进来。

1.从返回结果上对比: 
load方式检索不到的话会抛出org.hibernate.ObjectNotFoundException异常 
get方法检索不到的话会返回null

2.从检索执行机制上对比: 
get方法和find方法都是直接从数据库中检索 
而load方法的执行则比较复杂 
■ 首先查找session的persistent Context中是否有缓存,如果有则直接返回 
■ 如果没有则判断是否是lazy,如果不是直接访问数据库检索,查到记录返回,查不到抛出异常 
■ 如果是lazy则需要建立代理对象,对象的initialized属性为false,target属性为null 
■ 在访问获得的代理对象的属性时,检索数据库,如果找到记录则把该记录的对象复制到代理对象的target上,并将initialized=true,如果找不到就抛出异常 。

3.根本区别说明 
■ 如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记录,那没办法,只能抛异常。所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时(注意:这就是由于“延迟加载”在作怪)。

由于session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。

■ 对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。

对于load和get方法返回类型:虽然好多书中都这么说:“get()永远只返回实体类”,但实际上这是不正确的,get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。

get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。

4.简单总结

总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。 

引用
saveOrUpdate方法

public void saveOrUpdate(Object object) 
                  throws HibernateException 
Either save(Object) or update(Object) the given instance, depending upon resolution of the unsaved-value checks (see the manual for discussion of unsaved-value checking). 
This operation cascades to associated instances if the association is mapped with cascade="save-update". 
可能对给定的实例对象执行save或者update方法,如果关联关系映射配置了cascade="save-update"属性,update操作对关联对象级联操作。

Parameters: 
object - a transient or detached instance containing new or updated state 
object - 包含新的或者已更新状态的*态或者是游离态的 
对象实例 
Throws: 
HibernateException 
See Also: 
save(Object), update(Object) 

Hibernate3.2中saveOrUpdate()方法实现的源代码:

引用
  1. /**
  2. * Concrete implementation of a Session, and also the central, organizing component
  3. * of Hibernate's internal implementation. As such, this class exposes two interfaces;
  4. * Session itself, to the application, and SessionImplementor, to other components
  5. * of Hibernate. This class is not threadsafe.
  6. *
  7. * @author Gavin King
  8. */
  9. public final class SessionImpl extends AbstractSessionImpl
  10. implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
  11. //省略部分代码
  12. // saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  13. public void saveOrUpdate(Object object) throws HibernateException {
  14. saveOrUpdate(null, object);
  15. }
  16. public void saveOrUpdate(String entityName, Object obj) throws HibernateException {
  17. fireSaveOrUpdate( new SaveOrUpdateEvent(entityName, obj, this) );
  18. }
  19. private void fireSaveOrUpdate(SaveOrUpdateEvent event) {
  20. errorIfClosed();
  21. checkTransactionSynchStatus();
  22. SaveOrUpdateEventListener[] saveOrUpdateEventListener = listeners.getSaveOrUpdateEventListeners();
  23. for ( int i = 0; i < saveOrUpdateEventListener.length; i++ ) {
  24. saveOrUpdateEventListener[i].onSaveOrUpdate(event);
  25. }
  26. }
  27. //省略部分代码
  28. }

查看onSaveOrUpdate(event)方法的定义:

  1. public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener implements SaveOrUpdateEventListener {
  2. private static final Log log = LogFactory.getLog( DefaultSaveOrUpdateEventListener.class );
  3. /**
  4. * Handle the given update event.
  5. *
  6. * @param event The update event to be handled.
  7. */
  8. public void onSaveOrUpdate(SaveOrUpdateEvent event) {
  9. final SessionImplementor source = event.getSession();
  10. final Object object = event.getObject();
  11. final Serializable requestedId = event.getRequestedId();
  12. if ( requestedId != null ) {
  13. //assign the requested id to the proxy, *before*
  14. //reassociating the proxy
  15. if ( object instanceof HibernateProxy ) {
  16. ( ( HibernateProxy ) object ).getHibernateLazyInitializer().setIdentifier( requestedId );
  17. }
  18. }
  19. if ( reassociateIfUninitializedProxy( object, source ) ) {
  20. log.trace( "reassociated uninitialized proxy" );
  21. // an uninitialized proxy, noop, don't even need to
  22. // return an id, since it is never a save()
  23. }
  24. else {
  25. //initialize properties of the event:
  26. final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
  27. event.setEntity( entity );
  28. event.setEntry( source.getPersistenceContext().getEntry( entity ) );
  29. //return the id in the event object
  30. event.setResultId( performSaveOrUpdate( event ) );
  31. }
  32. }
  33. // 省略部分代码... ...
  34. }

这个方法包含了save()方法和update()方法的特点,如果传入该方法的是一个游离对象,那么这个方法就会执行update操作,如果传入该方法的是一个*对象,那么这个方法就会执行insert操作。 
这个方法幕后的工作原理如下: 
1.首先在缓存寻找,如果找到待保存的操作就直接返回。 
2.如果实体实现了拦截方法,那么就执行isUnsaved()方法,判断实体对象状态。 
3.如果实体处于*状态就执行save(),如果实体处于游离状态那么就执行update()。

这里存在一个问题,那就是Hibernate是怎样判断一个实体是处于游离态还是临时状态的? 
如果实体满足下面的一个条件,就认为这个实体处于临时状态。 
Java对象的OID值为null。 
1.如果Java对象具有version属性(可以了解并发加锁部分)且为null。 
2.如果实体的设置了属性unsaved-value,而且OID值与unsaved-value值相等。 
3.如果实体的version属性设置了unsaved-value,并且version属性的值与unsaved-value值相等。 
4.如果实体实现了Interceptor,而且Interceptor.isUnsaved()方法返回true。 
满足这些条件中的一个,这个实体就被认为是临时对象。

Session.delete()方法: 
delete()方法用于从数据库中删除一个或一批实体所对应的数据,如果传入的对象是持久化对象,那么当清理缓存时,就会执行delete操作。 
如果传入的是游离对象,那么首先会使该对象与session相关联,然后当清理缓存时,再执行delete操作。

看如下代码:

  1. Session session=sessionFactory().openSession();
  2. Transaction tx=session.beginTransaction();
  3. //加载持久态对象
  4. Customer customer=(Customer)session.load(Customer.class,”1”);
  5. //计划执行一条delete语句
  6. session.delete(customer);
  7. //清理缓存,执行一条delete语句
  8. tx.commit();
  9. //关闭session,这时将会把customer对象从缓存中删除
  10. session.close();

如果上面的代码中的customer对象是一个游离对象,那么当执行session.delete()方法时,会首先将游离的customer对象与session相关联(转换为持久态),然后再清理缓存时,再执行delete操作。 
如果你想一次删除多条数据,那么可以采用一个重载的delete()方法:delete("from Customer c where c.id > '8'");这个方法可以删除符合条件的所有数据。

上一篇:PhpStorm11.0 配置在浏览器中打开文件


下一篇:Nearest Common Ancestors POJ - 1330 (LCA)