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关联,保持游离态;
不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之
原文地址:https://www.cnblogs.com/lauyu/p/5223298.html