ejbhibernate:将遗留 Hibernate 应用程序迁移到 OpenJPA 和 EJB 3.0(上)

  引言

  Hibernate 是开放源代码持久性和查询框架提供传统 Java™ 对象 (POJO) 到关系数据库表和对象相关映射以及数据查询和检索功能Apache OpenJPA 项目将按照 EJB 3.0 Java Persistence API 规范标准定义为 POJO 实体提供类似开放源代码持久性和查询框架本文介绍 Enterprise JavaBeans™ (EJB) 2.1 中通用 Hibernate 场景并将它们和 OpenJPA 和 EJB 3.0 中实现等效场景进行比较具体来说您可以并排查看 Hibernate 应用源代码、对象关系映射和配置参数并将它们和等效 OpenJPA 源代码、映射和配置进行比较这里显示比较不仅使您能够了解如何进行这些更改而且介绍说明了将使用这些通用场景遗留 Hibernate 应用迁移到 OpenJPA 相当简单

  尽管本文重点介绍将遗留 Hibernate 应用迁移到 OpenJPA但是如果您熟悉 Hibernate还会发现其中价值并希望尽快使用新 JPA 规范标准以及使用 OpenJPA 持久性提供进行新应用开发

  本文假设您熟悉 Hibernate 基本概念并将专门介绍 Hibernate 3.0 实现本文中所有举例均在 EJB 2.1 中 Hibernate 3 中运行过并在使用 IBM® WebSphere® Application Server V6.1 Feature Pack for EJB 3.0 OpenJPA 0.9.7 中运行过

  将遗留 Hibernate 应用迁移到 OpenJPA 原因是多方面例如Hibernate 是个非标准、对象关系映射和持久性管理解决方案Hibernate 3 需要 JDK 1.3.1 或更高版本通过对比OpenJPA 可实现 JPA 规范标准该规范标准是 Java 5 规范标准核心部分并且 WebSphere Application Server V6.1 Feature Pack for EJB 3.0 实现基于该规范标准有关这些产品详细信息请参见参考资料

  考虑到本文JPA 表示该规范标准并且 OpenJPA 表示 JPA 规范标准实现

  本文没有介绍 Hibernate 所有特性和功能但介绍了该领域中经常使用最佳实战

  迁移 Hibernate 应用源代码

  Java Persistence API (JPA) 是作为 EJB 3.0 规范标准 (JSR220) 部分引入是让整个 Java 社区支持标准、单持久 APIJPA 将采用 Hibernate、TopLink、Java Data Objects 和 Container Managed Persistence (EJB-CMP) 2.1 规范标准最佳理念

  JPA 适用于 Java Platform Standard Edition (Java SE) 和 Enterprise Edition (Java EE) 环境它将实体表示为 JPA 持久性提供(如 OpenJPA)可以管理 POJO有关实体对象关系映射元数据是使用 Java 5 注释或在 XML 描述符中指定实体用于将 Java 对象持久保存到数据库

  有许多 JPA 持久性提供IBM JPA 规范标准实现基于 Apache OpenJPA 项目随着这些 JPA 持久性提供发布客户现在可以对标准 API 进行编码不必在不兼容非标准持久性提供的间进行决策

  为帮助您将遗留 Hibernate 应用迁移到 OpenJPA本部分将通常使用 Hibernate 非标准 API 和等效 OpenJPA 标准 API 进行了比较本部分先比较所使用类和接口然后通过常规使用方法场景比较 API

  以下各部分提供了详细信息:

  类和接口

  运行时配置

  会话管理

  事务管理

  实体管理

  分离实体

  1. 类和接口

  下表将通常使用 Hibernate 类和 OpenJPA 中等效类进行了比较所有 Hibernate 类都位于 org.hibernate 包中所有 JPA 接口(和 Persistence 类)都位于 javax.persistence 包中JPA 接口 OpenJPA 实现位于 org.apache.openjpa.* 包中

