1 22 package org.jboss.ejb.plugins.cmp.jdbc; 23 24 import java.lang.reflect.Method ; 25 import java.sql.Connection ; 26 import java.sql.PreparedStatement ; 27 import java.sql.ResultSet ; 28 import java.sql.SQLException ; 29 import java.util.ArrayList ; 30 import java.util.Collection ; 31 import java.util.List ; 32 import java.util.StringTokenizer ; 33 import java.util.Collections ; 34 import java.util.Iterator ; 35 import java.util.AbstractCollection ; 36 import java.util.NoSuchElementException ; 37 import java.util.Set ; 38 import javax.ejb.FinderException ; 39 import javax.ejb.EJBException ; 40 import javax.transaction.Synchronization ; 41 42 import org.jboss.deployment.DeploymentException; 43 import org.jboss.ejb.EntityEnterpriseContext; 44 import org.jboss.ejb.GenericEntityObjectFactory; 45 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMPFieldBridge; 46 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCEntityBridge; 47 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCCMRFieldBridge; 48 import org.jboss.ejb.plugins.cmp.jdbc.bridge.JDBCFieldBridge; 49 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData; 50 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCLeftJoinMetaData; 51 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationMetaData; 52 import org.jboss.ejb.plugins.cmp.ejbql.SelectFunction; 53 import org.jboss.logging.Logger; 54 55 67 public abstract class JDBCAbstractQueryCommand implements JDBCQueryCommand 68 { 69 private JDBCQueryMetaData queryMetaData; 70 protected Logger log; 71 72 private JDBCStoreManager selectManager; 73 private JDBCEntityBridge selectEntity; 74 private JDBCCMPFieldBridge selectField; 75 private SelectFunction selectFunction; 76 private boolean[] eagerLoadMask; 77 private String eagerLoadGroup; 78 private String sql; 79 private int offsetParam; 80 private int offsetValue; 81 private int limitParam; 82 private int limitValue; 83 private List parameters = new ArrayList (0); 84 private List onFindCMRList = Collections.EMPTY_LIST; 85 private QueryCollectionFactory collectionFactory; 86 87 public JDBCAbstractQueryCommand(JDBCStoreManager manager, JDBCQueryMetaData q) 88 throws DeploymentException 89 { 90 this.log = Logger.getLogger(this.getClass().getName() + 91 "." + 92 manager.getMetaData().getName() + 93 "#" + 94 q.getMethod().getName()); 95 96 queryMetaData = q; 97 collectionFactory = q.isLazyResultSetLoading() ? 98 new LazyCollectionFactory() : 99 (QueryCollectionFactory) new EagerCollectionFactory(); 100 101 setSelectEntity((JDBCEntityBridge) manager.getEntityBridge()); 104 } 105 106 public void setOffsetValue(int offsetValue) 107 { 108 this.offsetValue = offsetValue; 109 } 110 111 public void setLimitValue(int limitValue) 112 { 113 this.limitValue = limitValue; 114 } 115 116 public void setOffsetParam(int offsetParam) 117 { 118 this.offsetParam = offsetParam; 119 } 120 121 public void setLimitParam(int limitParam) 122 { 123 this.limitParam = limitParam; 124 } 125 126 public void setOnFindCMRList(List onFindCMRList) 127 { 128 this.onFindCMRList = onFindCMRList; 129 } 130 131 public JDBCStoreManager getSelectManager() 132 { 133 return selectManager; 134 } 135 136 public Collection execute(Method finderMethod, 137 Object [] args, 138 EntityEnterpriseContext ctx, 139 GenericEntityObjectFactory factory) 140 throws FinderException 141 { 142 int offset = toInt(args, offsetParam, offsetValue); 143 int limit = toInt(args, limitParam, limitValue); 144 return execute(sql, 145 args, 146 offset, 147 limit, 148 selectEntity, 149 selectField, 150 selectFunction, 151 selectManager, 152 eagerLoadMask, 153 parameters, 154 onFindCMRList, 155 queryMetaData, 156 factory, 157 log); 158 } 159 160 protected static int toInt(Object [] params, int paramNumber, int defaultValue) 161 { 162 if(paramNumber == 0) 163 { 164 return defaultValue; 165 } 166 Integer arg = (Integer ) params[paramNumber - 1]; 167 return arg.intValue(); 168 } 169 170 protected Collection execute(String sql, 171 Object [] args, 172 int offset, 173 int limit, 174 JDBCEntityBridge selectEntity, 175 JDBCCMPFieldBridge selectField, 176 SelectFunction selectFunction, 177 JDBCStoreManager selectManager, 178 boolean[] eagerLoadMask, 179 List parameters, 180 List onFindCMRList, 181 JDBCQueryMetaData queryMetaData, 182 GenericEntityObjectFactory factory, 183 Logger log) 184 throws FinderException 185 { 186 int count = offset; 187 Connection con = null; 188 PreparedStatement ps = null; 189 ResultSet rs = null; 190 final JDBCEntityBridge entityBridge = (JDBCEntityBridge)selectManager.getEntityBridge(); 191 boolean throwRuntimeExceptions = entityBridge.getMetaData().getThrowRuntimeExceptions(); 192 193 if (throwRuntimeExceptions) 196 { 197 try 198 { 199 con = entityBridge.getDataSource().getConnection(); 200 } 201 catch (SQLException sqle) 202 { 203 javax.ejb.EJBException ejbe = new javax.ejb.EJBException ("Could not get a connection; " + sqle); 204 ejbe.initCause(sqle); 205 throw ejbe; 206 } 207 } 208 209 210 try 211 { 212 if(log.isDebugEnabled()) 214 { 215 log.debug("Executing SQL: " + sql); 216 if(limit != 0 || offset != 0) 217 { 218 log.debug("Query offset=" + offset + ", limit=" + limit); 219 } 220 } 221 222 if ( ! throwRuntimeExceptions) 224 { 225 con = entityBridge.getDataSource().getConnection(); 226 } 227 ps = con.prepareStatement(sql); 228 229 if(entityBridge.getFetchSize() > 0) 231 { 232 ps.setFetchSize(entityBridge.getFetchSize()); 233 } 234 235 for(int i = 0; i < parameters.size(); i++) 237 { 238 QueryParameter parameter = (QueryParameter) parameters.get(i); 239 parameter.set(log, ps, i + 1, args); 240 } 241 242 rs = ps.executeQuery(); 244 245 while(count > 0 && rs.next()) 247 { 248 count--; 249 } 250 251 count = limit; 252 } 253 catch(Exception e) 254 { 255 JDBCUtil.safeClose(rs); 256 JDBCUtil.safeClose(ps); 257 JDBCUtil.safeClose(con); 258 259 log.error("Find failed", e); 260 FinderException fe = new FinderException ("Find failed: " + e); 261 fe.initCause(e); 262 throw fe; 263 } 264 265 return collectionFactory.createCollection(con, 266 ps, 267 rs, 268 limit, 269 count, 270 selectEntity, 271 selectField, 272 selectFunction, 273 selectManager, 274 onFindCMRList, 275 eagerLoadMask, 276 factory); 277 } 278 279 protected Logger getLog() 280 { 281 return log; 282 } 283 284 protected void setSQL(String sql) 285 { 286 this.sql = sql; 287 if(log.isDebugEnabled()) 288 { 289 log.debug("SQL: " + sql); 290 } 291 } 292 293 protected void setParameterList(List p) 294 { 295 for(int i = 0; i < p.size(); i++) 296 { 297 if(!(p.get(i) instanceof QueryParameter)) 298 { 299 throw new IllegalArgumentException ("Element " + 300 i + 301 " of list " + 302 "is not an instance of QueryParameter, but " + 303 p.get(i).getClass().getName()); 304 } 305 } 306 parameters = new ArrayList (p); 307 } 308 309 protected JDBCEntityBridge getSelectEntity() 310 { 311 return selectEntity; 312 } 313 314 protected void setSelectEntity(JDBCEntityBridge selectEntity) 315 throws DeploymentException 316 { 317 if(queryMetaData.getMethod().getName().startsWith("find") && 318 this.selectEntity != null && this.selectEntity != selectEntity) 319 { 320 throw new DeploymentException("Finder " + queryMetaData.getMethod().getName() + 321 " defined on " + this.selectEntity.getEntityName() + 322 " should return only instances of " + this.selectEntity.getEntityName() + 323 " but the query results in instances of " + selectEntity.getEntityName()); 324 } 325 326 this.selectField = null; 327 this.selectFunction = null; 328 this.selectEntity = selectEntity; 329 this.selectManager = (JDBCStoreManager) selectEntity.getManager(); 330 } 331 332 protected JDBCCMPFieldBridge getSelectField() 333 { 334 return selectField; 335 } 336 337 protected void setSelectField(JDBCCMPFieldBridge selectField) 338 { 339 this.selectEntity = null; 340 this.selectFunction = null; 341 this.selectField = selectField; 342 this.selectManager = (JDBCStoreManager) selectField.getManager(); 343 } 344 345 protected void setSelectFunction(SelectFunction func, JDBCStoreManager manager) 346 { 347 this.selectEntity = null; 348 this.selectField = null; 349 this.selectFunction = func; 350 this.selectManager = manager; 351 } 352 353 protected void setEagerLoadGroup(String eagerLoadGroup) 354 { 355 this.eagerLoadGroup = eagerLoadGroup; 356 this.eagerLoadMask = selectEntity.getLoadGroupMask(eagerLoadGroup); 357 } 358 359 protected String getEagerLoadGroup() 360 { 361 return eagerLoadGroup; 362 } 363 364 protected boolean[] getEagerLoadMask() 365 { 366 return this.eagerLoadMask; 367 } 368 369 379 protected String parseParameters(String sql) throws DeploymentException 380 { 381 StringBuffer sqlBuf = new StringBuffer (); 382 ArrayList params = new ArrayList (); 383 384 if(sql != null) 386 { 387 sql = sql.trim(); 388 389 StringTokenizer tokens = new StringTokenizer (sql, "{}", true); 390 while(tokens.hasMoreTokens()) 391 { 392 String token = tokens.nextToken(); 393 if(token.equals("{")) 394 { 395 token = tokens.nextToken(); 396 if(Character.isDigit(token.charAt(0))) 397 { 398 QueryParameter parameter = new QueryParameter(selectManager, queryMetaData.getMethod(), token); 399 400 sqlBuf.append("?"); 403 params.add(parameter); 404 405 if(!tokens.nextToken().equals("}")) 406 { 407 throw new DeploymentException("Invalid parameter - missing closing '}' : " + sql); 408 } 409 } 410 else 411 { 412 sqlBuf.append("{").append(token); 415 } 416 } 417 else 418 { 419 sqlBuf.append(token); 421 } 422 } 423 } 424 425 parameters = params; 426 427 return sqlBuf.toString(); 428 } 429 430 432 public static List getLeftJoinCMRNodes(JDBCEntityBridge entity, String path, Iterator leftJoinIter, Set declaredPaths) 433 throws DeploymentException 434 { 435 List leftJoinCMRNodes; 436 437 if(leftJoinIter.hasNext()) 438 { 439 leftJoinCMRNodes = new ArrayList (); 440 while(leftJoinIter.hasNext()) 441 { 442 JDBCLeftJoinMetaData leftJoin = (JDBCLeftJoinMetaData) leftJoinIter.next(); 443 JDBCCMRFieldBridge cmrField = entity.getCMRFieldByName(leftJoin.getCmrField()); 444 if(cmrField == null) 445 { 446 throw new DeploymentException("cmr-field in left-join was not found: cmr-field=" + 447 leftJoin.getCmrField() + ", entity=" + entity.getEntityName()); 448 } 449 450 List subNodes; 451 JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); 452 String childPath = path + '.' + cmrField.getFieldName(); 453 if(declaredPaths != null) 454 { 455 declaredPaths.add(childPath); 456 } 457 458 subNodes = getLeftJoinCMRNodes(relatedEntity, childPath, leftJoin.getLeftJoins(), declaredPaths); 459 460 boolean[] mask = relatedEntity.getLoadGroupMask(leftJoin.getEagerLoadGroup()); 461 LeftJoinCMRNode node = new LeftJoinCMRNode(childPath, cmrField, mask, subNodes); 462 leftJoinCMRNodes.add(node); 463 } 464 } 465 else 466 { 467 leftJoinCMRNodes = Collections.EMPTY_LIST; 468 } 469 470 return leftJoinCMRNodes; 471 } 472 473 public static final void leftJoinCMRNodes(String alias, 474 List onFindCMRNodes, 475 AliasManager aliasManager, 476 StringBuffer sb) 477 { 478 for(int i = 0; i < onFindCMRNodes.size(); ++i) 479 { 480 LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(i); 481 JDBCCMRFieldBridge cmrField = node.cmrField; 482 JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); 483 String relatedAlias = aliasManager.getAlias(node.path); 484 485 JDBCRelationMetaData relation = cmrField.getMetaData().getRelationMetaData(); 486 if(relation.isTableMappingStyle()) 487 { 488 String relTableAlias = aliasManager.getRelationTableAlias(node.path); 489 sb.append(" LEFT OUTER JOIN ") 490 .append(cmrField.getQualifiedTableName()) 491 .append(' ') 492 .append(relTableAlias) 493 .append(" ON "); 494 SQLUtil.getRelationTableJoinClause(cmrField, alias, relTableAlias, sb); 495 496 sb.append(" LEFT OUTER JOIN ") 497 .append(relatedEntity.getQualifiedTableName()) 498 .append(' ') 499 .append(relatedAlias) 500 .append(" ON "); 501 SQLUtil.getRelationTableJoinClause(cmrField.getRelatedCMRField(), relatedAlias, relTableAlias, sb); 502 } 503 else 504 { 505 sb.append(" LEFT OUTER JOIN ") 507 .append(relatedEntity.getQualifiedTableName()) 508 .append(' ') 509 .append(relatedAlias) 510 .append(" ON "); 511 SQLUtil.getJoinClause(cmrField, 512 alias, 513 relatedAlias, 514 sb); 515 } 516 517 List subNodes = node.onFindCMRNodes; 518 if(!subNodes.isEmpty()) 519 { 520 leftJoinCMRNodes(relatedAlias, subNodes, aliasManager, sb); 521 } 522 } 523 } 524 525 public static final void appendLeftJoinCMRColumnNames(List onFindCMRNodes, 526 AliasManager aliasManager, 527 StringBuffer sb) 528 { 529 for(int i = 0; i < onFindCMRNodes.size(); ++i) 530 { 531 LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(i); 532 JDBCCMRFieldBridge cmrField = node.cmrField; 533 JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); 534 String childAlias = aliasManager.getAlias(node.path); 535 536 SQLUtil.appendColumnNamesClause(relatedEntity.getPrimaryKeyFields(), 538 childAlias, 539 sb); 540 541 if(node.eagerLoadMask != null) 543 { 544 SQLUtil.appendColumnNamesClause(relatedEntity.getTableFields(), 545 node.eagerLoadMask, 546 childAlias, 547 sb); 548 } 549 550 List subNodes = node.onFindCMRNodes; 551 if(!subNodes.isEmpty()) 552 { 553 appendLeftJoinCMRColumnNames(subNodes, aliasManager, sb); 554 } 555 } 556 } 557 558 private static int loadOnFindCMRFields(Object pk, List onFindCMRNodes, ResultSet rs, int index, Logger log) 559 { 560 Object [] ref = new Object [1]; 561 for(int nodeInd = 0; nodeInd < onFindCMRNodes.size(); ++nodeInd) 562 { 563 LeftJoinCMRNode node = (LeftJoinCMRNode) onFindCMRNodes.get(nodeInd); 564 JDBCCMRFieldBridge cmrField = node.cmrField; 565 ReadAheadCache myCache = cmrField.getJDBCStoreManager().getReadAheadCache(); 566 JDBCEntityBridge relatedEntity = cmrField.getRelatedJDBCEntity(); 567 ReadAheadCache relatedCache = cmrField.getRelatedManager().getReadAheadCache(); 568 569 ref[0] = null; 571 index = relatedEntity.loadPrimaryKeyResults(rs, index, ref); 572 Object relatedId = ref[0]; 573 boolean cacheRelatedData = relatedId != null; 574 575 if(pk != null) 576 { 577 if(cmrField.getMetaData().getRelatedRole().isMultiplicityOne()) 578 { 579 myCache.addPreloadData(pk, 581 cmrField, 582 relatedId == null ? Collections.EMPTY_LIST : Collections.singletonList(relatedId)); 583 } 584 else 585 { 586 Collection cachedValue = myCache.getCachedCMRValue(pk, cmrField); 587 if(cachedValue == null) 588 { 589 cachedValue = new ArrayList (); 590 myCache.addPreloadData(pk, cmrField, cachedValue); 591 } 592 593 if(relatedId != null) 594 { 595 if(cachedValue.contains(relatedId)) 596 { 597 cacheRelatedData = false; 598 } 599 else 600 { 601 cachedValue.add(relatedId); 602 } 603 } 604 } 605 } 606 607 if(node.eagerLoadMask != null) 609 { 610 JDBCFieldBridge[] tableFields = relatedEntity.getTableFields(); 611 for(int fieldInd = 0; fieldInd < tableFields.length; ++fieldInd) 612 { 613 if(node.eagerLoadMask[fieldInd]) 614 { 615 JDBCFieldBridge field = tableFields[fieldInd]; 616 ref[0] = null; 617 index = field.loadArgumentResults(rs, index, ref); 618 619 if(cacheRelatedData) 620 { 621 if(log.isTraceEnabled()) 622 { 623 log.trace("Caching " + 624 relatedEntity.getEntityName() + 625 '[' + 626 relatedId + 627 "]." + 628 field.getFieldName() + "=" + ref[0]); 629 } 630 relatedCache.addPreloadData(relatedId, field, ref[0]); 631 } 632 } 633 } 634 } 635 636 List subNodes = node.onFindCMRNodes; 637 if(!subNodes.isEmpty()) 638 { 639 index = loadOnFindCMRFields(relatedId, subNodes, rs, index, log); 640 } 641 } 642 643 return index; 644 } 645 646 public static final class LeftJoinCMRNode 647 { 648 public final String path; 649 public final JDBCCMRFieldBridge cmrField; 650 public final boolean[] eagerLoadMask; 651 public final List onFindCMRNodes; 652 653 public LeftJoinCMRNode(String path, JDBCCMRFieldBridge cmrField, boolean[] eagerLoadMask, List onFindCMRNodes) 654 { 655 this.path = path; 656 this.cmrField = cmrField; 657 this.eagerLoadMask = eagerLoadMask; 658 this.onFindCMRNodes = onFindCMRNodes; 659 } 660 661 public boolean equals(Object o) 662 { 663 boolean result; 664 if(o == this) 665 { 666 result = true; 667 } 668 else if(o instanceof LeftJoinCMRNode) 669 { 670 LeftJoinCMRNode other = (LeftJoinCMRNode) o; 671 result = cmrField == other.cmrField; 672 } 673 else 674 { 675 result = false; 676 } 677 return result; 678 } 679 680 public int hashCode() 681 { 682 return cmrField == null ? Integer.MIN_VALUE : cmrField.hashCode(); 683 } 684 685 public String toString() 686 { 687 return '[' + cmrField.getFieldName() + ": " + onFindCMRNodes + ']'; 688 } 689 } 690 691 692 interface QueryCollectionFactory 693 { 694 Collection createCollection(Connection con, 695 PreparedStatement ps, 696 ResultSet rs, 697 int limit, int count, 698 JDBCEntityBridge selectEntity, 699 JDBCCMPFieldBridge selectField, 700 SelectFunction selectFunction, 701 JDBCStoreManager selectManager, 702 List onFindCMRList, 703 boolean[] eagerLoadMask, 704 GenericEntityObjectFactory factory) 705 throws FinderException ; 706 } 707 708 class EagerCollectionFactory 709 implements QueryCollectionFactory 710 { 711 public Collection createCollection(Connection con, 712 PreparedStatement ps, 713 ResultSet rs, 714 int limit, int count, 715 JDBCEntityBridge selectEntity, 716 JDBCCMPFieldBridge selectField, 717 SelectFunction selectFunction, 718 JDBCStoreManager selectManager, 719 List onFindCMRList, 720 boolean[] eagerLoadMask, 721 GenericEntityObjectFactory factory) 722 throws FinderException 723 { 724 try 725 { 726 List results = new ArrayList (); 727 728 if(selectEntity != null) 729 { 730 ReadAheadCache selectReadAheadCache = selectManager.getReadAheadCache(); 731 List ids = new ArrayList (); 732 733 boolean loadOnFindCmr = !onFindCMRList.isEmpty(); 734 Object [] ref = new Object [1]; 735 Object prevPk = null; 736 737 while((limit == 0 || count-- > 0) && rs.next()) 738 { 739 int index = 1; 740 741 index = selectEntity.loadPrimaryKeyResults(rs, index, ref); 743 Object pk = ref[0]; 744 745 boolean addPk = (loadOnFindCmr ? !pk.equals(prevPk) : true); 746 if(addPk) 747 { 748 ids.add(pk); 749 results.add(factory.getEntityEJBObject(pk)); 750 prevPk = pk; 751 } 752 753 if(eagerLoadMask != null) 755 { 756 JDBCFieldBridge[] tableFields = selectEntity.getTableFields(); 757 for(int i = 0; i < eagerLoadMask.length; i++) 758 { 759 if(eagerLoadMask[i]) 760 { 761 JDBCFieldBridge field = tableFields[i]; 762 ref[0] = null; 763 764 index = field.loadArgumentResults(rs, index, ref); 766 767 if(addPk) 768 { 769 selectReadAheadCache.addPreloadData(pk, field, ref[0]); 770 } 771 } 772 } 773 774 if(!onFindCMRList.isEmpty()) 775 { 776 index = loadOnFindCMRFields(pk, onFindCMRList, rs, index, log); 777 } 778 } 779 } 780 781 selectReadAheadCache.addFinderResults(ids, queryMetaData.getReadAhead()); 783 } 784 else if(selectField != null) 785 { 786 Object [] valueRef = new Object [1]; 788 while((limit == 0 || count-- > 0) && rs.next()) 789 { 790 valueRef[0] = null; 791 selectField.loadArgumentResults(rs, 1, valueRef); 792 results.add(valueRef[0]); 793 } 794 } 795 else 796 { 797 while(rs.next()) 798 { 799 results.add(selectFunction.readResult(rs)); 800 } 801 } 802 803 if(log.isDebugEnabled() && limit != 0 && count == 0) 804 { 805 log.debug("Query result was limited to " + limit + " row(s)"); 806 } 807 808 return results; 809 } 810 catch(Exception e) 811 { 812 log.error("Find failed", e); 813 throw new FinderException ("Find failed: " + e); 814 } 815 finally 816 { 817 JDBCUtil.safeClose(rs); 818 JDBCUtil.safeClose(ps); 819 JDBCUtil.safeClose(con); 820 } 821 } 822 823 } 824 825 class LazyCollectionFactory 826 implements QueryCollectionFactory 827 { 828 public Collection createCollection(Connection con, 829 PreparedStatement ps, 830 ResultSet rs, 831 int limit, int count, 832 JDBCEntityBridge selectEntity, 833 JDBCCMPFieldBridge selectField, 834 SelectFunction selectFunction, 835 JDBCStoreManager selectManager, 836 List onFindCMRList, 837 boolean[] eagerLoadMask, 838 GenericEntityObjectFactory factory) 839 throws FinderException 840 { 841 return new LazyCollection(con, 842 ps, 843 rs, 844 limit, 845 count, 846 selectEntity, 847 selectField, 848 selectFunction, 849 selectManager, 850 eagerLoadMask, 851 factory); 852 } 853 854 private class LazyCollection extends AbstractCollection 855 { 856 private final Connection con; 857 private final PreparedStatement ps; 858 private final ResultSet rs; 859 private final int limit; 860 private int count; 861 private final JDBCEntityBridge selectEntity; 862 private final JDBCCMPFieldBridge selectField; 863 private final SelectFunction selectFunction; 864 private final JDBCStoreManager selectManager; 865 private final boolean[] eagerLoadMask; 866 private final GenericEntityObjectFactory factory; 867 868 private Object prevPk; 869 private Object curPk; 870 private Object currentResult; 871 872 Object [] ref = new Object [1]; 873 874 boolean loadOnFindCmr; 875 876 private List results = null; 877 private Iterator firstIterator; 878 private int size; 879 private boolean resourcesClosed; 880 881 public LazyCollection(final Connection con, 882 final PreparedStatement ps, 883 final ResultSet rs, 884 int limit, 885 int count, 886 JDBCEntityBridge selectEntity, 887 JDBCCMPFieldBridge selectField, 888 SelectFunction selectFunction, 889 JDBCStoreManager selectManager, 890 boolean[] eagerLoadMask, 891 GenericEntityObjectFactory factory) 892 { 893 this.con = con; 894 this.ps = ps; 895 this.rs = rs; 896 this.limit = limit; 897 this.count = count; 898 this.selectEntity = selectEntity; 899 this.selectField = selectField; 900 this.selectFunction = selectFunction; 901 this.selectManager = selectManager; 902 this.eagerLoadMask = eagerLoadMask; 903 this.factory = factory; 904 loadOnFindCmr = !onFindCMRList.isEmpty(); 905 906 firstIterator = getFirstIterator(); 907 if(firstIterator.hasNext()) 908 { 909 try 910 { 911 size = rs.getInt(1); 912 } 913 catch(SQLException e) 914 { 915 throw new EJBException ("Failed to read ResultSet.", e); 916 } 917 918 if(limit > 0 && size > limit) 919 { 920 size = limit; 921 } 922 } 923 924 if(size < 1) 925 { 926 firstIterator = null; 927 results = new ArrayList (0); 928 closeResources(); 929 } 930 else 931 { 932 results = new ArrayList (size); 933 try 934 { 935 selectManager.getContainer().getTransactionManager().getTransaction().registerSynchronization(new Synchronization () 936 { 937 public void beforeCompletion() 938 { 939 closeResources(); 940 } 941 942 public void afterCompletion(int status) 943 { 944 closeResources(); 945 } 946 }); 947 } 948 catch(Exception e) 949 { 950 throw new EJBException ("Failed to obtain current transaction", e); 951 } 952 } 953 } 954 955 private void closeResources() 956 { 957 if(!resourcesClosed) 958 { 959 JDBCUtil.safeClose(rs); 960 JDBCUtil.safeClose(ps); 961 JDBCUtil.safeClose(con); 962 resourcesClosed = true; 963 } 964 } 965 966 public Iterator iterator() 967 { 968 return firstIterator != null ? firstIterator : results.iterator(); 969 } 970 971 public int size() 972 { 973 return firstIterator != null ? size : results.size(); 974 } 975 976 public boolean add(Object o) 977 { 978 if(firstIterator == null) 979 { 980 return results.add(o); 981 } 982 throw new IllegalStateException ("Can't modify collection while the first iterator is not exhausted."); 983 } 984 985 public boolean remove(Object o) 986 { 987 if(firstIterator == null) 988 { 989 return results.remove(o); 990 } 991 throw new IllegalStateException ("Can't modify collection while the first iterator is not exhausted."); 992 } 993 994 private boolean hasNextResult() 995 { 996 try 997 { 998 boolean has = (limit == 0 || count-- > 0) && rs.next(); 999 if(!has) 1000 { 1001 if(log.isTraceEnabled()) 1002 { 1003 log.trace("first iterator exhausted!"); 1004 } 1005 firstIterator = null; 1006 closeResources(); 1007 } 1008 return has; 1009 } 1010 catch(Exception e) 1011 { 1012 log.error("Failed to read ResultSet.", e); 1013 throw new EJBException ("Failed to read ResultSet: " + e.getMessage()); 1014 } 1015 } 1016 1017 private Object readNext() 1018 { 1019 try 1020 { 1021 if(selectEntity != null) 1022 { 1023 ReadAheadCache selectReadAheadCache = selectManager.getReadAheadCache(); 1024 1025 int index = 2; 1027 1028 index = selectEntity.loadPrimaryKeyResults(rs, index, ref); 1030 curPk = ref[0]; 1031 1032 boolean addPk = (loadOnFindCmr ? !curPk.equals(prevPk) : true); 1033 if(addPk) 1034 { 1035 prevPk = curPk; 1036 currentResult = factory.getEntityEJBObject(curPk); 1037 } 1038 1039 if(eagerLoadMask != null) 1041 { 1042 JDBCFieldBridge[] tableFields = selectEntity.getTableFields(); 1043 for(int i = 0; i < eagerLoadMask.length; i++) 1044 { 1045 if(eagerLoadMask[i]) 1046 { 1047 JDBCFieldBridge field = tableFields[i]; 1048 ref[0] = null; 1049 1050 index = field.loadArgumentResults(rs, index, ref); 1052 1053 if(addPk) 1054 { 1055 selectReadAheadCache.addPreloadData(curPk, field, ref[0]); 1056 } 1057 } 1058 } 1059 1060 if(!onFindCMRList.isEmpty()) 1061 { 1062 index = loadOnFindCMRFields(curPk, onFindCMRList, rs, index, log); 1063 } 1064 } 1065 } 1066 else if(selectField != null) 1067 { 1068 selectField.loadArgumentResults(rs, 2, ref); 1070 currentResult = ref[0]; 1071 } 1072 else 1073 { 1074 currentResult = selectFunction.readResult(rs); 1075 } 1076 1077 if(log.isTraceEnabled() && limit != 0 && count == 0) 1078 { 1079 log.trace("Query result was limited to " + limit + " row(s)"); 1080 } 1081 1082 return currentResult; 1083 } 1084 catch(Exception e) 1085 { 1086 log.error("Failed to read ResultSet", e); 1087 throw new EJBException ("Failed to read ResultSet: " + e.getMessage()); 1088 } 1089 } 1090 1091 private Iterator getFirstIterator() 1092 { 1093 return new Iterator () 1094 { 1095 private boolean hasNext; 1096 private Object cursor; 1097 1098 public boolean hasNext() 1099 { 1100 return hasNext ? hasNext : (hasNext = hasNextResult()); 1101 } 1102 1103 public Object next() 1104 { 1105 if(!hasNext()) 1106 { 1107 throw new NoSuchElementException (); 1108 } 1109 hasNext = false; 1110 1111 cursor = readNext(); 1112 results.add(cursor); 1113 1114 return cursor; 1115 } 1116 1117 public void remove() 1118 { 1119 --size; 1120 results.remove(cursor); 1121 } 1122 }; 1123 } 1124 } 1125 } 1126} 1127 | Popular Tags |