Поиск ключевых слов в базе данных
Я использую Eclipselink и у меня есть хитрая проблема с JPA NamedQueries.
Моя таблица базы данных содержит столбец типа VARCHAR и хранит список ключевых слов через запятую как одну строку.
Как я могу создать NamedQuery в JPA для поиска по этим ключевым словам? Я хотел бы дать список строк в качестве параметра, и в результате я хотел бы получить список объектов, список ключевых слов которых содержит одну из строк из списка параметров. Может быть, как следующее:
List<String> keywordList = new ArrayList<String>();
keywordList.add("test");
keywordList.add("car");
List<Object> result = em.createNamedQuery("findObjectByKeywords", Object.class)
.setParameter("keywords", keywordList)
.getResultList();
К сожалению, я не такой большой специалист по базам данных /SQL. Может быть, кто-то из вас может мне помочь?
Я надеюсь, вы понимаете мою проблему.
Изменить: я занимаюсь разработкой на Weblogic 10.3.6, что означает, что я не могу использовать функции JPA 2.0.
Edit2: мне удалось активировать JPA 2.0 на моем сервере Weblogic с помощью Oracle Enterprise Pack для Eclipse. Проблема решена, я думаю.
1 ответ
ДЕЙСТВИТЕЛЬНО ДЛЯ JPA2.0
Как прокомментировал Беш, простой JPQL этого не сделает. Результирующий SQL должен содержать предложение where, подобное следующему:
where keywords like '%keyword1%' or keywords like '%keyword2%' or ... or keywords like '%keywordN%'
Это значит: нам нужен цикл здесь!
Вы можете попытаться создать JPQL самостоятельно, как предложил Беш в своем первом комментарии, хотя, как он также заявил, это не блестящая идея. Но не волнуйтесь - JPA также предоставляет Criteria API, который пригодится в таких ситуациях. Итак, хотя у вас не будет именованного запроса, вы все равно можете сделать это с помощью JPA следующим образом:
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();
}
или (всегда немного лучше с гуавой)
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 + "%");
}
};
}