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

org.mentabean.jdbc.QueryBuilder Maven / Gradle / Ivy

There is a newer version: 2.2.4
Show newest version
package org.mentabean.jdbc;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.mentabean.BeanConfig;
import org.mentabean.BeanException;
import org.mentabean.DBField;
import org.mentabean.sql.Condition;
import org.mentabean.sql.Function;
import org.mentabean.sql.HasParams;
import org.mentabean.sql.Sentence;
import org.mentabean.sql.param.Param;
import org.mentabean.sql.param.ParamField;
import org.mentabean.sql.param.ParamFunction;
import org.mentabean.sql.param.ParamValue;
import org.mentabean.util.PropertiesProxy;
import org.mentabean.util.SQLUtils;

/**
 * Fluent QueryBuilder useful to create SQL queries
 * 
 * @author margel
 * @author erico
 *
 */
public class QueryBuilder {

	private StringBuilder sb = new StringBuilder();
	private final AnsiSQLBeanSession session;
	private List paramValues = new ArrayList();
	private List> aliases = new ArrayList>();
	private Map sentences = new HashMap();
	private Alias aliasFrom;
	private int parenthesis = 0;
	
	private boolean clauseIf;

	protected QueryBuilder(final AnsiSQLBeanSession session) {
		this.session = session;
	}
	
	/**
	 * Builds an initial SELECT statement with given aliases
	 * @param as - Alias(es) with properties that will be retrieved
	 * @return A new Select object
	 */
	public Select select(Alias... as){

		return new Select(as);
	}
	
	/**
	 * Builds an initial SELECT alias FROM alias statement.
* Same as select(alias).from(alias) * @param as - Alias with properties that will be retrieved * @return A new From object * @see #select(Alias...) * @see Select#from(Alias) */ public From selectFrom(Alias as){ return select(as).from(as); } /** * Builds an initial SELECT statement with given sentences * @param sentences - Sentence(s) to insert in SELECT clause * @return A new Select object */ public Select select(Sentence... sentences){ return new Select(sentences); } /** * Creates a new QueryBuilder with the same session. It's useful to build sub-queries from the main query * @return The new QueryBuilder instance */ public QueryBuilder subQuery() { return new QueryBuilder(session); } /** * Creates an alias to be used in this QueryBuilder * * @param clazz - Bean class that will be mapped to a BeanConfig * @param alias - String indicating the alia's name * @return - The alias object */ public Alias aliasTo(Class clazz, String alias){ return new Alias(clazz, alias); } /** * Creates an alias to be used in this QueryBuilder. The alia's name is the simple name of the bean class * * @param clazz - Bean class that will be mapped to a BeanConfig * @return - The alias object */ public Alias aliasTo(Class clazz){ return new Alias(clazz, clazz.getSimpleName().toLowerCase()); } private void appendTable(Alias a) { sb.append(a.config.getTableName()).append(' ').append(a.aliasStr); } private void append(Param param) { if (param != null) sb.append(param.paramInQuery()); add(param); } private void add(HasParams hasParams) { Param[] params = hasParams.getParams(); if (params != null) { for (Param param : params) { add(param); } } } private void applyRegex() { remove("(AND|OR|\\([\\s]*?\\)|WHERE|HAVING)[\\s]*?$"); } private void remove(String regex) { sb = new StringBuilder(sb.toString().replaceAll(regex, "")); } private void addAnd() { if (clauseIf) { applyRegex(); sb.append(" AND "); } } private void addOr() { if (clauseIf) { applyRegex(); sb.append(" OR "); } } private void add(Param param) { if (param != null && param.values() != null && param.values().length > 0) { for (Object value : param.values()) { if (value != null) { paramValues.add(value); } } } } private void openPar() { parenthesis++; sb.append('('); } private void closePar() { applyRegex(); parenthesis--; sb.append(')'); applyRegex(); } public class Alias { private BeanConfig config; private String aliasStr; private T pxy; private String[] returns; private String[] returnMinus; private Map joined = new HashMap(); private Alias(Class clazz, String aliasStr) { this.aliasStr = aliasStr; this.config = session.getConfigFor(clazz); if (this.config == null) throw new BeanException("BeanConfig not found for "+clazz); } /** * Defines the properties that will return. In other words, only these properties will be populated * @param returns */ public void setReturns(Object... returns){ this.returns = AnsiSQLBeanSession.getProperties(returns); } /** * Defines the properties that will NOT return. In other words, these properties will be not populated * @param returns */ public void setReturnMinus(Object... returns){ this.returnMinus = AnsiSQLBeanSession.getProperties(returns); } /** * Returns a proxy for bean class * @return The proxy */ @SuppressWarnings("unchecked") public T pxy(){ if (pxy == null) { this.pxy = (T) PropertiesProxy.create(config.getBeanClass()); } return pxy; } /** * Convert the given property to database column * @param property - The bean property (can be through proxy) * @return The database column name */ public String toColumn(Object property){ return session.propertyToColumn(config.getBeanClass(), property, aliasStr); } /** * Populates the bean according to ResultSet * @param rs * @param bean */ public void populateBean(ResultSet rs, T bean){ session.populateBeanImpl(rs, bean, aliasStr, returns, returnMinus, false); } public void populateAll(ResultSet rs, T bean) { populateBean(rs, bean); for (Map.Entry m : joined.entrySet()) { //only if alias is in SELECT clause if (aliases.contains(m.getValue())) { Object value = session.getPropertyBean(bean, m.getKey().property, m.getKey().forceInstance); if (value != null) { m.getValue().populateAll(rs, value); } } } } @Override public String toString() { return "Alias "+aliasStr+" of "+config.getBeanClass(); } /** * Configures a property name to receive data from alias. When forceIntance is true * it will force the creation of a new instance for the property, in other words, * the value will never be null * @param property - Bean property to populate * @param forceInstance * @param alias - Alias */ private void put(Object property, boolean forceInstance, Alias alias) { joined.put(new Key().property(property).forceInstance(forceInstance), alias); } private class Key { private String property; private boolean forceInstance; public Key property(Object property) { this.property = AnsiSQLBeanSession.getProperties(new Object[] {property})[0]; return this; } public Key forceInstance(boolean force) { this.forceInstance = force; return this; } } } public class Select implements Appendable