All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jpaquery.core.impl.JpaQueryImpl Maven / Gradle / Ivy

There is a newer version: 1.1.3
Show newest version
package com.jpaquery.core.impl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.persistence.EntityManager;
import javax.persistence.FlushModeType;
import javax.persistence.NoResultException;
import javax.persistence.Query;

import org.hibernate.Session;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.hql.internal.ast.ASTQueryTranslatorFactory;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.hql.spi.QueryTranslatorFactory;
import org.hibernate.query.NativeQuery;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import com.jpaquery.core.Querys;
import com.jpaquery.core.facade.Group;
import com.jpaquery.core.facade.GroupPath;
import com.jpaquery.core.facade.Having;
import com.jpaquery.core.facade.HavingPath;
import com.jpaquery.core.facade.Join;
import com.jpaquery.core.facade.JoinPath;
import com.jpaquery.core.facade.JpaQuery;
import com.jpaquery.core.facade.JpaQueryEach;
import com.jpaquery.core.facade.Order;
import com.jpaquery.core.facade.OrderPath;
import com.jpaquery.core.facade.Select;
import com.jpaquery.core.facade.SelectPath;
import com.jpaquery.core.facade.SubJpaQuery;
import com.jpaquery.core.facade.SubJpaQuery.SubJpaQueryType;
import com.jpaquery.core.facade.Where;
import com.jpaquery.core.facade.WherePath;
import com.jpaquery.core.render.JpaQueryRender;
import com.jpaquery.core.vo.EntityInfo;
import com.jpaquery.core.vo.FromInfo;
import com.jpaquery.core.vo.PathInfo;
import com.jpaquery.core.vo.QueryContent;
import com.jpaquery.util._Helper;
import com.jpaquery.util._MergeMap;
import com.jpaquery.util._Proxys;

/**
 * Finder实现类
 *
 * @author lujijiang
 *
 */
public class JpaQueryImpl implements JpaQuery {

	private static final Logger logger = LoggerFactory.getLogger(JpaQueryImpl.class);
	/**
	 * each遍历时每次查询的数量
	 */
	private static final int EACH_SIZE = 1024;
	/**
	 * 子查询
	 */
	List subFinderImpls = new ArrayList<>();
	/**
	 * finder处理器
	 */
	JpaQueryHandler finderHandler;
	/**
	 * 父finder实体信息
	 */
	Map parentFromInfos = new _MergeMap<>();
	/**
	 * finder实体信息
	 */
	Map currentFromInfos = new _MergeMap<>();
	/**
	 * 默认finder渲染器
	 */
	JpaQueryRender finderRender;

	/**
	 * select子句
	 */
	SelectImpl selectImpl;

	/**
	 * finder主where字句
	 */
	WhereImpl whereImpl;

	/**
	 * order子句
	 */
	OrderImpl orderImpl;
	/**
	 * group子句
	 */
	GroupImpl groupImpl;
	/**
	 * having子句
	 */
	HavingImpl havingImpl;
	/**
	 * join子句
	 */
	JoinImpl joinImpl;
	/**
	 * 父finder对象
	 */
	private JpaQueryImpl parentFinder;

	public List getSubFinderImpls() {
		return subFinderImpls;
	}

	public JpaQueryHandler getFinderHandler() {
		return finderHandler;
	}

	public Map getParentFromInfos() {
		return parentFromInfos;
	}

	public Map getCurrentFromInfos() {
		return currentFromInfos;
	}

	public JpaQueryRender getFinderRender() {
		return finderRender;
	}

	public SelectImpl getSelectImpl() {
		return selectImpl;
	}

	public WhereImpl getWhereImpl() {
		return whereImpl;
	}

	public OrderImpl getOrderImpl() {
		return orderImpl;
	}

	public GroupImpl getGroupImpl() {
		return groupImpl;
	}

	public HavingImpl getHavingImpl() {
		return havingImpl;
	}

	public JoinImpl getJoinImpl() {
		return joinImpl;
	}

