Doma2で任意のクラスにマッピングさせる

DomaのエンティティはデフォルトでInteger型やString型など基本型マッピングされるが、ドメインクラスを定義することで、別のクラスにラッピングさせることができる。
doma-codegen-pluginで現状エンティティの自動生成でドメインクラスにマッピングするようにはできない。

サンプルでは以下のDDLで生成されるテーブルを使用する。

create table sample (
  id VARCHAR(128) not null
  , customer_status_cd CHAR(1) not null
  , name NVARCHAR(30)
  , phone_no VARCHAR(13)
  , constraint sample_PKC primary key (id)
);

なお、基本型でのマッピングのみのエンティティは以下の通り。

import java.time.LocalDateTime;

import org.seasar.doma.Column;
import org.seasar.doma.Entity;
import org.seasar.doma.Id;
import org.seasar.doma.Table;

import lombok.EqualsAndHashCode;
import lombok.ToString;

@Entity
@Table(name = "sample")
@ToString
@EqualsAndHashCode
public class Sample {

    @Id
    @Column(name = "id")
    String id;

    @Column(name = "customer_status_cd")
    String customerStatusCd;

    @Column(name = "name")
    String name;

    @Column(name = "phone_no")
    String phoneNo;

    // getter, setter 省略
}

ドメインクラスの定義

1. 基本型に代わりラッピングさせるクラスに@Domainを付与する

アノテーションの属性値 valueType には 基本型 を指定する。

@Domain(valueType = String.class)
public class PhoneNumber {
}

2. アノテーションの属性factoryMethodインスタンスを生成するためのメソッド名を指定する

デフォルト値は new であり、privateでないコンストラクタでインスタンスを生成する。
そのため、コンストラクタでインスタンスを生成する場合は属性factoryMethodを省略することが出来る。

@Domain(valueType = String.class)
public class PhoneNumber {
    
    private String number;
    
    public PhoneNumber(String number) {
        this.number = number;
    }
}

コンストラクタではなく別のメソッドでインスタンスを生成したい場合はprivateでないstaticなファクトリーメソッドを定義し、属性 factoryMethod にそのメソッドの名前を指定する。

@Domain(valueType = String.class, factoryMethod = "getInstance")
public class PhoneNumber {
    
    private String number;
    
    private PhoneNumber(String number) {
        this.number = number;
    }
    
    public static PhoneNumber getInstance(String number) {
        return new PhoneNumber(number);
    }
}

インスタンスを生成するメソッドを呼ぶ上で、引数がnullであることを許容する場合は、属性 acceptNull にtrueを設定する(デフォルト:false)。

@Domain(valueType = String.class, factoryMethod = "getInstance", acceptNull = true)
public class PhoneNumber {
    
    private String number;
    
    private PhoneNumber(String number) {
        this.number = number;
    }
    
    public static PhoneNumber getInstance(String number) {
        return new PhoneNumber(number);
    }
}

3. アノテーションの属性accessorMethodでラップする値を取得するためのメソッド名を指定する

デフォルト値はgetValueである。メソッド名を変更したい場合は属性accessorMethodにメソッド名を指定する。
ここで指定するメソッドもprivateでないものとする。

@Domain(valueType = String.class, factoryMethod = "getInstance", accessorMethod = "getNumber")
public class PhoneNumber {
    
    private String number;
    
    private PhoneNumber(String number) {
        this.number = number;
    }
    
    public static PhoneNumber getInstance(String number) {
        return new PhoneNumber(number);
    }
    
    public String getNumber() {
        return number;
    }
}

ドメインクラスはクラスだけでなくenumでも定義することが出来る。以下は定義例である。

import org.seasar.doma.Domain;

@Domain(valueType = String.class, factoryMethod = "of")
public enum CustomerStatusCode {
    BLANK("1"),

    FILLED("2");

    private final String value;

    CustomerStatusCode(String value) {
        this.value = value;
    }

    public static CustomerStatusCode of(String value) {
        for (CustomerStatusCode jobType : CustomerStatusCode.values()) {
            if (jobType.value.equals(value)) {
                return jobType;
            }
        }
        throw new IllegalArgumentException(value);
    }

    public String getValue() {
        return value;
    }
}

エンティティの型を変更する

作成したドメインクラスでラッピングしたいカラムの型を変更する。

import java.time.LocalDateTime;

import org.seasar.doma.Column;
import org.seasar.doma.Entity;
import org.seasar.doma.Id;
import org.seasar.doma.Table;

import lombok.EqualsAndHashCode;
import lombok.ToString;

@Entity
@Table(name = "sample")
@ToString
@EqualsAndHashCode
public class Sample {

    @Id
    @Column(name = "id")
    String id;

    @Column(name = "customer_status_cd")
    CustomerStatusCode customerStatusCd;

    @Column(name = "name")
    String name;

    @Column(name = "phone_no")
    PhoneNumber phoneNo;

    // getter, setter 省略
}

ドメインクラスを使用したエンティティでのクエリの書き方

サンプルで使用するDaoインターフェースは以下の通り。
エンティティは上記で作成したものを使用する。

@ConfigAutowireable
@Dao
public interface SampleDao {

    @Select
    List<WatchingInfo> selectByWatchingInfo(Sample entity);
}

ドメインクラスを使用したエンティティでのselect文の記述例を示す。

select
  /*%expand*/*
from
  sample
where
  customer_status_cd = /* entity.customerStatusCd */'3'