org.hibernate javax.persistence 介绍说明
cfg.Configuration Persistence 配置会话工厂(在 Hibernate 中)或实体管理器工厂(在 OpenJPA 中)引导类通常用于为 JVM 创建单会话(或实体管理器)工厂
SessionFactory EntityManagerFactory 提供 API 以打开 Hibernate 会话(或 OpenJPA 实体管理器)并处理用户请求通常每个处理客户机请求线程都打开个会话(或实体管理器)
Session EntityManager 提供 API 以便在数据库的间存储和加载实体它还提供 API 以获取事务和创建查询
Transaction EntityTransaction 提供 API 以管理事务
Query Query 提供 API 以执行查询



  2. 运行时配置

  Hibernate 约定

  在 Hibernate 中运行时配置按照以下方式进行映射:

  使用静态 SessionFactory 变量

  使用 Configuration#configure 思路方法

  使用 Configuration#buildSessionFactory 思路方法

  清单 1. Hibernate 运行时配置

public ORMHelper {
 private SessionFactory sf;
 protected synchronized
 SessionFactory getSessionFactory(String name) {
   (sf null) {
   sf = Configuration.configure(name).buildSessionFactory;
  }
   sf;
 }
 ...
}


  使用遗留 Hibernate 应用您通常会发现个单静态 SessionFactory 例子该例子由 JVM 中处理客户机请求所有线程共享Hibernate 还可以创建多个 SessionFactory 例子但是实际很少这样做

  可以通过多种思路方法在 Hibernate 中配置 SessionFactory最常见场景是 configure 思路方法如果没有向 configure 传入名称它将在类路径根目录中查找 hibernate.cfg.xml如果传入 XML 配置文件名称它将在类路径上查找该名称

  找到 XML 配置文件后buildSessionFactory 思路方法将使用该配置文件中元数据创建和化 SessionFactory

  牢记以下事项:

  有些应用从 JNDI 注册表查找 SessionFactory而不使用静态变量但是在第次查找时您仍需要配置和 buildSessionFactory因此几乎没有什么效果而且静态变量是较常用思路方法

  您还可以使用 Configuration#Properties 思路方法以编程方式配置 Hibernate 配置参数而不使用 configure 思路方法从文件读取这些参数但是较好并且频繁使用思路方法是外部化 Hibernate 属性

  OpenJPA 约定

  在 OpenJPA 中等效运行时配置按照以下方式进行映射:

  使用静态 EntityManagerFactory 变量

  使用 Persistence#createEntityManagerFactory

  清单 2. OpenJPA 运行时配置

public ORMHelper {
  private EntityManagerFactory sf;
  protected synchronized
  EntityManagerFactory getSessionFactory(String name) {
    (sf null) {
     sf = Persistence.createEntityManagerFactory(name);
   }
    sf;
  }
  ...
}


  和 Hibernate 处理 JVM 中客户机请求所有线程都可以使用静态 EntityManagerFactory 例子如果需要多个例子还可以定义静态映射

  createEntityManagerFactory 在类路径(该类路径包含持久单元名称和思路方法中指定名称相同) META-INF 文件夹中查找 persistence.xml如果找到 persistence.xml 使用持久单元和给定名称匹配则 createEntityManagerFactory 使用该文件中元数据配置 EntityManagerFactory 例子如果没有找到具有匹配名称 persistence.xml则引发 javax.persistence.PersistenceException

  3. 会话管理

  通常应用收到客户机请求时将从 SessionFactory 获取会话并在请求结束时关闭会话其中请求可以是 HttpRequest 或对无状态会话 Bean 会话提供处理事务和从数据库加载实体(以及将实体存储到数据库)思路方法

  Hibernate 应用通常管理该会话为了达到目标它们通常将会话和线程本地存储关联这样无需将会话作为参数传递到需要访问它所有思路方法;相反它们可以从线程本地存储中检索它Hibernate 3.0.1 还提供了 getCurrentSession但是您通常会找到显式会话管理

  就异常而言Hibernate 3.0 会引发未经检查异常或运行时异常(对于 OpenJPA 也样)这意味着在思路方法签名中大多数应用不会引发 Hibernate 异常;它们也不在自己思路方法中捕获和处理 Hibernate 异常当然如果需要仍可以捕获和处理它们

  您通常还会发现在使用 Java SE 5 实现 OpenJPA 应用已使用 Java SE 1.4 实现了大多数现有遗留 Hibernate 应用

  下面举例使用 getSessionFactory helper 思路方法获取创建/打开会话(或实体管理器)所需会话工厂(或实体管理器工厂)(有关 getSessionFactory 思路方法详细信息请参见运行时配置)

  Hibernate 约定

  在 Hibernate 中会话管理按照以下方式进行映射:

  使用 ThreadLocal 获取当前会话

  使用 SessionFactory#openSession 打开会话

  使用 Session#isOpen and Session#close 关闭会话

  清单 3. Hibernate 会话管理

