目次 †複合キーについて †ネットに転がっているサンプルだと、プライマリキーが1個のパターンばかり。最初から1個と決めて設計を始める場合はそれでもいいのだけど、既存システムの改修なんかだと中々そうも言っていられない。 結論から言ってしまうと、CRUDモジュールで複合キーを使ったテーブルを扱うことは出来なかった。完全にできなかったわけではないが。 複合キーそのものには Hibernate?JPA?は対応していて、クラスとして表現できる。そのため、CRUDモジュールが全く動かせないわけではない。一覧表示や新規レコード登録までは動いたが、内容確認や編集ができなかった。PKでレコード指定する部分がうまく動いていない模様。確かな原因箇所や修正を行うほど、JPAの知識が無いのであきらめた。 英語の検索結果とかも辿って海外のサイトも調べたのだけど、私の語学力では答えに到達することは出来ず。 おまけ †ただ、せっかく複合キーのやり方を調べたので、そのメモを残しておく。 対象となるエンティティのクラスに加え、複合キークラスというのを用意する。 対象となるクラスには、@IdClass 注釈を付ける。引数は複合キークラス名を与える。こんな感じで。 @IdClass(HukugouPK.class) 複合キークラスには、@EmbeddedId 注釈を付ける。複合キークラスの条件
で、この複合キーをPKに持つテーブルを結合する側の記述について。まず指定のカラムを用いて結合してほしい場合は、@JoinColumn 注釈を用いる。 @ManyToOne @JoinColumn(name="ADDR_ID", referencedColumnName="SUB_ADDR_ID") public Address getAddress() { ... このように記述すると、Addressエンティティへの外部キーを"ADDR_ID"というカラム名で定義する。で、Addressエンティティと結合する際はSUB_ADDR_IDをキーとして、"ADDR_ID = Address.SUB_ADDRESS"という条件で結合する。 で、複合キーの場合は、@JoinColumn を @JoinColumns 注釈で束ねることで表現する。 @ManyToOne @JoinColumns({ @JoinColumn(name="ADDR_ID", referencedColumnName="SUB_ADDR_ID"), @JoinColumn(name="PREF_ID", referencedColumnName="SUB_PREF_ID") }) public Address getAddress() { ... 回避策 †複合キーは使いたいけど、CRUDモジュールも使いたい、という場合どうすればいいのか、と。無い知恵を絞った現時点の回避策を書いておきます。 例があった方が分かりやすいと思うので、UserMstとNameMstという2つのテーブルを結合する例を書く。UserMstはユーザ情報を保持するテーブル。NameMstは各種名称を管理するテーブル。 @Entity public class NameMst extends Model { public Integer category; public Integer number; public String toString() { return name; } public String name; } NameMstのcategoryとnumberには @Id 注釈は付けず、PKは自動生成に任せる。自動生成PKを使用するので、CRUDモジュールでの編集時はこのPKが使用されることになり、閲覧・編集が問題なく行える。 @Entity public class User extends Model { public String email; public String fullname; public String toString() { return fullname+"のデータ"; } @ManyToOne @JoinColumns({ @JoinColumn(name="name_category", referencedColumnName="category"), @JoinColumn(name="name_number", referencedColumnName="number") }) public NameMst groupName; } NameMstとの結合は @JoinColums注釈を用いて、自動生成PKを使用しない結合を強制する。こうすると、"name_category"と"name_number"という2つのカラムが用意される。 この2つのキーによる結合は、CRUDモジュールでもキチンと受け取られ、データの編集が可能です。 ようするに「自動生成PKを利用するけど、結合にはこのカラムを使ってね」と記述するわけです。NameMstへアプリからアクセスする際はcategoryとnumberで行うので、自動生成PKは冗長なカラムとなりますが、そこはCRUDモジュール相当を自作するか否かのトレードオフとなりますね。 続き †NameMstは汎用の名称マスタで、categoryの値で名称をグループ化している場合、結合するテーブルによって categoryの値は固定になりますよね。 User ⇒ NameMstへの結合時は、category = 1 という条件になる場合、Userには numberだけ保持すれば良いのですが、上記の書き方だと category と number の2カラムが確保されてしまいます。 固定条件をアノテーションで上手く表現できれば良いのだけど、今のところスマートにいきません。 一つ考えているのは、NameMstに@Where注釈を記述する方法です。 @Entity @Table(name="NameMst") @Where(clause="category=1") public class NameMstX extends Model { ... } 別名でクラスを作成し、永続化テーブルはNameMstと同名にする。 public class User extends Model { ... @ManyToOne @JoinColumn(name="name_number", referencedColumnName="number") public NameMst groupName; } Userの方は、"name_number"だけで結合する旨を記載する。 これで一応動いているようです。 NameMstXを継承で書いたりしたいのですが、DTYPEというカラムが自動生成されてしまい(継承を管理するためらしい)、今のところうまくいってません。 参考 †
|