Edited at

Spring Data JPA にてメタモデルを使用すると NullPointerException が発生する

More than 1 year has passed since last update.


概要

JPA にてメタモデルを使用して、実装した結果、NullPointerException が発生しました。


詳細

JPA では、メタモデルの機能を提供しています。

メタモデルはエンティティの構造を明確にするものです。

メタモデルを使用せず、Criteria API を書くと下記のような実装になります。


NumberSpecification.java

public class NumberSpecification {

public static Specification<Group> members(Map<Integer, Integer> lowerUpper) {
return (root, query, cb) -> {
final Collection<Predicate> predicates = new ArrayList<>();
lowerUpper.forEach((lower, upper) -> {
predicates.add(
cb.and(cb.between(root.get("number"), lower, upper))
);

});
return cb.or(predicates.toArray(new Predicate[predicates.size()]));
};
}
}


上記の場合、文字列で Group クラスの number フィールドを指定して、下記のような条件を作成しています。


condition.sql

where number between 1 and 2 or number between 3 and 6 ...


上記の文字列指定ではフィールド名が "number" から違う名前に変わったとき正常に動作しません。

メタモデルを使用すると、この問題を解消できます。


Group.java

@Entity

@Table(name ="group_member")
public class Group extends Artist {

@Getter
private int number;

}


上記エンティティのメタモデルを作成する場合、@StaticMetamodel() アノテーションをつけて、該当のエンティティのクラス名の最後に "_" をつけたクラスを作成します。


Group_.java

@StaticMetamodel(Group.class)

public class Group_ {
public static volatile SingularAttribute<Group, Integer> number;
}

これにより下記のように Specification を記述できます。


NumberSpecificationWithMetamodel.java

public class NumberSpecificationWithMetamodel {

public static Specification<Group> members(Map<Integer, Integer> lowerUpper) {
return (root, query, cb) -> {
final Collection<Predicate> predicates = new ArrayList<>();
lowerUpper.forEach((lower, upper) -> {
predicates.add(
cb.and(cb.between(root.get(Group_.number), lower, upper))
);

});
return cb.or(predicates.toArray(new Predicate[predicates.size()]));
};
}
}


しかしながら、上記実装において、Group_.number が Null になる現象が発生しました。


解決策

非常に単純です。

こちらのページ での示唆のように、メタモデルとエンティティを同じパッケージに配置したら問題は解消します。