public ORMHelper {
  private final ThreadLocal tls = ThreadLocal;
  public void openSession {
   Session s = (Session) tls.get;
    (s null) {
     s = getSessionFactory("test.cfg.xml").openSession;
     tls.(s);
   }
  }
  public Session getCurrentSession {
    (Session) tls.get;
  }
  public void closeSession {
   Session s = (Session)tls.get;
   tls.(null);
    (s != null && s.isOpen) s.close;
  }
  ...
}


  OpenJPA 约定

  在 OpenJPA 中等效 EntityManager 管理按照以下方式进行映射:

  使用 ThreadLocal 获取当前实体管理器

  使用 EntityManagerFactory#createEntityManager 打开会话

  使用 EntityManager#isOpen 和 EntityManager#close 关闭会话

  清单 4. OpenJPA 会话管理

public ORMHelper{
  private final ThreadLocal tls = ThreadLocal;
  public void openSession {
   EntityManager s = (EntityManager) tls.get;
    (s null) {
     s = getSessionFactory("test").createEntityManager;
     tls.(s);
   }
  } 
  public EntityManager getCurrentSession {
    (EntityManager) tls.get;
  }
  public void closeSession {
   EntityManager s = (EntityManager) tls.get;
   tls.(null);
    (s != null && s.isOpen) s.close;
  }
  ...
}


  4. 事务管理

  Hibernate 应用可以运行于使用区别事务策略环境中应用可以运行于使用本地 JDBC 或全局 Java Transaction API (JTA) 事务环境中

  使用本地 JDBC 事务是最常见场景如果具有异类数据存储(如数据库和消息队列)则 JTA 事务非常有用;JTA 允许您将其视为单个事务

  Hibernate 应用通过事务 API 来管理自己事务您使用事务策略(JDBC 或 JTA)是在 Hibernate 配置文件中设置所以它和应用无关

  Hibernate 约定

  在 Hibernate 中事务管理按照以下方式进行映射:

  使用 Session#beginTransaction 开始事务

  使用 Transaction#commit 提交事务

  使用 Transaction#isActive 和 Transaction#rollback 回滚事务

  清单 5. Hibernate 事务管理

public ORMHelper {
  private final ThreadLocal tltx = ThreadLocal;
  public void beginTransaction {
   Transaction tx = (Transaction) tltx.get;
    (tx null) {
     tx = getCurrentSession.beginTransaction;
     tltx.(tx);
   }
  }
  public void commitTransaction {
   Transaction tx = (Transaction)tltx.get;
    (tx != null && tx.isActive) tx.commit;
   tltx.(null);
  }
  public void rollbackTransaction {
   Transaction tx = (Transaction)tltx.get;
   tltx.(null);
    (tx != null && tx.isActive) tx.rollback;
  }
  ...
}


  OpenJPA 约定

  在 OpenJPA 中等效事务管理按照以下方式进行映射:

  使用 EntityManager#getTransaction 和 EntityTransaction#begin

  使用 EntityTransaction#commit

  使用 EntityTransaction#isActive 和 EntityTransaction#rollback

  清单 6. OpenJPA 事务管理

