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

Last change on this file since 37 was 37, checked in by fmguler, 12 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: 11.5 KB
Line 
1/*
2 *  fmgVen - A Convention over Configuration Java ORM Tool
3 *  Copyright 2010 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 com.fmguler.ven.util.VenList;
22import java.beans.PropertyDescriptor;
23import java.math.BigDecimal;
24import java.util.Date;
25import java.util.HashSet;
26import java.util.Iterator;
27import java.util.List;
28import java.util.Set;
29import org.springframework.beans.BeanWrapper;
30import org.springframework.beans.BeanWrapperImpl;
31
32/**
33 * Generates queries in the form of 'Convention over Configuration' for the specified objects.
34 * @author Fatih Mehmet Güler
35 */
36public class QueryGenerator {
37    private Set domainPackages;
38    private Set dbClasses;
39    private boolean debug = false;
40
41    public QueryGenerator() {
42        domainPackages = new HashSet();
43        dbClasses = new HashSet();
44        //the predefined database classes;
45        this.dbClasses.add(Integer.class);
46        this.dbClasses.add(String.class);
47        this.dbClasses.add(Date.class);
48        this.dbClasses.add(Double.class);
49        this.dbClasses.add(Boolean.class);
50        this.dbClasses.add(BigDecimal.class);
51    }
52
53    /**
54     * Generates select query for the specified object class and specified joins.
55     * @param joins set of joins that the query will contain
56     * @param objectClass the object class to select from
57     * @return the select SQL query
58     */
59    public String generateSelectQuery(Class objectClass, Set joins) {
60        long t1 = System.currentTimeMillis();
61        String objectName = Convert.toSimpleName(objectClass.getName());
62        String tableName = Convert.toDB(objectName);
63        StringBuffer selectClause = new StringBuffer("select ");
64        StringBuffer fromClause = new StringBuffer("from " + tableName);
65        generateRecursively(0, tableName, objectName, objectClass, joins, selectClause, fromClause);
66        selectClause.append(" 1=1");
67        if (debug) System.out.println("Ven - query generation time = " + (System.currentTimeMillis() - t1));
68        return selectClause.toString() + " \n" + fromClause.toString();
69    }
70
71    public String generateCountQuery() {
72        return null;
73    }
74
75    /**
76     * Generates insert query for the specified object
77     * @param object the object to generate insert query for
78     * @return the insert SQL query
79     */
80    public String generateInsertQuery(Object object) {
81        BeanWrapper wr = new BeanWrapperImpl(object);
82        String objectName = Convert.toSimpleName(object.getClass().getName());
83        String tableName = Convert.toDB(objectName);
84        PropertyDescriptor[] pdArr = wr.getPropertyDescriptors();
85
86        //generate insert query
87        StringBuffer query = new StringBuffer("insert into " + tableName + "(");
88        StringBuffer values = new StringBuffer(" values(");
89        for (int i = 0; i < pdArr.length; i++) {
90            Class fieldClass = pdArr[i].getPropertyType(); //field class
91            String columnName = Convert.toDB(pdArr[i].getName()); //column name
92            String fieldName = pdArr[i].getName(); //field name
93            //if (fieldName.equals("id")) continue; //remove if it does not break the sequence
94            if (dbClasses.contains(fieldClass)) { //direct database field (Integer,String,Date, etc)
95                query.append(columnName.equals("order")?"\"order\"":columnName);
96                query.append(",");
97                values.append(":").append(fieldName);
98                values.append(",");
99            }
100            if (fieldClass.getPackage() != null && domainPackages.contains(fieldClass.getPackage().getName())) { //object
101                query.append(Convert.toDB(fieldName)).append("_id");
102                query.append(",");
103                values.append(":").append(fieldName).append(".id");
104                values.append(",");
105            }
106        }
107        query.deleteCharAt(query.length() - 1);
108        query.append(")");
109        values.deleteCharAt(values.length() - 1);
110        values.append(");");
111        query.append(values);
112
113        return query.toString();
114    }
115
116    /**
117     * Generates update query for the specified object
118     * @param object the object to generate update query for
119     * @return the update SQL query
120     */
121    public String generateUpdateQuery(Object object) throws VenException {
122        BeanWrapper wr = new BeanWrapperImpl(object);
123        String objectName = Convert.toSimpleName(object.getClass().getName());
124        String tableName = Convert.toDB(objectName);
125        PropertyDescriptor[] pdArr = wr.getPropertyDescriptors();
126
127        StringBuffer query = new StringBuffer("update " + tableName + " set ");
128        for (int i = 0; i < pdArr.length; i++) {
129            Class fieldClass = pdArr[i].getPropertyType(); //field class
130            String columnName = Convert.toDB(pdArr[i].getName()); //column name
131            String fieldName = pdArr[i].getName(); //field name
132            if (dbClasses.contains(fieldClass)) { //direct database field (Integer,String,Date, etc)
133                query.append(columnName.equals("order")?"\"order\"":columnName).append("=:").append(fieldName);
134                query.append(",");
135            }
136            if (fieldClass.getPackage() != null && domainPackages.contains(fieldClass.getPackage().getName())) { //object
137                query.append(columnName).append("_id=:").append(fieldName).append(".id");
138                query.append(",");
139            }
140        }
141        query.deleteCharAt(query.length() - 1);
142        query.append(" where id = :id ;");
143        return query.toString();
144    }
145
146    /**
147     * Generates delete query for the specified object class
148     * @param objectClass the object class to generate query for
149     * @return the delete SQL query
150     */
151    public String generateDeleteQuery(Class objectClass) {
152        StringBuffer query = new StringBuffer();
153        query.append("delete from ").append(Convert.toDB(Convert.toSimpleName(objectClass.getName()))).append(" where id = :id;");
154        return query.toString();
155    }
156
157    /**
158     * Generates sequence query for the specified object
159     * @param object the objectc to generate sequence query for
160     * @return the SQL query to select next id
161     */
162    public String generateSequenceQuery(Object object) throws VenException {
163        String objectName = Convert.toSimpleName(object.getClass().getName());
164        String tableName = Convert.toDB(objectName);
165        return "select nextval('" + tableName + "_id_seq');";
166    }
167
168    //--------------------------------------------------------------------------
169    //PRIVATE METHODS
170    //recursively generate select query
171    private void generateRecursively(int level, String tableName, String objectPath, Class objectClass, Set joins, StringBuffer selectClause, StringBuffer fromClause) {
172        BeanWrapper wr = new BeanWrapperImpl(objectClass);
173        PropertyDescriptor[] pdArr = wr.getPropertyDescriptors();
174
175        for (int i = 0; i < pdArr.length; i++) {
176            Class fieldClass = pdArr[i].getPropertyType(); //field class
177            String fieldName = pdArr[i].getName(); //field name
178            Object fieldValue = wr.getPropertyValue(fieldName);
179            String columnName = Convert.toDB(pdArr[i].getName()); //column name
180
181            //direct database class (Integer, String, Date, etc)
182            if (dbClasses.contains(fieldClass)) {
183                selectClause.append(tableName).append(".").append(columnName).append(" as ").append(tableName).append("_").append(columnName); //column
184                selectClause.append(", ");
185            }
186
187            //many to one association (object property)
188            if (fieldClass.getPackage() != null && domainPackages.contains(fieldClass.getPackage().getName()) && joinsContain(joins, objectPath + "." + fieldName)) {
189                String joinTableAlias = tableName + "_" + columnName; //alias for table to join since there can be multiple joins to the same table
190                String joinTable = Convert.toDB(Convert.toSimpleName(fieldClass.getName())); //table to join
191                fromClause.append(" left join ").append(joinTable).append(" ").append(joinTableAlias);
192                fromClause.append(" on ").append(joinTableAlias).append(".id = ").append(tableName).append(".").append(columnName).append("_id");
193                generateRecursively(++level, joinTableAlias, objectPath + "." + fieldName, fieldClass, joins, selectClause, fromClause);
194            }
195
196            //one to many association (list property)
197            if (fieldValue instanceof List && joinsContain(joins, objectPath + "." + fieldName)) {
198                Class elementClass = VenList.findElementClass((List)fieldValue);
199                String joinTableAlias = tableName + "_" + columnName; //alias for table to join since there can be multiple joins to the same table
200                String joinTable = Convert.toDB(Convert.toSimpleName(elementClass.getName())); //table to join
201                String joinField = Convert.toDB(findJoinField((List)fieldValue)); //field to join
202                fromClause.append(" left join ").append(joinTable).append(" ").append(joinTableAlias);
203                fromClause.append(" on ").append(joinTableAlias).append(".").append(joinField).append("_id = ").append(tableName).append(".id");
204                generateRecursively(++level, joinTableAlias, objectPath + "." + fieldName, elementClass, joins, selectClause, fromClause);
205            }
206        }
207    }
208
209    //check if the joins contain the specified join
210    private boolean joinsContain(Set joins, String join) {
211        Iterator it = joins.iterator();
212        while (it.hasNext()) {
213            String str = (String)it.next();
214            if (str.startsWith(join)) {
215                if (str.length() == join.length()) return true;
216                else if (str.charAt(join.length()) == '.') return true;
217            }
218        }
219        return false;
220    }
221
222    //return the join field of the elements in the list
223    private String findJoinField(List list) {
224        if (list instanceof VenList) {
225            return ((VenList)list).getJoinField();
226        } else {
227            //find according to 1.5 generic or some convention (e.g. parent_obj_id)
228            return null;
229        }
230    }
231
232    //--------------------------------------------------------------------------
233    //SETTERS
234    /**
235     * Add the domain packages that will be considered persistent
236     * @param domainPackage the domain package
237     */
238    public void addDomainPackage(String domainPackage) {
239        domainPackages.add(domainPackage);
240    }
241
242    /**
243     * Set debug mode, true will log all debug messages to System.out
244     * <p>
245     * Note: Use debug mode to detect problems only. It is not a general purpose logging mode.
246     * @param debug set true to enable debug mode
247     */
248    public void setDebug(boolean debug) {
249        this.debug = debug;
250    }
251}
Note: See TracBrowser for help on using the repository browser.