Play framework/CRUD2

目次

複合キーについて

ネットに転がっているサンプルだと、プライマリキーが1個のパターンばかり。最初から1個と決めて設計を始める場合はそれでもいいのだけど、既存システムの改修なんかだと中々そうも言っていられない。

結論から言ってしまうと、CRUDモジュールで複合キーを使ったテーブルを扱うことは出来なかった。完全にできなかったわけではないが。

複合キーそのものには Hibernate?JPA?は対応していて、クラスとして表現できる。そのため、CRUDモジュールが全く動かせないわけではない。一覧表示や新規レコード登録までは動いたが、内容確認や編集ができなかった。PKでレコード指定する部分がうまく動いていない模様。確かな原因箇所や修正を行うほど、JPAの知識が無いのであきらめた。

英語の検索結果とかも辿って海外のサイトも調べたのだけど、私の語学力では答えに到達することは出来ず。

おまけ

ただ、せっかく複合キーのやり方を調べたので、そのメモを残しておく。

対象となるエンティティのクラスに加え、複合キークラスというのを用意する。

対象となるクラスには、@IdClass 注釈を付ける。引数は複合キークラス名を与える。こんな感じで。

@IdClass(HukugouPK.class)

複合キークラスには、@EmbeddedId 注釈を付ける。複合キークラスの条件

  • Plain Old Java Object(POJO)クラスである
  • publicであり、publicで引数のないコンストラクタを持つ
  • プロパティ・ベース・アクセスを使用するとき、主キー・クラスはpublicまたはprotecedである必要がある
  • 直列化が可能である
  • equalsメソッドおよびhashCodeメソッドを必ず定義する
http://otndnld.oracle.co.jp/products/ias/toplink/jpa/resources/toplink-jpa-annotations.html
JPAの注釈

で、この複合キーを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というカラムが自動生成されてしまい(継承を管理するためらしい)、今のところうまくいってません。

参考

http://d.hatena.ne.jp/shin/20080729/p5
しんさんの出張所 はてな編 JPAの複合キー

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2012-02-01 (水) 14:47:03 (4460d)