Spring Bootを使用したインタビューテストの1つで、一連のオプションの要求パラメーターを受け入れ、車のモデル、ナンバープレート、エンジンタイプ、メーカーなどのこれらのパラメーターに基づいて車のリストを返すエンドポイントを作成する必要がありました。 、ドライバー、レンタル先の会社など。また、車、ドライバー、メーカーはすべて別のエンティティです。
この機能をJPARepositoryに実装するには、LEFT JOINSを実装する単一のJPQLクエリと、licensePlate = licensePlateParameter OR licensePlatParameter is nullなどのwhere句でフィルターを適用します。
ソリューションは機能していましたが、面接担当者は、ソリューションはスケーラブルで保守可能であると述べました。述語を使用して実装する必要がありました。誰かが、維持しやすい述語を使用してそのような機能をどのように実装できるか、例を示すことができますか?コードを含むいくつかの例をいただければ幸いです。
オプションのパラメーターを提供し、パラメーターがnullであるかどうかを確認することにより、1回の呼び出しでレコードを見つけることで、私は賢いと思いました。私が念頭に置いていることに関連する別の質問は、DBからすべてのレコードを取得し、述語を使用してそれをフィルター処理することは本当に良い方法ですか?また、複数のオブジェクト/エンティティが関与している場合にフィルタリングする方法については、単一のタイプに対して述語を作成できます。
@Query("SELECT d FROM Driver d LEFT JOIN d.car c WHERE (d.name = :name OR :name is null) "
+ "and (c.licensePlate = :licensePlate OR :licensePlate is null) "
+ "and (c.rating = :rating OR :rating is null) " and so on
List<Driver> findByAttributes(@Param("name") String name,
@Param("licensePlate") String licensePlate,
@Param("rating") Integer rating,
and so on);
Springには、JPA基準API(述語を使用)のラッパーがあり、仕様APIと呼ばれています。
仕様書を作成するときにできることは次のとおりです。各基準の仕様書を作成します。
_public static Specification<Car> withLicensePlate(String licensePlate) {
return (root, query, cb) -> licensePlate == null ? null : cb.equal(root.get("licensePlate"), licensePlate);
}
public static Specification<Car> withRating(String rating) {
return (root, query, cb) -> rating == null ? null : cb.equal(root.get("rating"), rating);
}
public static Specification<Car> withName(String name) {
return (root, query, cb) -> name == null ? null : cb.equal(root.get("name"), name);
}
_
また、結合操作を作成することもできます。
_public static Specification<Car> withSeatType(String type) {
return (root, query, cb) -> {
return type == null ? null : cb.equal(root.join("interior", JoinType.LEFT).get("type"), type);
};
}
_
基準内でnull
を返すことができます。これにより、これらの指定を「オプション」にすることができます。その後、Specifications.where()
を使用してこれらの基準を組み合わせることができます。
_ Specification<Car> spec = Specifications
.where(withLicensePlate(licensePlate))
.and(withRating(rating))
.and(withName(name))
.and(withSeatType(seatType));
_
この例のように個別の仕様を作成した場合、必要に応じてそれらを再利用できます。それ以外の場合は、操作固有の仕様を記述する必要があり、インタビュアーもそのスケーラブルを見つけられない可能性があります。
仕様を書き込んだ後、JpaSpecificationExecutor
インターフェースからリポジトリを拡張し、findAll(Specification)
メソッドを使用する必要があります。
JPQLの代わりにCriteria Apiを使用できます。
たとえば、例1を参照してください。
https://www.programcreek.com/Java-api-examples/index.php?api=javax.persistence.criteria.Predicate
Spring JPAでは、次のように動的クエリを使用できます。
public List<Employee> findByCriteria(String employeeName,String employeeRole){
return employeeDAO.findAll(new Specification<Employee>() {
@Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(employeeName!=null) {
predicates.add(criteriaBuilder.and(criteriaBuilder.like(root.get("employeeName"), "%"+employeeName+"%")));
}
if(employeeRole!=null){
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("employeeRole"), employeeRole)));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
});
}
このためには、リポジトリにJpaSpecificationExecutor
を実装する必要があります。