package org.zstack.core.db; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.zstack.utils.logging.CLogger; import org.zstack.utils.logging.CLoggerImpl; import javax.persistence.NoResultException; import javax.persistence.Query; import javax.persistence.Tuple; import javax.persistence.TypedQuery; import javax.persistence.criteria.*; import javax.persistence.metamodel.SingularAttribute; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Configurable(preConstruction=true,autowire=Autowire.BY_TYPE,dependencyCheck=true) public class SimpleQueryImpl<T> implements SimpleQuery<T> { private static final CLogger _logger = CLoggerImpl.getLogger(SimpleQueryImpl.class); private final Class<T> _entityClass; private Root<T> _root; private List<AttrInfo> _selects = new ArrayList<AttrInfo>(); private List<Condition> _conditions = new ArrayList<Condition>(); private List<OrderInfo> orderInfos = new ArrayList<OrderInfo>(); private SingularAttribute groupByInfo = null; private List<Path> _paths = new ArrayList<Path>(); private CriteriaQuery _query; private final CriteriaBuilder _builder; private Integer limit; private Integer start; @Autowired private DatabaseFacade _dbf; class Condition { private final SingularAttribute _attr; private final Op _op; private final Object[] _val; Condition (SingularAttribute attr, Op op, Object... val) { this._attr = attr; this._op = op; this._val = val; } } class OrderInfo { private final SingularAttribute attr; private final Od od; OrderInfo(SingularAttribute attr, Od od) { this.attr = attr; this.od = od; } } class AttrInfo { private final SingularAttribute _attr; private final Class<?> _javaType; AttrInfo(SingularAttribute attr, Class<?> type) { this._attr = attr; this._javaType = type; } } SimpleQueryImpl(Class<T> vo) { _entityClass = vo; _builder = _dbf.getCriteriaBuilder(); } @Override public SimpleQuery<T> select(SingularAttribute... attrs) { for (int i=0; i<attrs.length; i++) { _selects.add(new AttrInfo(attrs[i], attrs[i].getJavaType())); } return this; } @Override public SimpleQuery<T> add(SingularAttribute attr, Op op, Collection vals) { _conditions.add(new Condition(attr, op, vals.toArray(new Object[vals.size()]))); return this; } @Override public SimpleQuery<T> add(SingularAttribute attr, Op op, Object... val) { _conditions.add(new Condition(attr, op, val)); return this; } private Order[] orderClause() { ArrayList<Order> orders = new ArrayList<Order>(orderInfos.size()); Order[] orderArr = (Order[]) Array.newInstance(Order.class, orderInfos.size()); for (OrderInfo info : orderInfos) { if (info.od == Od.ASC) { orders.add(_builder.asc(_root.get(info.attr))); } else if (info.od == Od.DESC) { orders.add(_builder.desc(_root.get(info.attr))); } } return orders.toArray(orderArr); } private CriteriaQuery groupByClause(CriteriaQuery q) { assert _root != null : "You just set root before call groupby clause"; if (groupByInfo != null) { q.groupBy(_root.get(groupByInfo)); } return q; } private Predicate[] whereClause() { List<Predicate> preds = new ArrayList<Predicate>(_conditions.size()); for (Condition con : _conditions) { Op op = con._op; Path p = _root.get(con._attr); Object[] vals = con._val; if (op == Op.EQ) { assert vals.length == 1 : String.format("Op.EQ needs one value, but %s given", vals.length); preds.add(_builder.equal(p, vals[0])); } else if (op == Op.NOT_EQ) { assert vals.length == 1 : String.format("Op.NOT_EQ needs one value, but %s given", vals.length); preds.add(_builder.notEqual(p, vals[0])); } else if (op == Op.NOT_NULL) { preds.add(_builder.isNotNull(p)); } else if (op == Op.IN) { //preds.add(_builder.in(p.in(vals))); assert vals.length !=0 : String.format("Op.IN needs more than on value, but %s given", vals.length); preds.add(p.in(vals)); } else if (op == Op.NOT_IN) { assert vals.length !=0 : String.format("Op.NOT_IN needs more than on value, but %s given", vals.length); preds.add(_builder.not(p.in(vals))); } else if (op == Op.NULL) { preds.add(_builder.isNull(p)); } else if (op == Op.LIKE) { assert vals.length == 1 : String.format("Op.LIKE needs one value, but %s given", vals.length); preds.add(_builder.like(p, (String)vals[0])); } else if (op == Op.NOT_LIKE) { assert vals.length == 1 : String.format("Op.NOTLIKE needs one value, but %s given", vals.length); preds.add(_builder.notLike(p, (String)vals[0])); } else if (op == Op.GT) { assert vals.length == 1 : String.format("Op.GT needs one value, but %s given", vals.length); preds.add(_builder.greaterThan(p, (Comparable)vals[0])); } else if (op == Op.LT) { assert vals.length == 1 : String.format("Op.LT needs one value, but %s given", vals.length); preds.add(_builder.lessThan(p, (Comparable)vals[0])); } else if (op == Op.GTE) { assert vals.length == 1 : String.format("Op.GT_EQ needs one value, but %s given", vals.length); preds.add(_builder.greaterThanOrEqualTo(p, (Comparable)vals[0])); } else if (op == Op.LTE) { assert vals.length == 1 : String.format("Op.LT_EQ needs one value, but %s given", vals.length); preds.add(_builder.lessThanOrEqualTo(p, (Comparable)vals[0])); } else { assert(false) : op.toString() + " has not been supported"; } } Predicate[] predArray = (Predicate[]) Array.newInstance(Predicate.class, preds.size()); return preds.toArray(predArray); } private void done() { if (_selects.size() == 0) { _query = _builder.createQuery(_entityClass); } else if (_selects.size() == 1) { Class<?> selectType = _selects.get(0)._javaType; _query = _builder.createQuery(selectType); } else { _query = _builder.createTupleQuery(); } _root = _query.from(_entityClass); if (_selects.size() == 0) { } else if (_selects.size() == 1) { Path p = _root.get(_selects.get(0)._attr); _query.select(p); } else { for (AttrInfo info : _selects) { _paths.add(_root.get(info._attr)); } _query.multiselect(_paths); } _query.where(whereClause()); _query.orderBy(orderClause()); groupByClause(_query); } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public T find() { return _find(); } @Transactional T _find() { assert _selects.size() == 0 : "find() for entity doesn't need any parameter in Query.Select(), you have put some parameter in Query.select(..), either removing these parameters or using findValue() or findTuple()"; done(); T vo = null; try { Query q = _dbf.getEntityManager().createQuery(_query); if (limit != null) { q.setMaxResults(limit); } vo = (T)q.getSingleResult(); } catch (NoResultException e) { } catch (EmptyResultDataAccessException e) { } if (vo != null) { return vo; } else { return null; } } @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) @Override public <K> List<K> list() { return _list(); } @Transactional <K> List<K> _list() { assert _selects.size() == 0 : "list() for entities doesn't need any parameter in Query.Select(), you have put some parameter in Query.select(..), either removing these parameters or using listValue() or listTuple()"; done(); Query q = _dbf.getEntityManager().createQuery(_query); if (limit != null) { q.setMaxResults(limit); } if (start != null) { q.setFirstResult(start); } List<T> vos = q.getResultList(); List<K> ros = new ArrayList<K>(vos.size()); for (T vo : vos) { ros.add((K) vo); } return ros; } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public <K> K findValue() { return _findValue(); } @Transactional <K> K _findValue() { assert _selects.size() == 1 : String.format("findValue() only need one parameter in Query.Select(), you have put %s parameter in Query.select(..), either correcting the parameter or using find() or findTuple()", _selects.size()); done(); K value = null; try { Query q = _dbf.getEntityManager().createQuery(_query); if (limit != null) { q.setMaxResults(limit); } value = (K)q.getSingleResult(); } catch (NoResultException e) { } catch (EmptyResultDataAccessException e) { } return value; } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public <K> List<K> listValue() { return _listValue(); } @Transactional <K> List<K> _listValue() { assert _selects.size() == 1 : String.format("listValue() only need one parameter in Query.Select(), you have put %s parameter in Query.select(..), either correcting the parameter or using list() or listTuple()", _selects.size()); done(); Query q = _dbf.getEntityManager().createQuery(_query); if (limit != null) { q.setMaxResults(limit); } if (start != null) { q.setFirstResult(start); } List<K> vals = q.getResultList(); return vals; } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public Tuple findTuple() { return _findTuple(); } @Transactional Tuple _findTuple() { assert _selects.size() > 1 : String.format("findTuple() needs more than one parameter in Query.Select(), you have put %s parameter in Query.select(..), either correcting the parameter or using find() or findValue()", _selects.size()); done(); Tuple ret = null; try { Query q = _dbf.getEntityManager().createQuery(_query); if (limit != null) { q.setMaxResults(limit); } ret = (Tuple)q.getSingleResult(); } catch (NoResultException e) { } catch (EmptyResultDataAccessException e) { } return ret; } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public List<Tuple> listTuple() { return _listTuple(); } @Transactional List<Tuple> _listTuple() { assert _selects.size() > 1 : String.format("listTuple() needs more than one parameter in Query.Select(), you have put %s parameter in Query.select(..), either correcting the parameter or using list() or listValue()", _selects.size()); done(); Query q = _dbf.getEntityManager().createQuery(_query); if (limit != null) { q.setMaxResults(limit); } if (start != null) { q.setFirstResult(start); } List<Tuple> rets = q.getResultList(); return rets; } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public Long count() { return _count(); } @Transactional Long _count() { assert _selects.size() == 0 : "count() for entity doesn't need any parameter in Query.Select(), you have put some parameter in Query.select(..), either removing these parameters or using findValue() or findTuple()"; _query = _builder.createQuery(Long.class); _root = _query.from(_entityClass); _query.select(_builder.count(_root)); _query.where(whereClause()); return (Long) _dbf.getEntityManager().createQuery(_query).getSingleResult(); } @Override public SimpleQuery<T> orderBy(SingularAttribute attr, org.zstack.core.db.SimpleQuery.Od order) { orderInfos.add(new OrderInfo(attr, order)); return this; } @Override public SimpleQuery<T> groupBy(SingularAttribute attr) { this.groupByInfo = attr; return this; } @Override public SimpleQuery<T> isSoftDeleted(SingularAttribute attr) { return add(attr, Op.NULL); } @Override @Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW) public boolean isExists() { return _isExists(); } @Transactional boolean _isExists() { assert _selects.size() == 0 : "isExists() for entity doesn't need any parameter in Query.Select(), you have put some parameter in Query.select(..), either removing these parameters or using findValue() or findTuple()"; _query = _builder.createQuery(Long.class); _root = _query.from(_entityClass); _query.select(_builder.count(_root)); _query.where(whereClause()); TypedQuery<Long> tq = _dbf.getEntityManager().createQuery(_query); tq.setMaxResults(1); long count = tq.getSingleResult(); return count >= 1; } @Override public SimpleQuery<T> setLimit(int limit) { this.limit = limit; return this; } @Override public SimpleQuery<T> setStart(int start) { this.start = start; return this; } }