Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
de.greenrobot.dao.query.QueryBuilder Maven / Gradle / Ivy
/*
* Copyright (C) 2011-2013 Markus Junginger, greenrobot (http://greenrobot.de)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 de.greenrobot.dao.query;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.AbstractDaoSession;
import de.greenrobot.dao.DaoException;
import de.greenrobot.dao.DaoLog;
import de.greenrobot.dao.InternalQueryDaoAccess;
import de.greenrobot.dao.Property;
import de.greenrobot.dao.internal.SqlUtils;
import de.greenrobot.dao.query.WhereCondition.PropertyCondition;
/**
* Builds custom entity queries using constraints and parameters and without SQL (QueryBuilder creates SQL for you). To
* acquire an QueryBuilder, use {@link AbstractDao#queryBuilder()} or {@link AbstractDaoSession#queryBuilder(Class)}.
* Entity properties are referenced by Fields in the "Properties" inner class of the generated DAOs. This approach
* allows compile time checks and prevents typo errors occuring at build time.
*
* Example: Query for all users with the first name "Joe" ordered by their last name. (The class Properties is an inner
* class of UserDao and should be imported before.)
*
* List joes = dao.queryBuilder().where(Properties.FirstName.eq("Joe")).orderAsc(Properties.LastName).list();
*
*
* @author Markus
*
* @param
* Entity class to create an query for.
*/
public class QueryBuilder {
/** Set to true to debug the SQL. */
public static boolean LOG_SQL;
/** Set to see the given values. */
public static boolean LOG_VALUES;
private StringBuilder orderBuilder;
private StringBuilder joinBuilder;
private final List whereConditions;
private final List values;
private final AbstractDao dao;
private final String tablePrefix;
private Integer limit;
private Integer offset;
/** For internal use by greenDAO only. */
public static QueryBuilder internalCreate(AbstractDao dao) {
return new QueryBuilder(dao);
}
protected QueryBuilder(AbstractDao dao) {
this(dao, "T");
}
protected QueryBuilder(AbstractDao dao, String tablePrefix) {
this.dao = dao;
this.tablePrefix = tablePrefix;
values = new ArrayList();
whereConditions = new ArrayList();
}
private void checkOrderBuilder() {
if (orderBuilder == null) {
orderBuilder = new StringBuilder();
} else if (orderBuilder.length() > 0) {
orderBuilder.append(",");
}
}
/**
* Adds the given conditions to the where clause using an logical AND. To create new conditions, use the properties
* given in the generated dao classes.
*/
public QueryBuilder where(WhereCondition cond, WhereCondition... condMore) {
whereConditions.add(cond);
for (WhereCondition whereCondition : condMore) {
checkCondition(whereCondition);
whereConditions.add(whereCondition);
}
return this;
}
/**
* Adds the given conditions to the where clause using an logical OR. To create new conditions, use the properties
* given in the generated dao classes.
*/
public QueryBuilder whereOr(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) {
whereConditions.add(or(cond1, cond2, condMore));
return this;
}
/**
* Creates a WhereCondition by combining the given conditions using OR. The returned WhereCondition must be used
* inside {@link #where(WhereCondition, WhereCondition...)} or
* {@link #whereOr(WhereCondition, WhereCondition, WhereCondition...)}.
*/
public WhereCondition or(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) {
return combineWhereConditions(" OR ", cond1, cond2, condMore);
}
/**
* Creates a WhereCondition by combining the given conditions using AND. The returned WhereCondition must be used
* inside {@link #where(WhereCondition, WhereCondition...)} or
* {@link #whereOr(WhereCondition, WhereCondition, WhereCondition...)}.
*/
public WhereCondition and(WhereCondition cond1, WhereCondition cond2, WhereCondition... condMore) {
return combineWhereConditions(" AND ", cond1, cond2, condMore);
}
protected WhereCondition combineWhereConditions(String combineOp, WhereCondition cond1, WhereCondition cond2,
WhereCondition... condMore) {
StringBuilder builder = new StringBuilder("(");
List combinedValues = new ArrayList();
addCondition(builder, combinedValues, cond1);
builder.append(combineOp);
addCondition(builder, combinedValues, cond2);
for (WhereCondition cond : condMore) {
builder.append(combineOp);
addCondition(builder, combinedValues, cond);
}
builder.append(')');
return new WhereCondition.StringCondition(builder.toString(), combinedValues.toArray());
}
protected void addCondition(StringBuilder builder, List values, WhereCondition condition) {
checkCondition(condition);
condition.appendTo(builder, tablePrefix);
condition.appendValuesTo(values);
}
protected void checkCondition(WhereCondition whereCondition) {
if (whereCondition instanceof PropertyCondition) {
checkProperty(((PropertyCondition) whereCondition).property);
}
}
/** Not supported yet. */
public QueryBuilder join(Class entityClass, Property toOneProperty) {
throw new UnsupportedOperationException();
// return new QueryBuilder();
}
/** Not supported yet. */
public QueryBuilder joinToMany(Class entityClass, Property toManyProperty) {
throw new UnsupportedOperationException();
// @SuppressWarnings("unchecked")
// AbstractDao joinDao = (AbstractDao) dao.getSession().getDao(entityClass);
// return new QueryBuilder(joinDao, "TX");
}
/** Adds the given properties to the ORDER BY section using ascending order. */
public QueryBuilder orderAsc(Property... properties) {
orderAscOrDesc(" ASC", properties);
return this;
}
/** Adds the given properties to the ORDER BY section using descending order. */
public QueryBuilder orderDesc(Property... properties) {
orderAscOrDesc(" DESC", properties);
return this;
}
private void orderAscOrDesc(String ascOrDescWithLeadingSpace, Property... properties) {
for (Property property : properties) {
checkOrderBuilder();
append(orderBuilder, property);
if (String.class.equals(property.type)) {
orderBuilder.append(" COLLATE LOCALIZED");
}
orderBuilder.append(ascOrDescWithLeadingSpace);
}
}
/** Adds the given properties to the ORDER BY section using the given custom order. */
public QueryBuilder orderCustom(Property property, String customOrderForProperty) {
checkOrderBuilder();
append(orderBuilder, property).append(' ');
orderBuilder.append(customOrderForProperty);
return this;
}
/**
* Adds the given raw SQL string to the ORDER BY section. Do not use this for standard properties: ordedAsc and
* orderDesc are prefered.
*/
public QueryBuilder orderRaw(String rawOrder) {
checkOrderBuilder();
orderBuilder.append(rawOrder);
return this;
}
protected StringBuilder append(StringBuilder builder, Property property) {
checkProperty(property);
builder.append(tablePrefix).append('.').append('\'').append(property.columnName).append('\'');
return builder;
}
protected void checkProperty(Property property) {
if (dao != null) {
Property[] properties = dao.getProperties();
boolean found = false;
for (Property property2 : properties) {
if (property == property2) {
found = true;
break;
}
}
if (!found) {
throw new DaoException("Property '" + property.name + "' is not part of " + dao);
}
}
}
/** Limits the number of results returned by queries. */
public QueryBuilder limit(int limit) {
this.limit = limit;
return this;
}
/**
* Sets the offset for query results in combination with {@link #limit(int)}. The first {@code limit} results are
* skipped and the total number of results will be limited by {@code limit}. You cannot use offset without limit.
*/
public QueryBuilder offset(int offset) {
this.offset = offset;
return this;
}
/**
* Builds a reusable query object (Query objects can be executed more efficiently than creating a QueryBuilder for
* each execution.
*/
public Query build() {
String select;
if (joinBuilder == null || joinBuilder.length() == 0) {
select = InternalQueryDaoAccess.getStatements(dao).getSelectAll();
} else {
select = SqlUtils.createSqlSelect(dao.getTablename(), tablePrefix, dao.getAllColumns());
}
StringBuilder builder = new StringBuilder(select);
appendWhereClause(builder, tablePrefix);
if (orderBuilder != null && orderBuilder.length() > 0) {
builder.append(" ORDER BY ").append(orderBuilder);
}
int limitPosition = -1;
if (limit != null) {
builder.append(" LIMIT ?");
values.add(limit);
limitPosition = values.size() - 1;
}
int offsetPosition = -1;
if (offset != null) {
if (limit == null) {
throw new IllegalStateException("Offset cannot be set without limit");
}
builder.append(" OFFSET ?");
values.add(offset);
offsetPosition = values.size() - 1;
}
String sql = builder.toString();
if (LOG_SQL) {
DaoLog.d("Built SQL for query: " + sql);
}
if (LOG_VALUES) {
DaoLog.d("Values for query: " + values);
}
return Query.create(dao, sql, values.toArray(), limitPosition, offsetPosition);
}
/**
* Builds a reusable query object for deletion (Query objects can be executed more efficiently than creating a
* QueryBuilder for each execution.
*/
public DeleteQuery buildDelete() {
String tablename = dao.getTablename();
String baseSql = SqlUtils.createSqlDelete(tablename, null);
StringBuilder builder = new StringBuilder(baseSql);
appendWhereClause(builder, tablename);
String sql = builder.toString();
if (LOG_SQL) {
DaoLog.d("Built SQL for delete query: " + sql);
}
if (LOG_VALUES) {
DaoLog.d("Values for delete query: " + values);
}
return DeleteQuery.create(dao, sql, values.toArray());
}
/**
* Builds a reusable query object for counting rows (Query objects can be executed more efficiently than creating a
* QueryBuilder for each execution.
*/
public CountQuery buildCount() {
String tablename = dao.getTablename();
String baseSql = SqlUtils.createSqlSelectCountStar(tablename);
StringBuilder builder = new StringBuilder(baseSql);
appendWhereClause(builder, tablename);
String sql = builder.toString();
if (LOG_SQL) {
DaoLog.d("Built SQL for count query: " + sql);
}
if (LOG_VALUES) {
DaoLog.d("Values for count query: " + values);
}
return CountQuery.create(dao, sql, values.toArray());
}
private void appendWhereClause(StringBuilder builder, String tablePrefixOrNull) {
values.clear();
if (!whereConditions.isEmpty()) {
builder.append(" WHERE ");
ListIterator iter = whereConditions.listIterator();
while (iter.hasNext()) {
if (iter.hasPrevious()) {
builder.append(" AND ");
}
WhereCondition condition = iter.next();
condition.appendTo(builder, tablePrefixOrNull);
condition.appendValuesTo(values);
}
}
}
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#list() list()}; see {@link Query#list()} for
* details. To execute a query more than once, you should build the query and keep the {@link Query} object for
* efficiency reasons.
*/
public List list() {
return build().list();
}
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#listLazy() listLazy()}; see
* {@link Query#listLazy()} for details. To execute a query more than once, you should build the query and keep the
* {@link Query} object for efficiency reasons.
*/
public LazyList listLazy() {
return build().listLazy();
}
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#listLazyUncached() listLazyUncached()}; see
* {@link Query#listLazyUncached()} for details. To execute a query more than once, you should build the query and
* keep the {@link Query} object for efficiency reasons.
*/
public LazyList listLazyUncached() {
return build().listLazyUncached();
}
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#listIterator() listIterator()}; see
* {@link Query#listIterator()} for details. To execute a query more than once, you should build the query and keep
* the {@link Query} object for efficiency reasons.
*/
public CloseableListIterator listIterator() {
return build().listIterator();
}
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#unique() unique()}; see {@link Query#unique()}
* for details. To execute a query more than once, you should build the query and keep the {@link Query} object for
* efficiency reasons.
*/
public T unique() {
return build().unique();
}
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#uniqueOrThrow() uniqueOrThrow()}; see
* {@link Query#uniqueOrThrow()} for details. To execute a query more than once, you should build the query and keep
* the {@link Query} object for efficiency reasons.
*/
public T uniqueOrThrow() {
return build().uniqueOrThrow();
}
/**
* Shorthand for {@link QueryBuilder#buildCount() buildCount()}.{@link CountQuery#count() count()}; see
* {@link CountQuery#count()} for details. To execute a query more than once, you should build the query and keep
* the {@link CountQuery} object for efficiency reasons.
*/
public long count() {
return buildCount().count();
}
}