Ignore:
Timestamp:
Feb 9, 2011, 10:53:45 PM (13 years ago)
Author:
fmguler
Message:

Refs #3 - Ven.get() is converted to the new format. QueryGenerator and QueryMapper are mostly OK, generateRecursively and mapRecursively are converted and checked. Handles joins (includes/associations) many to one and one to many. For one to many, the reverse join field can be determined in a couple of ways. First way is (prefereed) having VenList as the list implementation which specifies the element class and the join field. Second way is using Java 1.5 generic type to detect element class (not yet implemented) and guessing join field by convention (if multiple joins exist, this won't work). The last way is to have some kind of annotation or configuration, which is of course the least preferred way. VenList has a static method to determine the element class in the object list, which currently calls getElementClass if the list is an instance of VenList. In the future other options can be implemented.

Getting object using joins (includes/associations) are tested using dummy assocations between SomeDomainObject and AnotherDomainObject. The Sample class builds the database, tests the operations and rolls back to the initial state. Database refactoring operations are moved to the LiquibaseUtil for clarity.

In the future, the generated queries will be shortened using hashed aliases, and the criteria subsystem will be implemented.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/fmgVen/src/com/fmguler/ven/QueryGenerator.java

    r27 r28  
    1919 
    2020import com.fmguler.ven.util.Convert; 
     21import com.fmguler.ven.util.VenList; 
    2122import java.beans.PropertyDescriptor; 
    2223import java.util.Date; 
    2324import java.util.HashSet; 
     25import java.util.Iterator; 
     26import java.util.List; 
    2427import java.util.Set; 
    2528import org.springframework.beans.BeanWrapper; 
     
    3336    private Set domainPackages; 
    3437    private Set dbClasses; 
     38    private boolean debug = true; 
    3539 
    3640    public QueryGenerator() { 
     
    4549    } 
    4650 
    47     public String generateSelectQuery() { 
    48         return null; 
     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(); 
    4967    } 
    5068 
     
    129147     * @return the delete SQL query 
    130148     */ 
    131     public String generateDeleteQuery(Class objectClass){ 
     149    public String generateDeleteQuery(Class objectClass) { 
    132150        StringBuffer query = new StringBuffer(); 
    133151        query.append("delete from ").append(Convert.toDB(Convert.toSimpleName(objectClass.getName()))).append(" where id = :id;"); 
    134152        return query.toString(); 
    135153    } 
    136          
     154 
    137155    /** 
    138156     * Generates sequence query for the specified object 
     
    147165 
    148166    //-------------------------------------------------------------------------- 
     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    //-------------------------------------------------------------------------- 
    149231    //SETTERS 
     232    /** 
     233     * Add the domain packages that will be considered persistent 
     234     * @param domainPackage the domain package 
     235     */ 
    150236    public void addDomainPackage(String domainPackage) { 
    151237        domainPackages.add(domainPackage); 
Note: See TracChangeset for help on using the changeset viewer.