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)上面。(长的事务可能跨越多个数据库事物。)
实例可能以下面三种状态存在:
瞬态或者*态(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实例也是可序列化的。
一个典型的事务应该使用下面的形式:
- Session sess = factory.openSession();
- Transaction tx;
- try {
- tx = sess.beginTransaction();
- //do some work
- ...
- tx.commit();
- }
- catch (Exception e) {
- if (tx!=null) tx.rollback();
- throw e;
- }
- finally {
- sess.close();
- }
如果Session抛出了异常, 事务必须回滚而session会被废弃。在异常发生后Session的内部状态可能会与数据库失去同步。
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语句,完成数据的保存。
如下面的代码:
- public static void insertUser()
- {
- Session sess = HibernateSessionFactory.getSession();
- User user = new User();
- Transaction tx = null;
- try
- {
- tx = sess.beginTransaction();
- // do some work
- user.setLogonName("关羽");
- user.setNickName("关云长");
- user.setPassword("1234");
- sess.save(user);
- tx.commit();
- }
- catch (Exception e)
- {
- if (tx != null)
- tx.rollback();
- }
- finally
- {
- sess.close();
- }
- }
当执行到session.save()方法时,Hibernate并不会马上生成insert SQL语句来进行数据的保存,而是当稍后清理session的缓存时才有可能执行insert SQL语句,那么session.save()方法到底会执行哪些步骤呢?
Hibernate3.2中save()方法实现的源代码:
- /**
- * Concrete implementation of a Session, and also the central, organizing component
- * of Hibernate's internal implementation. As such, this class exposes two interfaces;
- * Session itself, to the application, and SessionImplementor, to other components
- * of Hibernate. This class is not threadsafe.
- *
- * @author Gavin King
- */
- public final class SessionImpl extends AbstractSessionImpl
- implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
- //省略部分代码
- // save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- public void save(Object obj, Serializable id) throws HibernateException {
- save(null, obj, id);
- }
- public Serializable save(Object obj) throws HibernateException {
- return save(null, obj);
- }
- public Serializable save(String entityName, Object object) throws HibernateException {
- return fireSave( new SaveOrUpdateEvent(entityName, object, this) );
- }
- public void save(String entityName, Object object, Serializable id) throws HibernateException {
- fireSave( new SaveOrUpdateEvent(entityName, object, id, this) );
- }
- private Serializable fireSave(SaveOrUpdateEvent event) {
- errorIfClosed();
- checkTransactionSynchStatus();
- SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners();
- for ( int i = 0; i < saveEventListener.length; i++ ) {
- saveEventListener[i].onSaveOrUpdate(event);
- }
- return event.getResultId();
- }
- //省略部分代码
- }
持久化指定的游离态对象,首先要分配一个自动生成的主键标示符。(或者如果主键生成器正在使用,使用当前主键标示符属性值)
一、在session的内部缓存中寻找保存对象,如果找到了,则认为此数据已经保存(曾经执行过insert操作),实体对象已经处于persistent状态,直接返回。此时即使数据相比之前的状态发生了变化,也将在事务提交时由脏数据检查来判定是否需要执行update操作。
二、如果实体对象实现了lifecycle接口,那么将执行待保存对象的onSave()方法。
三、如果实体对象实现了Validatable接口,那么将会执行相应的validate()方法。
四、如果存在拦截器对象,那么将会执行Interceptor.onSave()方法。
五、构造insert SQL语句完成数据保存。
六、数据保存成功后,设定实体对象的id为插入记录的id。
七、将保存后的实体对象纳入Hibernate的内部缓存(一级缓存)。注意Hibernate不会把保存后的实体对象纳入二级缓存,因为刚刚保存过的实体对象很可能在之后被修改,缓存的频繁更新以及带来的同步问题代价,超出了缓存该对象所带来的收益。
八、如果关联关系映射配置了cascade="save-update"属性,save操作对关联对象级联操作。
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()方法实现的源代码:
- /**
- * Concrete implementation of a Session, and also the central, organizing component
- * of Hibernate's internal implementation. As such, this class exposes two interfaces;
- * Session itself, to the application, and SessionImplementor, to other components
- * of Hibernate. This class is not threadsafe.
- *
- * @author Gavin King
- */
- public final class SessionImpl extends AbstractSessionImpl
- implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
- //省略部分代码
- // update() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- public void update(Object obj) throws HibernateException {
- update(null, obj);
- }
- public void update(Object obj, Serializable id) throws HibernateException {
- update(null, obj, id);
- }
- public void update(String entityName, Object object) throws HibernateException {
- fireUpdate( new SaveOrUpdateEvent(entityName, object, this) );
- }
- public void update(String entityName, Object object, Serializable id) throws HibernateException {
- fireUpdate(new SaveOrUpdateEvent(entityName, object, id, this));
- }
- private void fireUpdate(SaveOrUpdateEvent event) {
- errorIfClosed();
- checkTransactionSynchStatus();
- SaveOrUpdateEventListener[] updateEventListener = listeners.getUpdateEventListeners();
- for ( int i = 0; i < updateEventListener.length; i++ ) {
- updateEventListener[i].onSaveOrUpdate(event);
- }
- }
- //省略部分代码
- }
实例:
前面我在实体对象状态转化部分曾经讲过,session.update()方法能够将一个处于游离状态的对象,重新纳入Hibernate的内部缓存,变成持久化对象。
如下面的代码:
- Configuration cfg = new Configuration();
- SessionFactory sf=cfg. configure().buildSessionFactory();
- Customer customer=new Customer("关羽",27);//customer对象处于*状态
- Session session=sf.openSession();
- // 开启事务
- Transaction tx=session.beginTransaction();
- //保存后customer对象处于持久化状态
- session.save(customer);
- //清空缓存后customer对象处于游离状态
- session.flush();
- // 提交事务
- tx.commit();
- //session关闭
- session.close();
- // 又开启一个新的session
- Session session2=sf.openSession();
- Transaction tx2=session2.beginTransaction();
- //通过调用update()方法将游离状态的customer对象,再次转化成持久化状态
- session2.update(customer);
- //调用delete()方法后,当清空缓存时,会将customer对象移出缓存,同时会在数据库中生成delete事务,来删除customer对象对应的数据记录
- session2.delete(customer);
- tx.commit();
- session.close();
那么update方法到底执行了哪些步骤呢?它会按照下面的步骤进行操作:
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语句。
如下面的代码:
- Transaction tx=session.beginTransaction();
- session.update(customer);
- tx.commit();
- 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的持久化对象,那么这个方法会抛出异常。
如下面代码:
- Customer customer1=new Customer("刘备",25);
- Session session1=sf.openSession();
- Transaction tx=session1.beginTransaction();
- //保存后customer对象处于持久化状态
- session.save(customer1);
- //清空缓存后customer对象处于游离状态
- session.flush();
- tx.commit();
- session1.close();
- Session session2=sf.openSession();
- Transaction tx2=session2.beginTransaction();
- Customer othercustomer=(Customer)session2.load(Customer.class,"1");
- //通过调用update()方法将游离状态的customer1对象,再次转化成持久化状态
- session2.update(customer1)
- tx2.commit();
- session2.close();
当再次将游离对象customer1与session2关联时,此时因为load()操作,在缓存已经加载了一个和customer1具有相同OID的othercustomer对象,此时由于Hibernate缓存的对象缓存机制不允许把OID相同的对象缓存,所以会抛出异常。
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方式是根据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。
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()方法实现的源代码:
- /**
- * Concrete implementation of a Session, and also the central, organizing component
- * of Hibernate's internal implementation. As such, this class exposes two interfaces;
- * Session itself, to the application, and SessionImplementor, to other components
- * of Hibernate. This class is not threadsafe.
- *
- * @author Gavin King
- */
- public final class SessionImpl extends AbstractSessionImpl
- implements EventSource, org.hibernate.classic.Session, JDBCContext.Context {
- //省略部分代码
- // saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- public void saveOrUpdate(Object object) throws HibernateException {
- saveOrUpdate(null, object);
- }
- public void saveOrUpdate(String entityName, Object obj) throws HibernateException {
- fireSaveOrUpdate( new SaveOrUpdateEvent(entityName, obj, this) );
- }
- private void fireSaveOrUpdate(SaveOrUpdateEvent event) {
- errorIfClosed();
- checkTransactionSynchStatus();
- SaveOrUpdateEventListener[] saveOrUpdateEventListener = listeners.getSaveOrUpdateEventListeners();
- for ( int i = 0; i < saveOrUpdateEventListener.length; i++ ) {
- saveOrUpdateEventListener[i].onSaveOrUpdate(event);
- }
- }
- //省略部分代码
- }
查看onSaveOrUpdate(event)方法的定义:
- public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener implements SaveOrUpdateEventListener {
- private static final Log log = LogFactory.getLog( DefaultSaveOrUpdateEventListener.class );
- /**
- * Handle the given update event.
- *
- * @param event The update event to be handled.
- */
- public void onSaveOrUpdate(SaveOrUpdateEvent event) {
- final SessionImplementor source = event.getSession();
- final Object object = event.getObject();
- final Serializable requestedId = event.getRequestedId();
- if ( requestedId != null ) {
- //assign the requested id to the proxy, *before*
- //reassociating the proxy
- if ( object instanceof HibernateProxy ) {
- ( ( HibernateProxy ) object ).getHibernateLazyInitializer().setIdentifier( requestedId );
- }
- }
- if ( reassociateIfUninitializedProxy( object, source ) ) {
- log.trace( "reassociated uninitialized proxy" );
- // an uninitialized proxy, noop, don't even need to
- // return an id, since it is never a save()
- }
- else {
- //initialize properties of the event:
- final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
- event.setEntity( entity );
- event.setEntry( source.getPersistenceContext().getEntry( entity ) );
- //return the id in the event object
- event.setResultId( performSaveOrUpdate( event ) );
- }
- }
- // 省略部分代码... ...
- }
这个方法包含了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操作。
看如下代码:
- Session session=sessionFactory().openSession();
- Transaction tx=session.beginTransaction();
- //加载持久态对象
- Customer customer=(Customer)session.load(Customer.class,”1”);
- //计划执行一条delete语句
- session.delete(customer);
- //清理缓存,执行一条delete语句
- tx.commit();
- //关闭session,这时将会把customer对象从缓存中删除
- session.close();
如果上面的代码中的customer对象是一个游离对象,那么当执行session.delete()方法时,会首先将游离的customer对象与session相关联(转换为持久态),然后再清理缓存时,再执行delete操作。
如果你想一次删除多条数据,那么可以采用一个重载的delete()方法:delete("from Customer c where c.id > '8'");这个方法可以删除符合条件的所有数据。