问题
I'm using JPQL to write SQL queries in JPA's CurdRepository
interface. I was able to write more than one query with named parameters and they work like a charm.
However, my application requires that I match an unknown number of strings with more than one column in a table. To my knowledge, in SQL it'd look something like this:
SELECT * FROM TABLE WHERE COLUMN_1 LIKE "%string_1%" OR COLUMN_1 LIKE"%string_2%" ... OR COLUMN_2 LIKE "%string_1%" OR COLUMN_2 LIKE "%string_2%" ... ...
I tried this in MySQL and it works fine. I couldn't figure out how to depict this in JPQL, however. I was able to pass multiple values for the IN
clause by setting the named parameter to a List
, as follows:
@Query("SELECT t FROM Table t WHERE TABLE_ID IN (:table_ids)")
public Page<Table> findByParameters(@Param("table_ids") List<Integer> table_ids, Pageable page)
I tried doing the same thing with the LIKE
clause but I end up with 0 results if I pass more than 1 value. I face the same problem with the LOCATE
clause as well.
Worst case scenario, I can set up one query that works on one String, and call it several times and then merge all the results. I'd very much rather avoid this however, as it'd be nothing more than a duct-tape solution.
I looked online for solutions for hours but haven't found anything that addresses my concern. Any tip is highly appreciated.
Thanks.
回答1:
Because @Query
needs to be fixed at compile time using JPQL or even native query will make this kind of things hard to implement especially in a type safe way.
So I realize you are after JPQL solution but this is an awesome opportunity to learn and take advantage of Specification
interface and JPA's CriteriaQuery. This is exactly it is for.
Take a look at the following repository:
public interface Table1Repository // to use specifications in queries
extends JpaRepository<Table1, Long>, JpaSpecificationExecutor<Table1> {
@SuppressWarnings("serial")
public static Specification<Table1> multiLikeColumn1(List<String> likePatterns) {
return new Specification<Table1>() {
@Override
public Predicate toPredicate(Root<Table1> root, CriteriaQuery<?> query,
CriteriaBuilder criteriaBuilder) {
Path<String> column1 = root.get("column1");
// create a Predicate for each "column1 like 'xy%az%' you need
List<Predicate> predicates = likePatterns.stream()
.map(likePattern -> criteriaBuilder.like(column1, likePattern))
.collect(Collectors.toList());
// then "concatenate" list of likes with "OR"
return criteriaBuilder.or(predicates.toArray(new Predicate[]{}));
}
};
}
}
It might look a bit complex but actually it is not when you get familiar with it. Usage is simple, like:
@Resource
private Table1Repository repo;
repo.findAll(Table1Repository.multiLikeColumn1(Arrays.asList("%X%","%Z%")))
来源:https://stackoverflow.com/questions/54154045/spring-boot-jpa-passing-multiple-values-for-the-same-parameter-jpql