2013-07-30 47 views
23

TL; DR如何强制执行Hibernate模式创建以在每个具体类的表设置中创建外键约束AbstractProperty.ownerIdOwner.ownerId为下面显示的结构,而不需要将Owner属性添加到AbstractProperty在一对一每个具体类结构中创建外键

我正在一个项目,我有下面的类结构:

Class structure sample

Owner具有一一对一映射到一个AbstractProperty,这是由ConcreteProperty类扩展(和其他人一样AnotherProperty,但这不是真正相关的问题的其余部分)。

AbstractProperty确实只有一个属性abstractPropertyId。因此,我们希望使用table-per-concrete-class结构,最终以表Owner,ConcreteProperty和其他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(其中owner字段为Owner类型的在AbstractProperty java类)的逆约束一对一个字段从AbstractPropertyOwner

<?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没有这个Owner字段时,强制创建从AbstractProperty.ownerIdOwner.ownerId的外键?

+1

你为什么期望/需要从'* Property'表到'Owner'表有一个DB列/引用?根据你的方案,我会说在数据库中唯一的参考瓦特/外键是从'所有者'表格*属性... –

+0

我不能从'所有者'设置一个外键到'abstractPropertyId',因为我wouldn不知道它引用了哪个表(即哪个'AbstractProperty'实现)。 – drvdijk

+0

不是一个实际的答案,而是一个关于Why-What-How谱系的“为什么”的问题:您是否明确构建了一个实体 - 属性 - 值模型? –

回答

0

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

假设:

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

在这种情况下,该解决方案将是把引用在ConcretePropertyOwner(带外键),最终都与AnotherProperty延伸的同一ApplicationProperty,并使abstractPropertyId私人的,所以上设置所有者,它会自动设置。

+0

你的意思是'AbstractProperty'而不是'ApplicationProperty'?在我的应用程序中,我宁愿没有对“所有者”的引用,以防止以我不想要的方式解决循环序列化问题。否则,你的解决方案会让'AbstractProperty''' Owner'不知道,但是你会复制粘贴所有子类中的'Owner' getter和setter,通常由抽象超类来解决。 – drvdijk

+1

不,我的意思是'ApplicationProperty'只是另一个类(特定于应用程序),它扩展了'AbstractProperty'作为所有'拥有者'感知的具体类的超类型(像'ConcreteProperty'和'AnotherProperty') –

+0

正确的当然,很好!不幸的是,它仍然不能解决我的循环序列化问题,为此我无法在'AbstractProperty'树中拥有'Owner'。 – drvdijk

0

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

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

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

我看到的唯一的另一种方式是所有者属性推到每一个具体的性能等级,并改变你的映射

<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 
+0

实际上,数据库*应该*存储属性(使用外键),但由于循环序列化,它不应该出现在Java层次结构中。但是'transient'关键字可能实际上可以做到这一点,也许序列化框架(试图尽可能避免编写手动定制的东西)会在序列化时忽略它,而我仍然可以在'hbm'文件中指定属性。去看看,谢谢你的想法! – drvdijk

+0

而且?它有用吗? – xwoker

0

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

第二:有一个名为'所有者'与'ownerProperty'是另一个线索。这些名字推断出内置的关系。

第三:简单地说,您不希望AbstractProperty表中的所有者属性设置逻辑谬误的阶段,通常称为catch-22(http://en.wikipedia.org/wiki/False_dilemma)。

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

我的建议是从问题中退一步,然后重新评估它。例如,如果您只是使用spring-jdbc编写直接查询,那么您希望如何与SELECT,UPDATE和DELETE操作的数据进行交互? ......如果你解决了这些问题,你的解决方案/需求可能会更加清晰。更加尖锐的是,你期望的行为是级联 - 删除?如果我在单个所有者记录上发出DELETE语句,是否希望数据库自动从子表中删除记录?递归?一旦你有了这个孤立的东西,你就可以找出如何告诉Hibernate该怎么做 - 不要让团队成员过早地限制解决方案,把它放在马匹之前。例如,(情况1)如果您真的在处理所有者的“财产”,那么可以合理预见您需要存储有关所有者的多个属性(又名:OneToMany)。 (case 2)如果你正在处理所有者的“类型”(如在鉴别器字段中),那么你的'AbstractProperty'表应该扩展Owner ...在这种情况下,你可能能够将您的解决方案减少到3个表(拥有鉴别符的所有者,Concrete1 w/ownerId,Concrete2 w/ownerId)。

建议的解决方案1:在这两种情况下,'AbstractProperty'表对其父/所有者都有参考意义。如果确实如此,我认为级联DELETES可能会以您喜欢的方式工作。

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

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

1

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

Hibernate是一个对象关系映射器,应该把它当作这个。

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

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

相关问题