在一对一的每个具体类表结构中创建外键

时间:2013-07-30 14:34:00

标签: java hibernate orm

TL; DR 如何强制创建Hibernate架构,以便在从AbstractProperty.ownerIdOwner.ownerId的每个具体表的设置中创建外键约束下面显示的结构,没有向Owner添加AbstractProperty属性?

我正在开展一个项目,我有以下类结构:

Class structure sample

OwnerAbstractProperty的一对一映射,由ConcreteProperty类(以及其他类似AnotherProperty扩展,但实际上不是AbstractProperty与此问题的其余部分相关)。

abstractPropertyId实际上只有一个属性Owner。因此,我们希望使用table-per-concrete-class结构,最后是表ConcretePropertyAbstractProperty,以及其他AnotherProperty扩展类(Owner)的表

为此,我为<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.example"> <class name="Owner"> <id name="ownerId"> <generator class="identity"/> </id> <property name="ownerProperty"/> <one-to-one name="abstractProperty"/> </class> </hibernate-mapping> 创建了以下映射:

AbstractProperty

对于<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="com.example"> <class name="AbstractProperty" abstract="true"> <id name="ownerId"> <generator class="foreign"> <param name="property">ownerId</param> </generator> </id> <union-subclass name="ConcreteProperty"> <property name="concreteProperty"/> </union-subclass> <union-subclass name="AnotherProperty"> <property name="anotherProperty"/> </union-subclass> </class> </hibernate-mapping>

<property name="hbm2ddl.auto">create</property>

这很有效。

