|
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;
! }
}
|