package au.com.vaadinutils.dao; import java.lang.reflect.ParameterizedType; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.Table; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaDelete; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.Order; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.Metamodel; import javax.persistence.metamodel.SingularAttribute; import org.vaadin.addons.lazyquerycontainer.EntityContainer; import com.google.common.base.Preconditions; import com.vaadin.addon.jpacontainer.JPAContainer; import au.com.vaadinutils.entity.BaseCrudEntity; import au.com.vaadinutils.entity.BaseCrudEntity_; public class JpaBaseDao<E, K> implements Dao<E, K> { protected Class<E> entityClass; public interface Condition<E> { Condition<E> and(Condition<E> c1); Predicate getPredicates(); Condition<E> or(Condition<E> c1); } static public <E> JpaBaseDao<E, Long> getGenericDao(Class<E> class1) { return new JpaBaseDao<E, Long>(class1); } @SuppressWarnings("unchecked") public JpaBaseDao() { // hack to get the derived classes Class type. ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); Preconditions.checkNotNull(genericSuperclass); this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[0]; Preconditions.checkNotNull(this.entityClass); } /** * it's very important that we don't retain a reference to the * entitymanager, as when you instance this class and then use it in a * closure you will end up trying to access a closed entitymanager * * @return */ public static EntityManager getEntityManager() { EntityManager em = EntityManagerProvider.getEntityManager(); Preconditions.checkNotNull(em, "Entity manager has not been initialized, if you are using a worker thread you will have to call EntityManagerProvider.createEntityManager()"); Preconditions.checkState(em.isOpen(), "The entity manager is closed, this can happen if you instance this class " + "and then use it in a closure when the closure gets called on a " + "separate thread or servlet request"); return em; } public JpaBaseDao(Class<E> class1) { entityClass = class1; } @Override public void persist(E entity) { getEntityManager().persist(entity); } @Override public E merge(E entity) { return getEntityManager().merge(entity); } @Override public void remove(E entity) { getEntityManager().remove(entity); } @SuppressWarnings("unchecked") public E findById(Integer id) { return findById((K) new Long(id)); } public <T> JpaDslSelectAttributeBuilder<E, T> select(SingularAttribute<E, T> attribute) { return new JpaDslSelectAttributeBuilder<>(entityClass, attribute); } @Override public E findById(K id) { return getEntityManager().find(entityClass, id); } protected E findSingleBySingleParameter(String queryName, SingularAttribute<E, String> paramName, String paramValue) { E entity = null; Query query = getEntityManager().createNamedQuery(queryName); query.setParameter(paramName.getName(), paramValue); query.setMaxResults(1); @SuppressWarnings("unchecked") List<E> entities = query.getResultList(); if (entities.size() > 0) { entity = entities.get(0); } return entity; } protected E findSingleBySingleParameter(String queryName, String paramName, String paramValue) { E entity = null; Query query = getEntityManager().createNamedQuery(queryName); query.setParameter(paramName, paramValue); query.setMaxResults(1); @SuppressWarnings("unchecked") List<E> entities = query.getResultList(); if (entities.size() > 0) { entity = entities.get(0); } return entity; } protected List<E> findListBySingleParameter(String queryName, String paramName, Object paramValue) { Query query = getEntityManager().createNamedQuery(queryName); query.setParameter(paramName, paramValue); @SuppressWarnings("unchecked") List<E> entities = query.getResultList(); return entities; } /** * Runs the given query returning all entities that matched by the query. * * @param queryName * @return */ protected List<E> findList(String queryName) { Query query = getEntityManager().createNamedQuery(queryName); @SuppressWarnings("unchecked") List<E> entities = query.getResultList(); return entities; } @Override public List<E> findAll() { return findAll(null); } /** * Returns all rows ordered by the given set of entity attribues. * * You may pass in an array of attributes and a order by clause will be * added for each attribute in turn e.g. order by order[0], order[1] .... */ @Override public List<E> findAll(SingularAttribute<E, ?> order[]) { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<E> criteria = builder.createQuery(entityClass); Root<E> root = criteria.from(entityClass); criteria.select(root); if (order != null) { List<Order> ordering = new LinkedList<Order>(); for (SingularAttribute<E, ?> field : order) { ordering.add(builder.asc(root.get(field))); } criteria.orderBy(ordering); } List<E> results = getEntityManager().createQuery(criteria).getResultList(); return results; } /** * Returns all rows ordered by the given set of entity attribues. * * @param order * You may pass in an array of attributes and a order by clause * will be added for each attribute in turn e.g. order by * order[0], order[1] .... * * @param sortAscending * An array of booleans that must be the same size as the order. * The sort array controls whether each attribute will be sorted * ascending or descending. * */ public List<E> findAll(SingularAttribute<E, ?> order[], boolean sortAscending[]) { Preconditions.checkArgument(order.length == sortAscending.length, "Both arguments must have the same no. of array elements."); CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<E> criteria = builder.createQuery(entityClass); Root<E> root = criteria.from(entityClass); criteria.select(root); List<Order> ordering = new LinkedList<Order>(); for (SingularAttribute<E, ?> field : order) { if (sortAscending[ordering.size()] == true) { ordering.add(builder.asc(root.get(field))); } else { ordering.add(builder.desc(root.get(field))); } } criteria.orderBy(ordering); List<E> results = getEntityManager().createQuery(criteria).getResultList(); return results; } public <V> E findOneByAttribute(SingularAttribute<? super E, V> vKey, V value) { JpaDslBuilder<E> q = select(); return q.where(q.eq(vKey, value)).getSingleResultOrNull(); } public <V, SK> List<E> findAllByAttribute(SingularAttribute<E, V> vKey, V value, SingularAttribute<E, SK> order) { return findAllByAttribute(vKey, value, order, null); } public <V, SK> List<E> findAllByAttribute(SingularAttribute<E, V> vKey, V value, SingularAttribute<E, SK> order, Integer limit) { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<E> criteria = builder.createQuery(entityClass); Root<E> root = criteria.from(entityClass); criteria.select(root); criteria.where(builder.equal(root.get(vKey), value)); if (order != null) { criteria.orderBy(builder.asc(root.get(order))); } TypedQuery<E> query = getEntityManager().createQuery(criteria); if (limit != null) { query = query.setMaxResults(limit); } return query.getResultList(); } public <SK> List<E> findAllByAttributeLike(SingularAttribute<E, String> vKey, String value, SingularAttribute<E, SK> order) { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<E> criteria = builder.createQuery(entityClass); Root<E> root = criteria.from(entityClass); criteria.select(root); criteria.where(builder.like(root.<String> get(vKey), value)); if (order != null) { criteria.orderBy(builder.asc(root.get(order))); } List<E> results = getEntityManager().createQuery(criteria).getResultList(); return results; } /** * Find a single record by multiple attributes. Searches using AND. * * @param attributes * AttributeHashMap of SingularAttributes and values * @return first matching entity */ public E findOneByAttributes(AttributesHashMap<E> attributes) { E ret = null; List<E> results = findAllByAttributes(attributes, null); if (results.size() > 0) { ret = results.get(0); } return ret; } /** * Find multiple records by multiple attributes. Searches using AND. * * @param <SK> * attribute * @param attributes * AttributeHashMap of SingularAttributes and values * @param order * SingularAttribute to order by * @return a list of matching entities */ public <SK> List<E> findAllByAttributes(AttributesHashMap<E> attributes, SingularAttribute<E, SK> order) { final CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); final CriteriaQuery<E> criteria = builder.createQuery(entityClass); final Root<E> root = criteria.from(entityClass); criteria.select(root); Predicate where = builder.conjunction(); for (Entry<SingularAttribute<E, Object>, Object> attr : attributes.entrySet()) { where = builder.and(where, builder.equal(root.get(attr.getKey()), attr.getValue())); } criteria.where(where); if (order != null) { criteria.orderBy(builder.asc(root.get(order))); } List<E> results = getEntityManager().createQuery(criteria).getResultList(); return results; } /** * Find a single record by multiple attributes. Searches using OR. * * @param attributes * AttributeHashMap of SingularAttributes and values * @return first matching entity */ public E findOneByAnyAttributes(AttributesHashMap<E> attributes) { E ret = null; List<E> results = findAllByAnyAttributes(attributes, null); if (results.size() > 0) { ret = results.get(0); } return ret; } /** * Find multiple records by multiple attributes. Searches using OR. * * @param <SK> * attribute * @param attributes * AttributeHashMap of SingularAttributes and values * @param order * SingularAttribute to order by * @return a list of matching entities */ public <SK> List<E> findAllByAnyAttributes(AttributesHashMap<E> attributes, SingularAttribute<E, SK> order) { final CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); final CriteriaQuery<E> criteria = builder.createQuery(entityClass); final Root<E> root = criteria.from(entityClass); criteria.select(root); Predicate where = builder.conjunction(); for (Entry<SingularAttribute<E, Object>, Object> attr : attributes.entrySet()) { where = builder.or(where, builder.equal(root.get(attr.getKey()), attr.getValue())); } criteria.where(where); if (order != null) { criteria.orderBy(builder.asc(root.get(order))); } List<E> results = getEntityManager().createQuery(criteria).getResultList(); return results; } /** * get count of entity with a simple criteria * * @param vKey * @param value * @return */ public <V> Long getCount(SingularAttribute<E, V> vKey, V value) { CriteriaBuilder qb = getEntityManager().getCriteriaBuilder(); CriteriaQuery<Long> cq = qb.createQuery(Long.class); Root<E> root = cq.from(entityClass); cq.select(qb.count(root)); cq.where(qb.equal(root.get(vKey), value)); return getEntityManager().createQuery(cq).getSingleResult(); } public JPAContainer<E> createVaadinContainer() { JPAContainer<E> container = new JPAContainer<E>(entityClass); container.setEntityProvider(new BatchingPerRequestEntityProvider<E>(entityClass)); return container; } public EntityContainer<E> createLazyQueryContainer() { EntityManager em = getEntityManager(); boolean compositeItmes = true; boolean detachedEntities = true; String propertyId = getIdField().getName(); boolean applicationManagedTransactions = true; EntityContainer<E> entityContainer = new EntityContainer<E>(em, entityClass, propertyId, Integer.MAX_VALUE, applicationManagedTransactions, detachedEntities, compositeItmes); for (Attribute<? super E, ?> attrib : getIdField().getDeclaringType().getAttributes()) { entityContainer.addContainerProperty(attrib.getName(), attrib.getJavaType(), null, true, true); } return entityContainer; } static public <T> SingularAttribute<T, Long> getIdField(Class<T> type) { Metamodel metaModel = getEntityManager().getMetamodel(); EntityType<T> entityType = metaModel.entity(type); return entityType.getDeclaredId(Long.class); } public SingularAttribute<E, Long> getIdField() { return getIdField(entityClass); } public JPAContainer<E> createVaadinContainer(final int sizeLimit) { JPAContainer<E> container = new JPAContainer<E>(entityClass) { private static final long serialVersionUID = -3280358604354247501L; @Override public int size() { int size = super.size(); return Math.min(sizeLimit, size); } }; container.setEntityProvider(new BatchingPerRequestEntityProvider<E>(entityClass)); return container; } public void flushCache() { getEntityManager().getEntityManagerFactory().getCache().evict(entityClass); } public JPAContainer<E> createVaadinContainerAndFlushCache(final int sizeLimit) { getEntityManager().getEntityManagerFactory().getCache().evict(entityClass); return createVaadinContainer(sizeLimit); } public JPAContainer<E> createVaadinContainerAndFlushCache() { getEntityManager().getEntityManagerFactory().getCache().evict(entityClass); return createVaadinContainer(); } public int deleteAll() { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaDelete<E> criteria = builder.createCriteriaDelete(entityClass); int result = getEntityManager().createQuery(criteria).executeUpdate(); flushCache(); return result; } public <V> int deleteAllByAttribute(SingularAttribute<? super E, V> vKey, V value) { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaDelete<E> criteria = builder.createCriteriaDelete(entityClass); Root<E> root = criteria.from(entityClass); criteria.where(builder.equal(root.get(vKey), value)); int result = getEntityManager().createQuery(criteria).executeUpdate(); return result; } public <V, J> List<E> findAllByAttributeJoin(SingularAttribute<E, J> joinAttr, SingularAttribute<J, V> vKey, V value, JoinType joinType) { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaQuery<E> criteria = builder.createQuery(entityClass); Root<E> root = criteria.from(entityClass); Join<E, J> join = root.join(joinAttr, joinType); criteria.where(builder.equal(join.get(vKey), value)); return getEntityManager().createQuery(criteria).getResultList(); } public <V, J> int deleteAllByAttributeJoin(SingularAttribute<J, V> vKey, V value, SingularAttribute<E, J> joinAttr) { CriteriaBuilder builder = getEntityManager().getCriteriaBuilder(); CriteriaDelete<E> criteria = builder.createCriteriaDelete(entityClass); Root<E> root = criteria.from(entityClass); Join<E, J> join = root.join(joinAttr, JoinType.LEFT); criteria.where(builder.equal(join.get(vKey), value)); getEntityManager().getClass(); int result = getEntityManager().createQuery(criteria).executeUpdate(); return result; } /** * @return the number of entities in the table. */ public long getCount() { String entityName = entityClass.getSimpleName(); Table annotation = entityClass.getAnnotation(Table.class); String tableName; if (annotation != null) { tableName = annotation.name(); } else { tableName = entityName; } String qry = "select count(" + entityName + ") from " + tableName + " " + entityName; Query query = getEntityManager().createQuery(qry); Number countResult = (Number) query.getSingleResult(); return countResult.longValue(); } public void flush() { getEntityManager().flush(); } public void refresh(E entity) { getEntityManager().refresh(entity); } public void detach(E entity) { getEntityManager().detach(entity); } public JpaBaseDao<E, K>.FindBuilder findOld() { return new FindBuilder(); } public JpaDslBuilder<E> select() { return new JpaDslBuilder<E>(entityClass); } public JpaDslTupleBuilder<E> selectTuple() { return new JpaDslTupleBuilder<E>(entityClass); } public JpaDslBuilder<E> jpaContainerDelegate(CriteriaQuery<E> criteria) { return new JpaDslBuilder<E>(criteria, entityClass); } @SuppressWarnings("unchecked") public <M extends BaseCrudEntity> M findByEntityId(M entity) { if (entity.getId() != null) { // lookup by id return (M) getEntityManager().find(entityClass, entity.getId()); } // lookup by guid JpaDslBuilder<M> q = (JpaDslBuilder<M>) select(); return q.where(q.eq(BaseCrudEntity_.guid, entity.getGuid())).getSingleResultOrNull(); } public class FindBuilder { CriteriaBuilder builder = EntityManagerProvider.getEntityManager().getCriteriaBuilder(); CriteriaQuery<E> criteria = builder.createQuery(entityClass); Root<E> root = criteria.from(entityClass); List<Predicate> predicates = new LinkedList<>(); private Integer limit = null; private Integer startPosition = null; /** * specify that JPA should fetch child entities in a single query! * * @param field * @return */ public <L> FindBuilder fetch(SingularAttribute<E, L> field) { root.fetch(field, JoinType.LEFT); return this; } public <L> FindBuilder whereEqual(SingularAttribute<E, L> field, L value) { predicates.add(builder.equal(root.get(field), value)); return this; } public <J, L> FindBuilder joinWhereEqual(Join<E, J> join, SingularAttribute<J, L> field, L value) { predicates.add(builder.equal(join.get(field), value)); return this; } public FindBuilder whereLike(SingularAttribute<E, String> field, String value) { predicates.add(builder.like(root.get(field), value)); return this; } public <L extends Comparable<? super L>> FindBuilder whereGreaterThan(SingularAttribute<E, L> field, L value) { predicates.add(builder.greaterThan(root.get(field), value)); return this; } public <L extends Comparable<? super L>> FindBuilder whereGreaterThanOrEqualTo(SingularAttribute<E, L> field, L value) { predicates.add(builder.greaterThanOrEqualTo(root.get(field), value)); return this; } public FindBuilder limit(int limit) { this.limit = limit; return this; } public FindBuilder startPosition(int startPosition) { this.startPosition = startPosition; return this; } public FindBuilder orderBy(SingularAttribute<E, ?> field, boolean asc) { if (asc) { criteria.orderBy(builder.asc(root.get(field))); } else { criteria.orderBy(builder.desc(root.get(field))); } return this; } public <J> FindBuilder joinOrderBy(Join<E, J> join, SingularAttribute<J, ?> field, boolean asc) { if (asc) { criteria.orderBy(builder.asc(join.get(field))); } else { criteria.orderBy(builder.desc(join.get(field))); } return this; } FindBuilder() { criteria.select(root); } public E getSingleResult() { limit(1); TypedQuery<E> query = prepareQuery(); return query.getSingleResult(); } public List<E> getResultList() { TypedQuery<E> query = prepareQuery(); return query.getResultList(); } private TypedQuery<E> prepareQuery() { Predicate filter = null; for (Predicate predicate : predicates) { if (filter == null) { filter = predicate; } else { filter = builder.and(filter, predicate); } } if (filter != null) { criteria.where(filter); } TypedQuery<E> query = EntityManagerProvider.getEntityManager().createQuery(criteria); if (limit != null) { query.setMaxResults(limit); } if (startPosition != null) { query.setFirstResult(startPosition); } return query; } public <L> FindBuilder whereNotEqueal(SingularAttribute<E, L> field, L value) { predicates.add(builder.notEqual(root.get(field), value)); return this; } public <L> FindBuilder whereNotNull(SingularAttribute<E, L> field) { predicates.add(builder.isNotNull(root.get(field))); return this; } public <L> FindBuilder whereNull(SingularAttribute<E, L> field) { predicates.add(builder.isNull(root.get(field))); return this; } public Predicate like(SingularAttribute<E, String> field, String value) { return builder.like(root.get(field), value); } public <J> Join<E, J> join(SingularAttribute<E, J> joinAttribute, JoinType joinType) { return root.join(joinAttribute, joinType); } public <J> Predicate joinLike(Join<E, J> join, SingularAttribute<J, String> field, String value) { return builder.like(join.get(field), value); } public FindBuilder whereAnd(Predicate pred) { predicates.add(pred); return this; } public FindBuilder whereOr(List<Predicate> orPredicates) { Predicate or = null; for (Predicate pred : orPredicates) { if (or == null) { or = pred; } else { or = builder.or(or, pred); } } if (or != null) { predicates.add(or); } return this; } public <L extends Comparable<? super L>> FindBuilder whereLessThanOrEqualTo(SingularAttribute<E, L> field, L value) { predicates.add(builder.lessThanOrEqualTo(root.get(field), value)); return this; } public <L extends Comparable<? super L>> Predicate greaterThanOrEqualTo(SingularAttribute<E, L> field, L value) { return builder.greaterThanOrEqualTo(root.get(field), value); } public <L> Predicate isNull(SingularAttribute<E, L> field) { return builder.isNull(root.get(field)); } } public int getEntityCount() { return getGenericDao(entityClass).select().count().intValue(); } }