For a project at the university, I had to implement an abstract search in an abstract JPA Dao.
Maybe this class comes handy for some of you
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | package com.dominikdorn.rest.dao; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceException; import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author Dominik Dorn */ public class AbstractJpaDao<TYPE> { @PersistenceContext protected EntityManager em; protected Class entityClass; public Class getEntityClass() { return entityClass; } public void setEntityClass(Class entityClass) { this.entityClass = entityClass; } public AbstractJpaDao() { ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); this.entityClass = (Class<TYPE>) genericSuperclass.getActualTypeArguments()[0]; } public AbstractJpaDao(Class clazz) { this.entityClass = clazz; } public EntityManager getEm() { return em; } public AbstractJpaDao setEm(EntityManager em) { this.em = em; return this; } @Override public TYPE persist(TYPE item) { if (item == null) throw new PersistenceException("Item may not be null"); em.persist(item); return item; } @Override public TYPE update(TYPE item) { if (item == null) throw new PersistenceException("Item may not be null"); em.merge(item); return item; } @Override public List<TYPE> getAll() { CriteriaQuery cq = em.getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); return em.createQuery(cq).getResultList(); } @Override public TYPE getById(Long id) { if (id == null || id < 1) throw new PersistenceException("Id may not be null or negative"); return (TYPE) em.find(entityClass, id); } @Override public void delete(TYPE item) { if (item == null) throw new PersistenceException("Item may not be null"); em.remove(em.merge(item)); } @Override public List<TYPE> findByAttributes(Map<String, String> attributes) { List<TYPE> results; //set up the Criteria query CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<TYPE> cq = cb.createQuery(getEntityClass()); Root<TYPE> foo = cq.from(getEntityClass()); List<Predicate> predicates = new ArrayList<Predicate>(); for(String s : attributes.keySet()) { if(foo.get(s) != null){ predicates.add(cb.like((Expression) foo.get(s), "%" + attributes.get(s) + "%" )); } } cq.where(predicates.toArray(new Predicate[]{})); TypedQuery<TYPE> q = em.createQuery(cq); results = q.getResultList(); return results; } } |
To instantiate this for an Entity, e.g. “Item”, simply do this
1 2 3 | // get an entityManager somewhere here AbstractJpaDao<Item> dao = new AbstractJpaDao<Item>(); dao.setEm(em); |
now you can use this Data Access Object for your persistence stuff.
To now search for entities in your DB, you can use the method findByAttributes which takes a map<String,String> and searches for appropriate items.
If your Entity looks like this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Entity public class Item { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "ITEM_GEN") @SequenceGenerator(name="ITEM_GEN", allocationSize=25, sequenceName = "item_seq") private long id; @Basic private String name; @Basic private String description; @Basic private Integer size; // constructors, getters, setters |
you could search for an item which names contain “test” like this
1 2 3 4 | Map<String,String> attr = new Hashmap<String,String>(); attr.put("name", "test"); List<Item> results = dao.findByAttributes(attr); |
which comes quite handy in my opinion. Also note, that you don’t have to pre-generate your JPA2 Model classes.
2 Responses to JPA2 Abstract DAO, Criteria Query & the “like” Operator
Timo Westkämper
August 30th, 2010 at 14:04
Nice approach. If you want a more compact typesafe Query API for JPA, then try out Querydsl : http://source.mysema.com/display/querydsl/Querydsl
Querydsl also supports JDO, Java collections, SQL and Lucene.
a friend
February 4th, 2011 at 18:14
I was looking for a way to dynamically add multiple predicates to one criteria query and this was
a very useful example.
Thanks!