	public JpaQueryImpl(JpaQueryHandler finderHandler, JpaQueryRender finderRender) {
		this.finderHandler = finderHandler;
		this.finderRender = finderRender;
		// 初始化子句
		selectImpl = new SelectImpl(finderHandler, this, new _MergeMap>());
		whereImpl = new WhereImpl(finderHandler, this, Where.WhereType.and, new _MergeMap>());
		orderImpl = new OrderImpl(finderHandler, this);
		groupImpl = new GroupImpl(finderHandler, this);
		havingImpl = new HavingImpl(finderHandler, this);
		joinImpl = new JoinImpl(finderHandler, this);
	}

	public JpaQuery subJpaQuery() {
		JpaQueryImpl subFinderImpl = new JpaQueryImpl(finderHandler, finderRender);
		subFinderImpl.parentFinder = this;
		subFinderImpl.getParentFromInfos().putAll(getCurrentFromInfos());
		subFinderImpl.getParentFromInfos().putAll(getParentFromInfos());
		subFinderImpls.add(subFinderImpl);
		return subFinderImpl;
	}

	public  T from(Class type) {
		T proxy = finderHandler.proxy(null, type);
		EntityInfo entityInfo = new EntityInfo(finderHandler, type, proxy);
		FromInfo fromInfo = new FromInfo(entityInfo);
		getCurrentFromInfos().put(entityInfo.getKey(), fromInfo);
		return proxy;
	}

	public Where where() {
		return whereImpl;
	}

	public  WherePath where(T obj) {
		return where().get(obj);
	}

	public Select select() {
		return selectImpl;
	}

	public  SelectPath select(T obj) {
		return select().get(obj);
	}

	public Order order() {
		return orderImpl;
	}

	public OrderPath order(Object obj) {
		return order().get(obj);
	}

	public Group group() {
		return groupImpl;
	}

	public GroupPath group(Object obj) {
		return group().get(obj);
	}

	public Having having() {
		return havingImpl;
	}

	public  HavingPath having(T obj) {
		return having().get(obj);
	}

	public Join join() {
		return joinImpl;
	}

	public  JoinPath join(Collection list) {
		return join().get(list);
	}

	public  JoinPath join(T obj) {
		return join().get(obj);
	}

	public  Where on(T join) {
		JoinPathImpl joinPathImpl = joinImpl.getJoinPathMap().get(_Helper.identityHashCode(join));
		if (joinPathImpl == null) {
			throw new IllegalStateException(String.format("对象:%s 不是Finder:%s的Join子句代理对象", this, join));
		}
		return joinPathImpl.getWhereImpl();
	}

	/**
	 * 生成QueryContent
	 *
	 * @param countSwich
	 *            是否是统计查询
	 * @return
	 */
	private QueryContent toQueryContent(boolean countSwich) {
		// 重置参数序号
		if (parentFinder == null) {
			finderHandler.resetParamIndex();
		}
		QueryContent queryContent = new QueryContent();
		// select
		QueryContent selectQueryContent = selectImpl.toQueryContent();
		if (selectQueryContent != null) {
			queryContent.append("select ");
			queryContent.append(selectQueryContent);
		}

		// from
		QueryContent fromQueryContent = finderRender.toFrom(this);
		if (fromQueryContent != null) {
			if (queryContent.length() > 0) {
				queryContent.append(" ");
			}
			queryContent.append("from ");
			queryContent.append(fromQueryContent);
		} else {
			throw new IllegalStateException("Must be exist from statement query");
		}

		// where
		QueryContent whereQueryContent = whereImpl.toQueryContent();
		if (whereQueryContent != null) {
			queryContent.append(" where ");
			queryContent.append(whereQueryContent);
		}

		// group
		QueryContent groupQueryContent = groupImpl.toQueryContent();
		if (groupQueryContent != null) {
			queryContent.append(" group by ");
			queryContent.append(groupQueryContent);
		}

		// having
		QueryContent havingQueryContent = havingImpl.toQueryContent();
		if (havingQueryContent != null) {
			queryContent.append(" having ");
			queryContent.append(havingQueryContent);
		}

		// order
		if (!countSwich) {
			QueryContent orderQueryContent = orderImpl.toQueryContent();
			if (orderQueryContent != null) {
				queryContent.append(" order by ");
				queryContent.append(orderQueryContent);
			}
		}

		return queryContent;
	}

