特定の値しか入らないテーブルのカラムに対応する列挙型を用意し、フィールドとして値を保持している。
ORMとしてはJPA Entityを使用しているが、かなり古いソースなのでEnumeratedやConvertによる列挙型のフィールドマッピングなどは行われず、単純な int
でフィールドが宣言されている。
データアクセスはSpring DataでJpaRepositoryを使っているが、QueryアノテーションでJPQLを書くときに、列挙型のフィールドを参照できないかと思い調べたのでメモ。
問題
特定の値をフィールドとして持った列挙型を用意している。
package example; @Getter @RequiredArgsConstructor public enum ExampleEntityState { START(0), END(1); private final int value; } @Data @Entity public class ExampleEntity { private int state; // 0, 1 が入る }
この値に対応するJPA Entityのフィールドは、Enumeratedアノテーションなどで列挙型に変換されておらず、 int
で宣言されている。
Queryアノテーションのクエリ文字列内で、特定の値のデータを検索したいが、マジックナンバーは使いたくないので、列挙型を用いて値を参照したい。
だが、クエリ文字列は定数で記述する必要があるため、列挙型でもフィールド値の参照はできない。
// これはコンパイルエラーになる @Query("SELECT e FROM ExampleEntity e WHERE e.state = " + ExampleEntityState.END.getValue()) List<ExampleEntity> findAllOfEndState();
対応
Query内でSpring Expression Language(SpEL)を使える。これを使えば、クエリ文字列内でもフィールドアクセスが可能。
SpEL内で T(完全修飾クラス名).staticフィールドやメソッド
と書くことで、staticフィールドにアクセスできる。列挙型の値にも、これでアクセスできた。また、フィールドアクセスは通常のEL式と同様、Getter経由であればフィールド名のみでアクセスできる。
クエリ文字列では先頭に :
をつけ、今回の例だと以下のように記述できた。
// ?#{T...} でも動くが、Intellij上でシンタックスエラー扱いになる @Query("SELECT e FROM ExampleEntity e WHERE e.state = :#{T(example.ExampleEntityState).END.value}") List<ExampleEntity> findAllOfEndState();
振り返り
@Enumerated
や @Convert
で列挙型をフィールドとしてマッピングしてあれば、列挙型の値を完全修飾クラス名付き(上記では example.ExampleEntityState.START
や example.ExampleEntityState.END
)をクエリ文字列に記述できるが、修正量が多くなることから今回はこの方法で対応した。
// 列挙型をフィールドとしてマッピングしている場合 @Query("SELECT e FROM ExampleEntity e WHERE e.state = example.ExampleEntityState.END")
また、JPQLではなくネイティブクエリでSQLを書きたいときにも、この方法は使えそう。
もうちょっと簡単な方法がありそうな気はする。