23

TL; DR如何强制创建 Hibernate 模式以在每个具体类的 table-per-concrete-class 设置中为下面显示的结构创建外键约束AbstractProperty.ownerIdOwner.ownerId而不OwnerAbstractProperty?

我正在开发一个具有以下类结构的项目:

类结构示例

Owner一个到 的一对一映射AbstractProperty,它由ConcreteProperty类扩展(和其他类似AnotherProperty的,但这与这个问题的其余部分并不真正相关)。

TheAbstractProperty真的只有一个属性,the abstractPropertyId。因此,我们希望使用table-per-concrete-class结构,以表OwnerConcreteProperty和表作为其他AbstractProperty扩展类 ( 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>

这行得通。

但是,这是我的问题,使用此映射并让 Hibernate 为我创建模式 ( <property name="hbm2ddl.auto">create</property>),它不会创建从ConcreteProperty.ownerId数据库字段到Owner.ownerId字段的外键约束。当我使用此映射创建反向约束的一对一字段时AbstractProperty(其中该字段是java类中的类型):OwnerAbstractPropertyownerOwnerAbstractProperty

<?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

4 回答 4

1

简单的回答:永远不要让 Hibernate 为实际应用程序创建模式。

Hibernate 是一个对象关系映射器,应该这样对待它。

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

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

于 2015-05-12T12:30:15.693 回答
0

如果将 Abstract Property 上的 Owner 属性定义为“ transient ”,它不会自动工作吗?

变量可以被标记为瞬态的,以表明它们不是对象持久状态的一部分。

如果您实现自己的手动序列化,您可以检查字段上的修饰符并忽略它——>避免循环序列化问题。

我看到的唯一另一种方法是将 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
于 2013-08-09T13:03:14.570 回答
0

首先:Hibernate/JPA 能够处理很多场景——如果真的有很多人尝试和你一样的方法,我认为现在应该已经解决了——这不是春鸡。** 这是一个线索 ;-) **

第二:拥有一个名为 'Owner' 和 'ownerProperty' 的表是另一个线索。这些名称推断出一种内在的关系。

第三:只需声明您不希望在 AbstractProperty 表中拥有所有者属性,这就为通常称为 catch-22 的逻辑谬误奠定了基础(http://en.wikipedia.org/wiki/False_dilemma) .

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

我的建议是从问题中退后一步,重新评估它。例如,如果您只是使用 spring-jdbc 编写直接查询,您希望如何与 SELECT、UPDATE 和 DELETE 操作的数据进行交互?...如果您解决了这些问题,您的解决方案/需求可能会更清楚地呈现出来。更确切地说,您希望该行为在级联删除上是什么?如果我对单个 Owner 记录发出 DELETE 语句,您是否希望数据库自动从子表中删除记录?递归?一旦你把它隔离了,你就可以弄清楚如何告诉 Hibernate 该做什么——不要让团队成员通过过早地限制解决方案来本末倒置。

例如,(案例 1)如果您真的在处理所有者的“财产”,可以合理地预见您需要存储有关所有者的多个属性(又名:OneToMany)。

或者,(案例2)如果您正在处理所有者的“类型”(如在鉴别器字段中),那么您的“AbstractProperty”表应该扩展所有者......在这种情况下,您可以减少您的解决方案减少到 3 个表(带有鉴别器的所有者,带 ownerId 的 Concrete1,带 ownerId 的 Concrete2)。

建议的解决方案 1:在这两种情况下,“AbstractProperty”表仍然可以引用它的父/所有者。如果确实如此,我认为级联删除可能会以您喜欢的方式工作。

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

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

于 2015-03-19T01:53:31.687 回答
0

我们使用标准的 JPA(没有特定于休眠的 hack)并且遇到了同样的问题,但我们没有找到一个好的解决方案。

假设:

  1. AbstractProperty是包中的一个类,可在不同的应用程序中重用/共享,并且您不希望引用特定于应用程序的Owner类。
  2. ConcreteProperty&AnotherProperty是特定于应用程序的。

在这种情况下,解决方案是将引用放入ConcretePropertyOwner使用外键),最终都AnotherProperty扩展相同的ApplicationProperty,并使abstractPropertyId私有,以便在设置所有者时自动设置。

于 2013-08-07T20:42:35.427 回答