	public QueryContent toQueryContent() {
		return toQueryContent(false);
	}

	public QueryContent toCountQueryContent() {
		return toQueryContent(true);
	}

	public SubJpaQuery any() {
		return new SubJpaQueryImpl(this, SubJpaQueryType.any);
	}

	public SubJpaQuery some() {
		return new SubJpaQueryImpl(this, SubJpaQueryType.some);
	}

	public SubJpaQuery all() {
		return new SubJpaQueryImpl(this, SubJpaQueryType.all);
	}

	public String alias(Object proxyInstance) {
		PathInfo pathInfo = finderHandler.getPathInfo();
		if (pathInfo != null) {
			FromInfo fromInfo = getCurrentFromInfos().get(pathInfo.getRootKey());
			if (fromInfo == null) {
				fromInfo = getParentFromInfos().get(pathInfo.getRootKey());
			}
			if (fromInfo == null) {
				throw new IllegalArgumentException(
						String.format("The info path %s root proxy instance is not valid", pathInfo));
			}
			return fromInfo.getEntityInfo().getAlias().concat(".").concat(pathInfo.getPathBuilder().toString());
		}
		if (proxyInstance == null) {
			throw new IllegalArgumentException(String.format("The proxy instance should't be null"));
		}
		if (proxyInstance instanceof JpaQuery) {
			return ((JpaQuery) proxyInstance).toQueryContent().getQueryString();
		}

		long key = _Helper.identityHashCode(proxyInstance);

		FromInfo fromInfo = getCurrentFromInfos().get(key);
		if (fromInfo == null) {
			fromInfo = getParentFromInfos().get(key);
		}

		if (fromInfo != null) {
			return fromInfo.getEntityInfo().getAlias();
		}
		throw new IllegalStateException(String.format(
				"Should be call a model getter method or argument is model object in this finder or sub finder object"));
	}

	// 特设附加方法
	/**
	 * 获取所有From对象
	 *
	 * @return
	 */
	public Collection froms() {
		return getCurrentFromInfos().values();
	}

	/**
	 * 根据查询内容创建查询对象
	 *
	 * @param em
	 * @param queryContent
	 * @return
	 */
	private Query createQuery(EntityManager em, QueryContent queryContent) {
		if (logger.isDebugEnabled()) {
			String caller = _Helper.findCaller();
			logger.debug("JPQL({}):{}", caller, queryContent);
		}
		Query query = em.createQuery(queryContent.getQueryString());
		if (Querys.isReadonly()) {
			query.setFlushMode(FlushModeType.COMMIT);
		}
		for (String name : queryContent.getArguments().keySet()) {
			Object arg = queryContent.getArguments().get(name);
			if (arg != null && arg instanceof Date) {
				Timestamp value = new Timestamp(((Date) arg).getTime());
				query.setParameter(name, value);
				continue;
			}
			query.setParameter(name, arg);
		}
		return query;
	}

	private Query createQuery(EntityManager em, JpaQuery finder, boolean cacheable) {
		QueryContent queryContent = finder.toQueryContent();
		Query query = createQuery(em, queryContent);
		cacheable(query, cacheable);
		return query;
	}

	private Query createQuery(EntityManager em, boolean cacheable) {
		return createQuery(em, this, cacheable);
	}

