Changeset 28
- Timestamp:
- Feb 9, 2011, 10:53:45 PM (14 years ago)
- Location:
- trunk/fmgVen
- Files:
-
- 3 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/fmgVen/etc/test-db/test-db-changelog.xml
r27 r28 15 15 <column name="description" type="TEXT(2147483647)"/> 16 16 <column name="date" type="TIMESTAMP WITHOUT TIME ZONE"/> 17 <column name="another_domain_object_id" type="int"/> 17 18 </createTable> 18 19 </changeSet> 19 20 <changeSet author="fmguler" id="2"> 20 <tagDatabase tag="tag-single-table"/> 21 <createTable schemaName="public" tableName="another_domain_object"> 22 <column autoIncrement="true" name="id" type="serial"> 23 <constraints nullable="false" primaryKey="true" primaryKeyName="another_domain_object_pkey"/> 24 </column> 25 <column name="name" type="VARCHAR(100)"/> 26 <column name="description" type="TEXT(2147483647)"/> 27 <column name="date" type="TIMESTAMP WITHOUT TIME ZONE"/> 28 <column name="some_domain_object_id" type="int"/> 29 </createTable> 21 30 </changeSet> 22 31 <changeSet author="fmguler" id="3"> 32 <tagDatabase tag="tag-no-data"/> 33 </changeSet> 34 <changeSet author="fmguler" id="4"> 23 35 <insert schemaName="public" tableName="some_domain_object"> 24 <column name="id" value="1"/> 25 <column name="name" value="name1"/> 26 <column name="description" value="desc1"/> 36 <column name="name" value="sdo1"/> 37 <column name="description" value="sdo desc1"/> 27 38 <column name="date" value="2010-10-13"/> 39 <column name="another_domain_object_id" value="1"/> 28 40 </insert> 29 41 <rollback> … … 31 43 </rollback> 32 44 </changeSet> 33 <changeSet author="fmguler" id="4"> 34 <tagDatabase tag="tag-single-table-data"/> 45 <changeSet author="fmguler" id="5"> 46 <insert schemaName="public" tableName="another_domain_object"> 47 <column name="name" value="ado1"/> 48 <column name="description" value="ado desc1"/> 49 <column name="date" value="2011-01-19"/> 50 <column name="some_domain_object_id" value="1"/> 51 </insert> 52 <insert schemaName="public" tableName="another_domain_object"> 53 <column name="name" value="ado2"/> 54 <column name="description" value="ado desc2"/> 55 <column name="date" value="2011-02-09"/> 56 <column name="some_domain_object_id" value="1"/> 57 </insert> 58 <rollback> 59 delete from another_domain_object; 60 </rollback> 61 </changeSet> 62 <changeSet author="fmguler" id="6"> 63 <tagDatabase tag="tag-data"/> 35 64 </changeSet> 36 65 <!-- << TEST SCHEMA --> -
trunk/fmgVen/src/com/fmguler/ven/QueryGenerator.java
r27 r28 19 19 20 20 import com.fmguler.ven.util.Convert; 21 import com.fmguler.ven.util.VenList; 21 22 import java.beans.PropertyDescriptor; 22 23 import java.util.Date; 23 24 import java.util.HashSet; 25 import java.util.Iterator; 26 import java.util.List; 24 27 import java.util.Set; 25 28 import org.springframework.beans.BeanWrapper; … … 33 36 private Set domainPackages; 34 37 private Set dbClasses; 38 private boolean debug = true; 35 39 36 40 public QueryGenerator() { … … 45 49 } 46 50 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(); 49 67 } 50 68 … … 129 147 * @return the delete SQL query 130 148 */ 131 public String generateDeleteQuery(Class objectClass) {149 public String generateDeleteQuery(Class objectClass) { 132 150 StringBuffer query = new StringBuffer(); 133 151 query.append("delete from ").append(Convert.toDB(Convert.toSimpleName(objectClass.getName()))).append(" where id = :id;"); 134 152 return query.toString(); 135 153 } 136 154 137 155 /** 138 156 * Generates sequence query for the specified object … … 147 165 148 166 //-------------------------------------------------------------------------- 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 //-------------------------------------------------------------------------- 149 231 //SETTERS 232 /** 233 * Add the domain packages that will be considered persistent 234 * @param domainPackage the domain package 235 */ 150 236 public void addDomainPackage(String domainPackage) { 151 237 domainPackages.add(domainPackage); -
trunk/fmgVen/src/com/fmguler/ven/QueryMapper.java
r23 r28 18 18 package com.fmguler.ven; 19 19 20 import com.fmguler.ven.util.Convert; 21 import com.fmguler.ven.util.VenList; 22 import java.beans.PropertyDescriptor; 23 import java.sql.ResultSet; 24 import java.sql.SQLException; 25 import java.util.ArrayList; 26 import java.util.Date; 27 import java.util.HashSet; 28 import java.util.Iterator; 29 import java.util.LinkedList; 20 30 import java.util.List; 31 import java.util.Map; 32 import java.util.Set; 21 33 import javax.sql.DataSource; 34 import org.springframework.beans.BeanWrapper; 35 import org.springframework.beans.BeanWrapperImpl; 36 import org.springframework.jdbc.core.RowCallbackHandler; 22 37 import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; 23 38 … … 28 43 public class QueryMapper { 29 44 private NamedParameterJdbcTemplate template; 45 private Set domainPackages; 46 private Set dbClasses; 47 private boolean debug = true; 30 48 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); 33 58 } 34 59 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 */ 36 181 public void setDataSource(DataSource dataSource) { 37 182 if (dataSource == null) throw new RuntimeException("fmgVen - DataSource cannot be null"); … … 39 184 } 40 185 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); 43 192 } 44 193 } -
trunk/fmgVen/src/com/fmguler/ven/Ven.java
r27 r28 18 18 package com.fmguler.ven; 19 19 20 import com.fmguler.ven.util.Convert; 20 21 import java.util.HashMap; 21 22 import java.util.List; 22 23 import java.util.Map; 24 import java.util.Set; 23 25 import javax.sql.DataSource; 24 26 import org.springframework.beans.BeanWrapper; … … 36 38 private QueryGenerator generator; 37 39 private QueryMapper mapper; 40 private boolean debug = true; 38 41 39 42 public Ven() { … … 50 53 } 51 54 52 public Object get(int id, Class objectClass) { 53 return null; 55 public Object get(int id, Class objectClass, Set joins) { 56 String query = generator.generateSelectQuery(objectClass, joins); 57 query += " where 1=1 and " + Convert.toDB(Convert.toSimpleName(objectClass.getName())) + ".id = :___id "; 58 59 Map paramMap = new HashMap(); 60 paramMap.put("___id", new Integer(id)); 61 if (debug) System.out.println("Ven - SQL: " + query); 62 63 List result = mapper.list(query, paramMap, objectClass); 64 if (result.isEmpty()) return null; 65 if (result.size() > 1) System.out.println("Ven - WARNING >> get(id) returns more than one row"); 66 return result.get(0); 54 67 } 55 68 … … 114 127 if (dataSource == null) throw new RuntimeException("fmgVen - DataSource cannot be null"); 115 128 this.template = new NamedParameterJdbcTemplate(dataSource); 129 mapper.setDataSource(dataSource); 116 130 } 117 131 -
trunk/fmgVen/test/com/fmguler/ven/sample/Sample.java
r27 r28 18 18 package com.fmguler.ven.sample; 19 19 20 import com.fmguler.ven.LiquibaseUtil; 20 21 import com.fmguler.ven.Ven; 22 import com.fmguler.ven.sample.domain.AnotherDomainObject; 21 23 import com.fmguler.ven.sample.domain.SomeDomainObject; 22 import java.sql.SQLException;23 24 import java.util.Date; 25 import java.util.HashSet; 24 26 import java.util.List; 25 import java.util.Locale; 26 import javax.sql.DataSource; 27 import liquibase.FileSystemFileOpener; 28 import liquibase.exception.JDBCException; 29 import liquibase.exception.LiquibaseException; 30 import org.springframework.jdbc.datasource.DriverManagerDataSource; 31 import liquibase.Liquibase; 32 import liquibase.database.Database; 33 import liquibase.database.DatabaseFactory; 27 import java.util.Set; 34 28 35 29 /** … … 40 34 public static void main(String[] args) { 41 35 //build the sample database 42 buildDatabase();36 LiquibaseUtil.buildDatabase(); 43 37 44 38 //save an object 45 39 testSave(); 40 //get an object 41 testGet(); 46 42 //delete an object 47 43 testDelete(); 48 44 49 45 //rollback the sample database to original state 50 rollbackDatabase();46 LiquibaseUtil.rollbackDatabase("tag-init"); 51 47 } 52 48 … … 76 72 public static void testDelete() { 77 73 Ven ven = getVen(); 78 ven.delete( 1, SomeDomainObject.class);74 ven.delete(2, SomeDomainObject.class); 79 75 } 80 76 … … 84 80 public static void testGet() { 85 81 Ven ven = getVen(); 86 SomeDomainObject obj = (SomeDomainObject)ven.get(1, SomeDomainObject.class); 82 83 //get with includes 84 Set joins = new HashSet(); 85 joins.add("SomeDomainObject.anotherDomainObjects"); 86 joins.add("SomeDomainObject.anotherDomainObject"); 87 SomeDomainObject obj = (SomeDomainObject)ven.get(1, SomeDomainObject.class, joins); 87 88 System.out.println(obj); 89 90 Set joins2 = new HashSet(); 91 joins2.add("AnotherDomainObject.someDomainObject"); 92 AnotherDomainObject obj2 = (AnotherDomainObject)ven.get(1, AnotherDomainObject.class, joins2); 93 System.out.println(obj2); 88 94 } 89 95 … … 109 115 private static Ven getVen() { 110 116 Ven ven = new Ven(); 111 ven.setDataSource( getDataSource());117 ven.setDataSource(LiquibaseUtil.getDataSource()); 112 118 ven.addDomainPackage("com.fmguler.ven.sample.domain").addDomainPackage("another.package"); 113 119 return ven; 114 120 } 115 116 private static DataSource getDataSource() {117 DriverManagerDataSource ds = new DriverManagerDataSource();118 ds.setDriverClassName("org.postgresql.Driver");119 ds.setUsername("postgres");120 ds.setPassword("qwerty");121 ds.setUrl("jdbc:postgresql://127.0.0.1:5432/ven-test");122 return ds;123 }124 125 private static void buildDatabase() {126 try {127 Locale currLocale = Locale.getDefault();128 Locale.setDefault(Locale.ENGLISH);129 Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(getDataSource().getConnection());130 Liquibase liquibase = new Liquibase("etc/test-db/test-db-changelog.xml", new FileSystemFileOpener(), database);131 liquibase.update("");132 Locale.setDefault(currLocale);133 } catch (SQLException ex) {134 ex.printStackTrace();135 } catch (JDBCException ex) {136 ex.printStackTrace();137 } catch (LiquibaseException ex) {138 ex.printStackTrace();139 }140 }141 142 private static void rollbackDatabase() {143 try {144 Locale currLocale = Locale.getDefault();145 Locale.setDefault(Locale.ENGLISH);146 Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(getDataSource().getConnection());147 Liquibase liquibase = new Liquibase("etc/test-db/test-db-changelog.xml", new FileSystemFileOpener(), database);148 liquibase.rollback("tag-single-table", "");149 Locale.setDefault(currLocale);150 } catch (SQLException ex) {151 ex.printStackTrace();152 } catch (JDBCException ex) {153 ex.printStackTrace();154 } catch (LiquibaseException ex) {155 ex.printStackTrace();156 }157 }158 121 } -
trunk/fmgVen/test/com/fmguler/ven/sample/domain/SomeDomainObject.java
r26 r28 18 18 package com.fmguler.ven.sample.domain; 19 19 20 import com.fmguler.ven.util.VenList; 20 21 import java.util.Date; 22 import java.util.List; 21 23 22 24 /** … … 29 31 private String description; 30 32 private Date date; 33 private List anotherDomainObjects = new VenList(AnotherDomainObject.class, "someDomainObject"); 34 private AnotherDomainObject anotherDomainObject = new AnotherDomainObject(); 31 35 32 36 /** … … 86 90 } 87 91 92 /** 93 * @return the list of AnotherDomainObject 94 */ 95 public List getAnotherDomainObjects() { 96 return anotherDomainObjects; 97 } 98 99 /** 100 * @return the anotherDomainObject 101 */ 102 public AnotherDomainObject getAnotherDomainObject() { 103 return anotherDomainObject; 104 } 105 106 /** 107 * @param anotherDomainObject the anotherDomainObject to set 108 */ 109 public void setAnotherDomainObject(AnotherDomainObject anotherDomainObject) { 110 this.anotherDomainObject = anotherDomainObject; 111 } 112 88 113 public String toString() { 89 return id + " " + name + " " + description + " " + date;114 return id + " " + name + " " + description + " another domain object: {" + anotherDomainObject + "} another domain objects:\n" + anotherDomainObjects; 90 115 } 91 116 }
Note: See TracChangeset
for help on using the changeset viewer.