Using Java generics for JPA findAll() query with WHERE clause
criteria-api, generics, java, jpa
Solution
Hat tip to Adam Bien if you don't want to use `createQuery` with a `String` and want type safety:
@PersistenceContext
EntityManager em;
public List<ConfigurationEntry> allEntries() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<ConfigurationEntry> cq = cb.createQuery(ConfigurationEntry.class);
Root<ConfigurationEntry> rootEntry = cq.from(ConfigurationEntry.class);
CriteriaQuery<ConfigurationEntry> all = cq.select(rootEntry);
TypedQuery<ConfigurationEntry> allQuery = em.createQuery(all);
return allQuery.getResultList();
}
http://www.adam-bien.com/roller/abien/entry/selecting_all_jpa_entities_as
Problem
So, After a 10+ year break I'm coming back to Java and trying out stuff with JPA and Java generics. I've created a generics based `findAll(other)` JPA query that basically does ``` SELECT * FROM source WHERE other_id = other.id; ``` This is where I'm up to. It works, but I'm wondering if there's a better, cleaner way to do it. Using `ManagedType` was hard, and there's not much complete documentation or simple examples around. I've decided to keep my code as generic as possible (no pun intended) so I use JPA2. This is the root of all Entity Classes. I probably don't need it, but it stops me from having basic mistakes. ``` import java.io.Serializable; public abstract class DomainObject implements Serializable { private static final long serialVersionUID = 1L; public abstract void setId(Long id); public abstract Long getId(); } ``` This is the abstract DAO class. I extend this for the implementation classes as I need to be more specific doing other activities - mostly making sure lazy sets are loaded. ``` public abstract class GenericDAOImpl<T extends DomainObject, T2 extends DomainObject> implements GenericDAO<T, T2> { private Class<T> type; @PersistenceContext protected EntityManager entityManager; public GenericDAOImpl(Class<T> type) { super(); this.type = type; } ... save and delete classes go here @Override public List<T> findAll(T2 where) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(type); Root<T> rootQuery = criteriaQuery.from(type); if (where != null) { EntityType<T> entity = entityManager.getMetamodel().entity(type); SingularAttribute<? super T, ?> attribute = null; for (SingularAttribute<? super T, ?> singleAttribute: entity.getSingularAttributes()) { // loop through all attributes that match this class if (singleAttribute.getJavaType().equals(where.getClass())) { // winner! attribute = singleAttribute; break; } } // where t.object = object.getID() criteriaQuery.where(criteriaBuilder.equal(rootQuery.get(attribute), where)); } criteriaQuery.select(rootQuery); TypedQuery<T> query = entityManager.createQuery(criteriaQuery); // need this to make sure we have a clean list? // entityManager.clear(); return query.getResultList(); } ``` Any suggestions? If anything, I want this out there so other people can make use of it.