2012-06-15 19 views
9

状況

ように私は単一テーブル継承のために構成さDiscriminatorColumn、持つエンティティを有する:DiscriminatorColumn主キー/ IDの一部

@Entity 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="TYPE") 
public class ContainerAssignment{ 
... 
} 

'ContainerAssignment' は別のエンティティへの参照を有する。

@JoinColumn(name="CONTAINER_ID") 
private Container container; 

コンテナのタイプは、それぞれTYPEがContainerAssignmentです。つまり、ContainerAssignmentテーブルの主キーはCONTAINER_IDTYPEで定義されています。

ContainerAssignmentには、いくつかのサブクラスがあります。

@Entity 
@DiscriminatorValue("SOME_TYPE") 
public class SomeTypeOfContainerAssignment extends ContainerAssignment{ 
... 
} 

のみ与えられたCONTAINER_IDのための単一のSomeTypeOfContainerAssignmentインスタンスがあります。

問題

私はContainerAssignmentテーブルの上にJPA @Idとしてだけでコンテナを定義した場合、私は偉大である、entityManager.find(SomeTypeOfContainerAssignment.class, containerId)を行うことができます。これはSELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1 AND TYPE = 'SOME_TYPE';の行に沿って何かを実行します。これは、エンティティの@DiscriminatorValue("SOME_TYPE")注釈のために、ここでTYPEチェックが必要であることを知っています。

しかし、これは、Containerが実際にプライマリキーではないため、ContainerからContainerAssignmentへの逆参照が中断することを意味します。たとえば、コンテナに@OneToOne(mappedBy=container) private SomeTypeOfContainerAssignment assignment;がある場合、コンテナを読み込むと、タイプチェックなしでSELECT * FROM CONTAINER_ASSIGNMENT WHERE CONTAINER_ID = 1;のようなものが割り当て内に読み込まれます。これにより、コンテナのすべての代入が得られます。次に、一見無作為に、潜在的に間違った型を選択します。この場合、例外がスローされます。

コンテナとタイプを使用してContainerAssignmentのJPA @IdをコンポジットIDとして定義すると、ContainerAssignmentのサブクラスへの参照が正常に機能します。

ただし、containerIdはIDではないため、entityManager.find(SomeTypeOfContainerAssignment.class, containerId)はできません。私は@DiscriminatorValue("SOME_TYPE")のポイントを打ち負かしているようだentityManager.find(SomeTypeOfContainerAssignment.class, new MyPk(containerId, "SOME_TYPE"))をしなければならない。とにかく検索で型を指定しなければならない場合は、単一のContainerAssignment Entityを使用することもできます。

質問

もちょうどでEntityManager.findにできることながら、テーブルの主キーが識別カラム上の複合体である単一テーブル継承エンティティのサブクラスへの参照が働いている方法はあります弁別器ではない主キーの部分?

答えて

0

Container双方向ContainerAssignmentを拡張SomeTypeOfContainerAssignmentとOneToOne、次いでコンテナフィールドはContainerAssignmentで定義され、マッピングされるべきではないが、SomeTypeOfContainerAssignmentに有する場合:容器の割り当てのすべてのタイプは、を使用している場合

public class Container { 
    @Id 
    private Long id; 

    @OneToOne(mappedBy = "container") 
    private SomeTypeOfContainerAssignment someTypeOfContainerAssignment; 
} 

public class ContainerAssignment { 
    @Id 
    private Long id; 
} 

public class SomeTypeOfContainerAssignment extends ContainerAssignment { 
    @OneToOne 
    private Container container; 
} 

をあなたが同じ城の使用を許可している場合、コンテナとOneToOneの関連付けは、あなたが私にはわからない、正直に言うと

public abstract class ContainerAssignment { 
    @Id 
    private Long id; 

    public abstract Container getContainer(); 
    public abstract void setContainer(Container container); 
} 

としてコンテナを定義することができます各サブクラスの@OneToOne containerフィールドをマッピングするためにテーブルの列に追加します。

私は、これはあなたが持つことができる最高だと思います。コンテナフィールドを基底クラスに置く場合は、それが実際にあるので、アソシエーションをOneToMany/ManyToOneアソシエーションとして定義する必要があります。私はあなたが何をしたいとは思わない

が可能であり、私は彼らが正当な理由のために落胆しているように、複合のPKと混乱はないだろう、と悪夢を使用します。

+1

あなたの答えのおかげで、それは本当に問題を解決しない - ちょっとPKの内側 'DiscriminatorColumn'を配置する方法はありますか?私は、人工のPKが最良の選択であることに同意しますが、多くのシステムでは、スキーマを制御することができず、複合PKを処理する必要があります。 –

+0

Bill Karwinの本によると、複合主キーは決して反パターンではありません。 – atorres

0

ContainerAssignmentの複合プライマリキーが正常に動作していると思います(私は本当にJPAの実装に依存していると思います)、それでも気になるのはentityManager.findとPKの厄介な呼び出しですインスタンス化。

私のソリューションは、JPAのAPIの独立した検索メソッドを定義することです。 JPAに自分自身をロックしないでください。 最も簡単な方法は、ちょうどあなたのドメインクラスで静的ファインダーを定義する(または、あなたはJPA。それを行う方法を知っているのIoCで掘るん未結合ドメインを維持したい場合は、ちょうどファインダーで別のクラスを定義)することです。 ContainerAssignment(またはあなたのファインダークラス)で

:あなたのコードで

public static <T extends ContainerAssignment> T findByPK(EntityManager manager,Class<T> type,long id) { 
    DiscriminatorValue val = type.getAnnotation(DiscriminatorValue.class); // this is not optimal...can be cached... 
    return (T) manager.find(type, new MyPk(containerId, val.getValue())); 
} 

:PKのタイプの一部を作ることは、あなたが別個の2つのContainerAssignmentのインスタンスを持つことができることを意味していること

SomeTypeOfContainerAssignment ca = ContainerAssignment.findByPK(entityManager,SomeTypeOfContainerAssignment.class,containerId); 

お知らせ同じIDを持つ型。コンテナアサインメントを取得するクエリーが必要なのは、そのタイプがわからない場合です。しかし、あなたのIDがシーケンスから生成されている場合は、あなただけの結果セットの最初の結果を返す、エンティティフレームワークへの内部呼び出しを隠し、他のfinderメソッドを書くことができます。

関連する問題