<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
	<properties>
		<title>ドメインクラス</title>
	</properties>
	<body>
		<section name="目次">
			<ul>
				<li><a href="#概要">概要</a></li>
				<li><a href="#ドメイン定義">ドメイン定義</a>
					<ul>
						<li><a href="#コンストラクタで生成する">コンストラクタで生成する</a></li>
						<li><a href="#ファクトリメソッドで生成する">ファクトリメソッドで生成する</a></li>
						<li><a href="#型パラメータの利用">型パラメータの利用</a></li>
					</ul>
				</li>
				<li><a href="#外部ドメイン定義">外部ドメイン定義</a>
					<ul>
						<li><a href="#外部ドメイン定義で型パラメータの利用">外部ドメイン定義で型パラメータの利用</a></li>
					</ul>
				</li>
				<li><a href="#利用例">利用例</a>
					<ul>
						<li><a href="#エンティティクラス">エンティテクラス</a></li>
						<li><a href="#Daoインタフェース">Daoインタフェース</a></li>
					</ul>
				</li>
			</ul>
		</section>
		<section name="概要">
			<p>
				Domain（ドメイン）クラスの定義方法を示します。
				ドメインとは、値のとり得る範囲（定義域）のことです。
				Domaでは、テーブルのカラムの値を、ドメインと呼ばれるJavaオブジェクトで扱います。
			</p>
			<p>
				ドメインクラスを利用することで、データベース上のカラムの型が同じあってもアプリケーション上意味が異なるものを別のJavaの型で表現できます。
				これにより、意味を明確にしプログラミングミスを事前に防ぎやすくなります。
				また、ドメインクラスに振る舞いを持たせることで、よりわかりやすいプログラミングが可能です。
			</p>
			<p>
				ドメインクラスの作成と利用は任意です。
				ドメインクラスを利用しなくても<code>Integer</code>や<code>String</code>など基本型のみでデータアクセスは可能です。
			</p>
		</section>
		<section name="ドメイン定義">
			<p>
				ドメインクラスは <a href="../apidocs/org/seasar/doma/Domain.html"><code>@Domain</code></a>を注釈して示します。
			</p>
			<p>
				<code>@Domain</code>の<code>valueType</code>要素には、ドメインクラスで扱う<a href="basic.html">基本型</a>を指定します。この基本型が、データベースのカラムの型とのマッピングに使用されます。
			</p>
			<p>
				<code>accessorMethod</code>要素には、<code>valueType</code>要素に指定した型を返すアクセッサメソッドの名前を指定します。デフォルト値は<code>"getValue"</code>です。
			</p>
			<p>
				<code>factoryMethod</code>要素には、<code>valueType</code>要素に指定した型をパラメータとし受け取りドメインクラスの型を返すファクトリメソッドの名前を指定します。デフォルト値は<code>"new"</code>であり、これはコンストラクタにより生成することを示します。
			</p>
			<p>
				<code>@Domain</code>が注釈されたクラスは以下の制約を満たす必要があります。
			</p>
			<ul>
				<li>トップレベルのクラスまたは列挙型である。</li>
				<li><code>valueType</code>要素に指定した型を引数とする非<code>private</code>なコンストラクタを持ち、
					<code>factoryMethod</code>要素が<code>"new"</code>である。もしくは、<code>factoryMethod</code>
					要素に指定した名前の<code>static</code>で非<code>private</code>なメソッドを持ち、戻り値は注釈された型であり、 パラメータは
					<code>valueType</code>要素に指定した型である。</li>
				<li><code>accessorMethod</code>要素に指定した名前の非<code>private</code>なメソッドを持つ。このメソッドは、
					<code>valueType</code>要素に指定した型を戻り値とし、パラメータは受け取らない。</li>
			</ul>
			<p>
				任意ですが、ドメインクラスは不変オブジェクトとして作成することを推奨します。
				クラスには、任意のメソッドを持たせることができます。
			</p>
			<p>
				列挙型は非privateなコンストラクタを定義できないため、列挙型に対してはファクトリメソッドを用いた方法を使用する必要があります。
			</p>
			<subsection name="コンストラクタで生成する">
				<p>
					<code>@Domain</code>の<code>factoryMethod</code>要素のデフォルトの値は<code>new</code>であり、
					非privateなコンストラクタでインスタンスを生成することを示します。そのため、コンストラクタで生成する場合は<code>factoryMethod</code>要素を省略できます。
				</p>
				<p>
					次の例では、 publicなコンストラクタを持つドメインクラスを作成しています。
					このクラスは電話番号を表しています。
				</p>