	/**
	 * 生成统计专用的查询对象
	 *
	 * @param em
	 * @return
	 */
	private Query createCountQuery(EntityManager em) {
		QueryContent queryContent = toCountQueryContent();
		String hql = queryContent.getQueryString();
		List argList = new ArrayList<>();
		Pattern pattern = Pattern.compile(":[a-zA-Z0-9_]+");
		Matcher matcher = pattern.matcher(hql);
		while (matcher.find()) {
			String group = matcher.group();
			String name = group.substring(1);
			argList.add(queryContent.getArguments().get(name));
		}
		Session session = em.unwrap(Session.class);
		QueryTranslatorFactory translatorFactory = new ASTQueryTranslatorFactory();
		SessionFactoryImplementor factory = (SessionFactoryImplementor) session.getSessionFactory();
		QueryTranslator translator = translatorFactory.createQueryTranslator(hql, hql, Collections.EMPTY_MAP, factory,
				null);
		translator.compile(Collections.EMPTY_MAP, false);
		String sql = translator.getSQLString();
		sql = "SELECT COUNT(1) FROM (" + sql + ") TMP";
		{
			Pattern questionMaskPattern = Pattern.compile("\\?");
			StringBuffer stringBuffer = new StringBuffer();
			Matcher questionMaskMatcher = questionMaskPattern.matcher(sql);
			int i = 0;
			while (questionMaskMatcher.find()) {
				questionMaskMatcher.appendReplacement(stringBuffer, ":p" + i);
				i++;
			}
			questionMaskMatcher.appendTail(stringBuffer);
			sql = stringBuffer.toString();
		}
		NativeQuery query = session.createNativeQuery(sql);
		for (int i = 0; i < argList.size(); i++) {
			Object arg = argList.get(i);
			if (arg != null) {
				if (arg.getClass().isEnum()) {
					arg = ((Enum) arg).name();
				}
			}
			if (arg != null && arg instanceof Collection) {
				Collection argCollection = (Collection) arg;
				List list = new ArrayList<>();
				for (Object obj : argCollection) {
					if (obj != null && obj.getClass().isEnum()) {
						obj = ((Enum) obj).name();
					}
					list.add(obj);
				}
				query.setParameterList("p" + i, list);
			} else {
				query.setParameter("p" + i, arg);
			}

		}
		return query;
	}

	@Override
	public Object one(EntityManager em) {
		return one(em, false);
	}

	@Override
	public Object one(EntityManager em, boolean cacheable) {
		Query query = createQuery(em, cacheable);
		try {
			return query.getSingleResult();
		} catch (NoResultException e) {
			return null;
		}
	}

	@Override
	public List list(EntityManager em) {
		return list(em, false);
	}

	@Override
	public List list(EntityManager em, boolean cacheable) {
		Query query = createQuery(em, cacheable);
		return query.getResultList();
	}

	private void cacheable(Query query, boolean cacheable) {
		query.setHint("org.hibernate.cacheable", cacheable);
	}

	@Override
	public List list(EntityManager em, int start, int max) {
		return list(em, start, max, false);
	}

	@Override
	public List list(EntityManager em, int start, int max, boolean cacheable) {
		Query query = createQuery(em, cacheable);
		query.setFirstResult(start);
		query.setMaxResults(max);
		return query.getResultList();
	}

	@Override
	public List top(EntityManager em, int top) {
		return top(em, top, false);
	}

	@Override
	public List top(EntityManager em, int top, boolean cacheable) {
		return list(em, 0, top, cacheable);
	}

