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

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

Fixed typed criteria method parameters from Criteria.eq(String attribute, String value) to eq(String attribute, Object value). They accept values of any type, e.g. int, string, date.

Turned off debug messages by default. They can be enabled by setting Ven.setDebug(true).

Updated to Netbeans 7.0.1, which updated build-impl.xml, nothing to do with functionality.

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