軽量O/Rマッピングフレームワーク ActiveObjects その3

前回
blog1.mammb.com

の続きです。今回は Polymorphic について見ていきます。

多様性関連のマッピング

Person が請求明細(BillingDetails)を1つ持つことを考えます。この請求明細は、クレジットカード(CreditCard)か銀行口座(BankAccount)の何れかであるとします。このような多様性を表すEntityクラスとして以下の3つのクラスを定義します。

@Polymorphic
public interface BillingDetails extends Entity {
    public int getNumber();
    public void setNumber(int number);
}

public interface CreditCard extends BillingDetails {
    public String getExpYear();
    public void setExpYear(String expYear);
}

public interface BankAccount extends BillingDetails {
    public String getBankName();
    public void setBankName(String bankName);
}

請求明細(BillingDetails)には@Polymorphicアノテーションを指定して、ActiveObjectsに多様性構造であることを伝えます。


では請求明細を持つ Person エンティテイを以下のように修正しましょう。

public interface Person extends Entity {
    public String getName();
    public void setName(String name);

    public int getAge();
    public void setAge(int age);

    public BillingDetails getBillingDetails();
    public void setBillingDetails(BillingDetails bill);
}

EntityManagerの設定

多様性を扱うエンティティは以下のようにEntityManagerにタイプマッパを指定します。

em.setPolymorphicTypeMapper(
    new DefaultPolymorphicTypeMapper(BankAccount.class, CreditCard.class));

これにより、BankAccount クラスと CreditCard クラスのType定義文字列として、bankAccountと creditCard という文字が使われるようになります。

自動生成されるテーブル

上記インターフェース定義により以下のDDLが発行されます。これを見るとわかる通り、具象クラス毎に1つのテーブルが作成されます。

CREATE TABLE person (
    id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    age INTEGER,
    billingDetailsID INTEGER,
    billingDetailsType VARCHAR(127),
    name VARCHAR(255),
    PRIMARY KEY(id)
)

CREATE TABLE bankAccount (
    id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    bankName VARCHAR(255),
    number INTEGER,
    PRIMARY KEY(id)
)

CREATE TABLE creditCard (
    id INTEGER GENERATED BY DEFAULT AS IDENTITY NOT NULL,
    expYear VARCHAR(255),
    number INTEGER,
    PRIMARY KEY(id)
)

person テーブルの billingDetailsType に先程説明したType定義文字列が入ることになります。

実行テストコード

実行確認用のテストコードを以下のように実装します。

@Test
public void test() throws Exception {
    
    EntityManager em = new EntityManager("jdbc:hsqldb:hsql://localhost/test", "sa", "");
    em.setPolymorphicTypeMapper(
            new DefaultPolymorphicTypeMapper(BankAccount.class, CreditCard.class));
    em.migrate(Person.class, BankAccount.class, CreditCard.class);

    BankAccount bankAccount = em.create(BankAccount.class);
    bankAccount.setBankName("Tokyo");
    bankAccount.setNumber(999);
    bankAccount.save();

    CreditCard creditCard = em.create(CreditCard.class);
    creditCard.setNumber(666);
    creditCard.setExpYear("2010");
    creditCard.save();

    Person p1 = em.create(Person.class);
    p1.setName("Thom");
    p1.setAge(27);
    p1.setBillingDetails(bankAccount);
    p1.save();
    
    Person p2 = em.create(Person.class);
    p2.setName("Rachel");
    p2.setAge(27);
    p2.setBillingDetails(creditCard);
    p2.save();

    for(Person p : em.find(Person.class)) {
        System.out.println(p.getBillingDetails());
    }

実行結果は以下のようになります。

bankAccount {id = 0}
creditCard {id = 0}

Person に各具象クラスの内容が設定されていることが確認できます。