ドメインクラス
ドメインクラスはデータベースのカラムを表し、カラムの値を Java オブジェクトとして処理できるようにします。 Doma フレームワークでは、ドメイン はデータ型に含まれる可能性のあるすべての値を意味します。つまり、ドメインクラスは、カラムにマップできるユーザー定義のクラスです。ドメインクラスの使用はオプションです。
すべてのドメインクラスは、内部ドメインクラスまたは外部ドメインクラスのいずれかです。
内部ドメインクラス
内部ドメインクラスには @Domain
アノテーションを付ける必要があります。 @Domain
の valueType
要素は、カラムのデータ型に対応します。 valueType
要素には任意のタイプの 基本クラス を指定します。
コンストラクタを使用したインスタンス化
@Domain
の factoryMethod
要素のデフォルト値は new
です。値 new
は、アノテーションが付けられたクラスのオブジェクトがコンストラクタを使用して作成されることを意味します。
@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() {
...
}
}
注釈
Java 14 以降のバージョンでは、records に @Domain
アノテーションを付けることができます。
@Domain(valueType = String.class, accessorMethod = "value")
public record PhoneNumber(String value) {
public String getAreaCode() {
...
}
}
警告
レコードに @Domain
アノテーションを付けるのは少し冗長です。 valueType
などのいくつかのプロパティを明示的に @Domain
に指定する必要があるためです。 @Domain
の代わりに、レコードに @DataType
アノテーションを付けることができます。
@DataType
public record PhoneNumber(String value) {
public String getAreaCode() {
...
}
}
静的ファクトリメソッドによるインスタンス化
アノテーションが付与されたクラスのオブジェクトを静的ファクトリメソッドで作成するには、 @Domain
の factoryMethod
要素にメソッド名を指定します。
メソッドは静的で非プライベートである必要があります。
@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);
}
}
静的ファクトリメソッドを使用すると、@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;
}
}
内部ドメインクラスにおける型パラメータの使用
内部ドメインクラスには任意の数の型パラメータを宣言できます。
@Domain(valueType = int.class)
public class Identity<T> {
private final int value;
public Identity(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
静的ファクトリメソッドを使用してアノテーション付きクラスのオブジェクトを作成する場合、メソッド宣言にはクラス宣言と同じ型パラメータが必要です。
@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);
}
}
外部ドメインクラス
この機能を使用すると、@Domain
アノテーションでクラスに注釈を付けることができなくても、任意のクラスをドメインクラスとして定義できます。
外部ドメインクラスを定義するには、org.seasar.doma.jdbc.domainConverter
を実装するクラスを作成し、@ExternalDomain
を注釈する必要があります。
たとえば、ソースコードを変更できない PhoneNumber
クラスがあるとします。
public class PhoneNumber {
private final String value;
public PhoneNumber(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String getAreaCode() {
...
}
}
PhoneNumber
クラスを外部ドメインクラスとして定義するには、次のクラスを作成します。
@ExternalDomain
public class PhoneNumberConverter implements DomainConverter<PhoneNumber, String> {
public String fromDomainToValue(PhoneNumber domain) {
return domain.getValue();
}
public PhoneNumber fromValueToDomain(String value) {
if (value == null) {
return null;
}
return new PhoneNumber(value);
}
}
外部ドメインクラスにおける型パラメータの使用
外部ドメインクラスには任意の数の型パラメータを宣言できます。
public class Identity<T> {
private final int value;
public Identity(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
DomainConverter
実装クラスで、外部ドメインクラスへの型引数としてワイルドカード ?
を指定します。
@ExternalDomain
public class IdentityConverter implements DomainConverter<Identity<?>, String> {
public String fromDomainToValue(Identity<?> domain) {
return domain.getValue();
}
@SuppressWarnings("rawtypes")
public Identity<?> fromValueToDomain(String value) {
if (value == null) {
return null;
}
return new Identity(value);
}
}
外部ドメインクラスのデータベースへの直接マッピング
すべての外部ドメインクラスは、直接任意のデータベースの型にマッピングできます。
java.util.UUID
を PostgreSQL の UUID 型にマッピングする例を次に示します。
まず、マッピングを処理するために org.seasar.doma.jdbc.type.JdbcType
の実装を作成します。
class PostgresUUIDJdbcType extends AbstractJdbcType<UUID> {
protected PostgresUUIDJdbcType() {
super(Types.OTHER);
}
@Override
protected UUID doGetValue(ResultSet resultSet, int index) throws SQLException {
String value = resultSet.getString(index);
return value == null ? null : UUID.fromString(value);
}
@Override
protected void doSetValue(PreparedStatement preparedStatement, int index, UUID value)
throws SQLException {
preparedStatement.setObject(index, value, type);
}
@Override
protected UUID doGetValue(CallableStatement callableStatement, int index) throws SQLException {
String value = callableStatement.getString(index);
return value == null ? null : UUID.fromString(value);
}
@Override
protected String doConvertToLogFormat(UUID value) {
return value.toString();
}
}
次に、org.seasar.doma.it.domain.JdbcTypeProvider
を拡張したクラスを作成し、getJdbcType
メソッドで上記で作成した JdbcType
実装のインスタンスを返します。
@ExternalDomain
public class PostgresUUIDConverter extends JdbcTypeProvider<UUID> {
private static final PostgresUUIDJdbcType jdbcType = new PostgresUUIDJdbcType();
@Override
public JdbcType<UUID> getJdbcType() {
return jdbcType;
}
}
クラスに @ExternalDomain
を注釈することを忘れないでください。
例
上記のドメインクラスは次のように使用されます。
@Entity
public class Employee {
@Id
Identity<Employee> employeeId;
String employeeName;
PhoneNumber phoneNumber;
JobType jobType;
@Version
Integer versionNo();
...
}
@Dao
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();
}