7

I love the type safety CriteriaQuery brings ing JPA 2.0 but it also brings a bit of boiler-plate code. For example, let say I have an entity called NamedEntity, which simply has an id and a String field called "name" (assume it has the unique constraint set to true). Here's what the NamedEntityManager might look like:

public class NamedEntityManager
{
    //inject using your framework
    EntityManager entityManager;

    //retrieve all existing entities of type NamedEntity from DB
    public Iterable<NamedEntity> queryAll()
    {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
        return entityManager.createQuery(query).getResultList();
    }

    //retrieve a single entity of type NamedEntity from DB using specified name
    public NamedEntity queryByName(String name)
    {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<NamedEntity> query = builder.createQuery(NamedEntity.class);
        Root<NamedEntity> root = query.from(NamedEntity.class);
        query = query.where(root.<NamedEntity>get("name").in(name));

        //skipped the try/catch block for the sake of brevity
        return entityManager.createQuery(query).getSingleResult();
    }
}

Is there a way to condense the code in order to avoid copying/pasting the same lines of code into each query method? Perhaps somehow reuse the CriteriaQuery object?

Andrey
  • 8,882
  • 10
  • 58
  • 82
  • This situation can be easily addressed using strategy. Just create one private method that method should take one parameter(an interface) of type say, WhereClauseBuilder, the private method will get its varying part(where clause) from this parameter through a method call that passes the criteriaBuilder and query to it. All public methods, will just call the private method with a specific WhereClauseBuilder that returns required predicate where clause. – ring bearer Jan 25 '16 at 10:30

5 Answers5

5

I was looking for something like that, you could take a look at Querydsl (LGPL licensed) which can have JPA as backend.

Im still reading into it, but from their examples, it looks pretty clean.

HQLQuery q = new HibernateQuery(session);
QCat cat = new QCat("cat"); // query type
List<Cat> cats = q.from(cat).where(cat.name.between("A", "B")).list(cat);
Cojones
  • 1,906
  • 22
  • 25
  • Although this framework is not related to the Criteria API and stands on its own, it's certainly worth looking into. Thank you for the mentioning this, I'll explore it when I get the chance! – Andrey Jan 16 '11 at 05:34
  • Just a minor correction, Querydsl is LGPL licensed, not GPL licensed. – Timo Westkämper Feb 02 '11 at 09:18
  • http://www.querydsl.com/ it also appears that the license has been changed to Apache License, Version 2.0. – hooknc Feb 03 '21 at 16:02
4

In JPA 2.1, it will most probably be possible to mix JPQL and Criterias. With such an approach you could define a base query with JPQL and then use the Criteria API to dynamically add small parts.

I figure the API will be less verbose then, since you only need to use small parts of it.

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
Arjan Tijms
  • 37,782
  • 12
  • 108
  • 140
  • 1
    I'm not sure if it would make sense to mix JPQL with Criteria Queries. Including JPQL would go against the main idea behind Criteria API - to provide compile-time type safety. – Andrey Feb 15 '11 at 14:24
  • 2
    It's a tradeoff of course. It's just as using EL and in some cases annotations. You're trading in compile time safety for flexibility and less verbose code. Anyway, lot's of people do think it make senses as Linda (spec lead) is seriously considering this for JPA 2.1. The choice is then yours: pure JPQL, JPQL mixed with Criteria and even within Criterias you have the type-safe and non-type-safe variants. Use what works for you. – Arjan Tijms Feb 15 '11 at 20:01
1

Then Use JPA-2.0 MetaData model. http://docs.jboss.org/hibernate/jpamodelgen/1.0/reference/en-US/html_single/

Puspendu Banerjee
  • 2,631
  • 16
  • 19
  • Using the metamodel doesn't cut down the amount of code all that much but it certainly improves clarity and adds further type-safety. – Andrey Feb 15 '11 at 14:30
1

It seems there's no way to reduce the amount of code. I guess something had to be sacrificed to gain type safety.

Andrey
  • 8,882
  • 10
  • 58
  • 82
0

Way outdated, this post, but I want to add what I recently built for simple queries

    public static class Jpa2Whatsoever {

    private final EntityManager em;

    public class Jpa2WhatsoeverProgress<T> {

        private CriteriaQuery<T> cq;
        private List<Predicate> predicates = new ArrayList<>();
        private Root<T> root;

        public Jpa2WhatsoeverProgress(Class<T> type) {
            this.cq = em.getCriteriaBuilder().createQuery(type);
            this.root = cq.from(type);

        }

        public Jpa2WhatsoeverProgress<T> where(String attributeName, Object value) {

            Predicate equal = em.getCriteriaBuilder().equal(root.get(attributeName), value);

            predicates.add(equal);
            return this;
        }

        public List<T> getResultList() {
            Predicate[] predicatesArray = new Predicate[predicates.size()];
            TypedQuery<T> typedQuery = em.createQuery(cq.select(root).where(predicates.toArray(predicatesArray)));

            List<T> resultList = typedQuery.getResultList();

            return Collections.unmodifiableList(resultList);
        }

    }

    public Jpa2Whatsoever(EntityManager entityManager) {
        this.em = entityManager;
    }

    public <T> Jpa2WhatsoeverProgress<T> select(Class<T> type) {
        return new Jpa2WhatsoeverProgress<T>(type);
    }
}

You can use it like this

List<MyEntity> matchingEntities = new Jpa2Whatsoever(entityManager).select(MyEntity.class).where("id", id).where("due", new Date()).getResultList();

In the end I stopped this. Mainly because I saw that I had only two queries and I would have to extend the DSL to get the required query characteristics into it, such as

  • greater than, less than
  • Metamodel support
  • QueryBuilder.currentDate() and alike.

Further, I find it ugly to always call where while it actually corresponds to a more SQLly and. Anyway, if someone is interested in a very simple query API, it is still worth a try.

BTW: Forget about the names, this was a prototype, nothing more.

Andreas
  • 3,929
  • 2
  • 25
  • 22