package de.flower.common.validation.unique.impl; import de.flower.common.model.db.entity.IEntity; import de.flower.common.util.Check; import org.apache.commons.beanutils.PropertyUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.List; /** * Does the actual database operations used for validation. */ @Service public final class RowCountChecker implements IRowCountChecker { /** * Need to create own EntityManager. Otherwise when validating during a save/update the database query will force a * flush of the unsaved entity -> null id error from hibernate. So we need a separate session. However this opens * the risk for phantom reads. * * @see http://community.jboss.org/wiki/AccessingTheHibernateSessionWithinAConstraintValidator */ private final EntityManagerFactory emFactory; @Autowired public RowCountChecker(final EntityManagerFactory emFactory) { this.emFactory = Check.notNull(emFactory); } @Override public Long rowCount(final IEntity entity, final List<String> attributeNames) { EntityManager em = null; try { em = emFactory.createEntityManager(); return rowCount(entity, attributeNames, em); } finally { if (em != null) { em.close(); } } } private Long rowCount(final IEntity entity, final List<String> attributeNames, final EntityManager em) { final Class<? extends IEntity> entityClass = entity.getClass(); final CriteriaBuilder cb = em.getCriteriaBuilder(); final CriteriaQuery<Long> query = cb.createQuery(Long.class); final Root<? extends IEntity> root = query.from(entityClass); query.select(cb.count(root)); Predicate condition = null; for (final String attributeName : attributeNames) { Predicate tmp; try { tmp = cb.equal(root.<Object>get(attributeName), PropertyUtils.getProperty(entity, attributeName)); // CHECKSTYLE IGNORE 1 LINES } catch (final Exception e) { throw new RuntimeException(e); } condition = (condition == null) ? tmp : cb.and(condition, tmp); } // update on existing element? exclude the element itself if (!entity.isNew()) { condition = cb.and(condition, cb.notEqual(root.<Long>get("id"), "" + entity.getId())); } query.where(condition); return em.createQuery(query).getSingleResult(); } }