source: trunk/fmgVen/src/com/fmguler/ven/Criteria.java @ 37

Last change on this file since 37 was 37, checked in by fmguler, 13 years ago

Refs #7 - Implemented orderAsc and orderDesc methods of Criteria. Have been testing these for a while, no problem so far. Added BigDecimal to db classes (Numeric db type). If the column name is "order" it is escaped while insert/update. (This should be done for all db keywords). Fixed missing mapping of one to many assc. (lists) of many to one (object) assc (obj.obj.list).

File size: 12.5 KB
Line 
1/*
2 *  fmgVen - A Convention over Configuration Java ORM Tool
3 *  Copyright 2011 Fatih Mehmet Güler
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *  under the License.
17 */
18package com.fmguler.ven;
19
20import com.fmguler.ven.util.Convert;
21import java.util.HashMap;
22import java.util.LinkedList;
23import java.util.Map;
24import java.util.Stack;
25
26/**
27 * Criteria object, used to filter the result of a query according to some criteria.
28 * <p>
29 * Can be used in two modes; as string criteria or typed criteria.
30 * The string criteria is used like;
31 * <pre>
32 * new Criteria().add("and SomeDomainObject.anotherDomainObjects.name like :p1").param("p1", "a%");
33 * </pre>
34 * And the typed criteria is used like;
35 * <pre>
36 * new Criteria().eq(attr,value).like(attr,value).and().gt(attr, value).or().orderAsc(attr).orderDesc(attr).limit(limit, offset);
37 * </pre>
38 *
39 * This criteria object is used to filter SQL results by adding the "Criteria.criteriaToSQL()" output to the where clause.
40 *
41 * @author Fatih Mehmet Güler
42 */
43public class Criteria {
44    private StringBuffer criteriaStringBuffer = new StringBuffer(); //for string criteria
45    private StringBuffer orderStringBuffer = new StringBuffer(); //for ordering
46    private LinkedList criterionList = new LinkedList(); //for typed criteria
47    private Map parameters = new HashMap(); //the parameters used in criteria string
48    private int limit = 20;
49    private int offset = 0;
50    int paramCount = 0;
51
52    //--------------------------------------------------------------------------
53    //String Criteria Methods
54    /**
55     * Add a criteria string
56     */
57    public Criteria add(String criteriaString) {
58        this.criteriaStringBuffer.append(" ").append(criteriaString);
59        return this;
60    }
61
62    /**
63     * Add the parameter used in criteria string
64     */
65    public Criteria param(String parameter, Object value) {
66        this.parameters.put(parameter, value);
67        return this;
68    }
69
70    /**
71     * Converts criteria string to SQL
72     * @return the criteria string as SQL where clause
73     */
74    public String criteriaStringToSQL() {
75        StringBuffer resultBuffer = new StringBuffer();
76        String criteriaString = criteriaStringBuffer.toString();
77        criteriaString = criteriaString.replaceAll("\\(", "\\( ");
78        criteriaString = criteriaString.replaceAll("\\)", " \\)");
79
80        String[] parts = criteriaString.split(" ");
81        for (int i = 0; i < parts.length; i++) {
82            if (!parts[i].startsWith(":") && parts[i].indexOf(".") >= 0) {
83                int lastDot = parts[i].lastIndexOf('.');
84                resultBuffer.append(" ");
85                int u = parts[i].length();
86                resultBuffer.append(Convert.toDB(parts[i].substring(0, lastDot).replace('.', '_')));
87                resultBuffer.append(Convert.toDB(parts[i].substring(lastDot, u)));
88            } else {
89                resultBuffer.append(" ").append(parts[i]);
90            }
91        }
92        return resultBuffer.toString();
93    }
94
95    /**
96     * @return the parameter map
97     */
98    public Map getParameters() {
99        return parameters;
100    }
101
102    /**
103     * Return order string to SQL
104     */
105    public String orderStringToSQL() {
106        if (orderStringBuffer.length() == 0) return "";
107        orderStringBuffer.insert(0, " order by");
108        return orderStringBuffer.toString();
109    }
110
111    //--------------------------------------------------------------------------
112    //Typed Criteria Methods
113    /**
114     * Add a criterion where an attribue equals a value (SQL = operator)
115     */
116    public Criteria eq(String attribute, Object value) {
117        criterionList.add(new Criterion(attribute, Criterion.OP_EQUALS, value));
118        return this;
119    }
120
121    /**
122     * Add a criterion where an attribue is like a value (SQL like operator)
123     */
124    public Criteria like(String attribute, Object value) {
125        criterionList.add(new Criterion(attribute, Criterion.OP_LIKE, value));
126        return this;
127    }
128
129    /**
130     * Add a criterion where an attribue is similar to a value (SQL similar to operator)
131     */
132    public Criteria similarto(String attribute, Object value) {
133        criterionList.add(new Criterion(attribute, Criterion.OP_SIMILAR_TO, value));
134        return this;
135    }
136
137    /**
138     * Add a criterion where an attribue is greater than a value (SQL > operator)
139     */
140    public Criteria gt(String attribute, Object value) {
141        criterionList.add(new Criterion(attribute, Criterion.OP_GREATER_THAN, value));
142        return this;
143    }
144
145    /**
146     * Add a criterion where an attribue is less than a value (SQL < operator)
147     */
148    public Criteria lt(String attribute, Object value) {
149        criterionList.add(new Criterion(attribute, Criterion.OP_LESS_THAN, value));
150        return this;
151    }
152
153    /**
154     * Add a criterion where an attribue is null (SQL is null operator)
155     */
156    public Criteria isNull(String attribute) {
157        criterionList.add(new Criterion(attribute, Criterion.OP_ISNULL, null));
158        return this;
159    }
160
161    /**
162     * Add: order by attribute asc
163     */
164    public Criteria orderAsc(String attribute) {
165        if (this.orderStringBuffer.length() != 0) this.orderStringBuffer.append(",");
166        this.orderStringBuffer.append(" ").append(convertAttributeToAlias(attribute)).append(" asc");
167        return this;
168    }
169
170    /**
171     * Add: order by attribute desc
172     */
173    public Criteria orderDesc(String attribute) {
174        if (this.orderStringBuffer.length() != 0) this.orderStringBuffer.append(",");
175        this.orderStringBuffer.append(" ").append(convertAttributeToAlias(attribute)).append(" desc");
176        return this;
177    }
178
179    /**
180     * Add limit and offset to the clause
181     */
182    public Criteria limit(int limit, int offset) {
183        this.limit = limit;
184        this.offset = offset;
185        return this;
186    }
187
188    //--------------------------------------------------------------------------
189    //Connectives
190    /**
191     * Connect previous two criteria with logical and
192     * (as in postfix notation)
193     */
194    public Criteria and() {
195        criterionList.add(new Criterion(Criterion.CONN_AND));
196        return this;
197    }
198
199    /**
200     * Connect previous two criteria with logical or
201     * (as in postfix notation)
202     */
203    public Criteria or() {
204        criterionList.add(new Criterion(Criterion.CONN_OR));
205        return this;
206    }
207
208    /**
209     * Connect previous criterion with logical not
210     * (as in postfix notation)
211     */
212    public Criteria not() {
213        criterionList.add(new Criterion(Criterion.CONN_NOT));
214        return this;
215    }
216
217    /**
218     * Converts typed criteria to SQL
219     * @return the typed criteria as SQL where clause
220     */
221    public String criteriaToSQL() {
222        LinkedList clone = (LinkedList)criterionList.clone();
223        Stack stack = new Stack();
224        while (!clone.isEmpty()) {
225            Criterion criterion = (Criterion)clone.removeFirst();
226            if (!criterion.isConnective()) {
227                String s = "(" + convertOperatorToSQL(criterion) + ")";
228                stack.push(s);
229            } else {
230                String conn = criterion.connective;
231                if (conn.equals(Criterion.CONN_AND)) {
232                    String s1 = (String)stack.pop();
233                    String s2 = (String)stack.pop();
234                    String sr = "(" + s1 + " and " + s2 + ")";
235                    stack.push(sr);
236                } else if (conn.equals(Criterion.CONN_OR)) {
237                    String s1 = (String)stack.pop();
238                    String s2 = (String)stack.pop();
239                    String sr = "(" + s1 + " or " + s2 + ")";
240                    stack.push(sr);
241                } else if (conn.equals(Criterion.CONN_NOT)) {
242                    String s = (String)stack.pop();
243                    String sr = "not (" + s + ")";
244                    stack.push(sr);
245                }
246            }
247        }
248
249        if (stack.isEmpty()) return "1=1";
250
251        String result = (String)stack.pop();
252        return result;
253    }
254
255    //convert the attribute-operator-value criterion to SQL
256    private String convertOperatorToSQL(Criterion criterion) {
257        String attribute = convertAttributeToAlias(criterion.attribute);
258        String operator = criterion.operator;
259        Object value = criterion.value;
260        String result = "";
261
262        if (value == null) {
263            //no value e.g. is null operator
264            result = attribute + " " + operator;
265        } else {
266            String paramName = genParamName();
267            parameters.put(paramName, value);
268            result = attribute + " " + operator + " :" + paramName + "";
269        }
270
271        return result;
272    }
273
274    //convert attribute to SQL alias
275    protected String convertAttributeToAlias(String attribute) {
276        //we convert like: x.y.z.t to x_y_z.t
277        StringBuffer resultBuffer = new StringBuffer();
278        int lastDot = attribute.lastIndexOf('.');
279        resultBuffer.append(" ");
280        int u = attribute.length();
281        resultBuffer.append(Convert.toDB(attribute.substring(0, lastDot).replace('.', '_')));
282        resultBuffer.append(Convert.toDB(attribute.substring(lastDot, u)));
283        return resultBuffer.toString();
284    }
285
286    //generate parameter for typed criteria
287    private String genParamName() {
288        return "__p" + paramCount++;
289    }
290
291    //test the criteria object
292    public static void main(String[] args) {
293        Criteria criteria = new Criteria() //criteria object
294                .like("SomeDomainObject.anotherDomainObjects.name", "a%") //attribute like value
295                .eq("SomeDomainObject.name", "sdo1") //attribute equals value
296                .and() //connect previous criteria with and
297                .isNull("SomeDomainObject.description") //attribute is null
298                .or() //connect previous criteria with or
299                .orderDesc("SomeDomainObject.name"); //order by some attribute
300
301        //print the resulting where clause SQL
302        System.out.println(criteria.criteriaToSQL());
303
304        //print the resulting order clause SQL
305        System.out.println(criteria.orderStringToSQL());
306
307        //the result is;
308        //(( some_domain_object.description is null) or (( some_domain_object.name = :__p1) and ( some_domain_object_another_domain_objects.name like :__p0)))
309    }
310}
311
312/**
313 * Each criterion used in the criteria.
314 * Used to hold attribute-operator-value triplet.
315 * Used internally.
316 * @author Fatih Mehmet Güler
317 */
318class Criterion {
319    //available logical connectives
320    public static final String CONN_AND = "and";
321    public static final String CONN_OR = "or";
322    public static final String CONN_NOT = "not";
323    //available operators
324    public static final String OP_EQUALS = "=";
325    public static final String OP_LESS_THAN = "<";
326    public static final String OP_GREATER_THAN = ">";
327    public static final String OP_LIKE = "like";
328    public static final String OP_SIMILAR_TO = "similar to";
329    public static final String OP_NOT = "not";
330    public static final String OP_ISNULL = "is null";
331    //attribute-operator-value triplet
332    public String attribute;
333    public String operator;
334    public Object value;
335    //logical connective
336    public String connective = null;
337
338    public Criterion(String attribute, String operator, Object value) {
339        this.attribute = attribute;
340        this.operator = operator;
341        this.value = value;
342    }
343
344    public Criterion(String connective) {
345        this.connective = connective;
346    }
347
348    public boolean isConnective() {
349        return connective != null;
350    }
351
352    /**
353     * @return string representation of this criterion
354     */
355    public String toString() {
356        if (isConnective()) return connective;
357        return attribute + " " + operator + " " + value;
358    }
359}
Note: See TracBrowser for help on using the repository browser.