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

com.j256.ormlite.stmt.Where Maven / Gradle / Ivy

package com.j256.ormlite.stmt;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.field.FieldType;
import com.j256.ormlite.stmt.query.And;
import com.j256.ormlite.stmt.query.Between;
import com.j256.ormlite.stmt.query.Clause;
import com.j256.ormlite.stmt.query.Eq;
import com.j256.ormlite.stmt.query.Ge;
import com.j256.ormlite.stmt.query.Gt;
import com.j256.ormlite.stmt.query.In;
import com.j256.ormlite.stmt.query.IsNotNull;
import com.j256.ormlite.stmt.query.IsNull;
import com.j256.ormlite.stmt.query.Le;
import com.j256.ormlite.stmt.query.Like;
import com.j256.ormlite.stmt.query.Lt;
import com.j256.ormlite.stmt.query.Ne;
import com.j256.ormlite.stmt.query.NeedsFutureClause;
import com.j256.ormlite.stmt.query.Not;
import com.j256.ormlite.stmt.query.Or;
import com.j256.ormlite.table.TableInfo;

/**
 * Manages the various clauses that make up the WHERE part of a SQL statement. You get one of these when you call
 * {@link QueryBuilder#where} or you can set the where clause by calling {@link QueryBuilder#setWhere}.
 * 
 * 

* Here's a page with a good tutorial of SQL commands. *

* *

* To create a query which looks up an account by name and password you would do the following: *

* *
* *
 * QueryBuilder<Account, String> qb = accountDao.queryBuilder();
 * Where where = qb.where();
 * // the name field must be equal to "foo"
 * where.eq(Account.NAME_FIELD_NAME, "foo");
 * // and
 * where.and();
 * // the password field must be equal to "_secret"
 * where.eq(Account.PASSWORD_FIELD_NAME, "_secret");
 * PreparedQuery<Account, String> preparedQuery = qb.prepareQuery();
 * 
* *
* *

* In this example, the SQL query that will be generated will be approximately: *

* *
* *
 * SELECT * FROM account WHERE (name = 'foo' AND passwd = '_secret')
 * 
* *
* *

* If you'd rather chain the methods onto one line (like StringBuilder), this can also be written as: *

* *
* *
 * queryBuilder.where().eq(Account.NAME_FIELD_NAME, "foo").and().eq(Account.PASSWORD_FIELD_NAME, "_secret");
 * 
* *
* *

* If you'd rather use parens and the like then you can call: *

* *
* *
 * Where where = queryBuilder.where();
 * where.and(where.eq(Account.NAME_FIELD_NAME, "foo"), where.eq(Account.PASSWORD_FIELD_NAME, "_secret"));
 * 
* *
* *

* All three of the above call formats produce the same SQL. For complex queries that mix ANDs and ORs, the last format * will be necessary to get the grouping correct. For example, here's a complex query: *

* *
* *
 * Where where = queryBuilder.where();
 * where.or(where.and(where.eq(Account.NAME_FIELD_NAME, "foo"), where.eq(Account.PASSWORD_FIELD_NAME, "_secret")),
 * 		where.and(where.eq(Account.NAME_FIELD_NAME, "bar"), where.eq(Account.PASSWORD_FIELD_NAME, "qwerty")));
 * 
* *
* *

* This produces the following approximate SQL: *

* *
* *
 * SELECT * FROM account WHERE ((name = 'foo' AND passwd = '_secret') OR (name = 'bar' AND passwd = 'qwerty'))
 * 
* *
* * @author graywatson */ public class Where { private SimpleStack clauseList = new SimpleStack(); private NeedsFutureClause needsFuture = null; private final TableInfo tableInfo; Where(TableInfo tableInfo) { // limit the constructor scope this.tableInfo = tableInfo; } /** * AND operation which takes the previous clause and the next clause and AND's them together. */ public Where and() { addNeedsFuture(new And(removeLastClause("AND"))); return this; } /** * AND operation which takes 2 arguments and AND's them together. */ public Where and(Where left, Where right) { Clause rightClause = removeLastClause("AND"); Clause leftClause = removeLastClause("AND"); addClause(new And(leftClause, rightClause)); return this; } /** * Add a BETWEEN clause so the column must be between the low and high parameters. */ public Where between(String columnName, Object low, Object high) throws SQLException { addClause(new Between(columnName, checkIfColumnIsNumber(columnName), low, high)); return this; } /** * Add a '=' clause so the column must be equal to the value. */ public Where eq(String columnName, Object value) throws SQLException { addClause(new Eq(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Add a '>=' clause so the column must be greater-than or equals-to the value. */ public Where ge(String columnName, Object value) throws SQLException { addClause(new Ge(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Add a '>' clause so the column must be greater-than the value. */ public Where gt(String columnName, Object value) throws SQLException { addClause(new Gt(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Add a IN clause so the column must be equal-to one of the objects from the list passed in. */ public Where in(String columnName, Iterable objects) throws SQLException { addClause(new In(columnName, checkIfColumnIsNumber(columnName), objects)); return this; } /** * Add a IN clause so the column must be equal-to one of the objects passed in. */ public Where in(String columnName, Object... objects) throws SQLException { addClause(new In(columnName, checkIfColumnIsNumber(columnName), objects)); return this; } /** * Add a 'IS NULL' clause so the column must be null. '=' NULL does not work. */ public Where isNull(String columnName) throws SQLException { addClause(new IsNull(columnName, checkIfColumnIsNumber(columnName))); return this; } /** * Add a 'IS NOT NULL' clause so the column must not be null. '<>' NULL does not work. */ public Where isNotNull(String columnName) throws SQLException { addClause(new IsNotNull(columnName, checkIfColumnIsNumber(columnName))); return this; } /** * Add a '<=' clause so the column must be less-than or equals-to the value. */ public Where le(String columnName, Object value) throws SQLException { addClause(new Le(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Add a '<' clause so the column must be less-than the value. */ public Where lt(String columnName, Object value) throws SQLException { addClause(new Lt(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Add a LIKE clause so the column must be like the value (where you can specify '%' patterns. */ public Where like(String columnName, Object value) throws SQLException { addClause(new Like(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Add a '<>' clause so the column must be not-equal-to the value. */ public Where ne(String columnName, Object value) throws SQLException { addClause(new Ne(columnName, checkIfColumnIsNumber(columnName), value)); return this; } /** * Used to NOT the next clause specified. */ public Where not() { addNeedsFuture(new Not()); return this; } /** * Used to NOT the argument clause specified. */ public Where not(Where comparison) { addClause(new Not(removeLastClause("NOT"))); return this; } /** * OR operation which takes the previous clause and the next clause and OR's them together. */ public Where or() { addNeedsFuture(new Or(removeLastClause("OR"))); return this; } /** * OR operation which takes 2 arguments and OR's them together. */ public Where or(Where left, Where right) { Clause rightClause = removeLastClause("OR"); Clause leftClause = removeLastClause("OR"); addClause(new Or(leftClause, rightClause)); return this; } /** * Used by the internal classes to add the where SQL to the {@link StringBuilder}. */ void appendSql(DatabaseType databaseType, StringBuilder sb, List columnArgList) { if (clauseList.isEmpty()) { throw new IllegalStateException("No where clauses defined. Did you miss a where operation?"); } if (clauseList.size() != 1) { throw new IllegalStateException( "Both the \"left-hand\" and \"right-hand\" clauses have been defined. Did you miss an AND or OR?"); } // we don't pop here because we may want to run the query multiple times clauseList.peek().appendSql(databaseType, sb, columnArgList); } private void addNeedsFuture(NeedsFutureClause needsFuture) { if (this.needsFuture == null) { this.needsFuture = needsFuture; addClause(needsFuture); } else { throw new IllegalStateException(this.needsFuture + " is already waiting for a future clause, can't add: " + needsFuture); } } private void addClause(Clause clause) { if (needsFuture == null || clause == needsFuture) { clauseList.push(clause); } else { // we have a binary statement which was called before the right clause was defined needsFuture.setMissingClause(clause); needsFuture = null; } } private Clause removeLastClause(String label) { if (clauseList.isEmpty()) { throw new IllegalStateException("Expecting there to be a clause already defined for '" + label + "' operation"); } else { return clauseList.pop(); } } @Override public String toString() { if (clauseList.isEmpty()) { return "empty where clause"; } else { Clause clause = clauseList.peek(); return "where clause: " + clause; } } private boolean checkIfColumnIsNumber(String columnName) throws SQLException { FieldType fieldType = tableInfo.nameToFieldType(columnName); if (fieldType == null) { throw new SQLException("Unknown column name '" + columnName + "' in table " + tableInfo.getTableName()); } else { return fieldType.isNumber(); } } /** * Little inner class to provide stack features. The java.util.Stack extends Vector which is synchronized. */ private class SimpleStack extends ArrayList { private static final long serialVersionUID = -8116427380277806666L; public void push(T obj) { add(obj); } public T pop() { return remove(size() - 1); } public T peek() { return get(size() - 1); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy