Поиск ключевых слов в базе данных

Я использую 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 + "%");
        }
    };
}
Другие вопросы по тегам