0001: /*
0002: * Copyright 2001-2007 Geert Bevin <gbevin[remove] at uwyn dot com>
0003: * Distributed under the terms of either:
0004: * - the common development and distribution license (CDDL), v1.0; or
0005: * - the GNU Lesser General Public License, v2.1 or later
0006: * $Id: Select.java 3648 2007-01-29 12:16:16Z gbevin $
0007: */
0008: package com.uwyn.rife.database.queries;
0009:
0010: import java.util.*;
0011:
0012: import com.uwyn.rife.database.Datasource;
0013: import com.uwyn.rife.database.capabilities.Capabilities;
0014: import com.uwyn.rife.database.capabilities.Capability;
0015: import com.uwyn.rife.database.exceptions.DbQueryException;
0016: import com.uwyn.rife.database.exceptions.TableNameOrFieldsRequiredException;
0017: import com.uwyn.rife.database.exceptions.UnsupportedSqlFeatureException;
0018: import com.uwyn.rife.datastructures.EnumClass;
0019: import com.uwyn.rife.site.Constrained;
0020: import com.uwyn.rife.site.ConstrainedBean;
0021: import com.uwyn.rife.site.ConstrainedUtils;
0022: import com.uwyn.rife.template.Template;
0023: import com.uwyn.rife.template.TemplateFactory;
0024: import com.uwyn.rife.tools.StringUtils;
0025:
0026: /**
0027: * Object representation of a SQL "SELECT" query.
0028: *
0029: * <p>This object may be used to dynamically construct a SQL statement in a
0030: * database-independent fashion. After it is finished, it may be executed using
0031: * one of the query methods on {@link com.uwyn.rife.database.DbQueryManager
0032: * DbQueryManager}.
0033: *
0034: * @author Geert Bevin (gbevin[remove] at uwyn dot com)
0035: * @author Steven Grimm (koreth[remove] at midwinter dot com)
0036: * @version $Revision: 3648 $
0037: * @since 1.0
0038: */
0039: public class Select extends AbstractWhereQuery<Select> implements
0040: Cloneable, ReadQuery {
0041: private String mHint = null;
0042: private List<String> mFields = null;
0043: private String mFrom = null;
0044: private List<Join> mJoins = null;
0045: private List<String> mGroupBy = null;
0046: private List<String> mHaving = null;
0047: private boolean mDistinct = false;
0048: private List<String> mDistinctOn = null;
0049: private List<Union> mUnions = null;
0050: private List<OrderBy> mOrderBy = null;
0051: private int mLimit = -1;
0052: private int mOffset = -1;
0053:
0054: private Capabilities mCapabilities = null;
0055:
0056: private Class mConstrainedClass = null;
0057:
0058: public static final JoinCondition NATURAL = new JoinCondition(
0059: "NATURAL");
0060: public static final JoinCondition ON = new JoinCondition("ON");
0061: public static final JoinCondition USING = new JoinCondition("USING");
0062:
0063: public static final JoinType LEFT = new JoinType("LEFT");
0064: public static final JoinType RIGHT = new JoinType("RIGHT");
0065: public static final JoinType FULL = new JoinType("FULL");
0066:
0067: public static final OrderByDirection ASC = new OrderByDirection(
0068: "ASC");
0069: public static final OrderByDirection DESC = new OrderByDirection(
0070: "DESC");
0071:
0072: public Select(Datasource datasource) {
0073: this (datasource, null);
0074: }
0075:
0076: public Select(Datasource datasource, Class constrainedClass) {
0077: super (datasource);
0078:
0079: if (null == datasource)
0080: throw new IllegalArgumentException(
0081: "datasource can't be null.");
0082:
0083: mConstrainedClass = constrainedClass;
0084:
0085: clear();
0086: }
0087:
0088: public void clear() {
0089: super .clear();
0090:
0091: mHint = null;
0092: mFields = new ArrayList<String>();
0093: mFrom = null;
0094: mJoins = new ArrayList<Join>();
0095: mGroupBy = new ArrayList<String>();
0096: mHaving = new ArrayList<String>();
0097: mDistinct = false;
0098: mDistinctOn = new ArrayList<String>();
0099: mUnions = new ArrayList<Union>();
0100: mOrderBy = new ArrayList<OrderBy>();
0101: mLimit = -1;
0102: mOffset = -1;
0103: mCapabilities = null;
0104:
0105: assert 0 == mFields.size();
0106: assert 0 == mJoins.size();
0107: assert 0 == mGroupBy.size();
0108: assert 0 == mHaving.size();
0109: assert 0 == mDistinctOn.size();
0110: assert 0 == mOrderBy.size();
0111: }
0112:
0113: public void clearGenerated() {
0114: super .clearGenerated();
0115:
0116: mCapabilities = null;
0117: }
0118:
0119: public String getHint() {
0120: return mHint;
0121: }
0122:
0123: public Collection<String> getFields() {
0124: return mFields;
0125: }
0126:
0127: public boolean isDistinct() {
0128: return mDistinct;
0129: }
0130:
0131: public Collection<String> getDistinctOn() {
0132: return mDistinctOn;
0133: }
0134:
0135: public String getFrom() {
0136: return mFrom;
0137: }
0138:
0139: public Collection<Join> getJoins() {
0140: return mJoins;
0141: }
0142:
0143: public Collection<String> getGroupBy() {
0144: return mGroupBy;
0145: }
0146:
0147: public Collection<String> getHaving() {
0148: return mHaving;
0149: }
0150:
0151: public Collection<Union> getUnions() {
0152: return mUnions;
0153: }
0154:
0155: public Collection<OrderBy> getOrderBy() {
0156: return mOrderBy;
0157: }
0158:
0159: public int getLimit() {
0160: return mLimit;
0161: }
0162:
0163: public int getOffset() {
0164: return mOffset;
0165: }
0166:
0167: protected Template getTemplate() {
0168: return TemplateFactory.SQL.get("sql."
0169: + StringUtils.encodeClassname(mDatasource
0170: .getAliasedDriver()) + ".select");
0171: }
0172:
0173: public Capabilities getCapabilities() {
0174: if (null == mCapabilities) {
0175: Capabilities capabilities = null;
0176:
0177: if (getLimit() != -1) {
0178: if (null == capabilities) {
0179: capabilities = new Capabilities();
0180: }
0181:
0182: capabilities.put(Capability.LIMIT, getLimit());
0183: }
0184:
0185: if (getLimitParameter() != null) {
0186: if (null == capabilities) {
0187: capabilities = new Capabilities();
0188: }
0189:
0190: capabilities.put(Capability.LIMIT_PARAMETER,
0191: getLimitParameter());
0192: }
0193:
0194: if (getOffset() != -1) {
0195: if (null == capabilities) {
0196: capabilities = new Capabilities();
0197: }
0198:
0199: capabilities.put(Capability.OFFSET, getOffset());
0200: }
0201:
0202: if (getOffsetParameter() != null) {
0203: if (null == capabilities) {
0204: capabilities = new Capabilities();
0205: }
0206:
0207: capabilities.put(Capability.OFFSET_PARAMETER,
0208: getOffsetParameter());
0209: }
0210:
0211: mCapabilities = capabilities;
0212: }
0213:
0214: return mCapabilities;
0215: }
0216:
0217: public String getSql() throws DbQueryException {
0218: Constrained constrained = ConstrainedUtils
0219: .getConstrainedInstance(mConstrainedClass);
0220:
0221: // handle constrained beans meta-data that needs to be handled after all the
0222: // rest
0223: if (constrained != null) {
0224: ConstrainedBean constrained_bean = constrained
0225: .getConstrainedBean();
0226: if (constrained_bean != null) {
0227: // handle default ordering if no order statements have been
0228: // defined yet
0229: if (constrained_bean.hasDefaultOrdering()
0230: && 0 == mOrderBy.size()) {
0231: Iterator<ConstrainedBean.Order> ordering_it = constrained_bean
0232: .getDefaultOrdering().iterator();
0233: ConstrainedBean.Order order = null;
0234: while (ordering_it.hasNext()) {
0235: order = ordering_it.next();
0236: orderBy(order.getPropertyName(),
0237: OrderByDirection.getDirection(order
0238: .getDirection().toString()));
0239: }
0240: }
0241: }
0242: }
0243:
0244: if (null == mFrom && 0 == mFields.size()) {
0245: throw new TableNameOrFieldsRequiredException("Select");
0246: } else {
0247: if (null == mSql) {
0248: Template template = getTemplate();
0249: String block = null;
0250:
0251: if (mHint != null) {
0252: if (!template.hasValueId("HINT")) {
0253: throw new UnsupportedSqlFeatureException(
0254: "HINT", mDatasource.getAliasedDriver());
0255: }
0256: template.setValue("EXPRESSION", mHint);
0257: template.setBlock("HINT", "HINT");
0258: }
0259:
0260: if (mDistinct) {
0261: if (0 == mDistinctOn.size()) {
0262: block = template.getBlock("DISTINCT");
0263: if (0 == block.length()) {
0264: throw new UnsupportedSqlFeatureException(
0265: "DISTINCT", mDatasource
0266: .getAliasedDriver());
0267: }
0268: template.setValue("DISTINCT", block);
0269: } else {
0270: if (template.hasValueId("COLUMNS")) {
0271: template.setValue("COLUMNS", StringUtils
0272: .join(mDistinctOn, template
0273: .getBlock("SEPERATOR")));
0274: }
0275: block = template.getBlock("DISTINCTON");
0276: if (0 == block.length()) {
0277: throw new UnsupportedSqlFeatureException(
0278: "DISTINCT ON", mDatasource
0279: .getAliasedDriver());
0280: }
0281: template.setValue("DISTINCT", block);
0282: }
0283: }
0284:
0285: if (0 == mFields.size()) {
0286: template.setValue("FIELDS", template
0287: .getBlock("ALLFIELDS"));
0288: } else {
0289: template.setValue("FIELDS", StringUtils.join(
0290: mFields, template.getBlock("SEPERATOR")));
0291: }
0292:
0293: if (null != mFrom) {
0294: template.setValue("TABLE", mFrom);
0295: block = template.getBlock("FROM");
0296: if (0 == block.length()) {
0297: throw new UnsupportedSqlFeatureException(
0298: "FROM", mDatasource.getAliasedDriver());
0299: }
0300: template.setValue("FROM", block);
0301: }
0302:
0303: if (mJoins.size() > 0) {
0304: ArrayList<String> join_list = new ArrayList<String>();
0305: for (Join join : mJoins) {
0306: join_list.add(join.getSql(template));
0307: }
0308: template.setValue("JOINS", StringUtils.join(
0309: join_list, ""));
0310: }
0311:
0312: if (mWhere.length() > 0) {
0313: template.setValue("CONDITION", mWhere);
0314: block = template.getBlock("WHERE");
0315: if (0 == block.length()) {
0316: throw new UnsupportedSqlFeatureException(
0317: "WHERE", mDatasource.getAliasedDriver());
0318: }
0319: template.setValue("WHERE", block);
0320: }
0321:
0322: if (mGroupBy.size() > 0) {
0323: template.setValue("EXPRESSION", StringUtils.join(
0324: mGroupBy, template.getBlock("SEPERATOR")));
0325: block = template.getBlock("GROUPBY");
0326: if (0 == block.length()) {
0327: throw new UnsupportedSqlFeatureException(
0328: "GROUP BY", mDatasource
0329: .getAliasedDriver());
0330: }
0331: template.setValue("GROUPBY", block);
0332: }
0333:
0334: if (mHaving.size() > 0) {
0335: template.setValue("EXPRESSION", StringUtils.join(
0336: mHaving, template.getBlock("SEPERATOR")));
0337: block = template.getBlock("HAVING");
0338: if (0 == block.length()) {
0339: throw new UnsupportedSqlFeatureException(
0340: "HAVING", mDatasource
0341: .getAliasedDriver());
0342: }
0343: template.setValue("HAVING", block);
0344: }
0345:
0346: if (mUnions != null) {
0347: for (Union union : mUnions) {
0348: template.setValue("EXPRESSION", union
0349: .getExpression());
0350: if (union.isAll()) {
0351: block = template.getBlock("UNION_ALL");
0352: if (0 == block.length()) {
0353: throw new UnsupportedSqlFeatureException(
0354: "UNION_ALL", mDatasource
0355: .getAliasedDriver());
0356: }
0357: template.appendBlock("UNION", "UNION_ALL");
0358: } else {
0359: block = template.getBlock("UNION");
0360: if (0 == block.length()) {
0361: throw new UnsupportedSqlFeatureException(
0362: "UNION", mDatasource
0363: .getAliasedDriver());
0364: }
0365: template.appendBlock("UNION", "UNION");
0366: }
0367: }
0368: }
0369:
0370: if (mOrderBy.size() > 0) {
0371: ArrayList<String> orderby_list = new ArrayList<String>();
0372: for (OrderBy order_by : mOrderBy) {
0373: orderby_list.add(order_by.getSql(template));
0374: }
0375: template.setValue("ORDERBY_PARTS", StringUtils
0376: .join(orderby_list, template
0377: .getBlock("SEPERATOR")));
0378: block = template.getBlock("ORDERBY");
0379: if (0 == block.length()) {
0380: throw new UnsupportedSqlFeatureException(
0381: "ORDER BY", mDatasource
0382: .getAliasedDriver());
0383: }
0384: template.setValue("ORDERBY", block);
0385: }
0386:
0387: if (mLimit != -1 || getLimitParameter() != null) {
0388: // integrate a default value for offset if that has been provided
0389: // by the template
0390: if (-1 == mOffset
0391: && template.hasValueId("OFFSET_VALUE")) {
0392: String offset_value = template
0393: .getValue("OFFSET_VALUE");
0394: if (offset_value != null
0395: && offset_value.trim().length() > 0) {
0396: mOffset = Integer.parseInt(offset_value);
0397: }
0398: }
0399:
0400: if (mOffset > -1 || getOffsetParameter() != null) {
0401: if (template.hasValueId("OFFSET_VALUE")) {
0402: if (getOffsetParameter() != null) {
0403: template.setValue("OFFSET_VALUE", "?");
0404: } else {
0405: template.setValue("OFFSET_VALUE",
0406: mOffset);
0407: }
0408: }
0409:
0410: block = template.getBlock("OFFSET");
0411: if (0 == block.length()) {
0412: if (!mExcludeUnsupportedCapabilities) {
0413: throw new UnsupportedSqlFeatureException(
0414: "OFFSET", mDatasource
0415: .getAliasedDriver());
0416: }
0417: } else {
0418: template.setValue("OFFSET", block);
0419: }
0420: }
0421:
0422: if (template.hasValueId("LIMIT_VALUE")) {
0423: if (getLimitParameter() != null) {
0424: template.setValue("LIMIT_VALUE", "?");
0425: } else {
0426: template.setValue("LIMIT_VALUE", mLimit);
0427: }
0428: }
0429:
0430: block = template.getBlock("LIMIT");
0431: if (0 == block.length()) {
0432: if (!mExcludeUnsupportedCapabilities) {
0433: throw new UnsupportedSqlFeatureException(
0434: "LIMIT", mDatasource
0435: .getAliasedDriver());
0436: }
0437: } else {
0438: template.setValue("LIMIT", block);
0439: }
0440: }
0441:
0442: mSql = template.getBlock("QUERY");
0443:
0444: assert mSql != null;
0445: assert mSql.length() > 0;
0446: }
0447: }
0448:
0449: return mSql;
0450: }
0451:
0452: public Select hint(String hint) {
0453: clearGenerated();
0454: mHint = hint;
0455:
0456: return this ;
0457: }
0458:
0459: public Select field(String field) {
0460: if (null == field)
0461: throw new IllegalArgumentException("field can't be null.");
0462: if (0 == field.length())
0463: throw new IllegalArgumentException("field can't be empty.");
0464:
0465: clearGenerated();
0466: mFields.add(field);
0467:
0468: return this ;
0469: }
0470:
0471: public Select field(String alias, Select query) {
0472: if (null == alias)
0473: throw new IllegalArgumentException("alias can't be null.");
0474: if (0 == alias.length())
0475: throw new IllegalArgumentException("alias can't be empty.");
0476: if (null == query)
0477: throw new IllegalArgumentException("query can't be null.");
0478:
0479: StringBuilder buffer = new StringBuilder();
0480:
0481: buffer.append("(");
0482: buffer.append(query.toString());
0483: buffer.append(") AS ");
0484: buffer.append(alias);
0485:
0486: field(buffer.toString());
0487:
0488: fieldSubselect(query);
0489:
0490: return this ;
0491: }
0492:
0493: public Select fields(Class beanClass) throws DbQueryException {
0494: return fieldsExcluded(null, beanClass, (String[]) null);
0495: }
0496:
0497: public Select fieldsExcluded(Class beanClass,
0498: String... excludedFields) throws DbQueryException {
0499: return fieldsExcluded(null, beanClass, excludedFields);
0500: }
0501:
0502: public Select fields(String table, Class beanClass)
0503: throws DbQueryException {
0504: return fieldsExcluded(table, beanClass, (String[]) null);
0505: }
0506:
0507: public Select fieldsExcluded(String table, Class beanClass,
0508: String... excludedFields) throws DbQueryException {
0509: if (null == beanClass)
0510: throw new IllegalArgumentException(
0511: "beanClass can't be null.");
0512:
0513: Set<String> property_names = QueryHelper.getBeanPropertyNames(
0514: beanClass, excludedFields);
0515:
0516: Constrained constrained = ConstrainedUtils
0517: .getConstrainedInstance(beanClass);
0518:
0519: // handle the properties
0520: for (String property_name : property_names) {
0521: if (!ConstrainedUtils.persistConstrainedProperty(
0522: constrained, property_name, null)) {
0523: continue;
0524: }
0525:
0526: if (null == table) {
0527: field(property_name);
0528: } else {
0529: field(table + "." + property_name);
0530: }
0531: }
0532:
0533: return this ;
0534: }
0535:
0536: public Select fields(String... fields) {
0537: if (null == fields)
0538: throw new IllegalArgumentException("fields can't be null.");
0539:
0540: if (fields.length > 0) {
0541: clearGenerated();
0542: mFields.addAll(Arrays.asList(fields));
0543: }
0544:
0545: return this ;
0546: }
0547:
0548: public Select distinct() {
0549: clearGenerated();
0550: mDistinct = true;
0551:
0552: return this ;
0553: }
0554:
0555: public Select distinctOn(String column) {
0556: if (null == column)
0557: throw new IllegalArgumentException("column can't be null.");
0558: if (0 == column.length())
0559: throw new IllegalArgumentException("column can't be empty.");
0560:
0561: clearGenerated();
0562: mDistinct = true;
0563: mDistinctOn.add(column);
0564:
0565: return this ;
0566: }
0567:
0568: public Select distinctOn(String... columns) {
0569: if (null == columns)
0570: throw new IllegalArgumentException("columns can't be null.");
0571:
0572: if (columns.length > 0) {
0573: clearGenerated();
0574: mDistinct = true;
0575: mDistinctOn.addAll(Arrays.asList(columns));
0576: }
0577:
0578: return this ;
0579: }
0580:
0581: public Select from(String from) {
0582: if (null == from)
0583: throw new IllegalArgumentException("from can't be null.");
0584: if (0 == from.length())
0585: throw new IllegalArgumentException("from can't be empty.");
0586:
0587: clearGenerated();
0588: mFrom = from;
0589:
0590: return this ;
0591: }
0592:
0593: public Select from(Select query) {
0594: if (null == query)
0595: throw new IllegalArgumentException("query can't be null.");
0596:
0597: StringBuilder buffer = new StringBuilder();
0598:
0599: buffer.append("(");
0600: buffer.append(query.toString());
0601: buffer.append(")");
0602:
0603: from(buffer.toString());
0604:
0605: _tableSubselect(query);
0606:
0607: return this ;
0608: }
0609:
0610: public Select from(String alias, Select query) {
0611: if (null == alias)
0612: throw new IllegalArgumentException("alias can't be null.");
0613: if (0 == alias.length())
0614: throw new IllegalArgumentException("alias can't be empty.");
0615: if (null == query)
0616: throw new IllegalArgumentException("query can't be null.");
0617:
0618: StringBuilder buffer = new StringBuilder();
0619:
0620: buffer.append("(");
0621: buffer.append(query.toString());
0622: buffer.append(") ");
0623: buffer.append(alias);
0624:
0625: from(buffer.toString());
0626:
0627: _tableSubselect(query);
0628:
0629: return this ;
0630: }
0631:
0632: public Select join(String table) {
0633: if (null == table)
0634: throw new IllegalArgumentException("table can't be null.");
0635: if (0 == table.length())
0636: throw new IllegalArgumentException("table can't be empty.");
0637:
0638: clearGenerated();
0639: mJoins.add(new JoinDefault(table));
0640:
0641: return this ;
0642: }
0643:
0644: public Select join(String alias, Select query) {
0645: if (null == alias)
0646: throw new IllegalArgumentException("alias can't be null.");
0647: if (0 == alias.length())
0648: throw new IllegalArgumentException("alias can't be empty.");
0649: if (null == query)
0650: throw new IllegalArgumentException("query can't be null.");
0651:
0652: StringBuilder buffer = new StringBuilder();
0653: buffer.append("(");
0654: buffer.append(query.toString());
0655: buffer.append(") ");
0656: buffer.append(alias);
0657:
0658: join(buffer.toString());
0659:
0660: tableSubselect(query);
0661:
0662: return this ;
0663: }
0664:
0665: public Select joinCustom(String customJoin) {
0666: if (null == customJoin)
0667: throw new IllegalArgumentException(
0668: "customJoin can't be null.");
0669: if (0 == customJoin.length())
0670: throw new IllegalArgumentException(
0671: "customJoin can't be empty.");
0672:
0673: clearGenerated();
0674: mJoins.add(new JoinCustom(customJoin));
0675:
0676: return this ;
0677: }
0678:
0679: public Select joinCross(String table) {
0680: if (null == table)
0681: throw new IllegalArgumentException("table can't be null.");
0682: if (0 == table.length())
0683: throw new IllegalArgumentException("table can't be empty.");
0684:
0685: clearGenerated();
0686: mJoins.add(new JoinCross(table));
0687:
0688: return this ;
0689: }
0690:
0691: public Select joinInner(String table, JoinCondition condition,
0692: String conditionExpression) {
0693: if (null == table)
0694: throw new IllegalArgumentException("table can't be null.");
0695: if (0 == table.length())
0696: throw new IllegalArgumentException("table can't be empty.");
0697: if (null == condition)
0698: throw new IllegalArgumentException(
0699: "condition can't be null.");
0700: if (NATURAL == condition && conditionExpression != null)
0701: throw new IllegalArgumentException(
0702: "a NATURAL join condition can't have a join expression.");
0703: if (NATURAL != condition && null == conditionExpression)
0704: throw new IllegalArgumentException(
0705: "conditionExpression can't be null.");
0706: if (NATURAL != condition && 0 == conditionExpression.length())
0707: throw new IllegalArgumentException(
0708: "conditionExpression can't be empty.");
0709:
0710: clearGenerated();
0711: mJoins
0712: .add(new JoinInner(table, condition,
0713: conditionExpression));
0714:
0715: return this ;
0716: }
0717:
0718: public Select joinOuter(String table, JoinType type,
0719: JoinCondition condition, String conditionExpression) {
0720: if (null == table)
0721: throw new IllegalArgumentException("table can't be null.");
0722: if (0 == table.length())
0723: throw new IllegalArgumentException("table can't be empty.");
0724: if (null == type)
0725: throw new IllegalArgumentException("type can't be null.");
0726: if (null == condition)
0727: throw new IllegalArgumentException(
0728: "condition can't be null.");
0729: if (NATURAL == condition && conditionExpression != null)
0730: throw new IllegalArgumentException(
0731: "a NATURAL join condition can't have a join expression.");
0732: if (NATURAL != condition && null == conditionExpression)
0733: throw new IllegalArgumentException(
0734: "conditionExpression can't be null.");
0735: if (NATURAL != condition && 0 == conditionExpression.length())
0736: throw new IllegalArgumentException(
0737: "conditionExpression can't be empty.");
0738:
0739: clearGenerated();
0740: mJoins.add(new JoinOuter(table, type, condition,
0741: conditionExpression));
0742:
0743: return this ;
0744: }
0745:
0746: public Select fieldSubselect(Select query) {
0747: _fieldSubselect(query);
0748:
0749: return this ;
0750: }
0751:
0752: public Select tableSubselect(Select query) {
0753: _tableSubselect(query);
0754:
0755: return this ;
0756: }
0757:
0758: public Select groupBy(String groupBy) {
0759: if (null == groupBy)
0760: throw new IllegalArgumentException("groupBy can't be null.");
0761: if (0 == groupBy.length())
0762: throw new IllegalArgumentException(
0763: "groupBy can't be empty.");
0764:
0765: clearGenerated();
0766: mGroupBy.add(groupBy);
0767:
0768: return this ;
0769: }
0770:
0771: public Select groupBy(Class beanClass) throws DbQueryException {
0772: return groupByExcluded(beanClass, (String[]) null);
0773: }
0774:
0775: public Select groupByExcluded(Class beanClass,
0776: String... excludedFields) throws DbQueryException {
0777: if (null == beanClass)
0778: throw new IllegalArgumentException(
0779: "beanClass can't be null.");
0780:
0781: Set<String> property_names = QueryHelper.getBeanPropertyNames(
0782: beanClass, excludedFields);
0783:
0784: clearGenerated();
0785:
0786: for (String property_name : property_names) {
0787: mGroupBy.add(property_name);
0788: }
0789:
0790: return this ;
0791: }
0792:
0793: public Select having(String having) {
0794: if (null == having)
0795: throw new IllegalArgumentException("having can't be null.");
0796: if (0 == having.length())
0797: throw new IllegalArgumentException("having can't be empty.");
0798:
0799: clearGenerated();
0800: mHaving.add(having);
0801:
0802: return this ;
0803: }
0804:
0805: public Select union(String union) {
0806: if (null == union)
0807: throw new IllegalArgumentException("union can't be null.");
0808: if (0 == union.length())
0809: throw new IllegalArgumentException("union can't be empty.");
0810:
0811: clearGenerated();
0812: mUnions.add(new Union(union, false));
0813:
0814: return this ;
0815: }
0816:
0817: public Select union(Select union) throws DbQueryException {
0818: if (null == union)
0819: throw new IllegalArgumentException("union can't be null.");
0820:
0821: union(union.getSql());
0822: _unionSubselect(union);
0823:
0824: return this ;
0825: }
0826:
0827: public Select unionAll(String union) {
0828: if (null == union)
0829: throw new IllegalArgumentException("union can't be null.");
0830: if (0 == union.length())
0831: throw new IllegalArgumentException("union can't be empty.");
0832:
0833: clearGenerated();
0834: mUnions.add(new Union(union, true));
0835:
0836: return this ;
0837: }
0838:
0839: public Select unionAll(Select union) throws DbQueryException {
0840: if (null == union)
0841: throw new IllegalArgumentException("union can't be null.");
0842:
0843: unionAll(union.getSql());
0844: _unionSubselect(union);
0845:
0846: return this ;
0847: }
0848:
0849: public Select orderBy(String column) {
0850: clearGenerated();
0851: return orderBy(column, ASC);
0852: }
0853:
0854: public Select orderBy(String column, OrderByDirection direction) {
0855: if (null == column)
0856: throw new IllegalArgumentException("column can't be null.");
0857: if (0 == column.length())
0858: throw new IllegalArgumentException("column can't be empty.");
0859: if (null == direction)
0860: throw new IllegalArgumentException(
0861: "direction can't be null.");
0862:
0863: OrderBy orderby = new OrderBy(column, direction);
0864: clearGenerated();
0865: mOrderBy.add(orderby);
0866:
0867: return this ;
0868: }
0869:
0870: public Select limit(int limit) {
0871: if (limit < 1)
0872: throw new IllegalArgumentException(
0873: "limit must be at least 1.");
0874:
0875: clearGenerated();
0876: mLimit = limit;
0877: setLimitParameter(null);
0878:
0879: return this ;
0880: }
0881:
0882: public Select limitParameter(String name) {
0883: if (null == name)
0884: throw new IllegalArgumentException("name can't be null.");
0885: if (0 == name.length())
0886: throw new IllegalArgumentException("name can't be empty.");
0887:
0888: clearGenerated();
0889: mLimit = -1;
0890: setLimitParameter(name);
0891:
0892: return this ;
0893: }
0894:
0895: public Select offset(int offset) {
0896: if (offset < 0)
0897: throw new IllegalArgumentException(
0898: "offset must be at least 0.");
0899:
0900: clearGenerated();
0901: mOffset = offset;
0902: setOffsetParameter(null);
0903:
0904: return this ;
0905: }
0906:
0907: protected boolean isLimitBeforeOffset() {
0908: Template template = getTemplate();
0909: if (!template.hasValueId("OFFSET")
0910: || !template.hasValueId("LIMIT_VALUE")) {
0911: return super .isLimitBeforeOffset();
0912: }
0913:
0914: String offset = template.getValue("OFFSET");
0915: String limit_value = template.getValue("LIMIT_VALUE");
0916: template.setValue("OFFSET", "offset");
0917: template.setValue("LIMIT_VALUE", "limit");
0918: String limit = template.getBlock("LIMIT");
0919:
0920: template.setValue("OFFSET", offset);
0921: template.setValue("LIMIT_VALUE", limit_value);
0922:
0923: return limit.indexOf("offset") >= limit.indexOf("limit");
0924: }
0925:
0926: public Select offsetParameter(String name) {
0927: if (null == name)
0928: throw new IllegalArgumentException("name can't be null.");
0929: if (0 == name.length())
0930: throw new IllegalArgumentException("name can't be empty.");
0931:
0932: clearGenerated();
0933: mOffset = -1;
0934: setOffsetParameter(name);
0935:
0936: return this ;
0937: }
0938:
0939: public Select clone() {
0940: Select new_instance = super .clone();
0941: if (new_instance != null) {
0942: if (mFields != null) {
0943: new_instance.mFields = new ArrayList<String>();
0944: new_instance.mFields.addAll(mFields);
0945: }
0946:
0947: if (mJoins != null) {
0948: new_instance.mJoins = new ArrayList<Join>();
0949:
0950: for (Join join : mJoins) {
0951: new_instance.mJoins.add(join.clone());
0952: }
0953: }
0954:
0955: if (mUnions != null) {
0956: new_instance.mUnions = new ArrayList<Union>();
0957: for (Union union : mUnions) {
0958: new_instance.mUnions.add(union.clone());
0959: }
0960: }
0961:
0962: if (mGroupBy != null) {
0963: new_instance.mGroupBy = new ArrayList<String>();
0964: new_instance.mGroupBy.addAll(mGroupBy);
0965: }
0966:
0967: if (mHaving != null) {
0968: new_instance.mHaving = new ArrayList<String>();
0969: new_instance.mHaving.addAll(mHaving);
0970: }
0971:
0972: if (mDistinctOn != null) {
0973: new_instance.mDistinctOn = new ArrayList<String>();
0974: new_instance.mDistinctOn.addAll(mDistinctOn);
0975: }
0976:
0977: if (mOrderBy != null) {
0978: new_instance.mOrderBy = new ArrayList<OrderBy>();
0979: for (OrderBy order_by : mOrderBy) {
0980: new_instance.mOrderBy.add(order_by.clone());
0981: }
0982: }
0983: }
0984:
0985: return new_instance;
0986: }
0987:
0988: public static class JoinCondition extends EnumClass<String> {
0989: JoinCondition(String identifier) {
0990: super (identifier);
0991: }
0992: }
0993:
0994: public static class JoinType extends EnumClass<String> {
0995: JoinType(String identifier) {
0996: super (identifier);
0997: }
0998: }
0999:
1000: public static class OrderByDirection extends EnumClass<String> {
1001: OrderByDirection(String identifier) {
1002: super (identifier);
1003: }
1004:
1005: public static OrderByDirection getDirection(String identifier) {
1006: return getMember(OrderByDirection.class, identifier);
1007: }
1008: }
1009:
1010: public class JoinCustom extends Join {
1011: JoinCustom(String customJoin) {
1012: super (customJoin);
1013: }
1014:
1015: String getSql(Template template) {
1016: return " " + getData();
1017: }
1018: }
1019:
1020: public class JoinDefault extends Join implements Cloneable {
1021: JoinDefault(String table) {
1022: super (table);
1023: }
1024:
1025: String getSql(Template template) {
1026: assert template != null;
1027:
1028: String result = null;
1029:
1030: template.setValue("TABLE", getData());
1031: result = template.getBlock("JOIN_DEFAULT");
1032: template.removeValue("TABLE");
1033:
1034: assert result != null;
1035: assert result.length() > 0;
1036:
1037: return result;
1038: }
1039:
1040: public JoinDefault clone() {
1041: return (JoinDefault) super .clone();
1042: }
1043: }
1044:
1045: public class JoinCross extends Join implements Cloneable {
1046: JoinCross(String table) {
1047: super (table);
1048: }
1049:
1050: String getSql(Template template) throws DbQueryException {
1051: assert template != null;
1052:
1053: String result = null;
1054:
1055: template.setValue("TABLE", getData());
1056: result = template.getBlock("JOIN_CROSS");
1057: if (0 == result.length()) {
1058: throw new UnsupportedSqlFeatureException("CROSS JOIN",
1059: mDatasource.getAliasedDriver());
1060: }
1061: template.removeValue("TABLE");
1062:
1063: assert result != null;
1064: assert result.length() > 0;
1065:
1066: return result;
1067: }
1068:
1069: public JoinCross clone() {
1070: return (JoinCross) super .clone();
1071: }
1072: }
1073:
1074: public class JoinInner extends Join implements Cloneable {
1075: private JoinCondition mCondition = null;
1076: private String mExpression = null;
1077:
1078: JoinInner(String table, JoinCondition condition,
1079: String expression) {
1080: super (table);
1081:
1082: assert condition != null;
1083: assert condition == Select.NATURAL
1084: || (expression != null && expression.length() > 0);
1085:
1086: setCondition(condition);
1087: setExpression(expression);
1088: }
1089:
1090: String getSql(Template template) throws DbQueryException {
1091: assert template != null;
1092:
1093: String condition = null;
1094: String result = null;
1095:
1096: template.setValue("TABLE", getData());
1097: if (getExpression() != null) {
1098: template.setValue("EXPRESSION", getExpression());
1099: }
1100: condition = template.getBlock("JOIN_INNER_"
1101: + getCondition().toString());
1102: if (0 == condition.length()) {
1103: throw new UnsupportedSqlFeatureException(getCondition()
1104: .toString()
1105: + " for INNER JOIN", mDatasource
1106: .getAliasedDriver());
1107: }
1108: template.setValue(
1109: "JOIN_INNER_" + getCondition().toString(),
1110: condition);
1111: result = template.getBlock("JOIN_INNER");
1112: if (0 == result.length()) {
1113: throw new UnsupportedSqlFeatureException("INNER JOIN",
1114: mDatasource.getAliasedDriver());
1115: }
1116: template.removeValue("TABLE");
1117: template.removeValue("EXPRESSION");
1118: template.removeValue("JOIN_INNER_"
1119: + getCondition().toString());
1120:
1121: assert result != null;
1122: assert result.length() > 0;
1123:
1124: return result;
1125: }
1126:
1127: public JoinCondition getCondition() {
1128: return mCondition;
1129: }
1130:
1131: void setCondition(JoinCondition condition) {
1132: assert condition != null;
1133:
1134: mCondition = condition;
1135: }
1136:
1137: public String getExpression() {
1138: return mExpression;
1139: }
1140:
1141: void setExpression(String expression) {
1142: mExpression = expression;
1143: }
1144:
1145: public JoinInner clone() {
1146: return (JoinInner) super .clone();
1147: }
1148: }
1149:
1150: public class JoinOuter extends Join implements Cloneable {
1151: private JoinType mType = null;
1152: private JoinCondition mCondition = null;
1153: private String mExpression = null;
1154:
1155: JoinOuter(String table, JoinType type, JoinCondition condition,
1156: String expression) {
1157: super (table);
1158:
1159: assert type != null;
1160: assert condition != null;
1161: assert condition == Select.NATURAL
1162: || (expression != null && expression.length() > 0);
1163:
1164: setType(type);
1165: setCondition(condition);
1166: setExpression(expression);
1167: }
1168:
1169: String getSql(Template template) throws DbQueryException {
1170: assert template != null;
1171:
1172: String type = null;
1173: String condition = null;
1174: String result = null;
1175:
1176: template.setValue("TABLE", getData());
1177: if (getType() != null) {
1178: type = template.getBlock("JOIN_OUTER_"
1179: + getType().toString());
1180: if (0 == type.length()) {
1181: throw new UnsupportedSqlFeatureException(getType()
1182: .toString()
1183: + " for OUTER JOIN", mDatasource
1184: .getAliasedDriver());
1185: }
1186: template.setValue("JOIN_OUTER_TYPE", type);
1187: }
1188: if (getExpression() != null) {
1189: template.setValue("EXPRESSION", getExpression());
1190: }
1191: condition = template.getBlock("JOIN_OUTER_"
1192: + getCondition().toString());
1193: if (0 == condition.length()) {
1194: throw new UnsupportedSqlFeatureException(getCondition()
1195: .toString()
1196: + " for OUTER JOIN", mDatasource
1197: .getAliasedDriver());
1198: }
1199: template.setValue(
1200: "JOIN_OUTER_" + getCondition().toString(),
1201: condition);
1202: result = template.getBlock("JOIN_OUTER");
1203: if (0 == result.length()) {
1204: throw new UnsupportedSqlFeatureException("OUTER JOIN",
1205: mDatasource.getAliasedDriver());
1206: }
1207: template.removeValue("TABLE");
1208: template.removeValue("EXPRESSION");
1209: template.removeValue("JOIN_OUTER_"
1210: + getCondition().toString());
1211: template.removeValue("JOIN_OUTER_TYPE");
1212:
1213: assert result != null;
1214: assert result.length() > 0;
1215:
1216: return result;
1217: }
1218:
1219: public JoinType getType() {
1220: return mType;
1221: }
1222:
1223: void setType(JoinType type) {
1224: assert type != null;
1225:
1226: mType = type;
1227: }
1228:
1229: public JoinCondition getCondition() {
1230: return mCondition;
1231: }
1232:
1233: void setCondition(JoinCondition condition) {
1234: assert condition != null;
1235:
1236: mCondition = condition;
1237: }
1238:
1239: public String getExpression() {
1240: return mExpression;
1241: }
1242:
1243: void setExpression(String expression) {
1244: mExpression = expression;
1245: }
1246:
1247: public JoinOuter clone() {
1248: return (JoinOuter) super .clone();
1249: }
1250: }
1251:
1252: public abstract class Join implements Cloneable {
1253: private String mData = null;
1254:
1255: Join(String data) {
1256: assert data != null;
1257: assert data.length() > 0;
1258:
1259: setData(data);
1260: }
1261:
1262: abstract String getSql(Template template)
1263: throws DbQueryException;
1264:
1265: public String getData() {
1266: return mData;
1267: }
1268:
1269: void setData(String data) {
1270: assert data != null;
1271: assert data.length() > 0;
1272:
1273: mData = data;
1274: }
1275:
1276: public Join clone() {
1277: Join new_instance = null;
1278: try {
1279: new_instance = (Join) super .clone();
1280: } catch (CloneNotSupportedException e) {
1281: new_instance = null;
1282: }
1283:
1284: return new_instance;
1285: }
1286: }
1287:
1288: public class OrderBy implements Cloneable {
1289: private String mColumn = null;
1290: private OrderByDirection mDirection = null;
1291:
1292: OrderBy(String column, OrderByDirection direction) {
1293: assert column != null;
1294: assert column.length() > 0;
1295: assert direction != null;
1296:
1297: setColumn(column);
1298: setDirection(direction);
1299: }
1300:
1301: String getSql(Template template) {
1302: assert template != null;
1303:
1304: String result = null;
1305:
1306: template.setValue("COLUMN", getColumn());
1307: template.setValue("DIRECTION", template.getBlock("ORDERBY_"
1308: + getDirection().toString()));
1309: result = template.getBlock("ORDERBY_PART");
1310: template.removeValue("COLUMN");
1311: template.removeValue("DIRECTION");
1312:
1313: assert result != null;
1314: assert result.length() > 0;
1315:
1316: return result;
1317: }
1318:
1319: public String getColumn() {
1320: return mColumn;
1321: }
1322:
1323: void setColumn(String column) {
1324: assert column != null;
1325: assert column.length() > 0;
1326:
1327: mColumn = column;
1328: }
1329:
1330: public OrderByDirection getDirection() {
1331: return mDirection;
1332: }
1333:
1334: void setDirection(OrderByDirection direction) {
1335: assert direction != null;
1336:
1337: mDirection = direction;
1338: }
1339:
1340: public OrderBy clone() {
1341: OrderBy new_instance = null;
1342: try {
1343: new_instance = (OrderBy) super .clone();
1344: } catch (CloneNotSupportedException e) {
1345: new_instance = null;
1346: }
1347:
1348: return new_instance;
1349: }
1350: }
1351:
1352: public class Union implements Cloneable {
1353: private String mExpression = null;
1354: private boolean mAll = false;
1355:
1356: Union(String expression, boolean all) {
1357: setExpression(expression);
1358: setAll(all);
1359: }
1360:
1361: void setExpression(String expression) {
1362: assert expression != null;
1363: assert expression.length() > 0;
1364:
1365: mExpression = expression;
1366: }
1367:
1368: public String getExpression() {
1369: return mExpression;
1370: }
1371:
1372: void setAll(boolean all) {
1373: mAll = all;
1374: }
1375:
1376: public boolean isAll() {
1377: return mAll;
1378: }
1379:
1380: public Union clone() {
1381: Union new_instance = null;
1382: try {
1383: new_instance = (Union) super .clone();
1384: } catch (CloneNotSupportedException e) {
1385: new_instance = null;
1386: }
1387:
1388: return new_instance;
1389: }
1390: }
1391: }
|