Domaで任意のSQLの実行結果を集約オブジェクトにマッピングさせる仕様案
エンティティクラス
エンティティ間には、OneToMany、ManyToOne、OneToOneの関係性がある。
直接カラムにマッピングされないことを示すため、関連を表すプロパティには @Transient
が必要。
@Entity
public class Department {
private int id;
private String name;
@Transient
private List<Employee> employees;
// Getters and setters
}
@Entity
public class Employee {
private int id;
private String name;
@Transient
private Department department;
@Transient
private Address address;
// Getters and setters
}
@Entity
public class Address {
private int id;
private String street;
// Getters and setters
}
エンティティリンカー
新しく導入を考えている @EntityLinker
と @Association
を利用したクラス。
@EntityLinker
public class DepartmentLinker {
@Association(columnPrefix = "e_")
public final BiFunction<Department, Employee, Department> employees = (d, e) -> {
// 双方向の関連を持たせるかどうかは実装次第
d.getEmployees().add(e);
e.setDepartment(d);
return d;
};
@Association(columnPrefix = "a_")
public final BiFunction<Employee, Address, Employee> address = (e, a) -> {
e.setAddress(a);
return e;
};
}
@EntityLinker
を注釈されたクラスはエンティティ同士を関連づける役割を担う。@Association
は BiFunction
の1番目の型パラメターが表すクラスから @Association
が注釈されたフィールド名のプロパティを探し、2番目の型パラメーターを関連づけるということを表す。3番目の型パラメーターは必ず1番目の型パラメーターと同型とし、関連づけが終わった型を BiFunction
の呼び出し元に返せるようにする。
@Association
のcolumnPrefix
要素は、指定されたプレフィックスを持つカラムを2番目の型パラメータが表すエンティティクラスへマッピングすることを表す。
DAOインタフェース
@Select
のlinker要素は新しく導入を考えているもの。 エンティティリンカーを指定できるようにする。
@Dao
public interface DepartmentDao {
@Sql("""
SELECT
d.id,
d.name,
e.id AS e_id,
e.name AS e_name,
a.id AS a_id,
a.street AS a_street
FROM
department d
LEFT JOIN
employee e ON d.id = e.department_id
LEFT JOIN
address a ON e.address_id = a.id
WHERE
d.id = /* id */0
""")
@Select(linker = DepartmentLinker.class)
Department selectById(int id);
}
@Association
を付与するフィールドの型について。ミュータブルなエンティティを対象にするなら BiConsumer
でもよいが、 イミュータブルなエンティティに対応するために BiFunction
にする。
エンティティクラスを型にもつフィールドやエンティティクラスを要素とするコレクション型は、@Transient
が注釈されているとみなす注釈処理オプションがあってもいいかもしれない。
public interface DepartmentLinker {
@EntityLinker(columnPrefix = "e_")
BiFunction<Department, Employee, Department> employees = (d, e) -> {
d.getEmployees().add(d);
d.setEmployee(e);
return d;
};
}
public interface EmloyeeLinker {
@EntityLinker(columnPrefix = "a_")
BiFunction<Employee, Address, Employee> address = (e, a) -> {
e.setAddress(a);
return e;
};
}
再利用性を考えると上のように定義して、呼び出し元で複数指定できるようにするのもありか。
@Select(linkers = { DepartmentLinker.class, EmployeeLinker.class })
Department selectById(int id);
@Select(linkers = { EmployeeLinker.class })
List<Employee> selectEmployeesByDepartmentId(int departmentId);
PR作りました