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.

有帮助吗?

解决方案

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 + "%");
        }
    };
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top