Ignore:
Timestamp:
Feb 9, 2011, 10:53:45 PM (14 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/QueryMapper.java

    r23 r28  
    1818package com.fmguler.ven; 
    1919 
     20import com.fmguler.ven.util.Convert; 
     21import com.fmguler.ven.util.VenList; 
     22import java.beans.PropertyDescriptor; 
     23import java.sql.ResultSet; 
     24import java.sql.SQLException; 
     25import java.util.ArrayList; 
     26import java.util.Date; 
     27import java.util.HashSet; 
     28import java.util.Iterator; 
     29import java.util.LinkedList; 
    2030import java.util.List; 
     31import java.util.Map; 
     32import java.util.Set; 
    2133import javax.sql.DataSource; 
     34import org.springframework.beans.BeanWrapper; 
     35import org.springframework.beans.BeanWrapperImpl; 
     36import org.springframework.jdbc.core.RowCallbackHandler; 
    2237import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 
    2338 
     
    2843public class QueryMapper { 
    2944    private NamedParameterJdbcTemplate template; 
     45    private Set domainPackages; 
     46    private Set dbClasses; 
     47    private boolean debug = true; 
    3048 
    31     public List list() { 
    32         return null; 
     49    public QueryMapper() { 
     50        domainPackages = new HashSet(); 
     51        dbClasses = new HashSet(); 
     52        //the predefined database classes; 
     53        this.dbClasses.add(Integer.class); 
     54        this.dbClasses.add(String.class); 
     55        this.dbClasses.add(Date.class); 
     56        this.dbClasses.add(Double.class); 
     57        this.dbClasses.add(Boolean.class); 
    3358    } 
    3459 
    35     //SETTERS------------------------------------------------------------------- 
     60    /** 
     61     * Executes the specified query setting the specified parameters, 
     62     * and maps the results to the instances of the specified objectClass. 
     63     * @param query select SQL query in the conventional format 
     64     * @param parameters named query parameter values 
     65     * @param objectClass the type of the object to be mapped 
     66     * @return the list of mapped objects 
     67     */ 
     68    public List list(String query, Map parameters, final Class objectClass) { 
     69        long t1 = System.currentTimeMillis(); 
     70        final List results = new LinkedList(); 
     71        final String tableName = Convert.toDB(Convert.toSimpleName(objectClass.getName())); 
     72        final Set columns = new HashSet(); 
     73 
     74        template.query(query, parameters, new RowCallbackHandler() { 
     75            public void processRow(ResultSet rs) throws SQLException { 
     76                enumerateColumns(columns, rs); 
     77                mapRecursively(rs, columns, tableName, objectClass, results); 
     78            } 
     79        }); 
     80 
     81        System.out.println("Ven - list time = " + (System.currentTimeMillis() - t1)); 
     82        return results; 
     83    } 
     84 
     85    //-------------------------------------------------------------------------- 
     86    //PRIVATE METHODS 
     87    //recursively map the resultSet to the object and add to the list 
     88    protected void mapRecursively(ResultSet rs, Set columns, String tableName, Class objectClass, List parentList) { 
     89        try { 
     90            if (!columns.contains(tableName + "_id")) return; //this object does not exist in the columns 
     91            Object id = rs.getObject(tableName + "_id"); 
     92            if (id == null) return; //this object exists in the columns but null, probably because of left join 
     93 
     94            //create bean wrapper for the object class 
     95            BeanWrapper wr = new BeanWrapperImpl(objectClass); //already caches class introspection (CachedIntrospectionResults.forClass()) 
     96            wr.setPropertyValue("id", id); //set the id property 
     97            Object object = wr.getWrappedInstance(); 
     98            boolean map = true; 
     99 
     100            //check if this object exists in the parent list (since SQL joins are cartesian products, do not create new object if this row is just the same as previous) 
     101            for (Iterator it = parentList.iterator(); it.hasNext();) { 
     102                Object objectInList = (Object)it.next(); 
     103                if (objectIdEquals(objectInList, id)) { 
     104                    wr.setWrappedInstance(objectInList); //already exists in the list, use that instance 
     105                    map = false; // and do not map again 
     106                    break; 
     107                } 
     108            } 
     109            if (map) parentList.add(object); //could not find in the parent list, add the new object 
     110 
     111            PropertyDescriptor[] pdArr = wr.getPropertyDescriptors(); 
     112            for (int i = 0; i < pdArr.length; i++) { 
     113                PropertyDescriptor pd = pdArr[i]; 
     114                Class fieldClass = pd.getPropertyType(); //field class 
     115                String fieldName = Convert.toDB(pd.getName()); //field name 
     116                Object fieldValue = wr.getPropertyValue(pd.getName()); 
     117                String columnName = tableName + "_" + fieldName; 
     118 
     119                //database class (primitive property) 
     120                if (map && dbClasses.contains(fieldClass)) { 
     121                    if (columns.contains(columnName)) { 
     122                        if (debug) System.out.println(">>field is found: " + columnName); 
     123                        wr.setPropertyValue(pd.getName(), rs.getObject(columnName)); 
     124                    } else { 
     125                        if (debug) System.out.println("--field not found: " + columnName); 
     126                    } 
     127                } 
     128 
     129                //many to one association (object property) 
     130                if (map && fieldClass.getPackage() != null && domainPackages.contains(fieldClass.getPackage().getName())) { 
     131                    if (columns.contains(columnName + "_id")) { 
     132                        if (debug) System.out.println(">>object is found " + columnName); 
     133                        List list = new ArrayList(1); //we know there will be single result 
     134                        mapRecursively(rs, columns, columnName, fieldClass, list); 
     135                        if (list.size() > 0) wr.setPropertyValue(pd.getName(), list.get(0)); 
     136                    } else { 
     137                        if (debug) System.out.println("--object not found: " + columnName); 
     138                    } 
     139                } 
     140 
     141                //one to many association (list property) 
     142                if (fieldValue instanceof List) { //Note: here recurring row's list property is mapped and add to parent's list 
     143                    if (columns.contains(columnName + "_id")) { 
     144                        Class elementClass = VenList.findElementClass((List)fieldValue); 
     145                        if (debug) System.out.println(">>list is found " + columnName); 
     146                        mapRecursively(rs, columns, columnName, elementClass, (List)fieldValue); 
     147                    } else { 
     148                        if (debug) System.out.println("--list not found: " + columnName); 
     149                    } 
     150                } 
     151            } 
     152        } catch (Exception ex) { 
     153            if (debug) { 
     154                System.out.println("Ven - error while mapping row; "); 
     155                ex.printStackTrace(); 
     156            } 
     157        } 
     158    } 
     159 
     160    //enumerate columns from the resultset 
     161    private void enumerateColumns(Set columns, ResultSet rs) throws SQLException { 
     162        if (!columns.isEmpty()) return; 
     163        for (int i = 1; i < rs.getMetaData().getColumnCount() + 1; i++) { 
     164            columns.add(rs.getMetaData().getColumnName(i)); 
     165        } 
     166    } 
     167 
     168    //check if the specified objects are the same entity (according to id fields) 
     169    private boolean objectIdEquals(Object object, Object id) { 
     170        //return obj1.equals(obj2); //objects need to implement equals() 
     171        //TODO: more efficient (invoke getId method) 
     172        BeanWrapper wr = new BeanWrapperImpl(object); 
     173        return id.equals(wr.getPropertyValue("id")); 
     174    } 
     175 
     176    //-------------------------------------------------------------------------- 
     177    //SETTERS 
     178    /** 
     179     * @param dataSource used for accessing database 
     180     */ 
    36181    public void setDataSource(DataSource dataSource) { 
    37182        if (dataSource == null) throw new RuntimeException("fmgVen - DataSource cannot be null"); 
     
    39184    } 
    40185 
    41     public void addDomainPackage(String domainPackage){ 
    42  
     186    /** 
     187     * Add the domain packages that will be considered persistent 
     188     * @param domainPackage the domain package 
     189     */ 
     190    public void addDomainPackage(String domainPackage) { 
     191        domainPackages.add(domainPackage); 
    43192    } 
    44193} 
Note: See TracChangeset for help on using the changeset viewer.