You can subscribe to this list here.
2002 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
(1) |
Oct
(81) |
Nov
|
Dec
|
---|
From: <me...@us...> - 2002-10-01 03:37:33
|
Update of /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne In directory usw-pr-cvs1:/tmp/cvs-serv6637/src/cayenne/java/org/objectstyle/cayenne Modified Files: package.html Log Message: fixed UserGuide link Index: package.html =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/package.html,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- package.html 8 Sep 2002 22:46:46 -0000 1.1 +++ package.html 1 Oct 2002 03:37:30 -0000 1.2 @@ -3,7 +3,7 @@ Core Cayenne classes and interfaces. This package defines API to work with Cayenne DataObjects. -<p><i>For more information see <a href="../../../../../userguide/index.html" +<p><i>For more information see <a href="../../../../userguide/index.html" target="_top">Cayenne User Guide.</a></i></p> </body> </html> |
From: <me...@us...> - 2002-10-01 03:15:29
|
Update of /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne In directory usw-pr-cvs1:/tmp/cvs-serv2251/src/cayenne/java/org/objectstyle/cayenne Modified Files: CayenneDataObject.java Log Message: applied patch 616812 by Craig Miskell Index: CayenneDataObject.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/CayenneDataObject.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- CayenneDataObject.java 8 Sep 2002 22:46:46 -0000 1.1 +++ CayenneDataObject.java 1 Oct 2002 03:15:26 -0000 1.2 @@ -52,17 +52,23 @@ * information on the ObjectStyle Group, please see * <http://objectstyle.org/>. * - */ + */ package org.objectstyle.cayenne; -import java.util.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; import org.apache.log4j.Logger; import org.objectstyle.cayenne.access.DataContext; import org.objectstyle.cayenne.map.ObjRelationship; import org.objectstyle.cayenne.query.SelectQuery; - /** * A CayenneDataObject is a default implementation of DataObject interface. * It is normally used as a superclass of Cayenne persistent objects. @@ -81,21 +87,21 @@ * Used for debugging. */ public static String persistenceStateString(int persistenceState) { - switch(persistenceState) { - case PersistenceState.TRANSIENT: - return "transient"; - case PersistenceState.NEW: - return "new"; - case PersistenceState.MODIFIED: - return "modified"; - case PersistenceState.COMMITTED: - return "committed"; - case PersistenceState.HOLLOW: - return "hollow"; - case PersistenceState.DELETED: - return "deleted"; - default: - return "unknown"; + switch (persistenceState) { + case PersistenceState.TRANSIENT : + return "transient"; + case PersistenceState.NEW : + return "new"; + case PersistenceState.MODIFIED : + return "modified"; + case PersistenceState.COMMITTED : + return "committed"; + case PersistenceState.HOLLOW : + return "hollow"; + case PersistenceState.DELETED : + return "deleted"; + default : + return "unknown"; } } @@ -104,7 +110,6 @@ protected transient DataContext dataContext; protected HashMap props = new HashMap(); - /** Returns a data context this object is registered with, or null * if this object has no associated DataContext */ public DataContext getDataContext() { @@ -162,34 +167,32 @@ * */ public Object readNestedProperty(String path) { - StringTokenizer toks = new StringTokenizer(path, "."); - - Object obj = null; - CayenneDataObject dataObj = this; - boolean terminal = false; - while(toks.hasMoreTokens()) { - if(terminal) { - throw new CayenneRuntimeException("Invalid path: " + path); - } - String pathComp = toks.nextToken(); - obj = dataObj.readProperty(pathComp); - - if(obj == null) { - return null; - } - else if(obj instanceof CayenneDataObject) { - dataObj = (CayenneDataObject)obj; - } - else { - terminal = true; - } - } - - return obj; - } + StringTokenizer toks = new StringTokenizer(path, "."); + + Object obj = null; + CayenneDataObject dataObj = this; + boolean terminal = false; + while (toks.hasMoreTokens()) { + if (terminal) { + throw new CayenneRuntimeException("Invalid path: " + path); + } + String pathComp = toks.nextToken(); + obj = dataObj.readProperty(pathComp); + + if (obj == null) { + return null; + } else if (obj instanceof CayenneDataObject) { + dataObj = (CayenneDataObject) obj; + } else { + terminal = true; + } + } + + return obj; + } protected Object readProperty(String propName) { - if(persistenceState == PersistenceState.HOLLOW) { + if (persistenceState == PersistenceState.HOLLOW) { dataContext.refetchObject(objectId); } @@ -200,9 +203,8 @@ return props.get(propName); } - protected void writeProperty(String propName, Object val) { - if(persistenceState == PersistenceState.COMMITTED) { + if (persistenceState == PersistenceState.COMMITTED) { persistenceState = PersistenceState.MODIFIED; } @@ -217,82 +219,82 @@ Object toOneTarget = readProperty(relName); // known to be NULL - if(toOneTarget == nullValue) { + if (toOneTarget == nullValue) { return null; } // known to be NOT NULL - if(toOneTarget != null) { - return (DataObject)toOneTarget; + if (toOneTarget != null) { + return (DataObject) toOneTarget; } - // need to fetch - SelectQuery sel = QueryHelper.selectRelationshipObjects(dataContext, objectId, relName); + SelectQuery sel = + QueryHelper.selectRelationshipObjects(dataContext, objectId, relName); List results = dataContext.performQuery(sel); // unexpected - if(results.size() > 1) { - throw new CayenneRuntimeException("error retrieving 'to one' target, found " + results.size()); + if (results.size() > 1) { + throw new CayenneRuntimeException( + "error retrieving 'to one' target, found " + results.size()); } // null target - if(results.size() == 0) { + if (results.size() == 0) { writePropertyDirectly(relName, nullValue); return null; } // found a valid object - DataObject dobj = (DataObject)results.get(0); + DataObject dobj = (DataObject) results.get(0); writePropertyDirectly(relName, dobj); return dobj; } public void removeToManyTarget(String relName, DataObject val, boolean setReverse) { - List relList = (List)readProperty(relName); + List relList = (List) readProperty(relName); relList.remove(val); - if(val != null && setReverse) { + if (val != null && setReverse) { unsetReverseRelationship(relName, val); } } public void addToManyTarget(String relName, DataObject val, boolean setReverse) { - List relList = (List)readProperty(relName); + List relList = (List) readProperty(relName); relList.add(val); - if(val != null && setReverse) + if (val != null && setReverse) setReverseRelationship(relName, val); } public void setToOneDependentTarget(String relName, DataObject val) { - if(val == null) + if (val == null) val = nullValue; setToOneTarget(relName, val, true); } public void setToOneTarget(String relName, DataObject val, boolean setReverse) { - DataObject oldTarget = (DataObject)readPropertyDirectly(relName); - if(oldTarget == val) { + DataObject oldTarget = (DataObject) readPropertyDirectly(relName); + if (oldTarget == val) { return; } - if(setReverse) { + if (setReverse) { // unset old reverse relationship - if(oldTarget != null) + if (oldTarget != null) unsetReverseRelationship(relName, oldTarget); // set new reverse relationship - if(val != null) + if (val != null) setReverseRelationship(relName, val); } writeProperty(relName, val); } - /** * Initializes reverse relationship from object <code>val</code> * to this object. @@ -301,10 +303,13 @@ * to <code>val</code>. */ protected void setReverseRelationship(String relName, DataObject val) { - ObjRelationship rel = (ObjRelationship)dataContext.lookupEntity(objectId.getObjEntityName()).getRelationship(relName); - ObjRelationship revRel = rel.getReverseRelationship(); - if(revRel != null) { - if(revRel.isToMany()) + ObjRelationship rel = + (ObjRelationship) dataContext.lookupEntity( + objectId.getObjEntityName()).getRelationship( + relName); + ObjRelationship revRel = rel.getReverseRelationship(); + if (revRel != null) { + if (revRel.isToMany()) val.addToManyTarget(revRel.getName(), this, false); else val.setToOneTarget(revRel.getName(), this, false); @@ -314,19 +319,21 @@ /** Remove current object from reverse relationship of object <code>val</code> to this object. * @param relName name of relationship from this object to <code>val</code>. */ protected void unsetReverseRelationship(String relName, DataObject val) { - ObjRelationship rel = (ObjRelationship)dataContext.lookupEntity(objectId.getObjEntityName()).getRelationship(relName); + ObjRelationship rel = + (ObjRelationship) dataContext.lookupEntity( + objectId.getObjEntityName()).getRelationship( + relName); ObjRelationship revRel = rel.getReverseRelationship(); - if(revRel != null) { - if(revRel.isToMany()) + if (revRel != null) { + if (revRel.isToMany()) val.removeToManyTarget(revRel.getName(), this, false); - else if(revRel.isToDependentEntity()) + else if (revRel.isToDependentEntity()) val.setToOneTarget(revRel.getName(), nullValue, false); else val.setToOneTarget(revRel.getName(), null, false); } } - public Map getCommittedSnapshot() { return dataContext.getCommittedSnapshot(this); } @@ -335,39 +342,36 @@ return dataContext.takeObjectSnapshot(this); } - - /** A variation of "toString" method, that may be more efficient in some cases. * For example when printing a list of objects into the same String. */ public StringBuffer toStringBuffer(StringBuffer buf, boolean fullDesc) { // log all properties buf.append('{'); - if(fullDesc) + if (fullDesc) appendProperties(buf); - buf.append("<oid: ") - .append(objectId) - .append("; state: ") - .append(persistenceStateString(persistenceState)) - .append(">}\n"); + buf + .append("<oid: ") + .append(objectId) + .append("; state: ") + .append(persistenceStateString(persistenceState)) + .append(">}\n"); return buf; } protected void appendProperties(StringBuffer buf) { buf.append("["); Iterator it = props.keySet().iterator(); - while(it.hasNext()) { + while (it.hasNext()) { Object key = it.next(); buf.append('\t').append(key).append(" => "); Object val = props.get(key); - if(val instanceof CayenneDataObject) { - ((CayenneDataObject)val).toStringBuffer(buf, false); - } else if(val instanceof List) { - buf.append('(') - .append(val.getClass().getName()) - .append(')'); + if (val instanceof CayenneDataObject) { + ((CayenneDataObject) val).toStringBuffer(buf, false); + } else if (val instanceof List) { + buf.append('(').append(val.getClass().getName()).append(')'); } else buf.append(val); @@ -377,16 +381,54 @@ buf.append("]"); } - public String toString() { return toStringBuffer(new StringBuffer(), true).toString(); } - - - /** - * Default implementation does nothing. - * - * @see org.objectstyle.cayenne.DataObject#fetchFinished() - */ - public void fetchFinished() {} + + /** + * Default implementation does nothing. + * + * @see org.objectstyle.cayenne.DataObject#fetchFinished() + */ + public void fetchFinished() {} + + private void writeObject(ObjectOutputStream out) throws IOException { + out.writeInt(persistenceState); + + switch (persistenceState) { + //New, modified or transient - write the whole shebang + //The other states (committed, hollow, deleted) all need just ObjectId + case PersistenceState.TRANSIENT : + case PersistenceState.NEW : + case PersistenceState.MODIFIED : + out.writeObject(props); + break; + } + + out.writeObject(objectId); + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException { + this.persistenceState = in.readInt(); + + switch (persistenceState) { + case PersistenceState.TRANSIENT : + case PersistenceState.NEW : + case PersistenceState.MODIFIED : + props = (HashMap) in.readObject(); + break; + case PersistenceState.COMMITTED : + case PersistenceState.HOLLOW : + case PersistenceState.DELETED : + this.persistenceState = PersistenceState.HOLLOW; + //props will be populated when required (readProperty called) + props = new HashMap(); + break; + } + + this.objectId = (ObjectId) in.readObject(); + // dataContext will be set *IFF* the datacontext it came from is also + // deserialized. Setting of datacontext is handled by the datacontext itself + } } |
From: <me...@us...> - 2002-10-01 03:15:29
|
Update of /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/access In directory usw-pr-cvs1:/tmp/cvs-serv2251/src/cayenne/java/org/objectstyle/cayenne/access Modified Files: DataContext.java Log Message: applied patch 616812 by Craig Miskell Index: DataContext.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/access/DataContext.java,v retrieving revision 1.10 retrieving revision 1.11 diff -u -d -r1.10 -r1.11 --- DataContext.java 19 Sep 2002 03:14:23 -0000 1.10 +++ DataContext.java 1 Oct 2002 03:15:26 -0000 1.11 @@ -110,7 +110,8 @@ //Will not be directly serialized - see read/writeObject for details protected transient QueryEngine parent; - protected Map registeredMap = Collections.synchronizedMap(new HashMap()); + //Must be deserialized slightly differently - see read/writeObject + protected transient Map registeredMap = Collections.synchronizedMap(new HashMap()); protected Map committedSnapshots = Collections.synchronizedMap(new HashMap()); protected RelationshipDataSource relDataSource = new RelationshipDataSource(); @@ -1183,6 +1184,10 @@ out.writeObject(this.parent); //Hope that whatever this.parent is, that it is Serializable } + + //For writing, just write the objects. They will be serialized possibly + // as just objectIds... it's up to the object itself. Reading will do magic + out.writeObject(registeredMap); } private void readObject(ObjectInputStream in) @@ -1209,6 +1214,20 @@ + value); } + //CayenneDataObjects have a transient datacontext + // because at deserialize time the datacontext may need to be different + + // than the one at serialize time (for programmer defined reasons). + // So, when a dataobject is resurrected because it's datacontext was + // serialized, it will then set the objects datacontext to the correctone + // If deser'd "otherwise", it will not have a datacontext (good) + + this.registeredMap = (Map) in.readObject(); + Iterator it = registeredMap.values().iterator(); + while (it.hasNext()) { + DataObject obj = (DataObject) it.next(); + obj.setDataContext(this); + } } /** |
From: <me...@us...> - 2002-10-01 02:51:47
|
Update of /cvsroot/cayenne/cayenne/src/tests/java/org/objectstyle/cayenne/access/trans In directory usw-pr-cvs1:/tmp/cvs-serv26594/src/tests/java/org/objectstyle/cayenne/access/trans Modified Files: SelectTranslatorTst.java Log Message: added test case checking that translator would not generate unneeded aliases Index: SelectTranslatorTst.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/tests/java/org/objectstyle/cayenne/access/trans/SelectTranslatorTst.java,v retrieving revision 1.2 retrieving revision 1.3 diff -C2 -d -r1.2 -r1.3 *** SelectTranslatorTst.java 1 Oct 2002 02:47:15 -0000 1.2 --- SelectTranslatorTst.java 1 Oct 2002 02:51:44 -0000 1.3 *************** *** 269,272 **** --- 269,316 ---- } + /** + * Test aliases when the same table used in more then 1 relationship. + * Check translation of relationship path "ArtistExhibit.toArtist.artistName" + * and "ArtistExhibit.toArtist.paintingArray.paintingTitle". + */ + public void testCreateSqlString6() throws Exception { + Connection con = getSharedConnection(); + + try { + // query with qualifier and ordering + q.setObjEntityName("ArtistExhibit"); + q.setQualifier( + ExpressionFactory.binaryPathExp( + Expression.LIKE, + "toArtist.artistName", + "a%")); + q.andQualifier( + ExpressionFactory.binaryPathExp( + Expression.LIKE, + "toArtist.paintingArray.paintingTitle", + "p%")); + + SelectTranslator transl = buildTranslator(con); + String generatedSql = transl.createSqlString(); + // logObj.warn("Query: " + generatedSql); + + // do some simple assertions to make sure all parts are in + assertNotNull(generatedSql); + assertTrue(generatedSql.startsWith("SELECT ")); + assertTrue(generatedSql.indexOf(" FROM ") > 0); + assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM ")); + + // check that there is only one distinct alias for the ARTIST table + int ind1 = generatedSql.indexOf("ARTIST t", generatedSql.indexOf(" FROM ")); + assertTrue(ind1 > 0); + + int ind2 = generatedSql.indexOf("ARTIST t", ind1 + 1); + assertTrue(ind2 < 0); + } finally { + con.close(); + } + } + + public void testBuildColumnList1() throws Exception { Connection con = getSharedConnection(); |
From: <me...@us...> - 2002-10-01 02:47:18
|
Update of /cvsroot/cayenne/cayenne/src/tests/java/org/objectstyle/cayenne/access/trans In directory usw-pr-cvs1:/tmp/cvs-serv24600/src/tests/java/org/objectstyle/cayenne/access/trans Modified Files: QualifierTranslatorTst.java SelectTranslatorTst.java Log Message: added test case for the recent translator fix Index: QualifierTranslatorTst.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/tests/java/org/objectstyle/cayenne/access/trans/QualifierTranslatorTst.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** QualifierTranslatorTst.java 8 Sep 2002 23:02:01 -0000 1.1 --- QualifierTranslatorTst.java 1 Oct 2002 02:47:15 -0000 1.2 *************** *** 91,96 **** } } ! ! public void testNullQualifier() throws java.lang.Exception { try { assertNull(new QualifierTranslator(qa).doTranslation()); --- 91,96 ---- } } ! ! public void testNullQualifier() throws Exception { try { assertNull(new QualifierTranslator(qa).doTranslation()); Index: SelectTranslatorTst.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/tests/java/org/objectstyle/cayenne/access/trans/SelectTranslatorTst.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** SelectTranslatorTst.java 8 Sep 2002 23:02:01 -0000 1.1 --- SelectTranslatorTst.java 1 Oct 2002 02:47:15 -0000 1.2 *************** *** 73,324 **** public class SelectTranslatorTst extends CayenneTestCase { ! static Logger logObj = ! Logger.getLogger(SelectTranslatorTst.class.getName()); ! protected SelectQuery q; ! protected DbEntity artistEnt; ! public SelectTranslatorTst(String name) { ! super(name); ! } ! protected void setUp() throws Exception { ! q = new SelectQuery(); ! artistEnt = getSharedDomain().lookupEntity("Artist").getDbEntity(); ! } ! private SelectTranslator buildTranslator(Connection con) throws Exception { ! SelectTranslator t = new SelectTranslator(); ! t.setAdapter(getSharedNode().getAdapter()); ! t.setCon(con); ! t.setEngine(getSharedDomain()); ! t.setQuery(q); ! return t; ! } ! /** ! * Tests query creation with qualifier and ordering. ! */ ! public void testCreateSqlString1() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("Artist"); ! q.setQualifier( ! ExpressionFactory.binaryExp( ! Expression.LIKE, ! "artistName", ! "a%")); ! q.addOrdering("dateOfBirth", Ordering.ASC); ! String generatedSql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(generatedSql); ! assertTrue(generatedSql.startsWith("SELECT ")); ! assertTrue(generatedSql.indexOf(" FROM ") > 0); ! assertTrue( ! generatedSql.indexOf(" WHERE ") ! > generatedSql.indexOf(" FROM ")); ! assertTrue( ! generatedSql.indexOf(" ORDER BY ") ! > generatedSql.indexOf(" WHERE ")); ! } finally { ! con.close(); ! } ! } ! /** ! * Tests query creation with "distinct" specified. ! */ ! public void testCreateSqlString2() throws java.lang.Exception { ! Connection con = getSharedConnection(); ! try { ! // query with "distinct" set ! q.setObjEntityName("Artist"); ! q.setDistinct(true); ! String generatedSql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(generatedSql); ! assertTrue(generatedSql.startsWith("SELECT DISTINCT")); ! } finally { ! con.close(); ! } ! } ! /** ! * Tests query creation with relationship from derived entity. ! */ ! public void testCreateSqlString3() throws Exception { ! ObjectId id = new ObjectId("Artist", "ARTIST_ID", 35); ! Artist a1 = (Artist) createDataContext().registeredObject(id); ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("ArtistAssets"); ! q.setQualifier(ExpressionFactory.matchExp("toArtist", a1)); ! String sql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(sql); ! assertTrue(sql.startsWith("SELECT ")); ! assertTrue(sql.indexOf(" FROM ") > 0); ! // no WHERE clause ! assertTrue(sql.indexOf(" WHERE ") < 0); ! assertTrue(sql.indexOf(" GROUP BY ") > 0); ! assertTrue(sql.indexOf("ARTIST_ID =") > 0); ! assertTrue(sql.indexOf("ARTIST_ID =") > sql.indexOf(" GROUP BY ")); ! } finally { ! con.close(); ! } ! } ! /** ! * Tests query creation with relationship from derived entity. ! */ ! public void testCreateSqlString4() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("ArtistAssets"); ! q.setParentObjEntityName("Painting"); ! q.setParentQualifier( ! ExpressionFactory.matchExp("toArtist.artistName", "abc")); ! q.setQualifier(ExpressionFactory.matchExp("estimatedPrice", new BigDecimal(3))); ! String sql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(sql); ! assertTrue(sql.startsWith("SELECT ")); ! assertTrue(sql.indexOf(" FROM ") > 0); ! // no WHERE clause ! assertTrue("WHERE clause is expected: " + sql, sql.indexOf(" WHERE ") > 0); ! assertTrue( ! "GROUP BY clause is expected:" + sql, ! sql.indexOf(" GROUP BY ") > 0); ! assertTrue( ! "HAVING clause is expected", ! sql.indexOf(" HAVING ") > 0); ! assertTrue(sql.indexOf("ARTIST_ID =") > 0); ! assertTrue( ! "Relationship join must be in WHERE: " + sql, ! sql.indexOf("ARTIST_ID =") > sql.indexOf(" WHERE ")); ! assertTrue( ! "Relationship join must be in WHERE: " + sql, ! sql.indexOf("ARTIST_ID =") < sql.indexOf(" GROUP BY ")); ! assertTrue( ! "Qualifier for related entity must be in WHERE: " + sql, ! sql.indexOf("ARTIST_NAME") > sql.indexOf(" WHERE ")); ! assertTrue( ! "Qualifier for related entity must be in WHERE: " + sql, ! sql.indexOf("ARTIST_NAME") < sql.indexOf(" GROUP BY ")); ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList1() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with entity that maps one-to-one to DbEntity ! q.setObjEntityName("Artist"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! List dbAttrs = artistEnt.getAttributeList(); ! assertEquals(dbAttrs.size(), columns.size()); ! Iterator it = dbAttrs.iterator(); ! while (it.hasNext()) { ! assertTrue(columns.contains(it.next())); ! } ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList2() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with custom attributes ! q.setObjEntityName("Artist"); ! q.addCustDbAttribute("ARTIST_ID"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! Object[] dbAttrs = ! new Object[] { artistEnt.getAttribute("ARTIST_ID")}; ! assertEquals(dbAttrs.length, columns.size()); ! for (int i = 0; i < dbAttrs.length; i++) { ! assertTrue(columns.contains(dbAttrs[i])); ! } ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList3() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with entity that maps to a subset of DbEntity ! q.setObjEntityName("SubPainting"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! ObjEntity subPainting = ! getSharedDomain().lookupEntity("SubPainting"); ! // assert that the number of attributes in the query is right ! // 1 (obj attr) + 1 (pk) = 2 ! assertEquals(2, columns.size()); ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList4() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with derived entity that maps to a subset of DbEntity ! q.setObjEntityName("ArtistPaintingCounts"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! ObjEntity countsEnt = ! getSharedDomain().lookupEntity("ArtistPaintingCounts"); ! // assert that the number of attributes in the query is right ! // 1 (obj attr) + 1 (pk) = 2 ! assertEquals(2, columns.size()); ! } finally { ! con.close(); ! } ! } } --- 73,361 ---- public class SelectTranslatorTst extends CayenneTestCase { ! static Logger logObj = Logger.getLogger(SelectTranslatorTst.class.getName()); ! protected SelectQuery q; ! protected DbEntity artistEnt; ! public SelectTranslatorTst(String name) { ! super(name); ! } ! protected void setUp() throws Exception { ! q = new SelectQuery(); ! artistEnt = getSharedDomain().lookupEntity("Artist").getDbEntity(); ! } ! private SelectTranslator buildTranslator(Connection con) throws Exception { ! SelectTranslator transl = ! (SelectTranslator) getSharedNode().getAdapter().getQueryTranslator(q); ! transl.setEngine(getSharedNode()); ! transl.setCon(con); ! return transl; ! } ! /** ! * Tests query creation with qualifier and ordering. ! */ ! public void testCreateSqlString1() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("Artist"); ! q.setQualifier( ! ExpressionFactory.binaryExp(Expression.LIKE, "artistName", "a%")); ! q.addOrdering("dateOfBirth", Ordering.ASC); ! String generatedSql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(generatedSql); ! assertTrue(generatedSql.startsWith("SELECT ")); ! assertTrue(generatedSql.indexOf(" FROM ") > 0); ! assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM ")); ! assertTrue( ! generatedSql.indexOf(" ORDER BY ") > generatedSql.indexOf(" WHERE ")); ! } finally { ! con.close(); ! } ! } ! /** ! * Tests query creation with "distinct" specified. ! */ ! public void testCreateSqlString2() throws java.lang.Exception { ! Connection con = getSharedConnection(); ! try { ! // query with "distinct" set ! q.setObjEntityName("Artist"); ! q.setDistinct(true); ! String generatedSql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(generatedSql); ! assertTrue(generatedSql.startsWith("SELECT DISTINCT")); ! } finally { ! con.close(); ! } ! } ! /** ! * Tests query creation with relationship from derived entity. ! */ ! public void testCreateSqlString3() throws Exception { ! ObjectId id = new ObjectId("Artist", "ARTIST_ID", 35); ! Artist a1 = (Artist) createDataContext().registeredObject(id); ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("ArtistAssets"); ! q.setQualifier(ExpressionFactory.matchExp("toArtist", a1)); ! String sql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(sql); ! assertTrue(sql.startsWith("SELECT ")); ! assertTrue(sql.indexOf(" FROM ") > 0); ! // no WHERE clause ! assertTrue(sql.indexOf(" WHERE ") < 0); ! assertTrue(sql.indexOf(" GROUP BY ") > 0); ! assertTrue(sql.indexOf("ARTIST_ID =") > 0); ! assertTrue(sql.indexOf("ARTIST_ID =") > sql.indexOf(" GROUP BY ")); ! } finally { ! con.close(); ! } ! } ! /** ! * Tests query creation with relationship from derived entity. ! */ ! public void testCreateSqlString4() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("ArtistAssets"); ! q.setParentObjEntityName("Painting"); ! q.setParentQualifier( ! ExpressionFactory.matchExp("toArtist.artistName", "abc")); ! q.setQualifier( ! ExpressionFactory.matchExp("estimatedPrice", new BigDecimal(3))); ! String sql = buildTranslator(con).createSqlString(); ! // do some simple assertions to make sure all parts are in ! assertNotNull(sql); ! assertTrue(sql.startsWith("SELECT ")); ! assertTrue(sql.indexOf(" FROM ") > 0); ! // no WHERE clause ! assertTrue("WHERE clause is expected: " + sql, sql.indexOf(" WHERE ") > 0); ! assertTrue( ! "GROUP BY clause is expected:" + sql, ! sql.indexOf(" GROUP BY ") > 0); ! assertTrue("HAVING clause is expected", sql.indexOf(" HAVING ") > 0); ! assertTrue(sql.indexOf("ARTIST_ID =") > 0); ! assertTrue( ! "Relationship join must be in WHERE: " + sql, ! sql.indexOf("ARTIST_ID =") > sql.indexOf(" WHERE ")); ! assertTrue( ! "Relationship join must be in WHERE: " + sql, ! sql.indexOf("ARTIST_ID =") < sql.indexOf(" GROUP BY ")); ! assertTrue( ! "Qualifier for related entity must be in WHERE: " + sql, ! sql.indexOf("ARTIST_NAME") > sql.indexOf(" WHERE ")); ! assertTrue( ! "Qualifier for related entity must be in WHERE: " + sql, ! sql.indexOf("ARTIST_NAME") < sql.indexOf(" GROUP BY ")); ! } finally { ! con.close(); ! } ! } ! /** ! * Test aliases when the same table used in more then 1 relationship. ! * Check translation of relationship path "ArtistExhibit.toArtist.artistName" ! * and "ArtistExhibit.toExhibit.toGallery.paintingArray.toArtist.artistName". ! */ ! public void testCreateSqlString5() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // query with qualifier and ordering ! q.setObjEntityName("ArtistExhibit"); ! q.setQualifier( ! ExpressionFactory.binaryPathExp( ! Expression.LIKE, ! "toArtist.artistName", ! "a%")); ! q.andQualifier( ! ExpressionFactory.binaryPathExp( ! Expression.LIKE, ! "toExhibit.toGallery.paintingArray.toArtist.artistName", ! "a%")); ! SelectTranslator transl = buildTranslator(con); ! String generatedSql = transl.createSqlString(); ! // logObj.warn("Query: " + generatedSql); ! // do some simple assertions to make sure all parts are in ! assertNotNull(generatedSql); ! assertTrue(generatedSql.startsWith("SELECT ")); ! assertTrue(generatedSql.indexOf(" FROM ") > 0); ! assertTrue(generatedSql.indexOf(" WHERE ") > generatedSql.indexOf(" FROM ")); ! // check that there are 2 distinct aliases for the ARTIST table ! int ind1 = generatedSql.indexOf("ARTIST t", generatedSql.indexOf(" FROM ")); ! assertTrue(ind1 > 0); ! int ind2 = generatedSql.indexOf("ARTIST t", ind1 + 1); ! assertTrue(ind2 > 0); ! assertTrue( ! generatedSql.charAt(ind1 + "ARTIST t".length()) ! != generatedSql.charAt(ind2 + "ARTIST t".length())); ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList1() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with entity that maps one-to-one to DbEntity ! q.setObjEntityName("Artist"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! List dbAttrs = artistEnt.getAttributeList(); ! assertEquals(dbAttrs.size(), columns.size()); ! Iterator it = dbAttrs.iterator(); ! while (it.hasNext()) { ! assertTrue(columns.contains(it.next())); ! } ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList2() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with custom attributes ! q.setObjEntityName("Artist"); ! q.addCustDbAttribute("ARTIST_ID"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! Object[] dbAttrs = new Object[] { artistEnt.getAttribute("ARTIST_ID")}; ! assertEquals(dbAttrs.length, columns.size()); ! for (int i = 0; i < dbAttrs.length; i++) { ! assertTrue(columns.contains(dbAttrs[i])); ! } ! } finally { ! con.close(); ! } ! } ! public void testBuildColumnList3() throws Exception { ! Connection con = getSharedConnection(); ! try { ! // configure query with entity that maps to a subset of DbEntity ! q.setObjEntityName("SubPainting"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! List columns = transl.getColumnList(); ! ! ObjEntity subPainting = getSharedDomain().lookupEntity("SubPainting"); ! ! // assert that the number of attributes in the query is right ! // 1 (obj attr) + 1 (pk) = 2 ! assertEquals(2, columns.size()); ! ! } finally { ! con.close(); ! } ! } ! ! public void testBuildColumnList4() throws Exception { ! Connection con = getSharedConnection(); ! ! try { ! // configure query with derived entity that maps to a subset of DbEntity ! q.setObjEntityName("ArtistPaintingCounts"); ! SelectTranslator transl = buildTranslator(con); ! transl.createSqlString(); ! ! List columns = transl.getColumnList(); ! ! ObjEntity countsEnt = getSharedDomain().lookupEntity("ArtistPaintingCounts"); ! ! // assert that the number of attributes in the query is right ! // 1 (obj attr) + 1 (pk) = 2 ! assertEquals(2, columns.size()); ! ! } finally { ! con.close(); ! } ! } } |
From: <me...@us...> - 2002-10-01 02:07:43
|
Update of /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/access/trans In directory usw-pr-cvs1:/tmp/cvs-serv8640/src/cayenne/java/org/objectstyle/cayenne/access/trans Modified Files: QueryAssembler.java QueryAssemblerHelper.java SelectTranslator.java Log Message: applied patch by Craig Miskell Index: QueryAssembler.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/access/trans/QueryAssembler.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** QueryAssembler.java 8 Sep 2002 22:46:47 -0000 1.1 --- QueryAssembler.java 1 Oct 2002 02:07:39 -0000 1.2 *************** *** 97,100 **** --- 97,104 ---- public abstract String createSqlString() throws java.lang.Exception; + public String aliasForTable(DbEntity ent, DbRelationship rel) { + return aliasForTable(ent); //Default implementation + } + /** * Returns a name that can be used as column alias. *************** *** 128,132 **** /** Translates internal query into PreparedStatement. */ public PreparedStatement createStatement(Level logLevel) throws Exception { ! long t1 = System.currentTimeMillis(); String sqlStr = createSqlString(); QueryLogger.logQuery(logLevel, sqlStr, values, System.currentTimeMillis() - t1); --- 132,136 ---- /** Translates internal query into PreparedStatement. */ public PreparedStatement createStatement(Level logLevel) throws Exception { ! long t1 = System.currentTimeMillis(); String sqlStr = createSqlString(); QueryLogger.logQuery(logLevel, sqlStr, values, System.currentTimeMillis() - t1); *************** *** 155,160 **** if (attr == null) { stmt.setObject(i + 1, val); ! } ! else { int type = attr.getType(); int precision = attr.getPrecision(); --- 159,163 ---- if (attr == null) { stmt.setObject(i + 1, val); ! } else { int type = attr.getType(); int precision = attr.getPrecision(); *************** *** 164,169 **** else { ExtendedType map = ! adapter.getTypeConverter().getRegisteredType(val.getClass().getName()); ! Object jdbcVal = (map == null) ? val : map.toJdbcObject(val, type); stmt.setObject(i + 1, jdbcVal, type, precision); } --- 167,174 ---- else { ExtendedType map = ! adapter.getTypeConverter().getRegisteredType( ! val.getClass().getName()); ! Object jdbcVal = ! (map == null) ? val : map.toJdbcObject(val, type); stmt.setObject(i + 1, jdbcVal, type, precision); } Index: QueryAssemblerHelper.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/access/trans/QueryAssemblerHelper.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** QueryAssemblerHelper.java 8 Sep 2002 22:46:47 -0000 1.1 --- QueryAssemblerHelper.java 1 Oct 2002 02:07:39 -0000 1.2 *************** *** 70,391 **** */ public abstract class QueryAssemblerHelper { ! static Logger logObj = Logger.getLogger(QueryAssemblerHelper.class.getName()); ! protected QueryAssembler queryAssembler; ! public QueryAssemblerHelper() {} ! /** Creates QueryAssemblerHelper. Sets queryAssembler property. */ ! public QueryAssemblerHelper(QueryAssembler queryAssembler) { ! this.queryAssembler = queryAssembler; ! } ! /** Returns parent QueryAssembler that uses this helper. */ ! public QueryAssembler getQueryAssembler() { ! return queryAssembler; ! } ! public void setQueryAssembler(QueryAssembler queryAssembler) { ! this.queryAssembler = queryAssembler; ! } ! /** Translates the part of parent translator's query that is supported ! * by this PartTranslator. For example, QualifierTranslator will process ! * qualifier expression, OrderingTranslator - ordering of the query. ! * In the process of translation parent translator is notified of any ! * join tables added (so that it can update its "FROM" clause). ! * Also parent translator is consulted about table aliases to use ! * when translating columns. */ ! public abstract String doTranslation(); ! public ObjEntity getObjEntity() { ! return getQueryAssembler().getRootEntity(); ! } ! public DbEntity getDbEntity() { ! return getQueryAssembler().getRootEntity().getDbEntity(); ! } ! /** Processes parts of the OBJ_PATH expression. */ ! protected void appendObjPath(StringBuffer buf, Expression pathExp) { ! Iterator it = getObjEntity().resolvePathComponents(pathExp); ! while (it.hasNext()) { ! Object pathComp = it.next(); ! if (pathComp instanceof ObjRelationship) { ! ObjRelationship rel = (ObjRelationship) pathComp; ! // if this is a last relationship in the path, ! // it needs special handling ! if (!it.hasNext()) { ! processRelTermination(buf, rel); ! } else { ! // find and add joins .... ! processRelParts(rel); ! } ! } else { ! ObjAttribute objAttr = (ObjAttribute) pathComp; ! processColumn(buf, objAttr.getDbAttribute()); ! } ! } ! } ! protected void appendDbPath(StringBuffer buf, Expression pathExp) { ! Iterator it = getDbEntity().resolvePathComponents(pathExp); ! while (it.hasNext()) { ! Object pathComp = it.next(); ! if (pathComp instanceof DbRelationship) { ! DbRelationship rel = (DbRelationship) pathComp; ! // if this is a last relationship in the path, ! // it needs special handling ! if (!it.hasNext()) { ! processRelTermination(buf, rel); ! } else { ! // find and add joins .... ! queryAssembler.dbRelationshipAdded(rel); ! } ! } else { ! DbAttribute dbAttr = (DbAttribute) pathComp; ! processColumn(buf, dbAttr); ! } ! } ! } ! /** Appends column name of a column in a root entity. */ ! protected void processColumn(StringBuffer buf, Expression nameExp) { ! if (queryAssembler.supportsTableAliases()) { ! String alias = queryAssembler.aliasForTable(getDbEntity()); ! buf.append(alias).append('.'); ! } ! buf.append(nameExp.getOperand(0)); ! } ! protected void processColumn(StringBuffer buf, DbAttribute dbAttr) { ! String alias = ! (queryAssembler.supportsTableAliases()) ! ? queryAssembler.aliasForTable((DbEntity) dbAttr.getEntity()) ! : null; ! buf.append(dbAttr.getAliasedName(alias)); ! } ! /** ! * Appends SQL code to the query buffer to handle <code>val</code> as a ! * parameter to the PreparedStatement being built. Adds <code>val</code> ! * into QueryAssembler parameter list. ! * ! * <p>If <code>val</code> is null, "NULL" is appended to the query. </p> ! * ! * <p>If <code>val</code> is a DataObject, its primary key value is ! * used as a parameter. <i>Only objects with a single column primary key ! * can be used.</i> ! * ! * @param buf query buffer. ! * ! * @param val object that should be appended as a literal to the query. ! * Must be of one of "standard JDBC" types, null or a DataObject. ! * ! * @param attr DbAttribute that has information on what type of parameter ! * is being appended. ! * ! */ ! protected void appendLiteral(StringBuffer buf, Object val, DbAttribute attr) { ! if (val == null) { ! buf.append("NULL"); ! } else if (val instanceof DataObject) { ! ObjectId id = ((DataObject) val).getObjectId(); ! // check if this id is acceptable to be a parameter ! if (id == null) { ! throw new CayenneRuntimeException("Can't use TRANSIENT object as a query parameter."); ! } ! if (id.isTemporary()) { ! throw new CayenneRuntimeException("Can't use NEW object as a query parameter."); ! } ! Map snap = id.getIdSnapshot(); ! if (snap.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("Object must have a single primary key column ") ! .append("to serve as a query parameter. ") ! .append("This object has ") ! .append(snap.size()) ! .append(": ") ! .append(snap); ! throw new CayenneRuntimeException(msg.toString()); ! } ! // checks have been passed, use id value ! appendLiteralDirect(buf, snap.get(snap.keySet().iterator().next()), attr); ! } else { ! appendLiteralDirect(buf, val, attr); ! } ! } ! /** ! * Appends SQL code to the query buffer to handle <code>val</code> as a ! * parameter to the PreparedStatement being built. Adds <code>val</code> ! * into QueryAssembler parameter list. ! * ! * ! * @param buf query buffer ! * @param val object that should be appended as a literal to the query. ! * Must be of one of "standard JDBC" types. Can not be null. ! */ ! private final void appendLiteralDirect( ! StringBuffer buf, ! Object val, ! DbAttribute attr) { ! buf.append('?'); ! // we are hoping that when processing parameter list, ! // the correct type will be ! // guessed without looking at DbAttribute... ! queryAssembler.addToParamList(attr, val); ! } ! /** ! * Returns database type of expression parameters or ! * null if it can not be determined. ! */ ! protected DbAttribute paramsDbType(Expression e) { ! int len = e.getOperandCount(); ! // ignore unary expressions ! if (len < 2) { ! return null; ! } ! // naive algorithm: ! // if at least one of the sibling operands is a ! // OBJ_PATH expression, use its attribute type as ! // a final answer. ! for (int i = 0; i < len; i++) { ! Object op = e.getOperand(i); ! if (op instanceof Expression) { ! Expression ope = (Expression) op; ! if (ope.getType() == Expression.OBJ_PATH) { ! Iterator it = getObjEntity().resolvePathComponents(ope); ! while (it.hasNext()) { ! Object pathComp = it.next(); ! if (pathComp instanceof ObjAttribute) { ! return ((ObjAttribute) pathComp).getDbAttribute(); ! } ! } ! } ! } ! } ! return null; ! } ! /** ! * Processes ObjRelationship. Decomposes it into DbRelationships ! * and appends parts to the query buffer. ! */ ! protected void processRelParts(ObjRelationship rel) { ! Iterator it = rel.getDbRelationshipList().iterator(); ! while (it.hasNext()) { ! queryAssembler.dbRelationshipAdded((DbRelationship) it.next()); ! } ! } ! /** Processes case when an OBJ_PATH expression ends with relationship. ! * If this is a "to many" relationship, a join is added and a column ! * expression for the target entity primary key. If this is a "to one" ! * relationship, column expresion for the source foreign key is added. ! */ ! protected void processRelTermination(StringBuffer buf, ObjRelationship rel) { ! if (rel.isToMany()) { ! // append joins ! processRelParts(rel); ! } ! List dbRels = rel.getDbRelationshipList(); ! // get last DbRelationship on the list ! DbRelationship dbRel = (DbRelationship) dbRels.get(dbRels.size() - 1); ! List joins = dbRel.getJoins(); ! if (joins.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("OBJ_PATH expressions are only supported ") ! .append("for a single-join relationships. ") ! .append("This relationship has ") ! .append(joins.size()) ! .append(" joins."); ! throw new CayenneRuntimeException(msg.toString()); ! } ! DbAttributePair join = (DbAttributePair) joins.get(0); - DbAttribute att = join.getSource(); - processColumn(buf, att); - } - /** * Handles case when a DB_NAME expression ends with relationship. ! * If this is a "to many" relationship, a join is added and a column ! * expression for the target entity primary key. If this is a "to one" ! * relationship, column expresion for the source foreign key is added. ! */ ! protected void processRelTermination(StringBuffer buf, DbRelationship rel) { ! if (rel.isToMany()) { ! // append joins ! queryAssembler.dbRelationshipAdded(rel); ! } ! ! // get last DbRelationship on the list ! List joins = rel.getJoins(); ! if (joins.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("OBJ_PATH expressions are only supported ") ! .append("for a single-join relationships. ") ! .append("This relationship has ") ! .append(joins.size()) ! .append(" joins."); ! throw new CayenneRuntimeException(msg.toString()); ! } ! DbAttributePair join = (DbAttributePair) joins.get(0); ! DbAttribute att = null; ! if (rel.isToMany()) { ! DbEntity ent = (DbEntity) join.getTarget().getEntity(); ! List pk = ent.getPrimaryKey(); ! if (pk.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("DB_NAME expressions can only support ") ! .append("targets with a single column PK. ") ! .append("This entity has ") ! .append(pk.size()) ! .append(" columns in primary key."); ! throw new CayenneRuntimeException(msg.toString()); ! } ! att = (DbAttribute) pk.get(0); ! } else { ! att = join.getSource(); ! } ! processColumn(buf, att); ! } } --- 70,410 ---- */ public abstract class QueryAssemblerHelper { ! static Logger logObj = Logger.getLogger(QueryAssemblerHelper.class.getName()); ! protected QueryAssembler queryAssembler; ! public QueryAssemblerHelper() {} ! /** Creates QueryAssemblerHelper. Sets queryAssembler property. */ ! public QueryAssemblerHelper(QueryAssembler queryAssembler) { ! this.queryAssembler = queryAssembler; ! } ! /** Returns parent QueryAssembler that uses this helper. */ ! public QueryAssembler getQueryAssembler() { ! return queryAssembler; ! } ! public void setQueryAssembler(QueryAssembler queryAssembler) { ! this.queryAssembler = queryAssembler; ! } ! /** Translates the part of parent translator's query that is supported ! * by this PartTranslator. For example, QualifierTranslator will process ! * qualifier expression, OrderingTranslator - ordering of the query. ! * In the process of translation parent translator is notified of any ! * join tables added (so that it can update its "FROM" clause). ! * Also parent translator is consulted about table aliases to use ! * when translating columns. */ ! public abstract String doTranslation(); ! public ObjEntity getObjEntity() { ! return getQueryAssembler().getRootEntity(); ! } ! public DbEntity getDbEntity() { ! return getQueryAssembler().getRootEntity().getDbEntity(); ! } ! /** Processes parts of the OBJ_PATH expression. */ ! protected void appendObjPath(StringBuffer buf, Expression pathExp) { ! Iterator it = getObjEntity().resolvePathComponents(pathExp); ! ObjRelationship lastRelationship = null; ! while (it.hasNext()) { ! Object pathComp = it.next(); ! if (pathComp instanceof ObjRelationship) { ! ObjRelationship rel = (ObjRelationship) pathComp; ! // if this is a last relationship in the path, ! // it needs special handling ! if (!it.hasNext()) { ! processRelTermination(buf, rel); ! } else { ! // find and add joins .... ! processRelParts(rel); ! } ! lastRelationship = rel; ! } else { ! ObjAttribute objAttr = (ObjAttribute) pathComp; ! if (lastRelationship != null) { ! DbRelationship lastDbRel = ! (DbRelationship) lastRelationship.getDbRelationshipList().get(0); ! processColumn(buf, objAttr.getDbAttribute(), lastDbRel); ! } else { ! processColumn(buf, objAttr.getDbAttribute()); ! } ! } ! } ! } ! protected void appendDbPath(StringBuffer buf, Expression pathExp) { ! Iterator it = getDbEntity().resolvePathComponents(pathExp); ! while (it.hasNext()) { ! Object pathComp = it.next(); ! if (pathComp instanceof DbRelationship) { ! DbRelationship rel = (DbRelationship) pathComp; ! // if this is a last relationship in the path, ! // it needs special handling ! if (!it.hasNext()) { ! processRelTermination(buf, rel); ! } else { ! // find and add joins .... ! queryAssembler.dbRelationshipAdded(rel); ! } ! } else { ! DbAttribute dbAttr = (DbAttribute) pathComp; ! processColumn(buf, dbAttr); ! } ! } ! } ! /** Appends column name of a column in a root entity. */ ! protected void processColumn(StringBuffer buf, Expression nameExp) { ! if (queryAssembler.supportsTableAliases()) { ! String alias = queryAssembler.aliasForTable(getDbEntity()); ! buf.append(alias).append('.'); ! } ! buf.append(nameExp.getOperand(0)); ! } ! protected void processColumn( ! StringBuffer buf, ! DbAttribute dbAttr, ! DbRelationship rel) { ! String alias = ! (queryAssembler.supportsTableAliases()) ! ? queryAssembler.aliasForTable((DbEntity) dbAttr.getEntity(), rel) ! : null; ! buf.append(dbAttr.getAliasedName(alias)); ! } ! protected void processColumn(StringBuffer buf, DbAttribute dbAttr) { ! String alias = ! (queryAssembler.supportsTableAliases()) ! ? queryAssembler.aliasForTable((DbEntity) dbAttr.getEntity()) ! : null; ! buf.append(dbAttr.getAliasedName(alias)); ! } ! /** ! * Appends SQL code to the query buffer to handle <code>val</code> as a ! * parameter to the PreparedStatement being built. Adds <code>val</code> ! * into QueryAssembler parameter list. ! * ! * <p>If <code>val</code> is null, "NULL" is appended to the query. </p> ! * ! * <p>If <code>val</code> is a DataObject, its primary key value is ! * used as a parameter. <i>Only objects with a single column primary key ! * can be used.</i> ! * ! * @param buf query buffer. ! * ! * @param val object that should be appended as a literal to the query. ! * Must be of one of "standard JDBC" types, null or a DataObject. ! * ! * @param attr DbAttribute that has information on what type of parameter ! * is being appended. ! * ! */ ! protected void appendLiteral(StringBuffer buf, Object val, DbAttribute attr) { ! if (val == null) { ! buf.append("NULL"); ! } else if (val instanceof DataObject) { ! ObjectId id = ((DataObject) val).getObjectId(); ! // check if this id is acceptable to be a parameter ! if (id == null) { ! throw new CayenneRuntimeException("Can't use TRANSIENT object as a query parameter."); ! } ! if (id.isTemporary()) { ! throw new CayenneRuntimeException("Can't use NEW object as a query parameter."); ! } ! Map snap = id.getIdSnapshot(); ! if (snap.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("Object must have a single primary key column ") ! .append("to serve as a query parameter. ") ! .append("This object has ") ! .append(snap.size()) ! .append(": ") ! .append(snap); ! throw new CayenneRuntimeException(msg.toString()); ! } ! // checks have been passed, use id value ! appendLiteralDirect(buf, snap.get(snap.keySet().iterator().next()), attr); ! } else { ! appendLiteralDirect(buf, val, attr); ! } ! } ! /** ! * Appends SQL code to the query buffer to handle <code>val</code> as a ! * parameter to the PreparedStatement being built. Adds <code>val</code> ! * into QueryAssembler parameter list. ! * ! * ! * @param buf query buffer ! * @param val object that should be appended as a literal to the query. ! * Must be of one of "standard JDBC" types. Can not be null. ! */ ! private final void appendLiteralDirect( ! StringBuffer buf, ! Object val, ! DbAttribute attr) { ! buf.append('?'); ! // we are hoping that when processing parameter list, ! // the correct type will be ! // guessed without looking at DbAttribute... ! queryAssembler.addToParamList(attr, val); ! } ! /** ! * Returns database type of expression parameters or ! * null if it can not be determined. ! */ ! protected DbAttribute paramsDbType(Expression e) { ! int len = e.getOperandCount(); ! // ignore unary expressions ! if (len < 2) { ! return null; ! } ! // naive algorithm: ! // if at least one of the sibling operands is a ! // OBJ_PATH expression, use its attribute type as ! // a final answer. ! for (int i = 0; i < len; i++) { ! Object op = e.getOperand(i); ! if (op instanceof Expression) { ! Expression ope = (Expression) op; ! if (ope.getType() == Expression.OBJ_PATH) { ! Iterator it = getObjEntity().resolvePathComponents(ope); ! while (it.hasNext()) { ! Object pathComp = it.next(); ! if (pathComp instanceof ObjAttribute) { ! return ((ObjAttribute) pathComp).getDbAttribute(); ! } ! } ! } ! } ! } ! return null; ! } ! /** ! * Processes ObjRelationship. Decomposes it into DbRelationships ! * and appends parts to the query buffer. ! */ ! protected void processRelParts(ObjRelationship rel) { ! Iterator it = rel.getDbRelationshipList().iterator(); ! while (it.hasNext()) { ! queryAssembler.dbRelationshipAdded((DbRelationship) it.next()); ! } ! } ! /** Processes case when an OBJ_PATH expression ends with relationship. ! * If this is a "to many" relationship, a join is added and a column ! * expression for the target entity primary key. If this is a "to one" ! * relationship, column expresion for the source foreign key is added. ! */ ! protected void processRelTermination(StringBuffer buf, ObjRelationship rel) { ! if (rel.isToMany()) { ! // append joins ! processRelParts(rel); ! } ! List dbRels = rel.getDbRelationshipList(); ! // get last DbRelationship on the list ! DbRelationship dbRel = (DbRelationship) dbRels.get(dbRels.size() - 1); ! List joins = dbRel.getJoins(); ! if (joins.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("OBJ_PATH expressions are only supported ") ! .append("for a single-join relationships. ") ! .append("This relationship has ") ! .append(joins.size()) ! .append(" joins."); ! ! throw new CayenneRuntimeException(msg.toString()); ! } ! ! DbAttributePair join = (DbAttributePair) joins.get(0); ! ! DbAttribute att = join.getSource(); ! processColumn(buf, att); ! } /** * Handles case when a DB_NAME expression ends with relationship. ! * If this is a "to many" relationship, a join is added and a column ! * expression for the target entity primary key. If this is a "to one" ! * relationship, column expresion for the source foreign key is added. ! */ ! protected void processRelTermination(StringBuffer buf, DbRelationship rel) { ! if (rel.isToMany()) { ! // append joins ! queryAssembler.dbRelationshipAdded(rel); ! } ! // get last DbRelationship on the list ! List joins = rel.getJoins(); ! if (joins.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("OBJ_PATH expressions are only supported ") ! .append("for a single-join relationships. ") ! .append("This relationship has ") ! .append(joins.size()) ! .append(" joins."); ! throw new CayenneRuntimeException(msg.toString()); ! } ! DbAttributePair join = (DbAttributePair) joins.get(0); ! DbAttribute att = null; ! if (rel.isToMany()) { ! DbEntity ent = (DbEntity) join.getTarget().getEntity(); ! List pk = ent.getPrimaryKey(); ! if (pk.size() != 1) { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("DB_NAME expressions can only support ") ! .append("targets with a single column PK. ") ! .append("This entity has ") ! .append(pk.size()) ! .append(" columns in primary key."); ! throw new CayenneRuntimeException(msg.toString()); ! } ! att = (DbAttribute) pk.get(0); ! } else { ! att = join.getSource(); ! } ! processColumn(buf, att); ! } } Index: SelectTranslator.java =================================================================== RCS file: /cvsroot/cayenne/cayenne/src/cayenne/java/org/objectstyle/cayenne/access/trans/SelectTranslator.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** SelectTranslator.java 8 Sep 2002 22:46:47 -0000 1.1 --- SelectTranslator.java 1 Oct 2002 02:07:39 -0000 1.2 *************** *** 74,490 **** */ public class SelectTranslator extends SelectQueryAssembler { ! static Logger logObj = Logger.getLogger(SelectTranslator.class.getName()); ! ! private final HashMap aliasLookup = new HashMap(); ! private final ArrayList columnList = new ArrayList(); ! private final ArrayList tableList = new ArrayList(); ! private final ArrayList aliasList = new ArrayList(); ! private final ArrayList dbRelList = new ArrayList(); ! private List groupByList; ! private int aliasCounter; ! /** ! * If set to <code>true</code>, indicates that distinct ! * select query is required no matter what the original query ! * settings where. This flag can be set when joins are created ! * using "to-many" relationships. ! */ ! private boolean forceDistinct; ! /** ! * Returns a list of DbAttributes representing columns ! * in this query. ! */ ! protected List getColumnList() { ! return columnList; ! } ! public int getFetchLimit() { ! return getSelectQuery().getFetchLimit(); ! } ! /** ! * Returns an ordered list of DbAttributes that describe the ! * result columns in the in the ResultSet. ResultSet column names are ignored, ! * names specified in the query are used instead. */ ! public DbAttribute[] getSnapshotDesc(ResultSet rs) { ! int len = columnList.size(); ! if (len == 0) { ! throw new CayenneRuntimeException("Call 'createStatement' first"); ! } ! ! DbAttribute[] desc = new DbAttribute[len]; ! columnList.toArray(desc); ! return desc; ! } ! /** ! * Returns ordered list of Java class names that should be used for fetched values. ! * ResultSet types are ignored, types specified in the query are used instead. ! */ ! public String[] getResultTypes(ResultSet rs) { ! int len = columnList.size(); ! if (len == 0) { ! throw new CayenneRuntimeException("Call 'createStatement' first."); ! } ! String[] types = new String[len]; ! for (int i = 0; i < len; i++) { ! DbAttribute attr = (DbAttribute) columnList.get(i); ! ObjAttribute objAttr = ! getRootEntity().getAttributeForDbAttribute(attr); ! // use explicit type mapping specified in ObjAttribute, ! // or use default JDBC mapping if no ObjAttribute exists ! types[i] = ! (objAttr != null) ! ? objAttr.getType() ! : TypesMapping.getJavaBySqlType(attr.getType()); ! } ! return types; ! } ! /** ! * Returns query translated to SQL. This is a main work method of the SelectTranslator. ! */ ! public String createSqlString() throws Exception { ! forceDistinct = false; ! // build column list ! buildColumnList(); ! QualifierTranslator tr = ! adapter.getQualifierFactory().createTranslator(this); ! // build parent qualifier ! // Parent qualifier translation must PRECEED main qualifier ! // since it will be appended first and its parameters must ! // go first as well ! String parentQualifierStr = null; ! if (getSelectQuery().isQualifiedOnParent()) { ! tr.setTranslateParentQual(true); ! parentQualifierStr = tr.doTranslation(); ! } ! // build main qualifier ! tr.setTranslateParentQual(false); ! String qualifierStr = tr.doTranslation(); ! // build GROUP BY ! buildGroupByList(); ! // build ORDER BY, ! String orderByStr = new OrderingTranslator(this).doTranslation(); ! // assemble ! StringBuffer queryBuf = new StringBuffer(); ! queryBuf.append("SELECT "); ! if (forceDistinct || getSelectQuery().isDistinct()) { ! queryBuf.append("DISTINCT "); ! } ! // append columns (unroll the loop's first element) ! int columnCount = columnList.size(); ! appendColumn(queryBuf, 0); // assume there is at least 1 element ! for (int i = 1; i < columnCount; i++) { ! queryBuf.append(", "); ! appendColumn(queryBuf, i); ! } ! // append from clause ! queryBuf.append(" FROM "); ! // append table list (unroll loop's 1st element) ! int tableCount = tableList.size(); ! appendTable(queryBuf, 0); // assume there is at least 1 table ! for (int i = 1; i < tableCount; i++) { ! queryBuf.append(", "); ! appendTable(queryBuf, i); ! } ! // append db relationship joins if any ! boolean hasWhere = false; ! int dbRelCount = dbRelList.size(); ! if (dbRelCount > 0) { ! hasWhere = true; ! queryBuf.append(" WHERE "); ! appendDbRelJoins(queryBuf, 0); ! for (int i = 1; i < dbRelCount; i++) { ! queryBuf.append(" AND "); ! appendDbRelJoins(queryBuf, i); ! } ! } ! // append parent qualifier if any ! if (parentQualifierStr != null) { ! if (hasWhere) { ! queryBuf.append(" AND ("); ! queryBuf.append(parentQualifierStr); ! queryBuf.append(")"); ! } else { ! hasWhere = true; ! queryBuf.append(" WHERE "); ! queryBuf.append(parentQualifierStr); ! } ! } ! // append group by ! boolean hasGroupBy = false; ! if (groupByList != null) { ! int groupByCount = groupByList.size(); ! if (groupByCount > 0) { ! hasGroupBy = true; ! queryBuf.append(" GROUP BY "); ! appendGroupBy(queryBuf, 0); ! for (int i = 1; i < groupByCount; i++) { ! queryBuf.append(", "); ! appendGroupBy(queryBuf, i); ! } ! } ! } ! // append qualifier ! if (qualifierStr != null) { ! if (hasGroupBy) { ! queryBuf.append(" HAVING "); ! queryBuf.append(qualifierStr); ! } else { ! if (hasWhere) { ! queryBuf.append(" AND ("); ! queryBuf.append(qualifierStr); ! queryBuf.append(")"); ! } else { ! hasWhere = true; ! queryBuf.append(" WHERE "); ! queryBuf.append(qualifierStr); ! } ! } ! } ! // append prebuilt ordering ! if (orderByStr != null) { ! queryBuf.append(" ORDER BY ").append(orderByStr); ! } ! return queryBuf.toString(); ! } ! private SelectQuery getSelectQuery() { ! return (SelectQuery) getQuery(); ! } ! /** ! * Creates a list of columns used in the query. ! */ ! private void buildColumnList() { ! newAliasForTable(getRootEntity().getDbEntity()); ! appendAttributes(); ! } ! /** ! * Creates a list of columns used in the query's GROUP BY clause. ! */ ! private void buildGroupByList() { ! DbEntity dbEntity = getRootEntity().getDbEntity(); ! if (dbEntity instanceof DerivedDbEntity) { ! groupByList = ((DerivedDbEntity) dbEntity).getGroupByAttributes(); ! } ! } ! /** ! * Returns a list of DbAttributes used in query. ! */ ! private void appendAttributes() { ! ObjEntity oe = getRootEntity(); ! DbEntity dbe = oe.getDbEntity(); ! SelectQuery q = getSelectQuery(); ! // extract custom attributes from the query ! if (q.isFetchingCustAttributes()) { ! List custAttrNames = q.getCustDbAttributes(); ! int len = custAttrNames.size(); ! for (int i = 0; i < len; i++) { ! Attribute attr = ! dbe.getAttribute((String) custAttrNames.get(i)); ! if (attr == null) { ! throw new CayenneRuntimeException( ! "Attribute does not exist: " + custAttrNames.get(i)); ! } ! columnList.add(attr); ! } ! } else { ! // build a list of attributes mentioned in ObjEntity + PK's + FK's + GROUP BY's ! // ObjEntity attrs ! List attrs = oe.getAttributeList(); ! int len = attrs.size(); ! for (int i = 0; i < len; i++) { ! ObjAttribute oa = (ObjAttribute) attrs.get(i); ! Attribute dbAttr = oa.getDbAttribute(); ! if (dbAttr == null) { ! throw new CayenneRuntimeException( ! "ObjAttribute has no DbAttribute: " + oa.getName()); ! } ! columnList.add(dbAttr); ! } ! // relationship keys ! List rels = oe.getRelationshipList(); ! int rLen = rels.size(); ! for (int i = 0; i < rLen; i++) { ! ObjRelationship rel = (ObjRelationship) rels.get(i); ! DbRelationship dbRel = ! (DbRelationship) rel.getDbRelationshipList().get(0); ! List joins = dbRel.getJoins(); ! int jLen = joins.size(); ! for (int j = 0; j < jLen; j++) { ! DbAttributePair join = (DbAttributePair) joins.get(j); ! DbAttribute src = join.getSource(); ! if (!columnList.contains(src)) { ! columnList.add(src); ! } ! } ! } ! // add remaining needed attrs from DbEntity ! List dbattrs = dbe.getAttributeList(); ! int dLen = dbattrs.size(); ! for (int i = 0; i < dLen; i++) { ! DbAttribute dba = (DbAttribute) dbattrs.get(i); ! if (dba.isPrimaryKey()) { ! if (!columnList.contains(dba)) { ! columnList.add(dba); ! } ! } ! } ! } ! } ! private void appendColumn(StringBuffer queryBuf, int index) { ! DbAttribute attr = (DbAttribute) columnList.get(index); ! String alias = aliasForTable((DbEntity) attr.getEntity()); ! queryBuf.append(attr.getAliasedName(alias)); ! } ! private void appendGroupBy(StringBuffer queryBuf, int index) { ! DbAttribute attr = (DbAttribute) groupByList.get(index); ! DbEntity ent = (DbEntity)attr.getEntity(); ! queryBuf.append( ! attr.getAliasedName(aliasForTable(ent))); ! } ! private void appendTable(StringBuffer queryBuf, int index) { ! DbEntity ent = (DbEntity) tableList.get(index); ! queryBuf.append(ent.getFullyQualifiedName()); ! queryBuf.append(' ').append(aliasForTable(ent)); ! } ! private void appendDbRelJoins(StringBuffer queryBuf, int index) { ! DbRelationship rel = (DbRelationship) dbRelList.get(index); ! String srcAlias = aliasForTable((DbEntity) rel.getSourceEntity()); ! String targetAlias = aliasForTable((DbEntity) rel.getTargetEntity()); ! boolean andFlag = false; ! List joins = rel.getJoins(); ! int len = joins.size(); ! for (int i = 0; i < len; i++) { ! if (andFlag) ! queryBuf.append(" AND "); ! else ! andFlag = true; ! DbAttributePair join = (DbAttributePair) joins.get(i); ! DbAttribute src = join.getSource(); ! queryBuf ! .append(srcAlias) ! .append('.') ! .append(join.getSource().getName()) ! .append(" = ") ! .append(targetAlias) ! .append('.') ! .append(join.getTarget().getName()); ! } ! } ! /** ! * Stores a new relationship in an internal list. ! * Later it will be used to create joins to relationship ! * destination table. ! */ ! public void dbRelationshipAdded(DbRelationship rel) { ! if (rel.isToMany()) { ! forceDistinct = true; ! } ! String existAlias = (String) aliasLookup.get(rel); ! if (existAlias == null) { ! dbRelList.add(rel); ! // add alias for the destination table of the relationship ! String newAlias = ! newAliasForTable((DbEntity) rel.getTargetEntity()); ! aliasLookup.put(rel, newAlias); ! } ! } ! /** ! * Sets up and returns a new alias for a speciafied table. ! */ ! protected String newAliasForTable(DbEntity ent) { ! if (ent instanceof DerivedDbEntity) { ! ent = ((DerivedDbEntity) ent).getParentEntity(); ! } ! String newAlias = "t" + aliasCounter++; ! tableList.add(ent); ! aliasList.add(newAlias); ! return newAlias; ! } ! /** ! * Overrides superclass implementation. Will return an alias that ! * should be used for a specified DbEntity in the query ! * (or null if this DbEntity is not included in the FROM clause). ! */ ! public String aliasForTable(DbEntity ent) { ! if (ent instanceof DerivedDbEntity) { ! ent = ((DerivedDbEntity) ent).getParentEntity(); ! } ! int entIndex = tableList.indexOf(ent); ! if (entIndex >= 0) { ! return (String) aliasList.get(entIndex); ! } else { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("Alias not found, DbEntity: '") ! .append(ent != null ? ent.getName() : "<null entity>") ! .append("'\nExisting aliases:"); ! int len = aliasList.size(); ! for (int i = 0; i < len; i++) { ! String dbeName = ! (tableList.get(i) != null) ! ? ((DbEntity) tableList.get(i)).getName() ! : "<null entity>"; ! msg.append("\n").append(aliasList.get(0)).append( ! " => ").append( ! dbeName); ! } ! throw new CayenneRuntimeException(msg.toString()); ! } ! } ! public boolean supportsTableAliases() { ! return true; ! } } --- 74,486 ---- */ public class SelectTranslator extends SelectQueryAssembler { ! static Logger logObj = Logger.getLogger(SelectTranslator.class.getName()); ! private final HashMap aliasLookup = new HashMap(); ! private final ArrayList columnList = new ArrayList(); ! private final ArrayList tableList = new ArrayList(); ! private final ArrayList aliasList = new ArrayList(); ! private final ArrayList dbRelList = new ArrayList(); ! private List groupByList; ! private int aliasCounter; ! /** ! * If set to <code>true</code>, indicates that distinct ! * select query is required no matter what the original query ! * settings where. This flag can be set when joins are created ! * using "to-many" relationships. ! */ ! private boolean forceDistinct; ! /** ! * Returns a list of DbAttributes representing columns ! * in this query. ! */ ! protected List getColumnList() { ! return columnList; ! } ! public int getFetchLimit() { ! return getSelectQuery().getFetchLimit(); ! } ! /** ! * Returns an ordered list of DbAttributes that describe the ! * result columns in the in the ResultSet. ResultSet column names are ignored, ! * names specified in the query are used instead. */ ! public DbAttribute[] getSnapshotDesc(ResultSet rs) { ! int len = columnList.size(); ! if (len == 0) { ! throw new CayenneRuntimeException("Call 'createStatement' first"); ! } ! DbAttribute[] desc = new DbAttribute[len]; ! columnList.toArray(desc); ! return desc; ! } ! /** ! * Returns ordered list of Java class names that should be used for fetched values. ! * ResultSet types are ignored, types specified in the query are used instead. ! */ ! public String[] getResultTypes(ResultSet rs) { ! int len = columnList.size(); ! if (len == 0) { ! throw new CayenneRuntimeException("Call 'createStatement' first."); ! } ! String[] types = new String[len]; ! for (int i = 0; i < len; i++) { ! DbAttribute attr = (DbAttribute) columnList.get(i); ! ObjAttribute objAttr = getRootEntity().getAttributeForDbAttribute(attr); ! // use explicit type mapping specified in ObjAttribute, ! // or use default JDBC mapping if no ObjAttribute exists ! types[i] = ! (objAttr != null) ! ? objAttr.getType() ! : TypesMapping.getJavaBySqlType(attr.getType()); ! } ! return types; ! } ! /** ! * Returns query translated to SQL. This is a main work method of the SelectTranslator. ! */ ! public String createSqlString() throws Exception { ! forceDistinct = false; ! // build column list ! buildColumnList(); ! QualifierTranslator tr = adapter.getQualifierFactory().createTranslator(this); ! // build parent qualifier ! // Parent qualifier translation must PRECEED main qualifier ! // since it will be appended first and its parameters must ! // go first as well ! String parentQualifierStr = null; ! if (getSelectQuery().isQualifiedOnParent()) { ! tr.setTranslateParentQual(true); ! parentQualifierStr = tr.doTranslation(); ! } ! // build main qualifier ! tr.setTranslateParentQual(false); ! String qualifierStr = tr.doTranslation(); ! // build GROUP BY ! buildGroupByList(); ! // build ORDER BY, ! String orderByStr = new OrderingTranslator(this).doTranslation(); ! // assemble ! StringBuffer queryBuf = new StringBuffer(); ! queryBuf.append("SELECT "); ! if (forceDistinct || getSelectQuery().isDistinct()) { ! queryBuf.append("DISTINCT "); ! } ! // append columns (unroll the loop's first element) ! int columnCount = columnList.size(); ! appendColumn(queryBuf, 0); // assume there is at least 1 element ! for (int i = 1; i < columnCount; i++) { ! queryBuf.append(", "); ! appendColumn(queryBuf, i); ! } ! // append from clause ! queryBuf.append(" FROM "); ! // append table list (unroll loop's 1st element) ! int tableCount = tableList.size(); ! appendTable(queryBuf, 0); // assume there is at least 1 table ! for (int i = 1; i < tableCount; i++) { ! queryBuf.append(", "); ! appendTable(queryBuf, i); ! } ! // append db relationship joins if any ! boolean hasWhere = false; ! int dbRelCount = dbRelList.size(); ! if (dbRelCount > 0) { ! hasWhere = true; ! queryBuf.append(" WHERE "); ! appendDbRelJoins(queryBuf, 0); ! for (int i = 1; i < dbRelCount; i++) { ! queryBuf.append(" AND "); ! appendDbRelJoins(queryBuf, i); ! } ! } ! // append parent qualifier if any ! if (parentQualifierStr != null) { ! if (hasWhere) { ! queryBuf.append(" AND ("); ! queryBuf.append(parentQualifierStr); ! queryBuf.append(")"); ! } else { ! hasWhere = true; ! queryBuf.append(" WHERE "); ! queryBuf.append(parentQualifierStr); ! } ! } ! // append group by ! boolean hasGroupBy = false; ! if (groupByList != null) { ! int groupByCount = groupByList.size(); ! if (groupByCount > 0) { ! hasGroupBy = true; ! queryBuf.append(" GROUP BY "); ! appendGroupBy(queryBuf, 0); ! for (int i = 1; i < groupByCount; i++) { ! queryBuf.append(", "); ! appendGroupBy(queryBuf, i); ! } ! } ! } ! // append qualifier ! if (qualifierStr != null) { ! if (hasGroupBy) { ! queryBuf.append(" HAVING "); ! queryBuf.append(qualifierStr); ! } else { ! if (hasWhere) { ! queryBuf.append(" AND ("); ! queryBuf.append(qualifierStr); ! queryBuf.append(")"); ! } else { ! hasWhere = true; ! queryBuf.append(" WHERE "); ! queryBuf.append(qualifierStr); ! } ! } ! } ! // append prebuilt ordering ! if (orderByStr != null) { ! queryBuf.append(" ORDER BY ").append(orderByStr); ! } ! return queryBuf.toString(); ! } ! private SelectQuery getSelectQuery() { ! return (SelectQuery) getQuery(); ! } ! /** ! * Creates a list of columns used in the query. ! */ ! private void buildColumnList() { ! newAliasForTable(getRootEntity().getDbEntity()); ! appendAttributes(); ! } ! /** ! * Creates a list of columns used in the query's GROUP BY clause. ! */ ! private void buildGroupByList() { ! DbEntity dbEntity = getRootEntity().getDbEntity(); ! if (dbEntity instanceof DerivedDbEntity) { ! groupByList = ((DerivedDbEntity) dbEntity).getGroupByAttributes(); ! } ! } ! /** ! * Returns a list of DbAttributes used in query. ! */ ! private void appendAttributes() { ! ObjEntity oe = getRootEntity(); ! DbEntity dbe = oe.getDbEntity(); ! SelectQuery q = getSelectQuery(); ! // extract custom attributes from the query ! if (q.isFetchingCustAttributes()) { ! List custAttrNames = q.getCustDbAttributes(); ! int len = custAttrNames.size(); ! for (int i = 0; i < len; i++) { ! Attribute attr = dbe.getAttribute((String) custAttrNames.get(i)); ! if (attr == null) { ! throw new CayenneRuntimeException( ! "Attribute does not exist: " + custAttrNames.get(i)); ! } ! columnList.add(attr); ! } ! } else { ! // build a list of attributes mentioned in ObjEntity + PK's + FK's + GROUP BY's ! // ObjEntity attrs ! List attrs = oe.getAttributeList(); ! int len = attrs.size(); ! for (int i = 0; i < len; i++) { ! ObjAttribute oa = (ObjAttribute) attrs.get(i); ! Attribute dbAttr = oa.getDbAttribute(); ! if (dbAttr == null) { ! throw new CayenneRuntimeException( ! "ObjAttribute has no DbAttribute: " + oa.getName()); ! } ! columnList.add(dbAttr); ! } ! // relationship keys ! List rels = oe.getRelationshipList(); ! int rLen = rels.size(); ! for (int i = 0; i < rLen; i++) { ! ObjRelationship rel = (ObjRelationship) rels.get(i); ! DbRelationship dbRel = ! (DbRelationship) rel.getDbRelationshipList().get(0); ! List joins = dbRel.getJoins(); ! int jLen = joins.size(); ! for (int j = 0; j < jLen; j++) { ! DbAttributePair join = (DbAttributePair) joins.get(j); ! DbAttribute src = join.getSource(); ! if (!columnList.contains(src)) { ! columnList.add(src); ! } ! } ! } + // add remaining needed attrs from DbEntity + List dbattrs = dbe.getAttributeList(); + int dLen = dbattrs.size(); + for (int i = 0; i < dLen; i++) { + DbAttribute dba = (DbAttribute) dbattrs.get(i); + if (dba.isPrimaryKey()) { + if (!columnList.contains(dba)) { + columnList.add(dba); + } + } + } + } + } ! private void appendColumn(StringBuffer queryBuf, int index) { ! DbAttribute attr = (DbAttribute) columnList.get(index); ! String alias = aliasForTable((DbEntity) attr.getEntity()); ! queryBuf.append(attr.getAliasedName(alias)); ! } + private void appendGroupBy(StringBuffer queryBuf, int index) { + DbAttribute attr = (DbAttribute) groupByList.get(index); + DbEntity ent = (DbEntity) attr.getEntity(); + queryBuf.append(attr.getAliasedName(aliasForTable(ent))); + } ! private void appendTable(StringBuffer queryBuf, int index) { ! DbEntity ent = (DbEntity) tableList.get(index); ! queryBuf.append(ent.getFullyQualifiedName()); ! //The alias should be the alias from the same index in aliasList, not that ! // returned by aliasForTable. ! queryBuf.append(' ').append((String) aliasList.get(index)); ! } + private void appendDbRelJoins(StringBuffer queryBuf, int index) { + DbRelationship rel = (DbRelationship) dbRelList.get(index); + String srcAlias = aliasForTable((DbEntity) rel.getSourceEntity()); + String targetAlias = (String) aliasLookup.get(rel); ! boolean andFlag = false; ! List joins = rel.getJoins(); ! int len = joins.size(); ! for (int i = 0; i < len; i++) { ! if (andFlag) ! queryBuf.append(" AND "); ! else ! andFlag = true; ! DbAttributePair join = (DbAttributePair) joins.get(i); ! DbAttribute src = join.getSource(); ! queryBuf ! .append(srcAlias) ! .append('.') ! .append(join.getSource().getName()) ! .append(" = ") ! .append(targetAlias) ! .append('.') ! .append(join.getTarget().getName()); ! } ! } ! /** ! * Stores a new relationship in an internal list. ! * Later it will be used to create joins to relationship ! * destination table. ! */ ! public void dbRelationshipAdded(DbRelationship rel) { ! if (rel.isToMany()) { ! forceDistinct = true; ! } ! String existAlias = (String) aliasLookup.get(rel); ! if (existAlias == null) { ! dbRelList.add(rel); ! // add alias for the destination table of the relationship ! String newAlias = newAliasForTable((DbEntity) rel.getTargetEntity()); ! aliasLookup.put(rel, newAlias); ! } ! } ! /** ! * Sets up and returns a new alias for a speciafied table. ! */ ! protected String newAliasForTable(DbEntity ent) { ! if (ent instanceof DerivedDbEntity) { ! ent = ((DerivedDbEntity) ent).getParentEntity(); ! } ! String newAlias = "t" + aliasCounter++; ! tableList.add(ent); ! aliasList.add(newAlias); ! return newAlias; ! } ! public String aliasForTable(DbEntity ent, DbRelationship rel) { ! return (String) aliasLookup.get(rel); ! } ! /** ! * Overrides superclass implementation. Will return an alias that ! * should be used for a specified DbEntity in the query ! * (or null if this DbEntity is not included in the FROM clause). ! */ ! public String aliasForTable(DbEntity ent) { ! if (ent instanceof DerivedDbEntity) { ! ent = ((DerivedDbEntity) ent).getParentEntity(); ! } ! int entIndex = tableList.indexOf(ent); ! if (entIndex >= 0) { ! return (String) aliasList.get(entIndex); ! } else { ! StringBuffer msg = new StringBuffer(); ! msg ! .append("Alias not found, DbEntity: '") ! .append(ent != null ? ent.getName() : "<null entity>") ! .append("'\nExisting aliases:"); ! int len = aliasList.size(); ! for (int i = 0; i < len; i++) { ! String dbeName = ! (tableList.get(i) != null) ! ? ((DbEntity) tableList.get(i)).getName() ! : "<null entity>"; ! msg.append("\n").append(aliasList.get(0)).append(" => ").append(dbeName); ! } ! throw new CayenneRuntimeException(msg.toString()); ! } ! } ! public boolean supportsTableAliases() { ! return true; ! } } |
From: <me...@us...> - 2002-09-26 04:10:52
|
Update of /cvsroot/cayenne/cayenne In directory usw-pr-cvs1:/tmp/cvs-serv17505 Modified Files: build.xml Log Message: testing cvs updates Index: build.xml =================================================================== RCS file: /cvsroot/cayenne/cayenne/build.xml,v retrieving revision 1.38 retrieving revision 1.39 diff -C2 -d -r1.38 -r1.39 *** build.xml 24 Sep 2002 15:30:49 -0000 1.38 --- build.xml 26 Sep 2002 04:10:48 -0000 1.39 *************** *** 1,5 **** <?xml version="1.0"?> - <!-- ================================================= --> <!-- Cayenne master build file. --> --- 1,4 ---- |