	@Override
	public Page page(EntityManager em, Pageable pageable) {
		return page(em, pageable, false);
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Override
	public Page page(EntityManager em, Pageable pageable, boolean cacheable) {
		JpaQuery finder = this.copy();
		List content = createQuery(em, appendSortToFinder(finder, pageable.getSort()), cacheable)
				.setFirstResult(pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList();
		final long total = content.size() == pageable.getPageSize() ? -1 : pageable.getOffset() + content.size();
		final PageImpl page = new PageImpl(content, pageable, total);
		return _Proxys.newProxyInstance(new InvocationHandler() {
			private long totalElements = total;

			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				Page _this = (Page) proxy;
				if ("getTotalElements".equals(method.getName())) {
					fixTotal(em);
					return totalElements;
				}
				if ("getTotalPages".equals(method.getName())) {
					return _this.getSize() == 0 ? 1
							: (int) Math.ceil((double) _this.getTotalElements() / (double) _this.getSize());
				}
				if ("hasNext".equals(method.getName())) {
					return _this.getNumber() + 1 < _this.getTotalPages();
				}
				if ("isLast".equals(method.getName())) {
					return !_this.hasNext();
				}
				if ("nextPageable".equals(method.getName())) {
					return _this.hasNext() ? pageable.next() : null;
				}
				return method.invoke(page, args);
			}

			public void fixTotal(EntityManager em) {
				if (totalElements == -1) {
					totalElements = count(em);
				}
			}
		}, Page.class);
	}

	/**
	 * 将排序信息追加到Finder中,注意Finder将会被改变
	 *
	 * @param finder
	 * @param sort
	 */
	private JpaQuery appendSortToFinder(JpaQuery finder, Sort sort) {
		if (sort == null) {
			return finder;
		}
		JpaQueryImpl finderImpl = (JpaQueryImpl) finder;
		if (finderImpl.getSelectImpl().getSelectPaths().size() == 1) {
			Object selectPath = finderImpl.getSelectImpl().getSelectPaths().get(0);
			if (selectPath != null && selectPath instanceof SelectPathImpl) {
				SelectPathImpl selectPathImpl = (SelectPathImpl) selectPath;
				Object arg = selectPathImpl.getArg();
				if (arg != null && !(arg instanceof JpaQuery)) {
					try {
						String alias = finderImpl.alias(arg);
						for (Sort.Order order : sort) {
							finder.order().append(alias.concat(".").concat(order.getProperty()).concat(" ")
									.concat(order.getDirection().name().toLowerCase()));
						}
						return finder;
					} catch (IllegalStateException e) {

					}
				}
			}
		}
		for (Sort.Order order : sort) {
			finder.order().append(order.getProperty().concat(" ").concat(order.getDirection().name().toLowerCase()));
		}
		return finder;
	}

	public long count(EntityManager em) {
		Query query = createCountQuery(em);
		return ((Number) query.getSingleResult()).longValue();
	}

	public JpaQuery copy() {
		JpaQueryImpl finder = new JpaQueryImpl(this.finderHandler, this.finderRender);

		finder.parentFinder = parentFinder;
		finder.parentFromInfos = parentFromInfos;

		finder.currentFromInfos = new HashMap(currentFromInfos);
		finder.subFinderImpls = new ArrayList(subFinderImpls);

		finder.groupImpl = new GroupImpl(finderHandler, finder);
		finder.groupImpl.paths = new ArrayList(groupImpl.paths);

		finder.havingImpl = new HavingImpl(finderHandler, finder);
		finder.havingImpl.paths = new ArrayList(havingImpl.paths);

		finder.joinImpl = new JoinImpl(finderHandler, finder);
		finder.joinImpl.joinPathMap = new HashMap>(joinImpl.joinPathMap);

		finder.orderImpl = new OrderImpl(finderHandler, finder);
		finder.orderImpl.paths = new ArrayList(orderImpl.paths);

		finder.selectImpl = new SelectImpl(finderHandler, finder, selectImpl.entityInfoMap);
		finder.selectImpl.selectPaths = new ArrayList(selectImpl.selectPaths);

		finder.whereImpl = new WhereImpl(finderHandler, finder, whereImpl.type, whereImpl.entityInfoMap);
		finder.whereImpl.wherePaths = new ArrayList(whereImpl.wherePaths);

		return finder;
	}

	public String toString() {
		return "JpaQuery[" + hashCode() + "]";
	}

	@Override
	public  void each(EntityManager em, JpaQueryEach each) {
		each(em, each, false);
	}

	@SuppressWarnings({ "unchecked" })
	@Override
	public  void each(EntityManager em, JpaQueryEach each, boolean cacheable) {
		for (int i = 0;; i += EACH_SIZE) {
			List list = (List) list(em, i, EACH_SIZE, cacheable);
			if (list.isEmpty()) {
				break;
			}
			for (T entity : list) {
				each.handle(entity);
			}
		}
	}

	@Override
	public boolean isEmpty(EntityManager em, boolean cacheable) {
		return top(em, 1, cacheable).size() == 0;
	}

	@Override
	public boolean isEmpty(EntityManager em) {
		return isEmpty(em, false);
	}

}