然而,这是我的问题,使用此映射并让Hibernate为我创建模式(ConcreteProperty.ownerId),它不会创建从Owner.ownerId数据库字段到{的外键约束{1}}字段。当我使用AbstractProperty的映射(Owner字段的类型为{{AbstractProperty创建从ownerOwner的逆约束一对一字段时,它会执行此操作1 {}在AbstractProperty java类中):

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example">
    <class name="AbstractProperty" abstract="true">
        <id name="ownerId">
            <generator class="foreign">
                <param name="property">ownerId</param>
            </generator>
        </id>
        <one-to-one name="owner" constrained="true"/>
        <union-subclass name="ConcreteProperty">
            <property name="concreteProperty"/>
        </union-subclass>
        <union-subclass name="AnotherProperty">
            <property name="anotherProperty"/>
        </union-subclass>
    </class>
</hibernate-mapping>

如果AbstractProperty.ownerId中没有此Owner.ownerId字段,如何强制从OwnerAbstractProperty创建外键?

4 个答案:

答案 0 :(得分:1)

简单回答:永远不要让Hibernate为真实应用程序创建架构。

Hibernate是一个对象关系映射器,应该将其视为此。

Hibernate另外创建模式最初是第一次。但是在第一次发布之后的环境中,您不希望Hibernate控制架构。毕竟,您必须处理SQL才能拥有迁移脚本(手动或工具支持)。在第一个版本之后,您将拥有数据库中的数据。为了确保生产系统上的数据迁移具有较少的问题,您应该在生产环境中以与在开发环境中相同的方式考虑模式和数据迁移。

例外情况可能是任何很少更改数据的应用程序,这些数据可能会在数据丢失时快速重建。

答案 1 :(得分:0)

我们使用标准JPA(没有特定于hibernate的黑客)并遇到同样的问题,我们找不到一个好的解决方案。

假设:

  1. AbstractProperty是包中的一个类,可以在不同的应用程序中重用/共享,并且您不希望引用特定于应用程序的Owner类。
  2. ConcreteProperty&amp; AnotherProperty是特定于应用程序的。
  3. 在这种情况下,解决方案是将ConcreteProperty中的引用放入Owner(使用外键),最终两者都AnotherProperty扩展相同的ApplicationProperty,并使abstractPropertyId为私有,以便在设置所有者时自动设置。

答案 2 :(得分:0)

如果您将Abstract Property上的Owner属性定义为“transient”,它会不会自动生效?

  

变量可能被标记为瞬态,表示它们不属于对象的持久状态。

如果你实现自己的手动序列化,你可以检查一个字段上的修饰符,然后忽略它 - &gt;避免循环序列化问题。

我看到的唯一另一种方法是将Owner属性推送到每个具体的Property类并将映射更改为

<class name="AbstractProperty" abstract="true">
    <id name="ownerId">
        <generator class="foreign">
            <param name="property">ownerId</param>
        </generator>
    </id>

    <union-subclass name="ConcreteProperty">
        <property name="concreteProperty"/>
        <one-to-one name="owner" constrained="true"/>
    </union-subclass>
    <union-subclass name="AnotherProperty">
        <property name="anotherProperty"/>
        <one-to-one name="owner" constrained="true"/>
    </union-subclass>
</class>

创建以下sql:

create table AnotherProperty (
    ownerId integer not null,
    anotherProperty varchar(255),
    primary key (ownerId)
)

create table ConcreteProperty (
    ownerId integer not null,
    concreteProperty varchar(255),
    primary key (ownerId)
)

create table Owner (
    ownerId integer generated by default as identity,
    ownerProperty varchar(255),
    primary key (ownerId)
)

alter table AnotherProperty 
    add constraint FK_ceq89n6x2i1ax18bb4gqpq4m5 
    foreign key (ownerId) 
    references Owner

alter table ConcreteProperty 
    add constraint FK_i41buhvtxxtpsim2cc0ur1gxr 
    foreign key (ownerId) 
    references Owner

答案 3 :(得分:0)

首先:Hibernate / JPA能够处理很多场景 - 如果真的有很多人尝试过与你相同的方法,我会认为它现在已经解决了 - 它不是春鸡。 **这是一条线索;-) **

第二:拥有一张名为&#39; Owner&#39;拥有&#39; ownerProperty&#39;是另一条线索。这些名字推断出内在的关系。

第三:仅仅通过声明你不想在AbstractProperty表中拥有一个所有者属性,这就为逻辑谬误设置了阶段,通常称为catch-22(http://en.wikipedia.org/wiki/False_dilemma)。

我的观点 - &gt;这似乎更像是一个建模/设计问题,而不是技术/框架问题。

我的建议是从问题中退一步,并重新评估它。例如,如果您只是使用spring-jdbc编写直接查询,那么您希望如何与SELECT,UPDATE和DELETE操作的数据进行交互? ......如果你通过这些工作,你的解决方案/需求可能会更清楚地表现出来。更具针对性的是,您期望在级联删除上行为是什么?如果我在单个所有者记录上发出DELETE语句,您是否希望数据库自动删除子表中的记录?递归?一旦你将其隔离,你就可以弄清楚如何告诉Hibernate该做什么 - 不要让团队成员通过过早地限制解决方案将推车放到马前。

例如,(案例1)如果你真的正在处理一个&#39;属性&#39;对于所有者来说,可以合理地预见到您需要存储关于所有者的多个属性(又名:OneToMany)。

或者,(案例2)如果你正在处理&#39;类型&#39;所有者(如在鉴别领域),然后你的&#39; AbstractProperty&#39; table应该扩展所有者...在这种情况下,您可以将解决方案减少到3个表(带有鉴别器的所有者,Concrete1 w / ownerId,Concrete2 w / ownerId)。

提出的解决方案1:在任何一种情况下,对于“抽象属性”来说,它仍然是有意义的。表可以引用它的父/所有者。如果确实如此,我认为Cascading DELETES可能会按您喜欢的方式工作。

建议的解决方案2:但是,如果在所有者记录的级联删除方案中,您希望AbstractProperty中的行作为参考数据保留,那么可以认为您应该添加一个额外的表在Owner和AbstractProperty之间保护您的参考数据...作为具有唯一复合键的映射表。

专注于业务需求和用户故事,这有望指导您选择可用的解决方案。