<source><![CDATA[package example.domain;

import org.seasar.doma.Domain;

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

    private final String value;

    public PhoneNumber(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public String getAreaCode() {
       // ドメインに固有の振る舞いを記述できる。
       ...
    }
}]]></source>
			</subsection>
			<subsection name="ファクトリメソッドで生成する">
				<p>
					コンストラクタをprivateにし、ファクトリメソッドを使ってインスタンスを生成したい場合は、
					staticな非privateなメソッドを定義し<code>@Domain</code>の<code>factoryMethod</code>要素にそのメソッドの名前を指定します。
				</p>
				<p>
					次の例では、publicな ファクトリメソッドをもつドメインクラスを作成しています。
					このクラスは電話番号を表しています。
				</p>
<source><![CDATA[package example.domain;

import org.seasar.doma.Domain;

@Domain(valueType = String.class, factoryMethod = "of")
public class PhoneNumber {

    private final String value;

    private PhoneNumber(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public String getAreaCode() {
       // ドメインに固有の振る舞いを記述できる。
       ...
    }
    
    public static PhoneNumber of(String value) {
        return new PhoneNumber(value);
    }
}]]></source>
				<p>
					次の例では、publicな ファクトリメソッドをもつ列挙型をドメインクラスとして作成しています。
					この列挙型は仕事の種別を表しています。
				</p>
<source><![CDATA[package example.domain;

import org.seasar.doma.Domain;

@Domain(valueType = String.class, factoryMethod = "of")
public enum JobType {
    SALESMAN("10"), 
    MANAGER("20"), 
    ANALYST("30"), 
    PRESIDENT("40"), 
    CLERK("50");

    private final String value;

    private JobType(String value) {
        this.value = value;
    }

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

    public String getValue() {
        return value;
    }
}]]></source>
			</subsection>
			<subsection name="型パラメータの利用">
				<p>
					ドメインクラスには任意の数の型パラメータを宣言できます。
				</p>
				<p>
					次の例では、1つの型パラメータを持ち、さらにpublicなコンストラクタを持つドメインクラスを作成しています。
					このクラスは識別子を表しています。
				</p>
<source><![CDATA[package example.domain;

import org.seasar.doma.Domain;

@Domain(valueType = int.class)
public class Identity<T> {

    private final int value;

    public Identity(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}]]></source>
				<p>
					型パラメータを持ったドメインクラスはファクトリメソッドで生成することも可能です。
					この場合、ファクトリメソッドにはクラスの型変数宣言と同等の宣言が必要です。
				</p>
<source><![CDATA[package example.domain;

import org.seasar.doma.Domain;

@Domain(valueType = int.class, factoryMethod = "of")
public class Identity<T> {

    private final int value;

    private Identity(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static <T> Identity<T> of(int value) {
        return new Identity<T>(value);
    }
}]]></source>
			</subsection>
		</section>
		<section name="外部ドメイン定義">
			<p>
				ソースコードに手を加えられないなどの理由で任意の型をドメインクラスとして扱いたい場合は、外部ドメイン定義が使えます。
			</p>
			<p>
				外部ドメイン定義は、 <a href="../apidocs/org/seasar/doma/jdbc/domain/DomainConverter.html"><code>DomainConverter</code></a> 
				の実装クラスに <a href="../apidocs/org/seasar/doma/ExternalDomain.html"><code>@ExternalDomain</code></a> を注釈して示します。
			</p>
			<p>
				<code>DomainConverter</code>の第1型パラメータにはドメインクラスとして扱いたい型、第2型パラメータには <a href="basic.html">基本型</a> を指定します。
			</p>
			<p>
				<code>@ExternalDomain</code>が注釈された<code>DomainConverter</code>の実装クラスは以下の制約を満たす必要があります。
			</p>
			<ul>
				<li>トップレベルのクラスである。</li>
				<li>引数なしのpublicなコンストラクタを持つ。</li>
				<li>具象クラスである。</li>
				<li>スレッドセーフである。</li>
			</ul>
			<p>
				例えば、次のような<code>PhoneNumber</code>というクラスがあり、ソースコードに手を加えられないとします。
			</p>
<source><![CDATA[package sample;

public class PhoneNumber {

    private final String value;

    public PhoneNumber(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public String getAreaCode() {
       ...
    }
}]]></source>
			<p>
				上記の<code>PhoneNumber</code>をドメインクラスとして扱うには、次のようなクラスを作成します。
			</p>
<source><![CDATA[package example.domain;

import sample.PhoneNumber;

@ExternalDomain
public class PhoneNumberConverter implements DomainConverter<PhoneNumber, String> {

    public String fromDomainToValue(PhoneNumber domain) {
        return domain.getValue();
    }

    public PhoneNumber fromValueToDomain(String value) {
        return new PhoneNumber(value);
    }
}]]></source>
				<p>
					これで外部ドメイン定義は完成ですが、これだけではまだ利用できません。
					外部ドメインの定義は注釈処理のオプションで指定する必要があります。
				</p>
				<p>
					注釈処理のオプションで指定する前段階として、外部ドメイン定義を <a href="../apidocs/org/seasar/doma/DomainConverters.html"><code>@DomainConverters</code></a> で登録します。
					<code>@DomainConverters</code> には複数の外部ドメイン定義を登録可能です。
				</p>
<source><![CDATA[package example.domain;

@DomainConverters({ PhoneNumberConverter.class })
public class DomainConvertersProvider {
}]]></source>
				<p>
					そして、 <code>@DomainConverters</code> が注釈されたクラスの完全修飾名を注釈処理のオプションに指定します。
					オプションのkeyは、「domain.converters」です。
					オプションの指定の仕方については <a href="apt.html">注釈処理</a> を参照してください。
				</p>
				<subsection name="外部ドメイン定義で型パラメータの利用">
					<p>
						外部ドメイン定義では、任意の数の型パラメータを持ったクラスを扱えます。
					</p>
					<p>
						次の例のような1つの型パラメータを持つクラスがあるとします。
						このクラスは識別子を表しています。
					</p>
<source><![CDATA[package sample;

public class Identity<T> {

    private final int value;

    public Identity(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }
}]]></source>
			<p>
				上記の<code>Identity</code>をドメインクラスとして扱うには、次のようなクラスを作成します。
				<code>Identity</code>の型パラメータにはワイルドカード（?）を指定しなければいけません。
			</p>
<source><![CDATA[package example.domain;

import sample.Identity;

@ExternalDomain
public class IdentityConverter implements DomainConverter<Identity<?>, String> {

    public String fromDomainToValue(Identity<?> domain) {
        return domain.getValue();
    }

    @SuppressWarnings("rawtypes")
    public Identity<?> fromValueToDomain(String value) {
        return new Identity(value);
    }
}]]></source>
				</subsection>
				<p>
					その他の設定方法については、型パラメータを使用しない場合と同様です。
				</p>
		</section>
		<section name="利用例">
			<p>
				ドメインクラスが型パラメータを持つ場合、型パラメータには具体的な型が必要です。
				ワイルドカード（?）や型変数の指定はサポートされていません。
			</p>
			<subsection name="エンティティクラス">
				<p>
					エンティティクラスのフィールドの型での利用例です。
				</p>
<source><![CDATA[@Entity
public class Employee {

    @Id
    Identity<Employee> employeeId;

    String employeeName;

    PhoneNumber phoneNumber;

    JobType jobType;

    @Version
    Integer versionNo();
    
    ...
}]]></source>
			</subsection>
			<subsection name="Daoインタフェース">
				<p>
					Daoインタフェースのメソッドのパラメータや戻り値での利用例です。
				</p>
<source><![CDATA[@Dao(config = AppConfig.class)
public interface EmployeeDao {

    @Select
    Employee selectById(Identity<Employee> employeeId);

    @Select
    Employee selectByPhoneNumber(PhoneNumber phoneNumber);
    
    @Select
    List<PhoneNumber> selectAllPhoneNumber();
    
    @Select
    Employee selectByJobType(JobType jobType);
    
    @Select
    List<JobType> selectAllJobTypes();
}]]></source>
			</subsection>
		</section>
	</body>
</document>