Oracle遗留表没有很好的PK:如何进行Hibernate?

时间:2009-10-05 09:38:53

标签: java oracle hibernate

我在Oracle数据库中有遗留表,我想通过Hibernate从Java应用程序访问。问题是:表没有好的主键。例如,表格如下所示:

create table employee (
  first_name    varchar(64) not null,
  last_name     varchar(64) not null,
  hired_at      date,
  department    varchar(64)
);
create unique index emp_uidx on employee (firstname, lastname, hired_at);

原始设计师决定使用hired_at列作为状态字段,认为数据库永远不需要区分公司外部的John Smiths,但可以通过他们的名称来识别具有相同名称的员工hired_at日期。显然,这会创建一个部分可空和可修改的主键。由于数据库不允许将其作为主键,因此他使用了唯一索引。

现在,如果我尝试为此编写一个Hibernate映射,我可以想出这样的东西:

<hibernate-mapping>
  <class name="com.snakeoil.personnel" table="employee">
    <composite-id class="com.snakeoil.personnel.EmpId" name="id">
      <key-property name="firstName" column="first_name" type="string"/>
      <key-property name="lastName" column="last_name" type="string"/>
    </composite-id>
    <property name="hiredAt" column="hired_at" type="date"/>
    <property name="department" type="string">
  </class>
</hibernate-mapping>

这适用于读取数据,但是当我创建仅在hiredAt日期不同的新条目时,我会遇到org.hibernate.NonUniqueObjectExceptions。或者,我可能会考虑Oracle的ROWID功能:

<id column="ROWID" name="id" type="string">
   <generator class="native"/>
</id>

这也适用于读取数据。但显然,你不能坚持新的对象,因为Hibernate现在抛出一个org.hibernate.exception.SQLGrammarException:在尝试存储实体时无法获得下一个序列值。

显而易见的方法是通过代理键。然而,这需要更改数据库模式,这将我们带回到“传统”的东西。

我在俯瞰什么?有没有办法让Hibernate与Oracle的ROWID合作,可能使用不同的生成器?或者有没有办法让第一个想法发挥作用,也许有聪明的DAO可以驱逐和重新加载实体,并隐藏应用程序的复杂性?或者我应该寻找Hibernate的替代品,Ibatis可能吗?

2 个答案:

答案 0 :(得分:7)

您不希望将ROWID用作主键,因为它不能保证在一行的生命周期内保持稳定。

添加合成密钥是最好的选择。使用触发器使用序列值填充列。

你对传统方面有点模糊,因此很难确定其含义是什么。添加列会破坏任何未明确列出目标列的insert语句。它也可能会破坏任何SELECT *个查询(除非他们选择使用%ROWTYPE关键字声明的变量。但您不必更改任何其他应用程序,因此它使用新的主键而不是现有的列 - 除非你真的想要。

答案 1 :(得分:5)

我会添加一个带有后备序列的代理键。它不会改变你的任何遗留语义,并且会满足Hibernate。

只是好奇 - 如果索引有姓名和雇佣日期,为什么你的复合键不包括雇用日期?我原本希望在复合键中看到索引中的所有字段。