Hibernate实体对象状态和操作
一、Hibernate对象的状态
- 瞬态(Transient):当一个对象通过new 操作符创建,并且没有和Hibernate的session关联过,就处于瞬态。瞬态的对象在数据库里没有相应的记录和标识符,和普通Object没有区别。
- 持久化(Persistent):持久化的实例在数据库有对应的记录和标识符,并且在session的范围内,任何对实例的改动都会被持久化到数据库,不需要手动执行update或者delete操作。
- 游离态(Detached):被持久化过的实例并且session已经关闭就处于游离态。对象的索引仍然存在,可以修改实例后,在某个时间点附着session,变成持久化。一个典型应用场景是:从数据库取出数据后,会话关闭,呈现数据给用户,用户修改数据后,提交数据保存到数据库,这过程叫应用事务。
二、持久化对象
通过使新创建的对象和session关联,使对象变成持久化,方法有以下两种方式:
- session.save():save承诺返回标识符(一般为表主键),如果配置的主键生成策略要求insert才能生成,会立刻执行insert语句,而不管操作是否处于事务内。
- session.persist():不承诺会持久化对象会立刻获得标识符,可能要等到flush才会被分配到标识符。
对于已经持久化过的实例,数据库有相应的记录,通过session.load()可以加载出持久化对象,需要知道对象的标识符。如果在数据库找不到对应的对象,则会抛出异常。如果为该实例的类配置的proxy,load操作只会返回未初始化的代理,只有真正执行代理方法才会到数据库取数据。
如果你不确定数据库是否一定存在相应的记录,采用get()操作,它会立刻到数据库查询,找不到记录则返回null。此外,通过传递LockMode,将以特定的锁加载对象(执行select ... for update),如果没有配置cascade=all的级联关系,关联的对象不会被执行到(select...for update)。
对于已经加载出来的对象,可以通过session.refresh(object)对象的属性和所有关联的集合,这在数据库存在触发器生成对象的某些属性的情景十分有用。
sess.save(cat); sess.flush(); //force the SQL INSERT sess.refresh(cat); //re-read the state (after the trigger executes)
至于Hibernate如何从数据库中加载对象,这与抓取策略有关(Fetching strategy),详细情况抓取策略一章。
三、修改游离态的对象
前面说过,很多应用场景是:在一个事务中读取数据,然后在UI上呈现给用户修改,最后在新的事务中保存更改。读取数据出来后,到用户提交之前,对象可以认为的游离态(真实情况UI呈现时对象已经消亡,提交数据时会根据提交的数据创建对象,这时对象就是游离态)。Hibernate提供session.update()和session.merge()方法使更改后的游离态对象重新附着session变为持久化。一个例子:
// in the first session Cat cat = (Cat) firstSession.load(Cat.class, catId); Cat potentialMate = new Cat(); firstSession.save(potentialMate); // in a higher layer of the application cat.setMate(potentialMate); // later, in a new session secondSession.update(cat); // update cat secondSession.update(mate); // update mate
注意,从firstSession取出来的对象cat,不能在执行update之前已经附着secondSession,否则抛出异常。也就是说用来update的session要确定不能包含有着同样标识符的持久化实例。
使用merge来合并修改则不要考虑对象在session的状态。
lock方法也允许应用重新关联对象和session,条件是对象实例不能被更改过。注意lock也能和不同的lockMode结合实现不同程度的事务隔离。
四、自动检测对象会话状态
Hibernate提供saveOrUpdate(object)方法实现自动根据被操作的对象的状态执行相应的数据库操作。如果是瞬态对象则执行save操作,如果检测到是游离态(根据标识符)则执行update或者重关联实例。只要操作的实例不是由一个session到另一个session,你都不应该使用saveOrUpdate、update和merge操作。
saveOrUpdate和update的通用使用场景如下:
- 应用程序在第一个session中加载对象;
- 对象在UI中呈现给用户;
- 用户对对象数据进行了部分修改;
- 修改后的对象数据被传回业务逻辑层;
- 应用程序在第二个session里通过调用update来持久化这些更改;
saveOrUpdate的操作过程如下:
- 如果对象已经在session中被持久化,什么都不做;
- 如果session中存在有着同样标识符的其他对象(即有着同样标识符的对象被持久化),抛出异常;
- 如果对象没有标识符属性,执行save操作;
- 如果对象被分配了新的标识符,执行save操作;
- 如果对象是附带版本信息的(通过<version>或<timestamp>),并且版本属性值表名这是新的对象,执行save操作;
- 其他情况执行update操作;
merge操作以前两个很不同:
- 如果session里面已经关联了有同样标识符的对象,则复制被操作对象的数据到已经持久化的对象;
- 如果当前session没有关联到持久化对象(依据标识符判断?),则尝试数据库加载进来,又或者创建一个新的持久化对象;
- 返回持久化对象;
- 被操作的对象没有和session关联,保持游离态;