public ORMHelper {
  private final ThreadLocal tltx = ThreadLocal;
  public void beginTransaction {
   EntityTransaction tx = (EntityTransaction) tltx.get;
    (tx null) {
     tx = getCurrentSession.getTransaction;
     tx.begin;
     tltx.(tx);
   }
  }
  public void commitTransaction {
   EntityTransaction tx = (EntityTransaction)tltx.get;
    (tx != null && tx.isActive) tx.commit;
   tltx.(null);
  }
  public void rollbackTransaction {
   EntityTransaction tx = (EntityTransaction)tltx.get;
   tltx.(null);
    (tx != null && tx.isActive) tx.rollback;
  }
  ...
}


  尽管 OpenJPA 举例将事务存储在 ThreadLocal 中但通常仅 getCurrentSession.getTransaction.begin并在以后 getCurrentSession.getTransaction.commit因此使用 OpenJPA实际上不需要将事务存储在 ThreadLocal 中

  5. 实体管理

  实体管理常见场景包括:

  创建持久对象

  使用主键检索持久对象

  更新持久对象

  删除持久对象

  通常这些场景会映射到单个用例操作并使用分离实体在独立事务中执行但是它们还可以和连接实体起使用

  Hibernate 约定

  在 Hibernate 中实体管理按照以下方式进行映射:

  使用 Session#save 使临时对象持久化

  使用 Session#load 检索持久对象

  使用 Session#update 更新分离对象持久状态(如果修改持久(连接)对象状态则不需要更新;在 commitHibernate 自动将更新传播到数据库)

  使用 Session#delete 使分离或持久对象成为临时对象

  清单 7. Hibernate 实体管理

public ORMHelper {
  ...
  public void create(Serializable obj) {
   getCurrentSession.save(obj);      
  }
  public Object retrieve(Class clz, Serializable key) {
    getCurrentSession.load(clz, key);
  }
  
  public void update(Serializable obj ) {
   getCurrentSession.saveOrUpdate(obj);
  }
  public void delete(Serializable obj ) {
   getCurrentSession.delete(obj);
  }
}


  OpenJPA 约定

  在 OpenJPA 中等效实体管理按照以下方式进行映射:

  使用 EntityManager#persist 创建持久实体

  使用 EntityManager#find 检索持久实体

  使用 EntityManager#merge 更新分离实体(如果使用持久(连接)实体则无需 merge;OpenJPA 将更新传播到事务末尾数据库)

  使用 EntityManager#remove 删除分离或持久实体

  清单 8. OpenJPA 实体管理

public ORMHelper {
  ...
  public void create( Serializable obj ) {
   getCurrentSession.persist((Object) obj);      
  }
  public Object retrieve(Class clz, Serializable key) {
    getCurrentSession.find( clz, (Object) key );
  }
  
  public Object update( Serializable obj ) {
    getCurrentSession.merge((Object) obj);
  }
  public void delete( Serializable obj ) {
   getCurrentSession.remove( (Object) obj);
  }
}


  在本举例中ORMHelper#update 思路方法签名已更改 Hibernate update 思路方法将新托管持久状态复制到传递给该思路方法分离或(临时)对象所以它没有返回值相反在 OpenJPA merge 思路方法中原始分离(临时)对象未更改返回值包含新托管持久状态

  除更改 ORMHelper#update 签名外还必须更改该签名遗留应用以便将返回值显式分配给原始临时对象

  6. 分离实体

  基于分层体系结构 Hibernate 应用中另个常见场景是将无状态会话 EJB 用作会话 facade 来将“分离”实体返回给 Web 层在此场景中您将发现会话 EJB 启动和停止事务以响应来自 Web 层

  用于分层体系结构模式好处是由于 EJB 层根据用户交互来启动和停止事务因此在用户执行某项工作时事务从不保持打开状态因此所有事务生存时间很短应在数秒中完成

  大多数现有 Hibernate 应用使用 EJB 2.1 实现会话 EJB而大多数 OpenJPA 应用则使用 EJB 3.0您最初应使用资源本地实体管理器迁移到 EJB 3.0 会话 Bean这样不需要对事务逻辑进行更改但您还可以从 EJB 2.1 会话 Bean 使用 OpenJPA(请参见 通过 WebSphere Application Server V6.1 利用 OpenJPA)迁移完成后应考虑使用 JTA 实体管理器将应用重构到 EJB 3.0

  对于分离实体还应注意:如果在个事务中检索对象然后在事务外部修改该分离对象则必须该对象更新才能将其再保存到数据库这是 Hibernate 和 OpenJPA 中常见编程思路方法类似地如果您检索对象并在同事务中修改该对象则无需该对象更新就可以将其保存到数据库;提交事务后该对象会自动写入数据库此思路方法也是 Hibernate 和 OpenJPA 常见编程思路方法

  Hibernate 约定

  在 Hibernate 中EJB 2.1 分离实体按照以下方式进行映射:

  使用会话 facade 模式包装实体

  将分离实体 (POJO) 返回到 Web 层

  清单 9. EJB2.1 中 Hibernate 分离实体

