Reputation: 1535
I'm using Eclipselink and have a tricky problem regarding JPA NamedQueries.
My database table contains a column which is from type VARCHAR and stores a comma separated list of keywords as one String.
How can I create a NamedQuery in JPA to search theese keywords? I'd like to give a list of Strings as a parameter and as a result I'd like to have a list of objects which keyword list contain one of the Strings from the parameter list. Maybe like the following:
List<String> keywordList = new ArrayList<String>();
keywordList.add("test");
keywordList.add("car");
List<Object> result = em.createNamedQuery("findObjectByKeywords", Object.class)
.setParameter("keywords", keywordList)
.getResultList();
Unfortunately I'm not such a big database/SQL expert. Maybe someone of you can help me?
I hope you understand my problem.
Edit: I am developing on Weblogic 10.3.6, which means I am not able to use JPA 2.0 features.
Edit2: I managed to activate JPA 2.0 in my Weblogic Server with the help of Oracle Enterprise Pack for Eclipse. Problem solved, I think.
Upvotes: 4
Views: 2116
Reputation: 5220
VALID FOR JPA2.0
As Bhesh commented a simple JPQL won't make it. The resulting SQL has to contain a where clause similar to following:
where keywords like '%keyword1%' or keywords like '%keyword2%' or ... or keywords like '%keywordN%'
This means: We need a loop here!
You could try to build a JPQL by yourself like Bhesh suggested in his first comment, though as he also stated it is not a brilliant idea. But don't worry - JPA provides also a Criteria API which comes handy in such situations. So, although you're not going to have a named query, you can still make it with JPA this way:
public List<YourEntity> findAllByKeywords(List<String> keywords){
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<YourEntity> query = builder.createQuery(YourEntity.class);
Root<YourEntity> root = query.from(YourEntity.class);
List<Predicate> predicates = new LinkedList<>();
for (String keyword : keywords) {
predicates.add(builder.like(root.<String>get("keywords"), "%" + keyword + "%"));
}
return entityManager.createQuery(
query.select(root).where(
builder.or(
predicates.toArray(new Predicate[predicates.size()])
)
))
.getResultList();
}
or (always slightly better with Guava)
public List<YourEntity> findAllByKeywords(List<String> keywords){
final CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<YourEntity> query = builder.createQuery(YourEntity.class);
final Root<YourEntity> root = query.from(YourEntity.class);
return entityManager.createQuery(
query.select(root).where(
builder.or(
transform(keywords, toPredicateFunction(builder, root)).toArray(new Predicate[]{})
)
))
.getResultList();
}
private Function<String, Predicate> toPredicateFunction(final CriteriaBuilder builder, final Root<YourEntity> root) {
return new Function<String, Predicate>() {
@Override
public Predicate apply(String input) {
return builder.like(root.<String>get("keywords"), "%" + input + "%");
}
};
}
Upvotes: 4