首页 > 代码库 > 持久化上下文

持久化上下文

 
 org.hibernate.Session和javax.persistence.EntityManager API 代表处理持久化数据的一个上下文,持久化数据有一个状态涉及持久化上下文和底层数据库。
 
 实体状态
  
  new或transient
   刚刚被实例化的实体且没有关联一个持久化上下文,它没有持久化表示在数据库中并没有指定的标识符值
   
  managed或persistent
   实体都有一个关联的标识符和与一个持久化上下文相关联
   
  detached
   实体都有一个关联的标识符,但不再是与一个持久化上下文相关联(通常是因为持久化上下文被关闭或实例被evicted上下文)
  
  removed
   实体都有一个关联的标识符和与一个持久化上下文相关联,但是,它将计划从数据库中移除
  
 1、使实体持久化
  一旦你创建了一个新的实体实例(使用标准的new操作),它就处于new状态,可以通过关联org.hibernate.Session或javax.persistence.EntityManager变成persistent状态
  DomesticCat fritz = new DomesticCat();
  fritz.setColor( Color.GINGER );
  fritz.setSex( ‘M‘ );
  fritz.setName( "Fritz" );
  session.save( fritz ); //entityManager.persist( fritz );
  如果DomesticCat实体类型有一个生成标识符,当save或persist调用时该值就关联到该实例。
  如果标识符不是自动生成,应用程序分配key值到此实例上在save或persist调用前
  
 2、删除实体
  session.delete( fritz );//entityManager.remove( fritz );
  Hibernate本身可以处理删除 detached 状态,但是JPA 是不允许的。
  实体实例传递到org.hibernate.Session delete方法后不是 managed状态就是 detached状态。
  而实体实例传递到javax.persistence.EntityManager remove方法后就变成managed状态
  
 3、获取一个没有初始化数据实体引用
  有时提到延迟加载,能够获得一个实体引用无需加载它的数据是非常重要的。最常见情况,需要创建一个实体和另一个现有实体的关联。
  Book book = new Book();
  book.setAuthor( session.byId( Author.class ).getReference( authorId ) );
  //book.setAuthor( entityManager.getReference( Author.class, authorId ) );
  上面的工作假设的实体定义允许延迟加载,通常通过使用运行时代理。在这两种情况下会抛出一个异常:
   若给定的实体没有引用真实数据库的状态;当应用程序试图以任何方式使用返回的代理(需要对其数据的访问)
   
 4、获取一个初始化数据实体
  也是很常见的想获得一个实体连同其数据。
  session.byId( Author.class ).load( authorId );
  //entityManager.find( Author.class, authorId );
  在这两种情况下返回null如果没有找到匹配的数据库行
 
 5、通过natural-id获取实体
  除了允许通过标识符加载,Hibernate允许应用通过声明的natural标识符进行加载。
  @Entity
  public class User {
   @Id
   @GeneratedValue
   Long id;

   @NaturalId
   String userName;

   ...
  }
  
  // use getReference() to create associations...
  Resource aResource = (Resource) session.byId( Resource.class ).getReference( 123 );
  User aUser = (User) session.bySimpleNaturalId( User.class ).getReference( "steve" );
  aResource.assignTo( aUser );
  
  // use load() to pull initialzed data
  return session.bySimpleNaturalId( User.class ).load( "steve" );
  
  另外:
  @Entity
  public class User {
   @Id
   @GeneratedValue
   Long id;
   
   @NaturalId
   String system;

   @NaturalId
   String userName;

   ...
  }
  
  // use getReference() to create associations...
  Resource aResource = (Resource) session.byId( Resource.class ).getReference( 123 );
  User aUser = (User) session.byNaturalId( User.class )
     .using( "system", "prod" )
     .using( "userName", "steve" )
     .getReference();
  aResource.assignTo( aUser );
  
  // use load() to pull initialzed data
  return session.byNaturalId( User.class )
    .using( "system", "prod" )
    .using( "userName", "steve" )
    .load();
  通过标识符和通过natural-id访问持久化数据在Hibernate中是一致的,每个都定义了相同的2个数据库访问方法:
   getReference
    标识符是假定存在的情况下使用,实际不存在将是一个错误,不应该被用来测试存在。
    这是因为这种方法将更愿意创建并返回一个代理如果数据还没有与会话关联而不是调用数据库。
    使用这种方法的典型的用例是创建基于外键关联
    
   load
    将返回持久数据与给定的相关标识符值或null如果标识不存在
 
 6、刷新实体状态
  可以重新加载一个实体实例和它的集合
  Cat cat = session.get( Cat.class, catId );
  session.refresh( cat );
  //Cat cat = entityManager.find( Cat.class, catId );
  //entityManager.refresh( cat );
  自数据被读取后知道数据库状态有了改变时的情况是有用的,刷新可以使当前数据库的状态被拉成实体实例和持久化上下文
  另外也可能适用于当数据库触发器被用来初始化一些实体的属性,注意,只有实体实例及其集合是刷新,
  除非你刷新指定为一个级联风格的任何关联。但是,请注意,Hibernate有能力处理这种自动通过其生成的属性的概念
 
 7、修改managed/persistent状态
  处于managed/persistent的实体可能由应用操作,任何修改将自动检测且在持久上下文刷新时进行持久化。
  不需要调用一个特定的使其持久化的方法。
  Cat cat = session.get( Cat.class, catId );
  cat.setName( "Garfield" );
  session.flush(); //通常这不是显式地需要
  
  Cat cat = entityManager.find( Cat.class, catId );
  cat.setName( "Garfield" );
  entityManager.flush(); //通常这不是显式地需要
 
 8、使用detached数据
  Detachment是用任何持久上下文范围之外的数据使用的一个过程。
  数据变成detached有多种方式。一旦持久上下文关闭,所有关联它的数据将变成detached状态。
  清理持久化上下文也有同样的效果。驱逐(Evicting)一个特定实体的持久上下文使其变成detached状态。
  最后,序列化将使反序列化形式变成detached(原实例仍然是managed状态)
  
  Detached 的数据仍然可以被操作,但是持久化上下文将不再自动知道这些修改,并且应用程序将需要进行修改持久化操作。
  Reattaching detached数据
   Reattachment 是传入一个detached状态的实体实例并且使用当前的持久上下文与之重新关联的过程。
   JPA不提供这种模型,仅Hibernate的org.hibernate.Session支持。
   
   session.saveOrUpdate( someDetachedCat );
   这里的方法名称update有点误导,这并不意味着一个SQL UPDATE立即执行。
   但是,意味着当持久上下刷新因为Hibernate不知道先前比较变化的状态,一个SQL UPDATE将执行,
   除非实体使用select-before-update映射,Hibernate将从数据库拉下当前的状态并查看是否需要更新。
   提供实体是detached状态时,update 和saveOrUpdate 操作是相同的。
   
  Merging detached数据
   Merging是传入一个detached状态的实体实例并且copy它的数据到一个新的managed 状态的实例。
   可视化的merging实现:
   Object detached = ...;
   Object managed = entityManager.find( detached.getClass(), detached.getId() );
   managed.setXyz( detached.getXyz() );
   ...
   return managed;
   
   merging a detached entity:
   Cat theManagedInstance = session.merge( someDetachedCat );
   //Cat theManagedInstance = entityManager.merge( someDetachedCat );
  
 9、检查persistent状态
  应用程序可以检查实体和集合的状态相关的持久化上下文。
  检查managed状态例子:assert session.contains( cat );//assert entityManager.contains( cat );
  检查laziness例子:
  if ( Hibernate.isInitialized( customer.getAddress() ) {
   //display address if loaded
  }
  if ( Hibernate.isInitialized( customer.getOrders()) ) ) {
   //display orders if loaded
  }
  if (Hibernate.isPropertyInitialized( customer, "detailedBio" ) ) {
   //display property detailedBio if loaded
  }
  
  jpa写法:
  javax.persistence.PersistenceUnitUtil jpaUtil = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();
  if ( jpaUtil.isLoaded( customer.getAddress() ) {
   //display address if loaded
  }
  if ( jpaUtil.isLoaded( customer.getOrders()) ) ) {
   //display orders if loaded
  }
  if (jpaUtil.isLoaded( customer, "detailedBio" ) ) {
   //display property detailedBio if loaded
  }
  
 10、从JPA访问Hibernate api
  JPA定义了一个有用的方法允许应用程序访问底层的api提供者
  Session session = entityManager.unwrap( Session.class );
  SessionImplementor sessionImplementor = entityManager.unwrap( SessionImplementor.class );

持久化上下文