// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package com.cloud.utils.db; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.UUID; import com.cloud.utils.db.SearchCriteria.Op; /** * GenericQueryBuilder builds a search query during runtime. It allows the * search query to be built completely in Java rather than part SQL fragments * and part entity field like HQL or JPQL. This class is different from * GenericSearchBuilder in that it is used for building queries during runtime * where GenericSearchBuilder expects the query to be built during load time * and parameterized values to be set during runtime. * * GenericQueryBuilder allows results to be a native type, the entity bean, * and a composite type. If you are just retrieving the entity bean, there * is a simpler class called QueryBuilder that you can use. The usage * is approximately the same. * * <code> * // Note that in the following search, it selects a func COUNT to be the * // return result so for the second parameterized type is long. * // Note the entity object itself must have came from search and * // it uses the getters of the object to retrieve the field used in the search. * * GenericQueryBuilder<HostVO, Long> sc = GenericQueryBuilder.create(HostVO.class, Long.class); * HostVO entity = CountSearch.entity(); * sc.select(null, FUNC.COUNT, null, null).where(entity.getType(), Op.EQ, Host.Type.Routing); * sc.and(entity.getCreated(), Op.LT, new Date()); * Long count = sc.find(); * * </code> * * * @see GenericSearchBuilder * @see QueryBuilder * * @param <T> Entity object to perform the search on * @param <K> Result object */ public class GenericQueryBuilder<T, K> extends SearchBase<GenericQueryBuilder<T, K>, T, K> { final HashMap<String, Object[]> _params = new HashMap<String, Object[]>(); protected GenericQueryBuilder(Class<T> entityType, Class<K> resultType) { super(entityType, resultType); } /** * Creator method for GenericQueryBuilder. * @param entityType Entity to search on * @param resultType Result to return * @return GenericQueryBuilder */ @SuppressWarnings("unchecked") static public <T, K> GenericQueryBuilder<T, K> create(Class<T> entityType, Class<K> resultType) { GenericDao<T, ? extends Serializable> dao = (GenericDao<T, ? extends Serializable>)GenericDaoBase.getDao(entityType); assert dao != null : "Can not find DAO for " + entityType.getName(); return new GenericQueryBuilder<T, K>(entityType, resultType); } /** * Adds AND search condition * * @param field the field of the entity to perform the search on. * @param op operator * @param values parameterized values * @return this */ public GenericQueryBuilder<T, K> and(Object field, Op op, Object... values) { String uuid = UUID.randomUUID().toString(); constructCondition(uuid, " AND ", _specifiedAttrs.get(0), op); _params.put(uuid, values); return this; } /** * Adds OR search condition * * @param field the field of the entity to perform the search on. * @param op operator * @param values parameterized values * @return this */ public GenericQueryBuilder<T, K> or(Object field, Op op, Object... values) { String uuid = UUID.randomUUID().toString(); constructCondition(uuid, " OR ", _specifiedAttrs.get(0), op); _params.put(uuid, values); return this; } protected GenericQueryBuilder<T, K> left(Object field, Op op, Object... values) { String uuid = UUID.randomUUID().toString(); constructCondition(uuid, " ( ", _specifiedAttrs.get(0), op); _params.put(uuid, values); return this; } /** * Adds search condition that starts with an open parenthesis. Call cp() * to close the parenthesis. * * @param field the field of the entity to perform the search on. * @param op operator * @param values parameterized values * @return this */ public GenericQueryBuilder<T, K> op(Object field, Op op, Object... values) { return left(field, op, values); } /** * If the query is supposed to return a list, use this. * @return List of result objects */ @SuppressWarnings("unchecked") public List<K> list() { finalize(); if (isSelectAll()) { @SuppressWarnings("rawtypes") SearchCriteria sc1 = create(); return (List<K>)_dao.search(sc1, null); } else { SearchCriteria<K> sc1 = create(); return _dao.customSearch(sc1, null); } } /** * Creates a SearchCriteria to be used with dao objects. */ @Override public SearchCriteria<K> create() { SearchCriteria<K> sc = super.create(); sc.setParameters(_params); return sc; } private boolean isSelectAll() { return _selects == null || _selects.size() == 0; } /** * Convenience method to find the result so the result won't be a list. * @return result as specified. */ @SuppressWarnings("unchecked") public K find() { finalize(); if (isSelectAll()) { @SuppressWarnings("rawtypes") SearchCriteria sc1 = create(); return (K)_dao.findOneBy(sc1); } else { List<K> lst = list(); return lst.get(0); } } }