001: package simpleorm.data;
002:
003: import java.util.*;
004:
005: /**
006: * A simple query interface.
007: *
008: * executeQuery() returns a collection of the values, not an itterator, result set etc.
009: * This is partially just to keep it simple, and partially because the itterator's
010: * idiot hasNext()/next() semantics is both wront and incompatible with a
011: * ResultSet's next()/get* logic.
012: * The collection could, of course, be implemented lazily.
013: */
014: public class DQuery implements Comparator<DRecordInstance> {
015: DRecordMeta recordMeta;
016: List<DFieldMeta> selectFields = null;
017: List<Criteria> criteriaFields = new ArrayList(5);
018: List<DFieldMeta> orderbyFields = new ArrayList(2);
019: boolean lockRecords = false; // ie. SELECT FOR UPDATE
020: int offset = 0, limit = Integer.MAX_VALUE; // recs offset..offset+limit-1 are returned (0 based).
021:
022: public static class Criteria {
023: DFieldMeta eqField;
024: Object eqValue;
025:
026: public Criteria(DFieldMeta eqField, Object eqValue) {
027: this .eqField = eqField;
028: this .eqValue = eqValue;
029: }
030:
031: public DFieldMeta getEqField() {
032: return eqField;
033: }
034:
035: public Object getEqValue() {
036: return eqValue;
037: }
038: }
039:
040: public DQuery(DRecordMeta record) {
041: this .recordMeta = record;
042: }
043:
044: public Collection<DFieldMeta> getSelectFields() {
045: if (selectFields == null)
046: return recordMeta.getFields();
047: else
048: return selectFields;
049: }
050:
051: public DQuery addSelect(DFieldMeta field) {
052: if (selectFields == null)
053: selectFields = new ArrayList(10);
054: selectFields.add(field);
055: return this ;
056: }
057:
058: public DQuery eq(DFieldMeta field, Object value) {
059: criteriaFields.add(new Criteria(field, value));
060: return this ;
061: }
062:
063: public DQuery addOrderBy(DFieldMeta field) {
064: orderbyFields.add(field); // need descending later.
065: return this ;
066: }
067:
068: /** Just calls connection.executeQuery.
069: * (Is in query so that a thread local connection can be implemented later.
070: * Also enables wrappers such as *OneRecord to be implemented.)
071: */
072: public Iterator<DRecordInstance> execute(DConnection connection) {
073: return connection.executeQuery(this );
074: }
075:
076: // public Collection execute() {
077: // return DConnection.getThreadAdaptor().executeQuery(this);
078: // }
079:
080: /**
081: * Convenience method to just return the one record in a result.
082: * Returns null if none, exception if multiple.
083: */
084: public DRecordInstance executeOnlyRecord(DConnection database) {
085: Iterator<DRecordInstance> res = execute(database);
086: if (!res.hasNext())
087: return null;
088: DRecordInstance rec = res.next();
089: if (res.hasNext())
090: throw new DException("Multiple Records " + this + rec
091: + res.next());
092: return rec;
093: }
094:
095: /** same as executeOnlyRecord, but exception if none found. */
096: public DRecordInstance executeTheRecord(DConnection database) {
097: DRecordInstance rec = executeOnlyRecord(database);
098: if (rec == null)
099: throw new DException("No record found for " + this );
100: return rec;
101: }
102:
103: /** Convenience method to query by primary key. */
104: public DRecordInstance queryPrimaryKey(DConnection database,
105: Object... keyval) {
106: int kx = 0;
107: for (DFieldMeta field : recordMeta.getFields()) {
108: if (field.isPrimaryKey()) {
109: eq(field, keyval[kx++]);
110: }
111: addSelect(field);
112: }
113: return executeOnlyRecord(database);
114: }
115:
116: /** true if rec maches the criteria. Rarely called outside the package. */
117: public boolean match(DRecordInstance rec) {
118: for (Criteria cr : criteriaFields) {
119: Object value = rec.getObject(cr.eqField);
120: if (cr.eqValue == null && value != null)
121: return false;
122: if (!cr.eqValue.equals(value))
123: return false;
124: }
125: return true;
126: }
127:
128: /** Implements Comparator, for sorting based based on the orderbyFields.
129: * Beware that equals is not really correct, the sort fields will not in general
130: * form a complete key.
131: */
132: public int compare(DRecordInstance first, DRecordInstance second) {
133: for (DFieldMeta field : orderbyFields) {
134: int comp = field.compare(first, second);
135: if (comp != 0)
136: return comp;
137: }
138: return 0;
139: }
140:
141: public String toString() {
142: return "{DQuery " + recordMeta + "}";
143: }
144:
145: //////////////////////////////
146:
147: public DRecordMeta getRecordMeta() {
148: return recordMeta;
149: }
150:
151: public List<DFieldMeta> getOrderbyFields() {
152: return orderbyFields;
153: }
154:
155: public List<Criteria> getCriteriaFields() {
156: return criteriaFields;
157: }
158:
159: public boolean isLockRecords() {
160: return lockRecords;
161: }
162:
163: public DQuery setLockRecords(boolean lockRecords) {
164: this .lockRecords = lockRecords;
165: return this ;
166: }
167:
168: public int getOffset() {
169: return offset;
170: }
171:
172: public DQuery setOffset(int offset) {
173: this .offset = offset;
174: return this ;
175: }
176:
177: public int getLimit() {
178: return limit;
179: }
180:
181: public void setLimit(int limit) {
182: this.limit = limit;
183: }
184:
185: }
|