1 19 20 package org.apache.cayenne.access.trans; 21 22 import java.sql.Types ; 23 import java.util.ArrayList ; 24 import java.util.Collection ; 25 import java.util.Collections ; 26 import java.util.HashMap ; 27 import java.util.HashSet ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.Set ; 32 33 import org.apache.cayenne.CayenneRuntimeException; 34 import org.apache.cayenne.access.jdbc.ColumnDescriptor; 35 import org.apache.cayenne.exp.Expression; 36 import org.apache.cayenne.map.DbAttribute; 37 import org.apache.cayenne.map.DbEntity; 38 import org.apache.cayenne.map.DbJoin; 39 import org.apache.cayenne.map.DbRelationship; 40 import org.apache.cayenne.map.DerivedDbEntity; 41 import org.apache.cayenne.map.ObjAttribute; 42 import org.apache.cayenne.map.ObjEntity; 43 import org.apache.cayenne.map.ObjRelationship; 44 import org.apache.cayenne.query.PrefetchSelectQuery; 45 import org.apache.cayenne.query.PrefetchTreeNode; 46 import org.apache.cayenne.query.SelectQuery; 47 import org.apache.cayenne.reflect.ArcProperty; 48 import org.apache.cayenne.reflect.AttributeProperty; 49 import org.apache.cayenne.reflect.ClassDescriptor; 50 import org.apache.cayenne.reflect.PropertyVisitor; 51 import org.apache.cayenne.reflect.ToManyProperty; 52 import org.apache.cayenne.reflect.ToOneProperty; 53 54 61 public class SelectTranslator extends QueryAssembler { 62 63 protected static final int[] UNSUPPORTED_DISTINCT_TYPES = new int[] { 64 Types.BLOB, Types.CLOB, Types.LONGVARBINARY, Types.LONGVARCHAR 65 }; 66 67 protected static boolean isUnsupportedForDistinct(int type) { 68 for (int i = 0; i < UNSUPPORTED_DISTINCT_TYPES.length; i++) { 69 if (UNSUPPORTED_DISTINCT_TYPES[i] == type) { 70 return true; 71 } 72 } 73 74 return false; 75 } 76 77 final Map aliasLookup = new HashMap (); 78 79 final List tableList = new ArrayList (); 80 final List aliasList = new ArrayList (); 81 final List dbRelList = new ArrayList (); 82 83 List resultColumns; 84 List groupByList; 85 Map attributeOverrides; 86 Map defaultAttributesByColumn; 87 88 int aliasCounter; 89 90 boolean suppressingDistinct; 91 92 97 boolean forcingDistinct; 98 99 103 public String createSqlString() throws Exception { 104 forcingDistinct = false; 105 106 this.resultColumns = buildResultColumns(); 108 109 QualifierTranslator tr = adapter.getQualifierTranslator(this); 110 111 String parentQualifierStr = null; 116 if (getSelectQuery().isQualifiedOnParent()) { 117 tr.setTranslateParentQual(true); 118 parentQualifierStr = tr.doTranslation(); 119 } 120 121 tr.setTranslateParentQual(false); 123 String qualifierStr = tr.doTranslation(); 124 125 this.groupByList = buildGroupByList(); 127 128 OrderingTranslator orderingTranslator = new OrderingTranslator(this); 130 String orderByStr = orderingTranslator.doTranslation(); 131 132 StringBuffer queryBuf = new StringBuffer (); 134 queryBuf.append("SELECT "); 135 136 if (forcingDistinct || getSelectQuery().isDistinct()) { 139 140 suppressingDistinct = false; 141 Iterator it = resultColumns.iterator(); 142 while (it.hasNext()) { 143 ColumnDescriptor column = (ColumnDescriptor) it.next(); 144 if (isUnsupportedForDistinct(column.getJdbcType())) { 145 suppressingDistinct = true; 146 break; 147 } 148 } 149 150 if (!suppressingDistinct) { 151 queryBuf.append("DISTINCT "); 152 } 153 } 154 155 List selectColumnExpList = new ArrayList (); 157 158 Iterator it = resultColumns.iterator(); 159 while (it.hasNext()) { 160 ColumnDescriptor column = (ColumnDescriptor) it.next(); 161 selectColumnExpList.add(column.getQualifiedColumnName()); 162 } 163 164 if (forcingDistinct || getSelectQuery().isDistinct()) { 167 List orderByColumnList = orderingTranslator.getOrderByColumnList(); 168 for (int i = 0; i < orderByColumnList.size(); i++) { 169 Object orderByColumnExp = orderByColumnList.get(i); 171 if (!selectColumnExpList.contains(orderByColumnExp)) { 172 selectColumnExpList.add(orderByColumnExp); 173 } 174 } 175 } 176 177 int columnCount = selectColumnExpList.size(); 179 queryBuf.append((String ) selectColumnExpList.get(0)); 180 for (int i = 1; i < columnCount; i++) { 182 queryBuf.append(", "); 183 queryBuf.append((String ) selectColumnExpList.get(i)); 184 } 185 186 queryBuf.append(" FROM "); 188 189 int tableCount = tableList.size(); 191 appendTable(queryBuf, 0); for (int i = 1; i < tableCount; i++) { 193 queryBuf.append(", "); 194 appendTable(queryBuf, i); 195 } 196 197 boolean hasWhere = false; 199 int dbRelCount = dbRelList.size(); 200 if (dbRelCount > 0) { 201 hasWhere = true; 202 queryBuf.append(" WHERE "); 203 204 appendJoins(queryBuf, 0); 205 for (int i = 1; i < dbRelCount; i++) { 206 queryBuf.append(" AND "); 207 appendJoins(queryBuf, i); 208 } 209 } 210 211 if (parentQualifierStr != null) { 213 if (hasWhere) { 214 queryBuf.append(" AND ("); 215 queryBuf.append(parentQualifierStr); 216 queryBuf.append(")"); 217 } 218 else { 219 hasWhere = true; 220 queryBuf.append(" WHERE "); 221 queryBuf.append(parentQualifierStr); 222 } 223 } 224 225 boolean hasGroupBy = false; 227 if (groupByList != null) { 228 int groupByCount = groupByList.size(); 229 if (groupByCount > 0) { 230 hasGroupBy = true; 231 queryBuf.append(" GROUP BY "); 232 appendGroupBy(queryBuf, 0); 233 for (int i = 1; i < groupByCount; i++) { 234 queryBuf.append(", "); 235 appendGroupBy(queryBuf, i); 236 } 237 } 238 } 239 240 if (qualifierStr != null) { 242 if (hasGroupBy) { 243 queryBuf.append(" HAVING "); 244 queryBuf.append(qualifierStr); 245 } 246 else { 247 if (hasWhere) { 248 queryBuf.append(" AND ("); 249 queryBuf.append(qualifierStr); 250 queryBuf.append(")"); 251 } 252 else { 253 hasWhere = true; 254 queryBuf.append(" WHERE "); 255 queryBuf.append(qualifierStr); 256 } 257 } 258 } 259 260 if (orderByStr != null) { 262 queryBuf.append(" ORDER BY ").append(orderByStr); 263 } 264 265 return queryBuf.toString(); 266 } 267 268 273 public ColumnDescriptor[] getResultColumns() { 274 if (resultColumns == null || resultColumns.isEmpty()) { 275 return new ColumnDescriptor[0]; 276 } 277 278 return (ColumnDescriptor[]) resultColumns 279 .toArray(new ColumnDescriptor[resultColumns.size()]); 280 } 281 282 289 public Map getAttributeOverrides() { 290 return attributeOverrides != null ? attributeOverrides : Collections.EMPTY_MAP; 291 } 292 293 300 public boolean isSuppressingDistinct() { 301 return suppressingDistinct; 302 } 303 304 private SelectQuery getSelectQuery() { 305 return (SelectQuery) getQuery(); 306 } 307 308 311 private List buildGroupByList() { 312 DbEntity dbEntity = getRootDbEntity(); 313 return (dbEntity instanceof DerivedDbEntity) ? ((DerivedDbEntity) dbEntity) 314 .getGroupByAttributes() : Collections.EMPTY_LIST; 315 } 316 317 List buildResultColumns() { 318 319 this.defaultAttributesByColumn = new HashMap (); 320 321 newAliasForTable(getRootDbEntity()); 323 324 List columns = new ArrayList (); 325 SelectQuery query = getSelectQuery(); 326 327 if (query.isFetchingCustomAttributes()) { 329 appendCustomColumns(columns, query); 330 } 331 else { 332 appendQueryColumns(columns, query); 333 } 334 335 return columns; 336 } 337 338 341 List appendQueryColumns(final List columns, SelectQuery query) { 343 344 final Set attributes = new HashSet (); 345 346 354 ClassDescriptor descriptor = query 355 .getMetaData(getEntityResolver()) 356 .getClassDescriptor(); 357 ObjEntity oe = descriptor.getEntity(); 358 359 PropertyVisitor visitor = new PropertyVisitor() { 360 361 public boolean visitAttribute(AttributeProperty property) { 362 ObjAttribute oa = property.getAttribute(); 363 Iterator dbPathIterator = oa.getDbPathIterator(); 364 while (dbPathIterator.hasNext()) { 365 Object pathPart = dbPathIterator.next(); 366 if (pathPart instanceof DbRelationship) { 367 DbRelationship rel = (DbRelationship) pathPart; 368 dbRelationshipAdded(rel); 369 } 370 else if (pathPart instanceof DbAttribute) { 371 DbAttribute dbAttr = (DbAttribute) pathPart; 372 if (dbAttr == null) { 373 throw new CayenneRuntimeException( 374 "ObjAttribute has no DbAttribute: " + oa.getName()); 375 } 376 377 appendColumn(columns, oa, dbAttr, attributes, null); 378 } 379 } 380 return true; 381 } 382 383 public boolean visitToMany(ToManyProperty property) { 384 visitRelationship(property); 385 return true; 386 } 387 388 public boolean visitToOne(ToOneProperty property) { 389 visitRelationship(property); 390 return true; 391 } 392 393 private void visitRelationship(ArcProperty property) { 394 ObjRelationship rel = property.getRelationship(); 395 DbRelationship dbRel = (DbRelationship) rel.getDbRelationships().get(0); 396 397 List joins = dbRel.getJoins(); 398 int len = joins.size(); 399 for (int i = 0; i < len; i++) { 400 DbJoin join = (DbJoin) joins.get(i); 401 DbAttribute src = join.getSource(); 402 appendColumn(columns, null, src, attributes, null); 403 } 404 } 405 }; 406 407 if (query.isResolvingInherited()) { 408 descriptor.visitAllProperties(visitor); 409 } 410 else { 411 descriptor.visitProperties(visitor); 412 } 413 414 DbEntity table = getRootDbEntity(); 416 Iterator pk = table.getPrimaryKey().iterator(); 417 while (pk.hasNext()) { 418 DbAttribute dba = (DbAttribute) pk.next(); 419 appendColumn(columns, null, dba, attributes, null); 420 } 421 422 424 427 if (query instanceof PrefetchSelectQuery) { 428 429 Iterator extraPaths = ((PrefetchSelectQuery) query) 430 .getResultPaths() 431 .iterator(); 432 433 while (extraPaths.hasNext()) { 436 437 String path = (String ) extraPaths.next(); 438 Expression pathExp = oe.translateToDbPath(Expression.fromString(path)); 439 440 Iterator it = table.resolvePathComponents(pathExp); 441 442 Object pathComponent = null; 444 while (it.hasNext()) { 445 pathComponent = it.next(); 446 447 if (it.hasNext() && pathComponent instanceof DbRelationship) { 449 dbRelationshipAdded((DbRelationship) pathComponent); 450 } 451 } 452 453 String labelPrefix = pathExp.toString().substring("db:".length()); 454 455 if (pathComponent instanceof DbAttribute) { 457 458 appendColumn( 460 columns, 461 null, 462 (DbAttribute) pathComponent, 463 attributes, 464 labelPrefix); 465 } 466 else if (pathComponent instanceof DbRelationship) { 467 DbRelationship relationship = (DbRelationship) pathComponent; 468 469 if (relationship.isToMany()) { 471 dbRelationshipAdded(relationship); 472 } 473 474 Iterator joins = relationship.getJoins().iterator(); 475 while (joins.hasNext()) { 476 DbJoin j = (DbJoin) joins.next(); 477 478 DbAttribute attribute = relationship.isToMany() 479 ? j.getTarget() 480 : j.getSource(); 481 482 appendColumn(columns, null, attribute, attributes, labelPrefix 485 + '.' 486 + j.getTargetName()); 487 } 488 } 489 } 490 } 491 492 if (query.getPrefetchTree() != null) { 494 495 Iterator jointPrefetches = query 496 .getPrefetchTree() 497 .adjacentJointNodes() 498 .iterator(); 499 500 while (jointPrefetches.hasNext()) { 501 PrefetchTreeNode prefetch = (PrefetchTreeNode) jointPrefetches.next(); 502 503 Expression prefetchExp = Expression.fromString(prefetch.getPath()); 505 Expression dbPrefetch = oe.translateToDbPath(prefetchExp); 506 507 Iterator it = table.resolvePathComponents(dbPrefetch); 508 509 DbRelationship r = null; 510 while (it.hasNext()) { 511 r = (DbRelationship) it.next(); 512 dbRelationshipAdded(r); 513 } 514 515 if (r == null) { 516 throw new CayenneRuntimeException("Invalid joint prefetch '" 517 + prefetch 518 + "' for entity: " 519 + oe.getName()); 520 } 521 522 525 Collection skipColumns = Collections.EMPTY_LIST; 526 if (r.getSourceEntity() == table) { 527 skipColumns = new ArrayList (2); 528 Iterator joins = r.getJoins().iterator(); 529 while (joins.hasNext()) { 530 DbJoin join = (DbJoin) joins.next(); 531 if (attributes.contains(join.getSource())) { 532 skipColumns.add(join.getTarget()); 533 } 534 } 535 } 536 537 ObjRelationship targetRel = (ObjRelationship) prefetchExp.evaluate(oe); 539 Iterator targetObjAttrs = targetRel 540 .getTargetEntity() 541 .getAttributes() 542 .iterator(); 543 544 String labelPrefix = dbPrefetch.toString().substring("db:".length()); 545 while (targetObjAttrs.hasNext()) { 546 ObjAttribute oa = (ObjAttribute) targetObjAttrs.next(); 547 Iterator dbPathIterator = oa.getDbPathIterator(); 548 while (dbPathIterator.hasNext()) { 549 Object pathPart = dbPathIterator.next(); 550 if (pathPart instanceof DbRelationship) { 551 DbRelationship rel = (DbRelationship) pathPart; 552 dbRelationshipAdded(rel); 553 } 554 else if (pathPart instanceof DbAttribute) { 555 DbAttribute attribute = (DbAttribute) pathPart; 556 if (attribute == null) { 557 throw new CayenneRuntimeException( 558 "ObjAttribute has no DbAttribute: " 559 + oa.getName()); 560 } 561 562 if (!skipColumns.contains(attribute)) { 563 appendColumn( 564 columns, 565 oa, 566 attribute, 567 attributes, 568 labelPrefix + '.' + attribute.getName()); 569 } 570 } 571 } 572 } 573 574 Iterator targetAttributes = r 576 .getTargetEntity() 577 .getAttributes() 578 .iterator(); 579 while (targetAttributes.hasNext()) { 580 DbAttribute attribute = (DbAttribute) targetAttributes.next(); 581 if (!skipColumns.contains(attribute)) { 582 appendColumn(columns, null, attribute, attributes, labelPrefix 583 + '.' 584 + attribute.getName()); 585 } 586 } 587 } 588 } 589 590 return columns; 591 } 592 593 596 List appendCustomColumns(List columns, SelectQuery query) { 597 598 List customAttributes = query.getCustomDbAttributes(); 599 DbEntity table = getRootDbEntity(); 600 int len = customAttributes.size(); 601 602 for (int i = 0; i < len; i++) { 603 DbAttribute attribute = (DbAttribute) table 604 .getAttribute((String ) customAttributes.get(i)); 605 if (attribute == null) { 606 throw new CayenneRuntimeException("Attribute does not exist: " 607 + customAttributes.get(i)); 608 } 609 610 String alias = aliasForTable((DbEntity) attribute.getEntity()); 611 columns.add(new ColumnDescriptor(attribute, alias)); 612 } 613 614 return columns; 615 } 616 617 private void appendColumn( 618 List columns, 619 ObjAttribute objAttribute, 620 DbAttribute attribute, 621 Set skipSet, 622 String label) { 623 624 if (skipSet.add(attribute)) { 625 String alias = aliasForTable((DbEntity) attribute.getEntity()); 626 ColumnDescriptor column = (objAttribute != null) ? new ColumnDescriptor( 627 objAttribute, 628 attribute, 629 alias) : new ColumnDescriptor(attribute, alias); 630 631 if (label != null) { 632 column.setLabel(label); 633 } 634 635 columns.add(column); 636 637 defaultAttributesByColumn.put(column, objAttribute); 640 } 641 else if (objAttribute != null) { 642 643 for (int i = 0; i < columns.size(); i++) { 645 ColumnDescriptor column = (ColumnDescriptor) columns.get(i); 646 if (attribute.getName().equals(column.getName())) { 647 648 ObjAttribute original = (ObjAttribute) defaultAttributesByColumn 650 .remove(column); 651 652 if (original != null) { 653 if (attributeOverrides == null) { 654 attributeOverrides = new HashMap (); 655 } 656 657 attributeOverrides.put(original, column); 658 column.setJavaClass(Void.TYPE.getName()); 659 } 660 661 break; 662 } 663 } 664 } 665 } 666 667 private void appendGroupBy(StringBuffer queryBuf, int index) { 668 DbAttribute column = (DbAttribute) groupByList.get(index); 669 String alias = aliasForTable((DbEntity) column.getEntity()); 670 queryBuf.append(column.getAliasedName(alias)); 671 } 672 673 private void appendTable(StringBuffer queryBuf, int index) { 674 DbEntity ent = (DbEntity) tableList.get(index); 675 queryBuf.append(ent.getFullyQualifiedName()); 676 queryBuf.append(' ').append((String ) aliasList.get(index)); 679 } 680 681 private void appendJoins(StringBuffer queryBuf, int index) { 682 DbRelationship rel = (DbRelationship) dbRelList.get(index); 683 String srcAlias = aliasForTable((DbEntity) rel.getSourceEntity()); 684 String targetAlias = (String ) aliasLookup.get(rel); 685 686 boolean andFlag = false; 687 688 List joins = rel.getJoins(); 689 int len = joins.size(); 690 for (int i = 0; i < len; i++) { 691 DbJoin join = (DbJoin) joins.get(i); 692 693 if (andFlag) { 694 queryBuf.append(" AND "); 695 } 696 else { 697 andFlag = true; 698 } 699 700 queryBuf.append(srcAlias).append('.').append(join.getSourceName()).append( 701 " = ").append(targetAlias).append('.').append(join.getTargetName()); 702 } 703 } 704 705 709 public void dbRelationshipAdded(DbRelationship rel) { 710 if (rel.isToMany()) { 711 forcingDistinct = true; 712 } 713 714 String existAlias = (String ) aliasLookup.get(rel); 715 716 if (existAlias == null) { 717 dbRelList.add(rel); 718 719 String newAlias = newAliasForTable((DbEntity) rel.getTargetEntity()); 721 aliasLookup.put(rel, newAlias); 722 } 723 } 724 725 728 protected String newAliasForTable(DbEntity ent) { 729 if (ent instanceof DerivedDbEntity) { 730 ent = ((DerivedDbEntity) ent).getParentEntity(); 731 } 732 733 String newAlias = "t" + aliasCounter++; 734 tableList.add(ent); 735 aliasList.add(newAlias); 736 return newAlias; 737 } 738 739 public String aliasForTable(DbEntity ent, DbRelationship rel) { 740 return (String ) aliasLookup.get(rel); 741 } 742 743 748 public String aliasForTable(DbEntity ent) { 749 if (ent instanceof DerivedDbEntity) { 750 ent = ((DerivedDbEntity) ent).getParentEntity(); 751 } 752 753 int entIndex = tableList.indexOf(ent); 754 if (entIndex >= 0) { 755 return (String ) aliasList.get(entIndex); 756 } 757 else { 758 StringBuffer msg = new StringBuffer (); 759 msg.append("Alias not found, DbEntity: '").append( 760 ent != null ? ent.getName() : "<null entity>").append( 761 "'\nExisting aliases:"); 762 763 int len = aliasList.size(); 764 for (int i = 0; i < len; i++) { 765 String dbeName = (tableList.get(i) != null) ? ((DbEntity) tableList 766 .get(i)).getName() : "<null entity>"; 767 msg.append("\n").append(aliasList.get(i)).append(" => ").append(dbeName); 768 } 769 770 throw new CayenneRuntimeException(msg.toString()); 771 } 772 } 773 774 777 public boolean supportsTableAliases() { 778 return true; 779 } 780 } 781 | Popular Tags |