public CustomerFacadeBean implements SessionBean, CustomerFacade{
  
 public Customer createCustomer( Customer customerEntity ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   ORMHelper.create(customerEntity);
   ORMHelper.commitTransaction;
    customerEntity;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 public Customer updateCustomer( Customer customerEntity ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   ORMHelper.update(customerEntity);
   ORMHelper.commitTransaction;
    customerEntity;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 public Customer getCustomer( Long customerId ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   Customer customerEntity;
   customerEntity = ORMHelper.retrieve(Customer.,customerId);
   ORMHelper.commitTransaction;
    customerEntity;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 public void deleteCustomer( Customer customerEntity ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   ORMHelper.delete(customerEntity);
   ORMHelper.commitTransaction;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   Throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 ... 
}


  OpenJPA 约定

  在 OpenJPA 中EJB 3.0 分离实体按照以下方式进行映射:

  使用会话 facade 模式包装实体

  将分离实体 (POJO) 返回到 Web 层

  清单 10. EJB 3.0 中 OpenJPA 分离实体

@Stateless
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public CustomerFacadeBean implements CustomerFacade {
 public Customer createCustomer( Customer customerEntity ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   ORMHelper.create(customerEntity);
   ORMHelper.commitTransaction;
    customerEntity;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 public Customer updateCustomer( Customer customerEntity ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   Customer Value;
   Value = ORMHelper.update(customerEntity);
   ORMHelper.commitTransaction;
    Value;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 public Customer getCustomer( Long customerId ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   Customer customerEntity;
   customerEntity = ORMHelper.retrieve(Customer.,customerId);
   ORMHelper.commitTransaction;
    customerEntity;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
 public void deleteCustomer( Customer customerEntity ) {
  ORMHelper.openSession;
  try {
   ORMHelper.beginTransaction;
   ORMHelper.delete(customerEntity);
   ORMHelper.commitTransaction;
  } catch (RuntimeException ex) {
   ORMHelper.rollbackTransaction;
   throw ex;
  } finally {
   ORMHelper.closeSession;
  }
 }
}


  清单 10 中演示了必须为迁移进行更改

  通过比较EJB 3.0 SessionFacade 类不能实现 SessionBean 类也不能实现 SessionBean 回调思路方法(SessionContext、ejbCreate、ejbRemove、ejbActivate 和 ejbPassivate)此外EJB 3.0 中也不需要组件接口、home 接口和部署描述符在 EJB 2.1 部署描述符中指定值包括在具有 Java 5 注释 EJB 3.0 SessionFacade 类中

  也就是说对 SessionFacade 业务思路方法没有任何更改预期会在这里看到对 Hibernate 或 OpenJPA API 大量这是我们在 helper 类中封装了大多数 Hibernate/OpenJPA API并且会话 bean 使用了 helper 类当然应用可能没有构造为在 helper 类中封装 Hibernate/OpenJPA API但是如果并行比较对前些部分中 helper 类进行更改应该能够确定对用于会话、事务和实体管理 EJB 会话 bean 更改是必需

  迁移 Hibernate 对象关系映射

  Hibernate 对象关系映射可以在启动时加载 XML 映射文件集中定义可以直接使用这些映射文件或从嵌入源代码 javadoc 样式注释中生成在 Hibernate 较新版本中还可以通过 Java 5 注释定义对象关系映射

  可以在 XML 映射文件集中定义 OpenJPA 对象关系映射或者通过直接嵌入代码 Java 5 注释定义它们该对象关系映射完全不需要映射文件

  在开发中大多数遗留 Hibernate 应用使用 XML 映射文件而大多数 OpenJPA 应用使用 Java 5 注释但在生产中则将它们移动到 XML这样对映射简单更改不需要您修改源代码和重新构建

  由于 XML 对 Hibernate 很常见并且在 OpenJPA 中通常用于生产所以在本部分中对映射使用 XML为帮助了解对象模型 (POJO) 中哪些内容是必需哪些不是必需还包括了相应基础代码(不包括注释)

  如果遗留 Hibernate 应用不使用映射文件(例如使用 javadoc 样式注释或 Java 5 注释)则您仍应能够基于本部分中信息得出更改并将应用迁移到 OpenJPA方面如果希望将 Java 5 注释和 OpenJPA 起使用附录提供了这样举例

  还可以使用其他思路方法在 Java 对象和关系表的间进行映射本部分将介绍在企业应用中出现通用场景其中包括:

  继承

  关系

  延迟

  对象标识

  乐观锁定

  1. 继承

  企业应用数据模型通常有多个位置类的间般化/专业化在这里提供重要重用机会Hibernate 和 OpenJPA 都支持可以在关系表中建模继承 3种区别思路方法我们将讨论其中两项我们认为这两项是最常见场景:

  单个表继承

  连接继承

  第 3项(每个具体个表)由 Hibernate 提供但通常很少用它是 JPA 持久性提供(如 OpenJPA)可选实现

  a. 单个表继承

  对于 Java 基础类包含其所有子类大多数属性情况可以使用单个表映射继承该表中列值标识特定子类行所表示例子属于此类如果没有任何列映射到特定子类则这些列必须声明为可以为空它们在该子类数据库行中将为空

  此继承策略缺点是如果子类为该例子定义多个非空属性则非空约束丢失会带来数据完整性问题此思路方法主要优点是它为类层次范围实体和查询的间多态关系提供最佳支持不存在复杂连接

  对象模型

  映射 1. 单个表继承 (POJO)

// Participant (base)
public Participant implements Serializable{
  private Long participantId;
  ...
}
// SalesRep (sub)
public SalesRep extends Participant { ... }
// Administrator (sub)
public Administrator extends Participant { ... }


  Hibernate 约定

  在 Hibernate 中单个表继承按照以下方式进行映射:

  在基础类中将类和映射到表辨别器列起使用;还要为主键和其他属性定义映射(稍后进行介绍不在举例中显示)

  将子类和子类中独特辨别器值起使用;还要为子类独有属性定义映射您不能在子类中定义 ID 元素;它们没有自已因此使用(基础)类 ID

  映射 2. 单个表继承(Hibernate XML 映射)

<!-- Participant (base) -->
< name="Participant" table="T_PRTCPNT" >
  <id name="participantId" column="PRTCPNT_TID"/>
  <discriminator column="PRTCPNT_TYPE" type=""/>
  ...
</>
<!-- SalesRep sub -->
<sub
 name="SalesRep"
 extends="Participant"
 discriminator-value="SALES_REP">
 ...
</sub>
<!-- Administrator sub -->
<sub name="Administrator"
  extends="Participant"
  discriminator-value="ADMIN">
  ...
</sub>


  OpenJPA 约定

  在 OpenJPA 中单个表继承按照以下方式进行映射:

  在基础类中使用 SINGLE_TABLE 继承策略和辨别器列;还要定义基础类持久属性及其唯 ID

  使用子类中辨别器值表示其例子;还要定义子类持久属性而不是 ID子类不会有任何表;它们属性将提升到表示基础类

  映射 3. 单个表继承(OpenJPA XML 映射)

<entity ="Participant">
  <table name="T_PRTCPNT"/>
  <inheritance strategy="SINGLE_TABLE"/>
  <discriminator-column name="PRTCPNT_CLASS"/>
  <attributes>
   <id name="participantId">
     <column name="PRTCPNT_TID"/>
   </id>
   ...
  </attributes>
</entity>
// SalesRep sub
<entity ="SalesRep">
  <discriminator-value>SALES_REP</discriminator-value>
  ...
</entity>
// Administrator sub
<entity ="Administrator">
  <discriminator-value>ADMIN</discriminator-value>
  ...
</entity>


  b. 连接继承

  对于基础类不包含所有子类大多数属性情况对于每个子类个包含基础类属性表和个单独连接表起使用对于非继承属性该表仅包含列因此阅读子类例子需要跨基础类表和子类表进行连接

  连接继承策略优点是您可以在子类中定义非空属性而对应缺点是需要多个连接才能构造例子此思路方法也是最灵活思路方法即您可以定义新子类并将属性添加到现有子类而无需修改基础类表

  对象模型

  映射 4. 连接继承 (POJO)

// Participant (base)
public Participant implements Serializable {
  private Long participantId;
  ...
}
// SalesRep (sub)
public SalesRep extends Participant {
  ...
}
// Administrator (sub)
public Administrator extends Participant {
  ...
}


  Hibernate 约定

  在 Hibernate 中连接继承按照以下方式进行映射:

  在基础类中将类元素和主键 (id) 起使用;还要为这些属性定义映射以建立基础类

  在子类中将连接子类和包含基础类主键外键列起使用;还要在连接子类中定义其他属性映射(本例中不显示)

  映射 5. 连接继承(Hibernate XML 映射)

<!-- Participant (base) -->
< name="Participant" table="T_PRTCPNT" >
  <id name="participantId" column="PRTCPNT_TID"/>
  ...
</>
<!-- SalesRep joined-sub -->
<joined-sub
  name="SalesRep"
  extends="Participant"
  table="T_SALESREP">
  <key column="PRTCPNT_TID"/>
  ...
</joined-sub>
<!-- Administrator joined-sub -->
<joined-sub
  name="Administrator"
  extends="Participant"
  table="T_ADMIN">
  <key column="PRTCPNT_TID"/>
  ...
</joined-sub>


  OpenJPA 约定

  在 OpenJPA 中连接继承按照以下方式进行映射:

  在基础类中使用 JOINED 继承策略基础类还定义所有连接子类使用主键并可以选择定义版本列还要定义基础类属性映射

  在子类中定义子类持久属性;其表将包含这些属性并包含用作外键主键列以连接基础类主键子类不定义版本列

  映射 6. 连接继承(OpenJPA XML 映射)

<!-- Participant (base) -->
<entity ="Participant">
  <table name="T_PRTCPNT"/>
  <inheritance strategy="JOINED"/>
  <attributes>
   <id name="participantId">
     <column name="PRTCPNT_TID"/>
   </id>
   ...
  </attributes
</entity>
<!-- SalesRep sub -->
<entity ="SalesRep">
  <table name="T_SALESREP"/>
  <primary-key-join-column name="PRTCPNT_TID"/>
  ...
</entity>
<!—Administrator sub -->
<entity ="Administrator">
  <table name="T_ADMIN"/>
  <primary-key-join-column name="PRTCPNT_TID"/>
  ...
</entity>


  2. 关系

  对象模型中对象的间(和数据模型中表的间)需要多种关系当数据模型在类似数据类任何列的间未指定关系时对象模型必须明确对象的间关系以支持遍历此外数据模型中关系没有任何固有方向(尽管按个方向搜索可以比另个方向更有效)而对象模型关系固有包含从个对象到另个对象方向

  对象模型关系是在数据模型中实现思路方法是通过个表中外键引用另个表中主键具有外键表称为子对象其行表示对象该对象生命周期依赖于他们引用对象因此其表将包含父对象外键由于子对象具有外键所以它必须始终指向有效父对象;它不能是孤立这就意味着要删除父对象必须首先删除其子对象或从父对象到子对象执行级联删除

  为了维护关系子对象也称为关系所有者而父对象称为非所有者“所有者”这概念非常重要尽管 Java 员必须在两边设置双向关系但是数据库只需更新个值来反映这些更改;在子对象(或所有者)中该更新针对外键因此在子对象中对表示外键属性更改会传播到数据库而对父对象中反向属性更改不会传播到数据库

  关系映射分为 4个类别:

  

  多对

  对多

  多对多

  a. 关系

  关系定义到另个持久对象引用其中子对象生命周期完全依赖于父对象生命周期

  关系有异常情况如果发生异常Hibernate 中常见做法是将其建模为组件对象这样子表中所有属性都将展开到父表中因此在访问子表属性时无需连接父表和子表

  还可以使用两个其他思路方法在 Hibernate 中建模关系:

  将多对元素和表的间外键关联起使用;请按照多对关系指导原则进行操作

  将元素和表的间主键关联起使用;使用 OpenJPA 中元素进行映射

  本部分其余内容将介绍在 Hibernate 中使用组件元素迁移关系并介绍如何将其迁移到 OpenJPA 中嵌入对象

  对象模型

  映射 7. 关系 (POJO)

// Employee (parent)
public Employee implements Serializable {
  private Long employeeId;
  private EmployeeRecord employeeRec;
  ...
}
// EmployeeRecord (child)
public EmployeeRecord implements Serializable { ... }


  Hibernate 约定

  在 Hibernate 中使用组件对象关系按照以下方式进行映射:

  使用类建模父类;在父类中还要定义主键 (id) 和父类其他属性

  使用嵌套组件元素建模子类;还可以在嵌套组件中定义其他属性(如果需要)

  映射 8. 关系和组件对象(Hibernate XML 映射)

<!—Employee (parent)
< name="Employee" table="T_EMPLOYEE">
  ...
  <id name="employeeId" column="EMP_TID"/>
  <!-- EmployeeRecord (child)
  <component name="employeeRec" ="EmployeeRecord">
    ...
  </component>
</>


  OpenJPA 约定

  在 OpenJPA 中使用可嵌入对象关系按照以下方式进行映射:

  在父实体中声明嵌入字段(例如 Employee)将嵌入字段映射为父实体数据库记录部分并嵌入父实体而不是形成和子实体关系

  将子实体(例如 EmployeeRecord)定义为可嵌入实体

  映射 9. 关系和可嵌入对象(OpenJPA XML 映射)

<!-- Employee (parent) -->
<entity ="Employee">
  <table name="T_EMPLOYEE"/>
  <attributes>
   <id name="employeeId">
     <column name="EMP_TID"/>
   </id>
   <embedded name="employeeRec"/>
  ...
  </attributes>
</entity>
<!-- EmployeeRecord (child) -->
<embeddable ="EmployeeRecord">
  ...
</embeddable>


  b. 多对关系

  多对关系定义到单个持久对象引用尽管多对关系可以是单向但是经常将其定义为对多双向关系反向过程

  声明多对关系实体是子对象(或关系所有者)其表包含外键而声明多对关系实体引用对象是父对象由于其表不包含外键所以它是非所有者或关系反向

  对象模型

  映射 10. 多对关系 (POJO)

// Address (parent)
public Address implements Serializable {
  private Long addressId;
 ...
}
// Phone (child)
public Phone implements Serializable {
  private Address address;
  ...
}


  Hibernate 约定

  在 Hibernate 中多对关系按照以下方式进行映射:

  在子类中使用多对元素

  在父类中定义主键

  映射 11. 多对关系(Hibernate XML 映射)

<!-- Phone (child) -->
< name="Phone" table="T_PHONE">
  <many-to-one
    name="address"
    ="Address"
    column="ADDR_TID">
  </many-to-one>
  ...
</>
<!-- Address (parent) -->
< name="Address" table="T_ADDRESS">
  <id name="addressId" column="ADDR_TID"/>
  ...
</>


  OpenJPA 约定

  在 OpenJPA 中多对关系按照以下方式进行映射:

  在父实体中定义主键 (id)

  在子实体中使用多对元素定义关系并使用嵌套连接列元素定义外键连接列指定如何按照连接思路方法找到此子实体父实体

  映射 12. 多对关系(OpenJPA XML 映射)

<!-- Address (parent) -->
<entity ="Address">
  <table name="T_ADDRESS"/>
  <attributes>
   <id name="addressId">
     <column name="ADDR_TID"/>
   </id>
   ...
  </attributes>
</entity>
<!-- Phone (child) -->
<entity ="Child">
  <table name="T_PHONE"/>
  <attributes>
   <many-to-one name="address">
     <join-column name="ADDR_TID"/>
   </many-to-one>
   ...
  </attributes>
</entity>


Tags:  springhibernate hibernate openjpa ejbhibernate

延伸阅读

最新评论

发表评论