AccessionRefRepositoryCustomImpl.java

package org.genesys.server.persistence;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.genesys.server.model.genesys.AccessionRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.google.common.collect.Iterables;
import com.querydsl.jpa.impl.JPAQueryFactory;

public abstract class AccessionRefRepositoryCustomImpl<X, Y extends AccessionRef<X>> implements AccessionRefCustomRepository<X, Y> {
	private static final Logger LOG = LoggerFactory.getLogger(AccessionRefRepositoryCustomImpl.class);
	private static final Predicate[] EMPTY_PREDICATE_ARRAY = new Predicate[] {};
	
	@Autowired
	protected JPAQueryFactory jpaQueryFactory;

	@PersistenceContext
	protected EntityManager em;

	private final Class<Y> targetType;

	protected abstract Class<Y> getAccessionRefType();
	
	public AccessionRefRepositoryCustomImpl() {
		this.targetType = getAccessionRefType();
	}
	
	@Override
	public List<Y> findExisting(X list, Iterable<Y> accessionRefs) {
		final CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
		final AtomicInteger batchCounter = new AtomicInteger(0);
		
		List<Y> results = new ArrayList<>();
		
		Iterables.partition(accessionRefs, 1000).forEach(batch -> {
			LOG.info("Batch {} with size {}", batchCounter.incrementAndGet(), batch.size());
			CriteriaQuery<Y> cq = criteriaBuilder.createQuery(this.targetType);
			Root<Y> root = cq.from(this.targetType);
			cq.distinct(true);
//			cq.select(root);

//			Path<Object> theDoi = root.get("doi");
			Path<Object> theList = root.get("list");
			Path<Object> theInstCode = root.get("instCode");
			Path<Object> theAcceNumb = root.get("acceNumb");
			Path<Object> theGenus = root.get("genus");
	
			// A lot of .. (instCode=? and acceNumb=? and genus=?)
			List<Predicate> restrictions = new ArrayList<Predicate>();
			for (Y ref : batch) {
				restrictions.add(criteriaBuilder.and(criteriaBuilder.equal(theInstCode, ref
					.getInstCode()), criteriaBuilder.equal(theAcceNumb, ref
					.getAcceNumb()), criteriaBuilder.equal(theGenus, ref.getGenus())));
			}

			cq.where(criteriaBuilder.and(criteriaBuilder.equal(theList, list), criteriaBuilder.or(restrictions.toArray(EMPTY_PREDICATE_ARRAY))));

			List<Y> existing = em.createQuery(cq).getResultList();
			LOG.debug("Got {} existing records", existing.size());
			for (Y aRef : batch) {
				int indexOf = existing.indexOf(aRef);
				if (indexOf != -1) {
					results.add(existing.get(indexOf));
				} else {
					results.add(aRef);
				}
			}
		});
		LOG.info("Done finding {} {} batches", batchCounter.get(), this.targetType.getSimpleName());
		return results;
	}
}