2013-07-30 21 views
23

TL; DR Hibernateスキーマの作成を強制して、コンクリートクラスごとのテーブル設定で外部キー制約を作成する方法を教えてください。以下に表示される構造のAbstractPropertyOwnerプロパティを追加しないで、AbstractProperty.ownerIdOwner.ownerIdを入力しますか?1対1コンクリートクラス構造の外部キーの作成

私は、私は以下のクラス構造を持ってプロジェクトに取り組んでいます:

Class structure sample

OwnerConcretePropertyクラスによって拡張されAbstractPropertyへの1対1のマッピングを、(持っていますAnotherPropertyのようなものがありますが、これはこの質問の残りの部分ではあまり関係ありません)。

AbstractPropertyには実際には1つのプロパティ、abstractPropertyIdがあります。そのため、テーブルOwner,ConcretePropertyおよびその他のAbstractProperty拡張クラス(AnotherProperty)のテーブルで終わるtable-per-concrete-class構造を使用します。

このため、私は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>)、それはOwner.ownerIdフィールドへConcreteProperty.ownerIdデータベースフィールドからの外部キー制約を作成しません。

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

どのように私ができる:私は逆(ownerフィールドはAbstractProperty JavaクラスのタイプOwnerである)AbstractPropertyため、このマッピングを使用してOwnerAbstractPropertyから一対一のフィールドを制約作成するときにそれはありません外部キーの作成をAbstractPropertyOwnerフィールドなしでAbstractProperty.ownerIdからOwner.ownerIdに強制しますか?

+1

'* Property'テーブルから' Owner'テーブルまでのDB列/参照があると期待したいのですが?あなたのスキームによると、DB内の外部キーは 'Owner'テーブルから* Propertyへの唯一の参照であると言います... –

+0

' Owner'から 'abstractPropertyId'への外部キーを設定できません。どのテーブル(つまり、どの抽象プロパティー)が参照しているかを知ることはできません。 – drvdijk

+0

実際の答えではありませんが、Why-What-Howスペクトルの「Why」に関する質問:Entity-Attribute-Valueモデルを明示的に作成していますか? –

答えて

0

私たちは標準JPA(ハイバネート固有のハッキングなし)を使用し、同じ問題を抱えていました。

仮定:

  1. AbstractPropertyは、異なるアプリケーションでの共有/再利用可能で、パッケージ内のクラスであり、そしてあなたは、アプリケーション固有のOwnerクラスへの参照を望んでいないこと。
  2. ConcreteProperty & AnotherPropertyはアプリケーションによって異なります。この場合

、解決策は、所有者を設定する上で、最終的に両方AnotherPropertyとなるように、同じApplicationPropertyを拡張し、abstractPropertyIdはプライベート作り、(外部キーで)OwnerConcretePropertyに参照を置くことであろう自動的に設定されます。

+0

'ApplicationProperty'ではなく' AbstractProperty'を意味しますか?私のアプリケーションでは、私が望んでいないやり方で循環的な直列化の問題を解決することを避けるために、 '所有者 'への参照をまったく持っていないでしょう。さもなければ、あなたのソリューションは 'AbstractProperty''を所有者には認識されませんが、抽象スーパークラスによって通常解決されるすべてのサブクラスで' Owner'ゲッターとセッターをコピーしてコピーします。 – drvdijk

+1

いいえ、 'ApplicationProperty'は' AbstractProperty'を 'Owner'を意識したすべての具象クラス(' ConcreteProperty'や 'AnotherProperty'など)のスーパータイプとして拡張する別のクラス(アプリケーション固有)になります。 –

+0

もちろん、素敵!残念ながら、 'AbstractProperty'ツリーに' Owner'を持つことはできません。 – drvdijk

0

抽象プロパティの所有者属性を "transient"と定義すると、自動的には機能しませんか?

変数は、オブジェクトの永続状態の一部ではないことを示すために一時的にマークされることがあります。

手動でシリアル化を実装する場合、フィールドの修飾子を確認して無視するだけで、循環シリアル化の問題を回避できます。

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

まず:私は見

唯一の他の方法は、具体的なプロパティクラスのそれぞれに所有者属性を押して、次のSQLを作成し

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

にあなたのマッピングを変更することです: Hibernate/JPAは多くのシナリオを扱うことができます。あなたと同じアプローチを試みている人が本当にたくさんいたなら、今はこれが解決されていると思います。 **これは単なる手掛かりです;-) **

2番目: 'ownerProperty'という名前の 'Owner'というテーブルを持つことは別の手がかりです。名前は関係を構築していると推測します。

第3段階:AbstractPropertyテーブル内に所有者プロパティを必要としないことを単に示すだけで、一般的にcatch-22(http://en.wikipedia.org/wiki/False_dilemma)と呼ばれる論理的な誤りの段階を設定します。

私のポイント - >これは技術的/フレームワークの問題よりもモデリング/設計の問題のほうが多いようです。

私の提案は、問題から一歩踏み出して、それを再評価することです。たとえば、spring-jdbcを使用して直接クエリを作成しているだけの場合、SELECT、UPDATE、およびDELETE操作のデータとどのようにやりとりすると思いますか? ...あなたがそれらを介して作業する場合、あなたのソリューション/ニーズは、より明確に存在する可能性が高いでしょう。さらに指摘されるためには、その動作がカスケード・デリートになることをどのように期待しますか? 1つのOwnerレコードでDELETE文を発行すると、データベースが子テーブルからレコードを自動的に削除しますか?再帰的に?一度それを分離したら、Hibernateに何をすべきかを理解することができます。チームメンバーが早期にソリューションを制限することによって、馬の前にカートを置くことを許可しないでください。

たとえば、所有者の「プロパティ」を本当に扱っている場合は、所有者(別名OneToMany)について複数のプロパティを格納する必要があることが合理的に予測できます。

(ケース2)オーナーの 'タイプ'(識別子フィールドのように)を扱う場合、 'AbstractProperty'テーブルはオーナーを拡張する必要があります。ソリューションを3つのテーブルに減らすことができます(弁別子を持つ所有者、Concrete1 w/ownerId、Concrete2 w/ownerId)。

提案された解決策1:いずれの場合でも、 'AbstractProperty'テーブルが親/所有者への参照を持つことは意味があります。それができたら、カスケーディングDELETESが好きなように動作すると思います。

提案された解決策2:ただし、オーナーレコードのカスケード削除シナリオでは、AbstractPropertyの行が参照データとして使用されることをお勧めします。追加する必要があると主張できます独自の複合キーを持つマッピングテーブルとして参照データを保護するために、OwnerとAbstractPropertyの間のテーブル。

ビジネスニーズとユーザーストーリーに焦点を当てています。利用可能なソリューションの選択肢を導くうえで役立ちます。

1

簡単な答え:実際のアプリケーションのためにHibernateにスキーマを作成させてはいけません。

Hibernateはオブジェクトリレーショナルマッパーであり、これをこのように扱う必要があります。

さらに、Hibernateはスキーマを最初に作成することが最も親切です。しかし、最初のリリース後の環境では、Hibernateにスキーマを制御させたくありません。結局のところ、移行スクリプト(手動またはツールのサポート)を持つためにSQLを処理する必要があります。最初のリリース後、データベースにデータがあります。問題の少ない本番システムでのデータ移行を確実に行うには、開発環境と同じように、本番環境でスキーマとデータの移行を同じ方法で行う必要があります。

例外は、ほとんどデータを変更することができないアプリケーションで、データが失われてもすぐに再構成可能な場合があります。

関連する問題