一尘不染

Oracle旧表没有好的PK:如何休眠?

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来逐出并重新加载实体,并从应用程序中隐藏复杂性?还是我应该寻找Ibatis的Hibernate替代产品?


阅读 237

收藏
2020-06-20

共1个答案

一尘不染

您不想用ROWID作主键,因为不能保证它在行的整个生命周期内都是稳定的。

最好添加一个合成密钥。使用触发器用序列值填充列。

您在遗留方面有点含糊,因此很难确定其含义。添加列将破坏任何未显式列出目标列的插入语句。它也可能会打断任何SELECT *查询(除非它们选择使用%ROWTYPE关键字声明的变量。但是您不必更改任何其他应用程序,因此它使用新的主键而不是现有的列-除非您确实愿意。

2020-06-20