アグリゲート戦略

アグリゲート戦略は、任意のSELECT文からエンティティアグリゲートを構築する方法を定義します。リレーショナルなクエリ結果を階層構造のエンティティにマッピングするために、エンティティ同士の関連付け方法を指定する構造化されたアプローチを提供します。

アグリゲート戦略の定義

アグリゲート戦略は、インターフェースに @AggregateStrategy アノテーションを付与することで表されます。このアノテーションは、クエリ結果からエンティティアグリゲートをどのように再構築するかを定義するために使用されます。

@AggregateStrategy(root = Department.class, tableAlias = "d")
interface DepartmentAggregateStrategy {
    ...
}
  • root 要素は、アグリゲートのルートとして機能するエンティティクラスを指定します。

  • tableAlias 要素は、ルートエンティティクラスに対応するテーブルのエイリアスを指定します。 このエイリアスは、SELECT文でクエリの結果をエンティティプロパティに正しくマップするために使用する必要があります。

関連付けリンカーの定義

アグリゲート戦略には、@AssociationLinker で注釈された BiFunction 型のフィールドが少なくとも1つ含まれている必要があります。BiFunction は、2つのエンティティインスタンスを動的に関連付ける責任があります。

@AggregateStrategy(root = Department.class, tableAlias = "d")
interface DepartmentAggregateStrategy {
  @AssociationLinker(propertyPath = "employees", tableAlias = "e")
  BiFunction<Department, Employee, Department> employees =
      (d, e) -> {
        d.getEmployees().add(e);
        e.setDepartment(d);
        return d;
      };

  @AssociationLinker(propertyPath = "employees.address", tableAlias = "a")
  BiFunction<Employee, Address, Employee> address =
      (e, a) -> {
        e.setAddress(a);
        return e;
      };
}
  • BiFunction の最初と 3 番目の型パラメータはプロパティを所有するクラスを表し、2 番目の型パラメータはプロパティの型を表します。

  • propertyPath 要素は、ルートエンティティクラスからドットで区切られたパスとしてターゲットプロパティの名前を指定します。

  • tableAlias 要素は、BiFunction の 2 番目の型パラメータとして使用されるエンティティクラスに対応するテーブルのエイリアスを指定します。 このエイリアスはSELECT文で使用する必要があります。

上記の DepartmentAggregateStrategy は以下のエンティティ定義に基づいています。

@Entity(naming = NamingType.SNAKE_LOWER_CASE)
public class Department {
  @Id Integer id;
  String name;
  @Association List<Employee> employees = new ArrayList<>();

  // getter, setter
}

@Entity(naming = NamingType.SNAKE_LOWER_CASE)
public class Employee {
  @Id Integer id;
  String name;
  Integer departmentId;
  Integer addressId;
  @Association Department department;
  @Association Address address;

  // getter, setter
}

@Entity(naming = NamingType.SNAKE_LOWER_CASE)
public class Address {
  @Id Integer id;
  String street;

  // getter, setter
}

エンティティクラスでは、関連プロパティに @Association を付与する必要があります。これらのプロパティは @AssociationLinker を使用してリンクできます。

アグリゲート戦略の使用

DepartmentAggregateStrategy@SelectaggregateStrategy 要素に指定することで使用されます。

@Dao
interface DepartmentDao {
  @Select(aggregateStrategy = DepartmentAggregateStrategy.class)
  Department selectById(Integer id);
}

selectById メソッドでは、以下の SELECT ステートメントが必要になります。

select
    d.id as d_id,
    d.name as d_name,
    a.id as a_id,
    a.street as a_street,
    e.id as e_id,
    e.name as e_name,
    e.department_id as e_department_id,
    e.address_id as e_address_id
from
    department d
    left outer join
    employee e on (d.id = e.department_id)
    left outer join
    address a on (e.address_id = a.id)
where
    d.id = /* id */0

注釈

SELECTリストにはアグリゲートを形成するすべてのエンティティのIDを含める必要があります。

カラムエイリアスのルール

  • テーブルエイリアスは DepartmentAggregateStrategy で定義されているものと一致する必要があります。

  • カラムのエイリアスは、テーブルのエイリアスにアンダースコア(_)を付けた形式で指定する必要があります。例えば、d.idd_ide.ide_id としてエイリアスされます。

選択カラムリスト展開ディレクティブの使用

カラムリスト展開ディレクティブ を使用することで、上記のSELECT文をより簡潔に記述することができます。

select
    /*%expand*/*
from
    department d
    left outer join
    employee e on (d.id = e.department_id)
    left outer join
    address a on (e.address_id = a.id)
where
    d.id = /* id */0

カラムリスト展開の仕組み

  • /*%expand */* ディレクティブは、あらかじめ定義されたエイリアスルールを使用して列リストに自動的に展開します。

  • デフォルトでは、すべてのテーブルのすべての列が結果セットに含まれます。

特定のテーブルのみを選択的に展開するには、テーブルエイリアスのカンマ区切りリストを渡します。

select
    /*%expand "e, d" */*,
    a.id as a_id,
    a.street as a_street
from
    department d
    left outer join
    employee e on (d.id = e.department_id)
    left outer join
    address a on (e.address_id = a.id)
where
    d.id = /* id */0
  • ここでは、テーブルの e (employee) と d (department) のカラムのみが展開されます。

  • テーブル a (address) の列は明示的に指定されています。