001package gu.sql2java; 002 003import java.lang.reflect.Array; 004import java.nio.ByteBuffer; 005import java.sql.Connection; 006import java.sql.PreparedStatement; 007import java.sql.ResultSet; 008import java.sql.SQLException; 009import java.sql.Statement; 010import java.util.ArrayList; 011import java.util.Arrays; 012import java.util.Collection; 013import java.util.Collections; 014import java.util.Iterator; 015import java.util.LinkedHashMap; 016import java.util.LinkedHashSet; 017import java.util.LinkedList; 018import java.util.List; 019import java.util.Map; 020import java.util.ServiceLoader; 021import java.util.Map.Entry; 022import java.util.NoSuchElementException; 023import java.util.concurrent.Callable; 024import java.util.concurrent.atomic.AtomicInteger; 025 026import com.google.common.base.Function; 027import com.google.common.base.Joiner; 028import com.google.common.base.MoreObjects; 029import com.google.common.base.Predicate; 030import com.google.common.base.Throwables; 031import com.google.common.collect.ImmutableBiMap; 032import com.google.common.collect.ImmutableList; 033import com.google.common.collect.ImmutableMap; 034import com.google.common.collect.Iterables; 035import com.google.common.collect.Lists; 036import com.google.common.collect.Maps; 037import gu.sql2java.ForeignKeyMetaData.ForeignKeyRule; 038import gu.sql2java.Manager.AutoKeyRetrieveType; 039import gu.sql2java.exception.DaoException; 040import gu.sql2java.exception.DataAccessException; 041import gu.sql2java.exception.DataRetrievalException; 042import gu.sql2java.exception.ObjectRetrievalException; 043import gu.sql2java.exception.OptimisticLockingException; 044import gu.sql2java.exception.RuntimeDaoException; 045 046import static com.google.common.base.Preconditions.*; 047import static gu.sql2java.SimpleLog.*; 048 049/** 050 * implementation of {@link TableManager} 051 * @author guyadong 052 * 053 * @param <B> java bean type 054 */ 055public class BaseTableManager<B extends BaseBean> implements TableManager<B>,Constant{ 056 protected final RowMetaData metaData; 057 /** lazy load */ 058 private volatile Map<String,TableListener<BaseBean>> foreignKeyDeleteListeners; 059 /** lazy load */ 060 private volatile ListenerContainer<B> listenerContainer; 061 private static boolean debug = false; 062 protected BaseTableManager(String tablename){ 063 metaData = RowMetaData.getMedaData(tablename); 064 } 065 066 public ListenerContainer<B> getListenerContainer() { 067 // double checking 068 if(listenerContainer == null){ 069 synchronized (this) { 070 if(listenerContainer == null){ 071 listenerContainer = new ListenerContainer<B>(); 072 } 073 } 074 } 075 return listenerContainer; 076 } 077 078 /** 079 * @return map with foreignKey name TO TableListener 080 */ 081 public Map<String, TableListener<BaseBean>> getForeignKeyDeleteListeners(){ 082 // double checking 083 if(foreignKeyDeleteListeners == null){ 084 synchronized (this) { 085 if(foreignKeyDeleteListeners == null){ 086 LinkedHashMap<String, TableListener<BaseBean>> map = Maps.newLinkedHashMap(); 087 for(ForeignKeyMetaData fk : metaData.getForeignKeysForListener()){ 088 map.put(fk.name, new DeleteRuleListener<BaseBean>(fk.name)); 089 } 090 foreignKeyDeleteListeners = Collections.unmodifiableMap(map); 091 } 092 } 093 } 094 return foreignKeyDeleteListeners; 095 } 096 /** 097 * Creates a new B instance. 098 * 099 * @return the new B instance 100 */ 101 @SuppressWarnings("unchecked") 102 protected final B createBean() 103 { 104 try { 105 return (B) metaData.beanType.newInstance(); 106 } catch (Exception e) { 107 Throwables.throwIfUnchecked(e); 108 throw new RuntimeException(e); 109 } 110 } 111 /** 112 * Creates a new B instance. 113 * @param primaryValues values of primary keys 114 * @return B instance 115 */ 116 protected final B createBean(Object... primaryValues) 117 { 118 checkArgument(primaryValues != null && primaryValues.length== metaData.primaryKeyNames.length,"INVALID primaryValues"); 119 B bean = createBean(); 120 int[] pkIds = metaData.primaryKeyIds; 121 for(int i=0;i<primaryValues.length;++i){ 122 int columnId = pkIds[i]; 123 Object value = primaryValues[i]; 124 checkArgument(null == value || metaData.columnTypeOf(columnId).isInstance(value), 125 "INVALID primkey type for %s,%s required",metaData.columnNameOf(columnId),metaData.columnTypeOf(columnId).getName()); 126 bean.setValue(columnId, value); 127 } 128 return bean; 129 } 130 private static int indexOfFirstNull(Object...objects) { 131 if(objects != null){ 132 for(int i=0;i< objects.length;++i){ 133 if(null == objects[i]){ 134 return i; 135 } 136 } 137 return -1; 138 } 139 return -2; 140 } 141 /** 142 * @param objects 143 * @return true if any one of object is null or objects is null 144 */ 145 private static boolean hasNull(Object...objects) { 146 return indexOfFirstNull(objects) != -1; 147 } 148 149 /** 150 * @param bean 151 * @return true if any primary key of B is null or B is null 152 */ 153 private static <T extends BaseBean >boolean hasNullPk(T bean){ 154 return (null == bean || hasNull(bean.primaryValues())); 155 } 156 157 private void prepareAutoincrement( Connection c,PreparedStatement ps, B bean) throws SQLException 158 { 159 if (!bean.isModified(metaData.autoincrementColumnId)) 160 { 161 PreparedStatement ps2 = null; 162 ResultSet rs = null; 163 try { 164 if(AutoKeyRetrieveType.auto.equals(getManager().getGeneratedkeyRetrieveType(c))){ 165 rs = ps.getGeneratedKeys(); 166 }else{ 167 ps2 = c.prepareStatement(metaData.getGeneratedkeyStatement(c)); 168 rs = ps2.executeQuery(); 169 } 170 if(rs.next()) { 171 setColumnValue(bean, 172 metaData.autoincrementColumnId, 173 Manager.getObject(rs,1,metaData.columnTypes.get(metaData.autoincrementColumnId))); 174 } else { 175 throw new IllegalStateException("ATTENTION: Could not retrieve generated key!"); 176 } 177 } finally { 178 getManager().close(ps2, rs); 179 } 180 } 181 } 182 183 //13 184 /** 185 * Insert the B bean into the database. 186 * 187 * @param bean the B bean to be saved 188 * @return the inserted bean 189 * @throws RuntimeDaoException 190 */ 191 protected B insert(final B bean) 192 { 193 // mini checks 194 if (null == bean || !bean.isModified()) { 195 return bean; 196 } 197 if (!bean.isNew()){ 198 return this.update(bean); 199 } 200 201 Connection c = null; 202 PreparedStatement ps = null; 203 204 try 205 { 206 c = this.getConnection(); 207 final AutoKeyRetrieveType retrieveType = getManager().getGeneratedkeyRetrieveType(c); 208 //-------------writePreInsert 209 if(metaData.autoincrementColumnId >= 0 210 && AutoKeyRetrieveType.before.equals(retrieveType)){ 211 prepareAutoincrement(c, null, bean); 212 } 213 //------------/writePreInsert 214 // listener callback 215 getListenerContainer().beforeInsert(bean); 216 StringBuilder sql = new StringBuilder("INSERT into " + metaData.tablename + " ("); 217 List<String> modifiedList = Lists.newArrayList(Iterables.filter(metaData.columnNames,new Predicate<String>() { 218 219 @Override 220 public boolean apply(String input) { 221 return bean.isModified(input); 222 } 223 })); 224 String fields=Joiner.on(",").join(modifiedList); 225 // fields 226 sql.append(fields); 227 sql.append(") values ("); 228 // values 229 sql.append(fields.replaceAll("[^,]+", "?")); 230 sql.append(")"); 231 if(debug){ 232 log("insert : " + sql.toString()); 233 } 234 if(metaData.autoincrementColumnId >= 0 && AutoKeyRetrieveType.auto.equals(retrieveType)){ 235 ps = c.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS); 236 }else{ 237 ps = c.prepareStatement(sql.toString(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); 238 } 239 240 fillPreparedStatement(ps, bean, SEARCH_EXACT, true); 241 242 ps.executeUpdate(); 243 //------------------writePostInsert 244 if(metaData.autoincrementColumnId >= 0 && !AutoKeyRetrieveType.before.equals(retrieveType)){ 245 prepareAutoincrement(c, ps, bean); 246 } 247 //-------------------/writePostInsert 248 bean.setNew(false); 249 bean.resetIsModified(); 250 // listener callback 251 getListenerContainer().afterInsert(bean); 252 return bean; 253 } 254 catch(SQLException e) 255 { 256 throw new RuntimeDaoException(new DataAccessException(e)); 257 } 258 finally 259 { 260 // listener callback 261 getListenerContainer().done(); 262 getManager().close(ps); 263 freeConnection(c); 264 } 265 } 266 267 private static final Function<String, String> SQL_FUN1 = new Function<String,String>(){ 268 269 @Override 270 public String apply(String input) { 271 return input + "=?"; 272 }}; 273 //14 274 /** 275 * Update the B bean record in the database according to the changes. 276 * 277 * @param bean the B bean to be updated 278 * @return the updated bean 279 * @throws RuntimeDaoException 280 */ 281 protected B update(final B bean) throws RuntimeDaoException 282 { 283 // mini checks 284 if (null == bean || !bean.isModified()) { 285 return bean; 286 } 287 if (bean.isNew()){ 288 return this.insert(bean); 289 } 290 291 Connection c = null; 292 PreparedStatement ps = null; 293 294 try 295 { 296 c = this.getConnection(); 297 298 // listener callback 299 getListenerContainer().beforeUpdate(bean); 300 Object oldLockValue = null; 301 if(metaData.lockColumnType != null){ 302 oldLockValue = bean.getValue(metaData.lockColumnName); 303 // lockColumnType is String or Long 304 if(String.class == metaData.lockColumnType){ 305 bean.setValue(metaData.lockColumnName,String.valueOf(System.currentTimeMillis())); 306 }else if(Long.class ==metaData.lockColumnType){ 307 bean.setValue(metaData.lockColumnName,System.currentTimeMillis()); 308 }else{ 309 throw new RuntimeException("INVALID LOCK COLUMN TYPE:String or Long required"); 310 } 311 } 312 StringBuilder sql = new StringBuilder("UPDATE " + metaData.tablename + " SET "); 313 List<String> modified = Lists.newArrayList(Iterables.filter(metaData.columnNames,new Predicate<String>() { 314 315 @Override 316 public boolean apply(String input) { 317 return bean.isModified(input); 318 } 319 })); 320 sql.append(Joiner.on(",").join(Lists.transform(modified,SQL_FUN1))); 321 322 sql.append(" WHERE "); 323 sql.append(Joiner.on(" AND ").join( 324 Lists.transform(Arrays.asList(metaData.primaryKeyNames), SQL_FUN1))); 325 if(metaData.lockColumnType != null){ 326 if(metaData.primaryKeyNames.length > 0){ 327 sql.append(" AND "); 328 } 329 checkArgument(metaData.lockColumnName != null, "NOT DEFINED lock column name"); 330 sql.append(metaData.lockColumnName + "=?"); 331 } 332 if(debug){ 333 log("update : " + sql.toString()); 334 } 335 ps = c.prepareStatement(sql.toString(), 336 ResultSet.TYPE_SCROLL_INSENSITIVE, 337 ResultSet.CONCUR_READ_ONLY); 338 339 int dirtyCount = this.fillPreparedStatement(ps, bean, SEARCH_EXACT,true); 340 341 if (dirtyCount == 0) { 342 if(debug){ 343 log("The bean to look is not initialized... do not update."); 344 } 345 return bean; 346 } 347 int[] pkIds = metaData.primaryKeyIds; 348 for(int i = 0; i<pkIds.length; ++i){ 349 setPreparedStatement(ps, ++dirtyCount, bean, pkIds[i]); 350 } 351 if(metaData.lockColumnType != null){ 352 setPreparedStatement(ps, ++dirtyCount, oldLockValue, metaData.columnIDOf(metaData.lockColumnName)); 353 } 354 int rowCount = ps.executeUpdate(); 355 if (metaData.lockColumnType != null && rowCount==0) { 356 throw new OptimisticLockingException("sql2java.exception.optimisticlock"); 357 } 358 // listener callback 359 getListenerContainer().afterUpdate(bean); 360 bean.resetIsModified(); 361 362 return bean; 363 } 364 catch(SQLException e) 365 { 366 throw new RuntimeDaoException(new DataAccessException(e)); 367 } 368 finally 369 { 370 // listener callback 371 getListenerContainer().done(); 372 getManager().close(ps); 373 freeConnection(c); 374 } 375 } 376 377 class ListAction implements TableManager.Action<B> { 378 final List<B> list; 379 public ListAction() { 380 list=new LinkedList<B>(); 381 } 382 383 public List<B> getList() { 384 return list; 385 } 386 387 @Override 388 public void call(B bean) { 389 list.add(bean); 390 } 391 392 } 393 394 @Override 395 public int countAll()throws RuntimeDaoException{ 396 return this.countWhere(""); 397 } 398 399 @Override 400 public int countUsingTemplate(B bean)throws RuntimeDaoException{ 401 return this.countUsingTemplate(bean, SEARCH_EXACT); 402 } 403 404 @Override 405 public int deleteAll()throws RuntimeDaoException{ 406 return this.deleteByWhere(""); 407 } 408 409 @Override 410 public B[] loadAll()throws RuntimeDaoException{ 411 return this.loadByWhere(null); 412 } 413 414 @Override 415 public int loadAll(TableManager.Action<B> action)throws RuntimeDaoException{ 416 return this.loadByWhere(null, action); 417 } 418 419 @SuppressWarnings("unchecked") 420 @Override 421 public B[] loadAll(int startRow, int numRows)throws RuntimeDaoException{ 422 return this.loadByWhereAsList(null, null, startRow, numRows).toArray((B[]) Array.newInstance(metaData.beanType, 0)); 423 } 424 425 @Override 426 public int loadAll(int startRow, int numRows, TableManager.Action<B> action)throws RuntimeDaoException{ 427 return this.loadByWhereForAction(null, null, startRow, numRows, action); 428 } 429 430 @Override 431 public List<B> loadAllAsList()throws RuntimeDaoException{ 432 return this.loadUsingTemplateAsList(null,1, -1, SEARCH_EXACT); 433 } 434 435 @Override 436 public List<B> loadAllAsList(int startRow, int numRows)throws RuntimeDaoException{ 437 return this.loadUsingTemplateAsList(null, startRow, numRows, SEARCH_EXACT); 438 } 439 440 @Override 441 public B loadByPrimaryKey(B bean)throws RuntimeDaoException{ 442 return bean==null ? null : loadByPrimaryKey(bean.primaryValues()); 443 } 444 445 @Override 446 public B loadByPrimaryKeyChecked(B bean)throws RuntimeDaoException,ObjectRetrievalException{ 447 return loadByPrimaryKeyChecked(checkNotNull(bean,"bean is null").primaryValues()); 448 } 449 @Override 450 public final B loadByPrimaryKeyChecked(Object ...keys)throws RuntimeDaoException,ObjectRetrievalException{ 451 // {{check parameters 452 if(metaData.primaryKeyNames.length == 0){ 453 throw new UnsupportedOperationException(); 454 } 455 String[] pkNames = metaData.primaryKeyNames; 456 int[] pkIds = metaData.primaryKeyIds; 457 if(hasNull(keys)){ 458 throw new ObjectRetrievalException(new NullPointerException("primary key must not be null")); 459 } 460 checkArgument(keys.length == pkNames.length, 461 "INVALID ARGUMENT NUM, %s required",pkNames.length); 462 for(int i=0; i<pkNames.length; ++i){ 463 Object key = keys[i]; 464 Class<?> type = metaData.columnTypeOf(pkIds[i]); 465 checkArgument(type.isAssignableFrom(key.getClass()), 466 "INVALID type for pk %s,%s required",pkNames[i],type.getName()); 467 } 468 // }}check parameters 469 return doLoadByPrimaryKeyChecked(keys); 470 } 471 472 protected B doLoadByPrimaryKeyChecked(Object ...keys)throws RuntimeDaoException,ObjectRetrievalException{ 473 if(metaData.primaryKeyNames.length == 0){ 474 throw new UnsupportedOperationException(); 475 } 476 String[] pkNames = metaData.primaryKeyNames; 477 int[] pkIds = metaData.primaryKeyIds; 478 if(hasNull(keys)){ 479 throw new ObjectRetrievalException(new NullPointerException("primary key must not be null")); 480 } 481 checkArgument(keys.length == pkNames.length, 482 "INVALID ARGUMENT NUM, %s required",pkNames.length); 483 for(int i=0; i<pkNames.length; ++i){ 484 Object key = keys[i]; 485 Class<?> type = metaData.columnTypeOf(pkIds[i]); 486 checkArgument(type.isAssignableFrom(key.getClass()), 487 "INVALID type for pk %s,%s required",pkNames[i],type.getName()); 488 } 489 Connection c = null; 490 PreparedStatement ps = null; 491 try 492 { 493 c = this.getConnection(); 494 StringBuilder sql = new StringBuilder("SELECT " + metaData.columnFields + " FROM " + metaData.tablename + " WHERE "); 495 sql.append(Joiner.on(" AND ").join( 496 Lists.transform(Arrays.asList(pkNames), SQL_FUN1))); 497 if(debug){ 498 log("LOAD BY PK: " + sql.toString()); 499 } 500 ps = c.prepareStatement(sql.toString(), 501 ResultSet.TYPE_SCROLL_INSENSITIVE, 502 ResultSet.CONCUR_READ_ONLY); 503 for(int i = 0; i<pkNames.length; ++i){ 504 setPreparedStatement(ps, i+1, keys[i], pkIds[i]); 505 } 506 List<B> pReturn = this.loadByPreparedStatementAsList(ps,null,1,-1); 507 if (1 == pReturn.size()) { 508 return pReturn.get(0); 509 } else { 510 throw new ObjectRetrievalException(); 511 } 512 } 513 catch(ObjectRetrievalException e) 514 { 515 throw e; 516 } 517 catch(SQLException e) 518 { 519 throw new RuntimeDaoException(new DataRetrievalException(e)); 520 } 521 finally 522 { 523 this.getManager().close(ps); 524 this.freeConnection(c); 525 } 526 } 527 528 @Override 529 public B loadByPrimaryKey(Object ...keys)throws RuntimeDaoException{ 530 try{ 531 return loadByPrimaryKeyChecked(keys); 532 }catch(ObjectRetrievalException e){ 533 // not found 534 return null; 535 } 536 } 537 538 protected <K> List<B> loadByPks(Collection<K> keys){ 539 checkState(metaData.primaryKeyCount == 1,"UNSUPPORTED OPERATION"); 540 if(null == keys){ 541 return Collections.emptyList(); 542 } 543 ArrayList<B> list = new ArrayList<B>(keys.size()); 544 for(K key:keys){ 545 list.add(loadByPrimaryKey(key)); 546 } 547 return list; 548 } 549 550 @SuppressWarnings("unchecked") 551 protected <K> List<B> loadByPks(K... keys){ 552 if(null == keys){ 553 return Collections.emptyList(); 554 } 555 return loadByPks(Arrays.asList(keys)); 556 } 557 558 @Override 559 public boolean existsByPrimaryKey(B bean)throws RuntimeDaoException{ 560 return null == bean ? false : existsPrimaryKey(bean.primaryValues()); 561 } 562 @Override 563 public B checkDuplicate(B bean)throws RuntimeDaoException,ObjectRetrievalException{ 564 if(existsByPrimaryKey(bean)){ 565 throw new ObjectRetrievalException("Duplicate entry ("+ bean.primaryValues() +") for key 'PRIMARY'"); 566 } 567 return bean; 568 } 569 @Override 570 public final boolean existsPrimaryKey(Object ...keys)throws RuntimeDaoException{ 571 // {{check parameters 572 if(metaData.primaryKeyNames.length == 0){ 573 throw new UnsupportedOperationException(); 574 } 575 String[] pkNames = metaData.primaryKeyNames; 576 int[] pkIds = metaData.primaryKeyIds; 577 if(null == keys || hasNull(keys)){ 578 return false; 579 } 580 checkArgument(keys.length == pkNames.length, 581 "INVALID ARGUMENT NUM, %s required",pkNames.length); 582 for(int i=0; i<pkNames.length; ++i){ 583 Object key = keys[i]; 584 Class<?> type = metaData.columnTypeOf(pkIds[i]); 585 checkArgument(type.isInstance(key), 586 "INVALID type for pk %s,%s required",pkNames[i],type.getName()); 587 } 588 // }}check parameters 589 return doExistsPrimaryKey(keys); 590 } 591 protected boolean doExistsPrimaryKey(Object ...keys)throws RuntimeDaoException{ 592 String[] pkNames = metaData.primaryKeyNames; 593 int[] pkIds = metaData.primaryKeyIds; 594 Connection c = null; 595 PreparedStatement ps = null; 596 try{ 597 c = this.getConnection(); 598 StringBuilder sql = new StringBuilder("SELECT COUNT(*) AS MCOUNT FROM " + metaData.tablename + " WHERE "); 599 sql.append(Joiner.on(" AND ").join(Lists.transform(Arrays.asList(pkNames),SQL_FUN1))); 600 if(debug){ 601 log("ExistsPrimaryKey: " + sql); 602 } 603 ps = c.prepareStatement(sql.toString(), 604 ResultSet.TYPE_SCROLL_INSENSITIVE, 605 ResultSet.CONCUR_READ_ONLY); 606 607 for(int i = 0; i<pkNames.length; ++i){ 608 setPreparedStatement(ps, i+1, keys[i], pkIds[i]); 609 } 610 return 1 == getManager().runPreparedStatementForValue(Long.class, ps); 611 }catch(SQLException e){ 612 throw new RuntimeDaoException(new ObjectRetrievalException(e)); 613 }finally{ 614 this.getManager().close(ps); 615 this.freeConnection(c); 616 } 617 } 618 protected <T>T checkDuplicateByPk(T primaryKeyValue)throws ObjectRetrievalException{ 619 if(metaData.primaryKeyNames.length != 1){ 620 throw new UnsupportedOperationException(); 621 } 622 if(existsPrimaryKey(primaryKeyValue)){ 623 throw new ObjectRetrievalException("Duplicate entry '"+ primaryKeyValue +"' for key 'PRIMARY'"); 624 } 625 return primaryKeyValue; 626 } 627 628 @Override 629 public B[] loadByWhere(String where)throws RuntimeDaoException{ 630 return this.loadByWhere(where, (int[])null); 631 } 632 633 @Override 634 public int loadByWhere(String where, TableManager.Action<B> action)throws RuntimeDaoException{ 635 return this.loadByWhere(where, null, action); 636 } 637 638 @Override 639 public B[] loadByWhere(String where, int[] fieldList)throws RuntimeDaoException{ 640 return this.loadByWhere(where, fieldList, 1, -1); 641 } 642 643 @Override 644 public int loadByWhere(String where, int[] fieldList, TableManager.Action<B> action)throws RuntimeDaoException{ 645 return this.loadByWhere(where, fieldList, 1, -1, action); 646 } 647 648 @SuppressWarnings("unchecked") 649 @Override 650 public B[] loadByWhere(String where, int[] fieldList, int startRow, int numRows)throws RuntimeDaoException{ 651 return this.loadByWhereAsList(where, fieldList, startRow, numRows).toArray((B[])Array.newInstance(metaData.beanType,0)); 652 } 653 654 @Override 655 public int loadByWhere(String where, int[] fieldList, int startRow, int numRows, 656 TableManager.Action<B> action)throws RuntimeDaoException{ 657 return this.loadByWhereForAction(where, fieldList, startRow, numRows, action); 658 } 659 660 @Override 661 public List<B> loadByWhereAsList(String where)throws RuntimeDaoException{ 662 return this.loadByWhereAsList(where, null, 1, -1); 663 } 664 665 @Override 666 public List<B> loadByWhereAsList(String where, int[] fieldList)throws RuntimeDaoException{ 667 return this.loadByWhereAsList(where, fieldList, 1, -1); 668 } 669 670 @Override 671 public List<B> loadByWhereAsList(String where, int[] fieldList, int startRow, int numRows)throws RuntimeDaoException{ 672 ListAction action = new ListAction(); 673 loadByWhereForAction(where, fieldList, startRow, numRows, action); 674 return action.getList(); 675 } 676 677 @Override 678 public int loadByWhereForAction(String where, int[] fieldList, int startRow, int numRows,TableManager.Action<B> action)throws RuntimeDaoException{ 679 String sql=createSelectSql(fieldList, where); 680 return this.loadBySqlForAction(sql, null, fieldList, startRow, numRows, action); 681 } 682 683 @Override 684 public B[] loadUsingTemplate(B bean)throws RuntimeDaoException{ 685 return this.loadUsingTemplate(bean, 1, -1, SEARCH_EXACT); 686 } 687 688 @Override 689 public int loadUsingTemplate(B bean, TableManager.Action<B> action)throws RuntimeDaoException{ 690 return this.loadUsingTemplate(bean, null, 1, -1, SEARCH_EXACT, action); 691 } 692 693 @Override 694 public B[] loadUsingTemplate(B bean, int startRow, int numRows)throws RuntimeDaoException{ 695 return this.loadUsingTemplate(bean, startRow, numRows, SEARCH_EXACT); 696 } 697 698 @Override 699 public int loadUsingTemplate(B bean, int startRow, int numRows, 700 TableManager.Action<B> action)throws RuntimeDaoException{ 701 return this.loadUsingTemplate(bean, null, startRow, numRows,SEARCH_EXACT, action); 702 } 703 704 @SuppressWarnings("unchecked") 705 @Override 706 public B[] loadUsingTemplate(B bean, int startRow, int numRows, int searchType)throws RuntimeDaoException{ 707 return this.loadUsingTemplateAsList(bean, startRow, numRows, searchType).toArray((B[])Array.newInstance(metaData.beanType,0)); 708 } 709 710 @Override 711 public List<B> loadUsingTemplateAsList(B bean)throws RuntimeDaoException{ 712 return this.loadUsingTemplateAsList(bean, 1, -1, SEARCH_EXACT); 713 } 714 715 @Override 716 public List<B> loadUsingTemplateAsList(B bean, int startRow, int numRows)throws RuntimeDaoException{ 717 return this.loadUsingTemplateAsList(bean, startRow, numRows, SEARCH_EXACT); 718 } 719 720 @Override 721 public List<B> loadUsingTemplateAsList(B bean, int startRow, int numRows, int searchType)throws RuntimeDaoException{ 722 ListAction action = new ListAction(); 723 loadUsingTemplate(bean,null,startRow,numRows,searchType, action); 724 return action.getList(); 725 } 726 //18 727 @Override 728 public B loadUniqueUsingTemplate(B bean) 729 { 730 try { 731 return loadUniqueUsingTemplateChecked(bean); 732 } catch (ObjectRetrievalException e) { 733 return null; 734 } 735 } 736 //18-1 737 @Override 738 public B loadUniqueUsingTemplateChecked(B bean) throws ObjectRetrievalException 739 { 740 List<B> beans = this.loadUsingTemplateAsList(bean); 741 switch(beans.size()){ 742 case 0: 743 throw new ObjectRetrievalException("Not found element !!"); 744 case 1: 745 return beans.get(0); 746 default: 747 throw new RuntimeDaoException(new ObjectRetrievalException("More than one element !!")); 748 } 749 } 750 751 @Override 752 public int loadUsingTemplate(B bean, int[] fieldList, int startRow, int numRows,int searchType, Action<B> action) 753 { 754 StringBuilder sqlWhere = new StringBuilder(""); 755 String sql=createSelectSql(fieldList, 756 this.fillWhere(sqlWhere, bean, searchType) > 0 ? " WHERE "+ sqlWhere.toString() : null); 757 PreparedStatement ps = null; 758 Connection connection = null; 759 try { 760 connection = this.getConnection(); 761 if(debug){ 762 log("loadUsingTemplate:" + sql); 763 } 764 ps = connection.prepareStatement(sql, 765 ResultSet.TYPE_FORWARD_ONLY, 766 ResultSet.CONCUR_READ_ONLY); 767 this.fillPreparedStatement(ps, bean, searchType,false); 768 return this.loadByPreparedStatement(ps, fieldList, startRow, numRows, action); 769 } catch (DaoException e) { 770 throw new RuntimeDaoException(e); 771 }catch (SQLException e) { 772 throw new RuntimeDaoException(new DataAccessException(e)); 773 } finally { 774 this.getManager().close(ps); 775 this.freeConnection(connection); 776 } 777 } 778 /** 779 * @param <F> bean type of foreign table 780 * @param left 781 * @param columnsMap 782 * @param startRow 783 * @param numRows 784 * @return list of B or empty list 785 */ 786 private <F extends BaseBean> 787 List<B> loadByForeignKeyAsList(F left,Map<Integer,Integer>columnsMap,int startRow, int numRows) 788 { 789 if(null == left){ 790 return Collections.emptyList(); 791 } 792 checkArgument(columnsMap != null && !columnsMap.isEmpty(),"columnsMap is null or empty"); 793 B bean = createBean().copy(left, columnsMap); 794 return loadUsingTemplateAsList(bean,startRow,numRows); 795 } 796 /** 797 * @param <F> bean type of foreign table 798 * @param left 799 * @param fkName 800 * @param startRow 801 * @param numRows 802 * @return list of B or empty list 803 */ 804 public <F extends BaseBean> 805 List<B> loadByForeignKeyAsList(F left,String fkName,int startRow, int numRows) 806 { 807 ImmutableBiMap<Integer, Integer> columnsMap = metaData.foreignKeyIdMapOf(fkName).inverse(); 808 return loadByForeignKeyAsList(left,columnsMap,startRow,numRows); 809 } 810 811 @Override 812 public B save(B bean)throws RuntimeDaoException{ 813 if(null != bean){ 814 if (bean.isNew()) { 815 this.insert(bean); 816 } else { 817 this.update(bean); 818 } 819 } 820 return bean; 821 } 822 823 @Override 824 public B[] save(B[] beans)throws RuntimeDaoException{ 825 if(null != beans){ 826 for (B bean : beans) 827 { 828 this.save(bean); 829 } 830 } 831 return beans; 832 } 833 834 @Override 835 public <C extends Collection<B>> C save(C beans)throws RuntimeDaoException{ 836 if(null != beans){ 837 for (B bean : beans) 838 { 839 this.save(bean); 840 } 841 } 842 return beans; 843 } 844 845 @Override 846 public <C extends Collection<B>> C saveAsTransaction(final C beans)throws RuntimeDaoException{ 847 return this.runAsTransaction(new Callable<C>(){ 848 @Override 849 public C call() throws Exception { 850 return save(beans); 851 }}); 852 } 853 854 @Override 855 public B[] saveAsTransaction(final B[] beans)throws RuntimeDaoException{ 856 return this.runAsTransaction(new Callable<B[]>(){ 857 @Override 858 public B[] call() throws Exception { 859 return save(beans); 860 }}); 861 } 862 863 private String checkWhere(String where){ 864 where = MoreObjects.firstNonNull(where, "").trim(); 865 checkArgument(where.isEmpty() || where.toUpperCase().startsWith("WHERE "), 866 "WHERE expression must start with 'WHERE'(case insensitive)"); 867 return where; 868 } 869 870 @SuppressWarnings("unchecked") 871 @Override 872 public <T> List<T> loadColumnAsList(String column,boolean distinct,String where,int startRow,int numRows)throws RuntimeDaoException{ 873 int columnId = metaData.columnIDOf(column); 874 checkArgument(columnId>=0,"INVALID column name %s",column); 875 String sql = String.format("SELECT %s " + metaData.columnNameOf(columnId) + " FROM %s %s", 876 distinct ? "DISTINCT" : "", 877 metaData.tablename, 878 checkWhere(where)); 879 return runSqlAsList((Class<T>)metaData.columnTypes.get(columnId), sql); 880 } 881 /** 882 * generate SQL query(SELECT) statement,such as: 'SELECT id,name from mytable WHERE id=1' 883 * @param fieldList 884 * @param where where condition expression statement that start with 'WHERE',or {@code null},or empty string 885 * @return SQL statement string 886 * @throws IllegalArgumentException where condition expression don't start with 'WHERE' 887 */ 888 private String createSelectSql(int[] fieldList, String where){ 889 StringBuffer sql = new StringBuffer(128); 890 String fullFields = metaData.columnFullFields; 891 if(null == fieldList || 0 == fieldList.length) { 892 sql.append("SELECT ").append(fullFields); 893 } else{ 894 sql.append("SELECT "); 895 String[] names=fullFields.split(","); 896 for(int i = 0; i < fieldList.length; ++i){ 897 if(i > 0) { 898 sql.append(","); 899 } 900 sql.append(names[fieldList[i]]); 901 } 902 } 903 sql.append(" FROM " + this.metaData.tablename + " "); 904 sql.append(checkWhere(where)); 905 return sql.toString(); 906 } 907 908 //2.2 909 910 @Override 911 public int delete(B bean){ 912 if(metaData.primaryKeyNames.length==0){ 913 throw new UnsupportedOperationException(); 914 } 915 if(hasNullPk(bean)){ 916 return 0; 917 } 918 Connection c = null; 919 PreparedStatement ps = null; 920 String [] pks = metaData.primaryKeyNames; 921 int[] pkIds = metaData.primaryKeyIds; 922 try 923 { 924 // listener callback 925 getListenerContainer().beforeDelete(bean); 926 c = this.getConnection(); 927 StringBuilder sql = new StringBuilder("DELETE FROM " + metaData.tablename +" WHERE "); 928 sql.append(Joiner.on(" AND ").join(Lists.transform(Arrays.asList(pks),SQL_FUN1))); 929 if(debug){ 930 log("deleteByPrimaryKey: " + sql); 931 } 932 ps = c.prepareStatement(sql.toString(), 933 ResultSet.TYPE_SCROLL_INSENSITIVE, 934 ResultSet.CONCUR_READ_ONLY); 935 for(int i = 0; i<pkIds.length; ++i){ 936 setPreparedStatement(ps, i+1, bean, pkIds[i]); 937 } 938 int rows=ps.executeUpdate(); 939 if(rows>0){ 940 // listener callback 941 getListenerContainer().afterDelete(bean); 942 } 943 return rows; 944 } 945 catch(SQLException e) 946 { 947 throw new RuntimeDaoException(new DataAccessException(e)); 948 } 949 finally 950 { 951 // listener callback 952 getListenerContainer().done(); 953 getManager().close(ps); 954 freeConnection(c); 955 } 956 } 957 958 protected <K> int deleteByPks(Collection<K> keys){ 959 checkState(metaData.primaryKeyCount == 1,"UNSUPPORTED OPERATION"); 960 int count = 0; 961 if(null != keys){ 962 for(K key :keys){ 963 count += deleteByPrimaryKey(key); 964 } 965 } 966 return count; 967 } 968 969 @SuppressWarnings("unchecked") 970 protected <K> int deleteByPks(K... keys){ 971 if(null == keys){ 972 return 0; 973 } 974 return deleteByPks(Arrays.asList(keys)); 975 } 976 977 @Override 978 public int deleteByPrimaryKey(Object ...keys)throws RuntimeDaoException{ 979 if(hasNull(keys)){ 980 return 0; 981 } 982 checkArgument(keys.length == metaData.primaryKeyCount,"argument number mismatch with primary key number"); 983 return delete(createBean(keys)); 984 } 985 986 //2.4 987 988 @SuppressWarnings("unchecked") 989 @Override 990 public int delete(B... beans)throws RuntimeDaoException{ 991 int count = 0; 992 if(null != beans){ 993 for(B bean :beans){ 994 count += delete(bean); 995 } 996 } 997 return count; 998 } 999 1000 //2.5 1001 1002 @Override 1003 public int delete(Collection<B> beans)throws RuntimeDaoException{ 1004 int count = 0; 1005 if(null != beans){ 1006 for(B bean :beans){ 1007 count += delete(bean); 1008 } 1009 } 1010 return count; 1011 } 1012 1013 @SuppressWarnings("unchecked") 1014 @Override 1015 public <T extends BaseBean> T getReferencedBean(B bean, String fkName) throws RuntimeDaoException{ 1016 if(null == bean){ 1017 return null; 1018 } 1019 ForeignKeyMetaData foreignkey = checkNotNull(metaData.foreignKeys.get(fkName), 1020 "INVALID fkName %s for table %s", fkName, metaData.tablename); 1021 BaseTableManager<T> foreignManager = (BaseTableManager<T>)managerOf(foreignkey.foreignTable); 1022 T t = foreignManager.createBean().copy(bean, metaData.foreignKeyIdMapOf(fkName)); 1023 return foreignManager.loadByPrimaryKey(t); 1024 } 1025 1026 @SuppressWarnings("unchecked") 1027 @Override 1028 public <T extends BaseBean> T setReferencedBean(B bean, T beanToSet, String fkName) throws RuntimeDaoException{ 1029 if(null != bean){ 1030 ForeignKeyMetaData foreignkey = checkNotNull(metaData.foreignKeys.get(fkName), 1031 "INVALID fkName %s for table %s", fkName, metaData.tablename); 1032 BaseTableManager<T> foreignManager = (BaseTableManager<T>)managerOf(foreignkey.foreignTable); 1033 foreignManager.save(beanToSet); 1034 bean.copy(beanToSet, metaData.foreignKeyIdMapOf(fkName).inverse()); 1035 } 1036 return beanToSet; 1037 } 1038 @SuppressWarnings("unchecked") 1039 @Override 1040 public <T extends BaseBean> T[] getImportedBeans(B bean,String fkName) throws RuntimeDaoException{ 1041 ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName); 1042 RowMetaData foreignMetaData = RowMetaData.getMedaData(foreignkey.foreignTable); 1043 return getImportedBeansAsList(bean,fkName).toArray((T[]) Array.newInstance(foreignMetaData.beanType, 0)); 1044 } 1045 1046 @SuppressWarnings("unchecked") 1047 @Override 1048 public <T extends BaseBean> List<T> getImportedBeansAsList(B bean, String fkName)throws RuntimeDaoException{ 1049 ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName); 1050 BaseTableManager<T> foreignManager = (BaseTableManager<T>)managerOf(foreignkey.ownerTable); 1051 return foreignManager.loadByForeignKeyAsList(bean,fkName, 1, -1); 1052 } 1053 protected <T extends BaseBean> List<T> getImportedBeansAsList(String fkName,Object...keys) throws RuntimeDaoException{ 1054 return getImportedBeansAsList(createBean(keys),fkName); 1055 } 1056 @SuppressWarnings("unchecked") 1057 protected <T extends BaseBean> T[] getImportedBeans(String fkName,Object...keys) throws RuntimeDaoException{ 1058 ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName); 1059 RowMetaData foreignMetaData = RowMetaData.getMedaData(foreignkey.foreignTable); 1060 return getImportedBeansAsList(fkName,keys).toArray((T[]) Array.newInstance(foreignMetaData.beanType, 0)); 1061 } 1062 @SuppressWarnings("unchecked") 1063 @Override 1064 public <T extends BaseBean, C extends Collection<T>> C setImportedBeans(B bean, C importedBeans, 1065 String fkName)throws RuntimeDaoException{ 1066 if(null != importedBeans){ 1067 ForeignKeyMetaData foreignkey =metaData.getImportedKey(fkName); 1068 BaseTableManager<T> foreignManager = (BaseTableManager<T>)managerOf(foreignkey.ownerTable); 1069 for( T importBean : importedBeans ){ 1070 foreignManager.setReferencedBean(importBean , bean,fkName); 1071 } 1072 } 1073 return importedBeans; 1074 } 1075 1076 @Override 1077 public <T extends BaseBean> T[] setImportedBeans(B bean, T[] importedBeans, String fkName) throws RuntimeDaoException{ 1078 if(null != importedBeans){ 1079 setImportedBeans(bean, Arrays.asList(importedBeans), fkName); 1080 } 1081 return importedBeans; 1082 } 1083 @SuppressWarnings("unchecked") 1084 @Override 1085 public int deleteImportedBeans(B bean,String fkName){ 1086 if(bean == null){ 1087 return 0; 1088 } 1089 ForeignKeyMetaData foreignkey = metaData.getImportedKey(fkName); 1090 RowMetaData foreignMetaData = RowMetaData.getMedaData(foreignkey.foreignTable); 1091 BaseTableManager<BaseBean> foreignManager = (BaseTableManager<BaseBean>)managerOf(foreignkey.ownerTable); 1092 BaseBean tmpl = foreignManager.createBean().copy(bean,foreignMetaData.foreignKeyIdMapOf(fkName)); 1093 return foreignManager.deleteUsingTemplate(tmpl); 1094 } 1095 protected int deleteImportedBeans(Map<Integer, Object> idValueMap, String fkName) throws RuntimeDaoException{ 1096 B bean = createBean().copy(idValueMap); 1097 return deleteImportedBeans(bean,fkName); 1098 } 1099 1100 protected int deleteImportedBeans(String fkName,Object...keys) throws RuntimeDaoException{ 1101 return deleteImportedBeans(createBean(keys),fkName); 1102 } 1103 private Map<Integer, Object> makeIndexValueMap(String indexName,Object ...indexValues){ 1104 IndexMetaData indexMetaData = metaData.indices.get(indexName); 1105 checkArgument(null != indexMetaData,"INVALID index name %s",indexName); 1106 checkArgument(null != indexValues && indexValues.length == indexMetaData.columns.size(),"INVALID index value"); 1107 ImmutableMap.Builder<Integer,Object> builder = ImmutableMap.<Integer,Object>builder(); 1108 for(int i=0;i<indexValues.length;++i){ 1109 Object value = indexValues[i]; 1110 if(null != value){ 1111 String column = indexMetaData.columns.get(i); 1112 Class<?> columnType = metaData.columnTypeOf(column); 1113 checkArgument(columnType.isInstance(value), 1114 "INVALID value type %s for %s,%s required",value.getClass(),column,columnType); 1115 builder.put(metaData.columnIDOf(column), value); 1116 } 1117 } 1118 return builder.build(); 1119 } 1120 //2.4 override IDeviceManager 1121 1122 @SuppressWarnings("unchecked") 1123 @Override 1124 public B[] loadByIndex(String indexName,Object ...keys)throws RuntimeDaoException{ 1125 return this.loadByIndexAsList(indexName,keys).toArray((B[])Array.newInstance(metaData.beanType,0)); 1126 } 1127 @Override 1128 public List<B> loadByIndexAsList(String indexName,Object ...indexValues)throws RuntimeDaoException{ 1129 Map<Integer, Object> map = makeIndexValueMap(indexName,indexValues); 1130 return loadUsingTemplateAsList(map); 1131 } 1132 @Override 1133 public final B loadUniqueByIndex(String indexName,Object ...indexValues)throws RuntimeDaoException{ 1134 return doLoadUniqueByIndex(indexName, indexValues); 1135 } 1136 protected B doLoadUniqueByIndex(String indexName,Object ...indexValues)throws RuntimeDaoException{ 1137 Map<Integer, Object> keys = makeIndexValueMap(indexName,indexValues); 1138 B bean = createBean().copy(keys); 1139 return loadUniqueUsingTemplate(bean); 1140 } 1141 @Override 1142 public final B loadUniqueByIndexChecked(String indexName,Object ...indexValues)throws ObjectRetrievalException{ 1143 if(hasNull(indexValues)){ 1144 throw new ObjectRetrievalException(new NullPointerException("index keys must not be null")); 1145 } 1146 return doLoadUniqueByIndexChecked(indexName, indexValues); 1147 } 1148 protected B doLoadUniqueByIndexChecked(String indexName,Object ...indexValues)throws ObjectRetrievalException{ 1149 Map<Integer, Object> keys = makeIndexValueMap(indexName,indexValues); 1150 B bean = createBean().copy(keys); 1151 return loadUniqueUsingTemplateChecked(bean); 1152 } 1153 @Override 1154 public int deleteByIndex(String indexName,Object ...indexValues)throws RuntimeDaoException{ 1155 Map<Integer, Object> map = makeIndexValueMap(indexName,indexValues); 1156 return deleteUsingTemplate(map); 1157 } 1158 1159 private List<B> loadUsingTemplateAsList(Map<Integer, Object>keys){ 1160 B bean = createBean().copy(keys); 1161 return loadUsingTemplateAsList(bean); 1162 } 1163 1164 private int deleteUsingTemplate(Map<Integer, Object>keys){ 1165 B bean = createBean().copy(keys); 1166 return deleteUsingTemplate(bean); 1167 } 1168 protected <T>List<B> loadByIndexForIndices(String indexName,Collection<T> indexs) 1169 { 1170 IndexMetaData indexMetaData = metaData.indices.get(indexName); 1171 checkArgument(null != indexMetaData,"INVALID index name %s",indexName); 1172 checkArgument(1 == indexMetaData.columns.size(),"column count of index must be 1"); 1173 checkArgument(indexMetaData.unique,"index must be unique"); 1174 if(null == indexs ){ 1175 return Collections.emptyList(); 1176 } 1177 List<B> list = new ArrayList<B>(indexs.size()); 1178 for(T key: indexs){ 1179 list.add(loadUniqueByIndex(indexName,key)); 1180 } 1181 return list; 1182 } 1183 @SuppressWarnings("unchecked") 1184 protected <T>List<B> loadByIndexForIndices(String indexName,T... indexs){ 1185 if(indexs == null || indexs.length == 0){ 1186 return Collections.emptyList(); 1187 } 1188 return loadByIndexForIndices(indexName,Arrays.asList(indexs)); 1189 } 1190 protected <T>int deleteByIndexForIndices(String indexName,Collection<T> indexs) 1191 { 1192 IndexMetaData indexMetaData = metaData.indices.get(indexName); 1193 checkArgument(null != indexMetaData,"INVALID index name %s",indexName); 1194 checkArgument(1 == indexMetaData.columns.size(),"column count of index must be 1"); 1195 1196 if(null == indexs ){ 1197 return 0; 1198 } 1199 int count = 0; 1200 for(T key: indexs){ 1201 if(key != null){ 1202 count += deleteByIndex(indexName, key); 1203 } 1204 } 1205 return count; 1206 } 1207 @SuppressWarnings("unchecked") 1208 protected <T>int deleteByIndexForIndices(String indexName,T... indexs){ 1209 if(indexs == null || indexs.length == 0){ 1210 return 0; 1211 } 1212 return deleteByIndexForIndices(indexName,Arrays.asList(indexs)); 1213 } 1214 private boolean checkPkValid(B bean) 1215 { 1216 if(metaData.primaryKeyNames.length ==0){ 1217 return false; 1218 } 1219 for(String name:metaData.primaryKeyNames){ 1220 if(! (bean.isInitialized(name) && null != bean.getValue(name))){ 1221 return false; 1222 } 1223 } 1224 return true; 1225 } 1226 //21 1227 1228 @Override 1229 public int deleteUsingTemplate(B bean) 1230 { 1231 if(bean == null || !bean.isModified()){ 1232 return 0; 1233 } 1234 if(checkPkValid(bean)){ 1235 return this.deleteByPrimaryKey(bean); 1236 } 1237 if( !getListenerContainer().isEmpty()){ 1238 final DeleteBeanAction action=new DeleteBeanAction(); 1239 this.loadUsingTemplate(bean,action); 1240 return action.getCount(); 1241 } 1242 Connection c = null; 1243 PreparedStatement ps = null; 1244 StringBuilder sql = new StringBuilder("DELETE FROM " + metaData.tablename +" "); 1245 StringBuilder sqlWhere = new StringBuilder(""); 1246 1247 try 1248 { 1249 fillWhere(sqlWhere, bean, SEARCH_EXACT); 1250 sql.append(" WHERE ").append(sqlWhere); 1251 c = this.getConnection(); 1252 if(debug){ 1253 log("deleteUsingTemplate: " + sql.toString()); 1254 } 1255 ps = c.prepareStatement(sql.toString(), 1256 ResultSet.TYPE_SCROLL_INSENSITIVE, 1257 ResultSet.CONCUR_READ_ONLY); 1258 this.fillPreparedStatement(ps, bean, SEARCH_EXACT, false); 1259 1260 return ps.executeUpdate(); 1261 } 1262 catch(SQLException e) 1263 { 1264 throw new RuntimeDaoException(new DataAccessException(e)); 1265 } 1266 finally 1267 { 1268 this.getManager().close(ps); 1269 this.freeConnection(c); 1270 } 1271 } 1272 1273 //11 1274 1275 @Override 1276 public int deleteByWhere(String where) 1277 { 1278 if( !getListenerContainer().isEmpty()){ 1279 final DeleteBeanAction action = new DeleteBeanAction(); 1280 this.loadByWhere(where,action); 1281 return action.getCount(); 1282 } 1283 Connection c = null; 1284 PreparedStatement ps = null; 1285 1286 try 1287 { 1288 c = this.getConnection(); 1289 StringBuilder sql = new StringBuilder("DELETE FROM " + metaData.tablename + " " + where); 1290 if(debug){ 1291 log("deleteByWhere: " + sql); 1292 } 1293 ps = c.prepareStatement(sql.toString()); 1294 return ps.executeUpdate(); 1295 } 1296 catch(SQLException e) 1297 { 1298 throw new RuntimeDaoException(new DataAccessException(e)); 1299 } 1300 finally 1301 { 1302 this.getManager().close(ps); 1303 this.freeConnection(c); 1304 } 1305 } 1306 private void setPreparedStatement(PreparedStatement ps,int pos,Object value,int columnId) 1307 throws SQLException { 1308 Manager.setPreparedStatement(ps, pos, value, metaData.sqlTypes[columnId]); 1309 } 1310 private void setPreparedStatement(PreparedStatement ps,int pos,B bean,int columnId) 1311 throws SQLException { 1312 setPreparedStatement(ps,pos,bean.getValue(columnId),columnId); 1313 } 1314 1315 @Override 1316 public B save(B bean,Map<String, BaseBean> referenceBeans,Map<String, Collection<BaseBean>> importedBeans) throws RuntimeDaoException{ 1317 if(null == bean){ 1318 return null; 1319 } 1320 referenceBeans = MoreObjects.firstNonNull(referenceBeans, Collections.<String, BaseBean>emptyMap()); 1321 importedBeans = MoreObjects.firstNonNull(importedBeans, Collections.<String, Collection<BaseBean>>emptyMap()); 1322 for(Entry<String, BaseBean> entry:referenceBeans.entrySet()){ 1323 BaseBean beanToSet = entry.getValue(); 1324 if(beanToSet != null){ 1325 setReferencedBean(bean, beanToSet, entry.getKey()); 1326 } 1327 } 1328 bean = this.save( bean ); 1329 for(Entry<String, Collection<BaseBean>> entry:importedBeans.entrySet()){ 1330 String ikName = entry.getKey(); 1331 Collection<BaseBean> importeds = entry.getValue(); 1332 if(null != importeds){ 1333 setImportedBeans(bean, importeds, ikName); 1334 TableManager<BaseBean> impManager = managerOf(metaData.getImportedKey(ikName).ownerTable); 1335 impManager.save(importeds); 1336 } 1337 } 1338 return bean; 1339 } 1340 1341 @Override 1342 public B saveAsTransaction(final B bean,final Map<String, BaseBean> referenceBeans,final Map<String, Collection<BaseBean>> importedBeans)throws RuntimeDaoException{ 1343 return this.runAsTransaction(new Callable<B>(){ 1344 @Override 1345 public B call() throws Exception { 1346 return save(bean , referenceBeans, importedBeans ); 1347 }}); 1348 } 1349 1350 //_____________________________________________________________________ 1351 // 1352 // COUNT 1353 //_____________________________________________________________________ 1354 //25 1355 1356 @Override 1357 public int countWhere(String where) 1358 { 1359 String sql = new StringBuffer("SELECT COUNT(*) AS MCOUNT FROM " + metaData.tablename + " ") 1360 .append(null == where ? "" : where).toString(); 1361 return runSqlForValue(Long.class, sql).intValue(); 1362 } 1363 //20 1364 /** 1365 * count the number of elements of a specific B bean given the search type 1366 * 1367 * @param bean the B template to look for 1368 * @param searchType exact ? like ? starting like ? 1369 * @return the number of rows returned 1370 */ 1371 @Override 1372 public int countUsingTemplate(B bean, int searchType) 1373 { 1374 Connection c = null; 1375 PreparedStatement ps = null; 1376 StringBuilder sql = new StringBuilder("SELECT COUNT(*) AS MCOUNT FROM " + metaData.tablename); 1377 StringBuilder sqlWhere = new StringBuilder(""); 1378 1379 try 1380 { 1381 if (this.fillWhere(sqlWhere, bean, SEARCH_EXACT) > 0) { 1382 sql.append(" WHERE ").append(sqlWhere); 1383 } else { 1384 if(debug){ 1385 log("The bean to look is not initialized... counting all..."); 1386 } 1387 } 1388 1389 c = this.getConnection(); 1390 if(debug){ 1391 log("countUsingTemplate: " + sql.toString()); 1392 } 1393 ps = c.prepareStatement(sql.toString(), 1394 ResultSet.TYPE_SCROLL_INSENSITIVE, 1395 ResultSet.CONCUR_READ_ONLY); 1396 this.fillPreparedStatement(ps, bean, searchType,false); 1397 return getManager().runPreparedStatementForValue(Long.class,ps).intValue(); 1398 } 1399 catch(SQLException e) 1400 { 1401 throw new RuntimeDaoException(new DataAccessException(e)); 1402 } 1403 finally 1404 { 1405 this.getManager().close(ps); 1406 this.freeConnection(c); 1407 sql = null; 1408 sqlWhere = null; 1409 } 1410 } 1411 /** 1412 * fills the given StringBuilder with the sql where clauses constructed using the bean and the search type 1413 * @param sqlWhere the StringBuilder that will be filled 1414 * @param bean the bean to use for creating the where clauses 1415 * @param searchType exact ? like ? starting like ? 1416 * @return the number of clauses returned 1417 */ 1418 private int fillWhere(StringBuilder sqlWhere, B bean, int searchType) 1419 { 1420 if (bean == null) { 1421 return 0; 1422 } 1423 int dirtyCount = 0; 1424 String sqlEqualsOperation = searchType == SEARCH_EXACT ? "=" : " like "; 1425 List<String> fields = metaData.columnNames; 1426 for(int i=0; i<fields.size(); ++i){ 1427 if(bean.isModified(i)){ 1428 ++dirtyCount; 1429 if(bean.getValue(i) == null){ 1430 sqlWhere.append((sqlWhere.length() == 0) ? " " : " AND ").append(metaData.columnNameOf(i)).append(" IS NULL"); 1431 }else{ 1432 if(metaData.columnTypeOf(i) == String.class){ 1433 sqlWhere.append((sqlWhere.length() == 0) ? " " : " AND ").append(metaData.columnNameOf(i)).append(" ").append(sqlEqualsOperation).append("?"); 1434 } else{ 1435 sqlWhere.append((sqlWhere.length() == 0) ? " " : " AND ").append(metaData.columnNameOf(i)).append(" = ?"); 1436 } 1437 } 1438 } 1439 } 1440 1441 return dirtyCount; 1442 } 1443 /** 1444 * fill the given prepared statement with the bean values and a search type 1445 * @param ps the PreparedStatement that will be filled 1446 * @param bean the bean to use for creating the where clauses 1447 * @param searchType exact ? like ? starting like ? 1448 * @param fillNull wether fill null for null field 1449 * @return the number of clauses returned 1450 */ 1451 private int fillPreparedStatement(PreparedStatement ps, B bean, int searchType,boolean fillNull) throws DaoException 1452 { 1453 if (bean == null) { 1454 return 0; 1455 } 1456 int dirtyCount = 0; 1457 try 1458 { 1459 List<String> fields = metaData.columnNames; 1460 for(int columnId=0; columnId<fields.size(); ++columnId){ 1461 Object value = bean.getValue(columnId); 1462 if(null != value || fillNull){ 1463 if(bean.isModified(columnId)){ 1464 if(String.class == metaData.columnTypeOf(columnId)){ 1465 switch (searchType) { 1466 case SEARCH_EXACT: 1467 setPreparedStatement(ps, ++dirtyCount , value, columnId); 1468 break; 1469 case SEARCH_LIKE: 1470 setPreparedStatement(ps, ++dirtyCount , SQL_LIKE_WILDCARD + value + SQL_LIKE_WILDCARD, columnId); 1471 break; 1472 case SEARCH_STARTING_LIKE: 1473 setPreparedStatement(ps, ++dirtyCount , SQL_LIKE_WILDCARD + value, columnId); 1474 break; 1475 case SEARCH_ENDING_LIKE: 1476 setPreparedStatement(ps, ++dirtyCount , value + SQL_LIKE_WILDCARD, columnId); 1477 break; 1478 default: 1479 throw new DaoException("Unknown search type : " + searchType); 1480 } 1481 }else{ 1482 setPreparedStatement(ps, ++dirtyCount , bean, columnId); 1483 } 1484 } 1485 } 1486 } 1487 } 1488 catch(SQLException e) 1489 { 1490 throw new DataAccessException(e); 1491 } 1492 return dirtyCount; 1493 } 1494 1495 /** 1496 * 检查指定的字段是否为{@code null} 1497 * @author guyadong 1498 * 1499 */ 1500 private static class FieldNullChecker implements Predicate<String>{ 1501 1502 private final BaseBean bean; 1503 1504 public FieldNullChecker(BaseBean bean) { 1505 this.bean = checkNotNull(bean,"bean is null"); 1506 } 1507 1508 @Override 1509 public boolean apply(String input) { 1510 return null == bean.getValue(input); 1511 } 1512 1513 } 1514 //_____________________________________________________________________ 1515 // 1516 // MANY TO MANY: LOAD OTHER BEAN VIA JUNCTION TABLE 1517 //_____________________________________________________________________ 1518 1519 /** 1520 * Retrieves an list of R using the junction table junction table(junctionTablename), given a linked table bean, 1521 * specifying the start row and the number of rows. 1522 * @param <L> 1523 * @param <R> 1524 * @param left 1525 * @param rightType 1526 * @param startRow the start row to be used (first row = 1, last row = -1) 1527 * @param numRows the number of rows to be retrieved (all rows = a negative number) 1528 * @return a list of R 1529 */ 1530 @SuppressWarnings("unchecked") 1531 public <L extends BaseBean,R extends BaseBean> 1532 List<R> loadViaJunctionTableAsList( L left,Class<R> rightType, int startRow, int numRows) 1533 { 1534 checkState(!metaData.getJunctionTablePkMap().isEmpty(),"%s is not junction table",metaData.tablename); 1535 checkArgument(null != left,"left is null"); 1536 checkArgument(null != rightType,"rightType is null"); 1537 RowMetaData leftMetaData = RowMetaData.getMedaData(left.tableName()); 1538 RowMetaData rightMetaData = RowMetaData.getMedaData(rightType); 1539 checkArgument(!leftMetaData.equals(rightMetaData),"same metadata FOR left AND right type"); 1540 checkArgument(metaData.isLinkedTable(leftMetaData.tablename),"INVALID left type %s",left.getClass()); 1541 checkArgument(metaData.isLinkedTable(rightMetaData.tablename),"INVALID right type %s",rightType); 1542 Map<String, String> leftFields = metaData.junctionMapOf(leftMetaData.tablename); 1543 Map<String, String> rightFields = metaData.junctionMapOf(rightMetaData.tablename); 1544 if(Iterables.tryFind(leftFields.values(), new FieldNullChecker(left)).isPresent()){ 1545 return Collections.emptyList(); 1546 } 1547 Connection c = null; 1548 PreparedStatement ps = null; 1549 String sql = " SELECT " + rightMetaData.columnFullFields 1550 + " FROM "+ metaData.tablename + ", " + rightMetaData.tablename 1551 + " WHERE " 1552 + Joiner.on(" AND ").join(Iterables.transform(leftFields.keySet(), 1553 new Function<String,String>(){ 1554 @Override 1555 public String apply(String input) { 1556 return input + "=?"; 1557 }})) 1558 + " AND " 1559 + Joiner.on(" AND ").join(Iterables.transform(rightFields.entrySet(), 1560 new Function<Map.Entry<String,String>,String>(){ 1561 1562 @Override 1563 public String apply(Map.Entry<String, String> input) { 1564 return input.getKey() + "=" + input.getValue(); 1565 }})); 1566 1567 try 1568 { 1569 c = this.getConnection(); 1570 if(debug){ 1571 log("loadViaJunctionTableAsList" + sql); 1572 } 1573 ps = c.prepareStatement(sql, 1574 ResultSet.TYPE_SCROLL_INSENSITIVE, 1575 ResultSet.CONCUR_READ_ONLY); 1576 int pos =1; 1577 for(String key:leftFields.keySet()){ 1578 String fullName = leftFields.get(key); 1579 int columnId = leftMetaData.columnIDOf(fullName); 1580 Manager.setPreparedStatement(ps, pos++, 1581 left.getValue(columnId), 1582 leftMetaData.sqlTypeOf(columnId)); 1583 } 1584 return ((BaseTableManager<R>) managerOf(rightMetaData.beanType)) 1585 .loadByPreparedStatementAsList(ps, null, startRow, numRows); 1586 } 1587 catch (SQLException e) 1588 { 1589 throw new RuntimeDaoException(new DaoException(e.getMessage(), e)); 1590 } 1591 finally 1592 { 1593 this.getManager().close(ps); 1594 this.freeConnection(c); 1595 } 1596 } 1597 1598 private <L extends BaseBean,R extends BaseBean> B makeJunctionBean(L left,R right){ 1599 Map<String, Object[]> map = metaData.getJunctionTablePkMap(); 1600 checkState(!map.isEmpty(), "%s is not junction table",metaData.tablename); 1601 Object[] primaryValues = new Object[metaData.primaryKeyCount]; 1602 for(int i=0; i<primaryValues.length; ++i){ 1603 String pk = metaData.columnNameOf(i); 1604 Object[] data = map.get(pk); 1605 checkArgument(data!=null,"PRIMARY KEY %s NOT DEFINED",pk); 1606 String linkedTableName = (String)data[0]; 1607 int linkedColumnId = ((Integer)data[1]).intValue(); 1608 1609 if(left.tableName().equals(linkedTableName)){ 1610 primaryValues[i] = left.getValueChecked(linkedColumnId); 1611 }else if (right.tableName().equals(linkedTableName)){ 1612 primaryValues[i] = right.getValueChecked(linkedColumnId); 1613 } else { 1614 throw new IllegalArgumentException(String.format("%s NOT linked table %s", linkedTableName,metaData.tablename)); 1615 } 1616 } 1617 return createBean(primaryValues); 1618 } 1619 1620 /** 1621 * add junction between L and R if junction not exists 1622 * @param <L> 1623 * @param <R> 1624 * @param left 1625 * @param right 1626 */ 1627 public <L extends BaseBean,R extends BaseBean> 1628 void addJunction(L left,R right){ 1629 if(hasNullPk(left) || hasNullPk(right)){ 1630 return ; 1631 } 1632 B junction = makeJunctionBean(left,right); 1633 if(!existsByPrimaryKey(junction)){ 1634 save(junction); 1635 } 1636 } 1637 1638 /** 1639 * remove junction between L and R if junction not exists 1640 * @param <L> 1641 * @param <R> 1642 * @param left 1643 * @param right 1644 */ 1645 public <L extends BaseBean,R extends BaseBean> 1646 int deleteJunction(L left,R right){ 1647 if(hasNullPk(left) || hasNullPk(right)){ 1648 return 0; 1649 } 1650 B junction = makeJunctionBean(left,right); 1651 return delete(junction); 1652 } 1653 1654 @SuppressWarnings("unchecked") 1655 public <L extends BaseBean,R extends BaseBean> 1656 void addJunction(L left,R... rights){ 1657 if(null != rights){ 1658 addJunction(left, Arrays.asList(rights)); 1659 } 1660 } 1661 1662 public <L extends BaseBean,R extends BaseBean> 1663 void addJunction(L left,Collection<R> rights){ 1664 if(null != rights){ 1665 for(R linked:rights){ 1666 addJunction(left,linked); 1667 } 1668 } 1669 } 1670 @SuppressWarnings("unchecked") 1671 public <L extends BaseBean,R extends BaseBean> 1672 int deleteJunction(L left,R... rights){ 1673 if(null != rights){ 1674 return deleteJunction(left, Arrays.asList(rights)); 1675 } 1676 return 0; 1677 } 1678 public <L extends BaseBean,R extends BaseBean> 1679 int deleteJunction(L left,Collection<R> rights){ 1680 int count = 0; 1681 if(null != rights){ 1682 for(R right:rights){ 1683 count += deleteJunction(left,right); 1684 } 1685 } 1686 return count; 1687 } 1688 1689 //28-2 1690 /** decode a resultset and call action 1691 * @param rs the resultset to decode 1692 * @param fieldList table of the field's associated constants 1693 * @param startRow the start row to be used (first row = 1, last row = -1) 1694 * @param numRows the number of rows to be retrieved (all rows = a negative number) 1695 * @param action interface obj for do something 1696 * @return the count dealt by action 1697 * @throws IllegalArgumentException 1698 */ 1699 protected int actionOnResultSet(ResultSet rs, int[] fieldList, int startRow, int numRows, Action<B> action) throws DaoException{ 1700 try{ 1701 int count = 0; 1702 if(0!=numRows){ 1703 checkArgument(startRow>=1,"invalid argument:startRow (must >=1)"); 1704 checkArgument(null !=action && null != rs,"invalid argument:action OR rs (must not be null)"); 1705 rs.absolute(startRow - 1); 1706 if (fieldList == null) { 1707 if(numRows<0){ 1708 for(;rs.next();++count){ 1709 action.call(decodeRow(rs)); 1710 } 1711 }else{ 1712 for(;rs.next() && count<numRows;++count){ 1713 action.call(decodeRow(rs)); 1714 } 1715 } 1716 }else { 1717 if(numRows<0){ 1718 for(;rs.next();++count){ 1719 action.call(decodeRow(rs, fieldList)); 1720 } 1721 }else{ 1722 for(;rs.next() && count<numRows;++count){ 1723 action.call(decodeRow(rs, fieldList)); 1724 } 1725 } 1726 } 1727 } 1728 return count; 1729 }catch(DaoException e){ 1730 throw e; 1731 }catch(SQLException e){ 1732 throw new DataAccessException(e); 1733 } 1734 } 1735 1736 @SuppressWarnings("unchecked") 1737 protected <T> T getColumnValue(ResultSet resultSet, int columnId) throws SQLException { 1738 return (T) Manager.getObject(resultSet,columnId + 1, metaData.columnTypeOf(columnId)); 1739 } 1740 protected void setColumnValue(B bean,int columnId, Object value ){ 1741 if(value instanceof byte[] && ByteBuffer.class.equals(metaData.columnTypeOf(columnId))){ 1742 value = ByteBuffer.wrap((byte[])value); 1743 } 1744 bean.setValue(columnId, value); 1745 } 1746 //29 1747 /** 1748 * Transforms a ResultSet iterating on a B bean. 1749 * 1750 * @param rs the ResultSet to be transformed 1751 * @return bean resulting B bean 1752 */ 1753 private B decodeRow(ResultSet rs) throws DaoException 1754 { 1755 B bean = createBean(); 1756 try 1757 { 1758 for(int i=0; i<metaData.columnNames.size(); ++i){ 1759 setColumnValue(bean, i, getColumnValue(rs,i)); 1760 } 1761 } 1762 catch(SQLException e) 1763 { 1764 throw new DataAccessException(e); 1765 } 1766 bean.setNew(false); 1767 bean.resetIsModified(); 1768 1769 return bean; 1770 } 1771 1772 //30 1773 /** 1774 * Transforms a ResultSet iterating on a B bean according to a list of fields. 1775 * 1776 * @param rs the ResultSet to be transformed 1777 * @param fieldList table of the field's associated constants 1778 * @return bean resulting B bean 1779 */ 1780 private B decodeRow(ResultSet rs, int[] fieldList) throws DaoException 1781 { 1782 fieldList = MoreObjects.firstNonNull(fieldList, metaData.defaultColumnIdList); 1783 B bean = createBean(); 1784 try 1785 { 1786 for(int i = 0; i < fieldList.length; i++) 1787 { 1788 if(fieldList[i]>=0 && fieldList[i]<metaData.columnNames.size()){ 1789 int columnId = fieldList[i]; 1790 setColumnValue(bean, columnId, getColumnValue(rs, columnId) ); 1791 }else{ 1792 throw new DaoException("Unknown field id " + fieldList[i]); 1793 } 1794 } 1795 } 1796 catch(SQLException e) 1797 { 1798 throw new DataAccessException(e); 1799 } 1800 bean.setNew(false); 1801 bean.resetIsModified(); 1802 1803 return bean; 1804 } 1805 ////////////////////////////////////// 1806 // PREPARED STATEMENT LOADER 1807 ////////////////////////////////////// 1808 1809 //34-1 1810 /** 1811 * Loads all the elements using a prepared statement specifying a list of fields to be retrieved, 1812 * and specifying the start row and the number of rows. 1813 * 1814 * @param ps the PreparedStatement to be used 1815 * @param startRow the start row to be used (first row = 1, last row = -1) 1816 * @param numRows the number of rows to be retrieved (all rows = a negative number) 1817 * @param fieldList table of the field's associated constants 1818 * @return an array of B 1819 */ 1820 protected List<B> loadByPreparedStatementAsList(PreparedStatement ps, int[] fieldList, int startRow, int numRows) throws DaoException 1821 { 1822 ListAction action = new ListAction(); 1823 loadByPreparedStatement(ps,fieldList,startRow,numRows,action); 1824 return action.getList(); 1825 } 1826 //34-2 1827 /** 1828 * Loads each element using a prepared statement specifying a list of fields to be retrieved, 1829 * and specifying the start row and the number of rows 1830 * and dealt by action. 1831 * 1832 * @param ps the PreparedStatement to be used 1833 * @param startRow the start row to be used (first row = 1, last row = -1) 1834 * @param numRows the number of rows to be retrieved (all rows = a negative number) 1835 * @param fieldList table of the field's associated constants 1836 * @param action Action object for do something(not null) 1837 * @return the count dealt by action 1838 */ 1839 private int loadByPreparedStatement(PreparedStatement ps, int[] fieldList, int startRow, int numRows,Action<B> action) throws DaoException 1840 { 1841 ResultSet rs = null; 1842 try { 1843 ps.setFetchSize(100); 1844 rs = ps.executeQuery(); 1845 return this.actionOnResultSet(rs, fieldList, startRow, numRows, action); 1846 } catch (DaoException e) { 1847 throw e; 1848 } catch (SQLException e) { 1849 throw new DataAccessException(e); 1850 } finally { 1851 this.getManager().close(rs); 1852 } 1853 } 1854 //_____________________________________________________________________ 1855 // 1856 // LISTENER 1857 //_____________________________________________________________________ 1858 1859 /** foreign key listener for DEELTE RULE */ 1860 protected class DeleteRuleListener<F extends BaseBean> extends BaseForeignKeyListener<F,B>{ 1861 protected final String fkName; 1862 private final ForeignKeyRule deleteRule; 1863 private final int[] foreignKeyIds; 1864 DeleteRuleListener(String fkName) { 1865 checkArgument(metaData.foreignKeys.containsKey(fkName),"INVALID foreign key name %s",fkName); 1866 this.fkName = fkName; 1867 this.deleteRule = metaData.foreignKeys.get(fkName).deleteRule; 1868 this.foreignKeyIds = metaData.foreignKeyIdArrayOf(fkName); 1869 } 1870 @Override 1871 protected List<B> getImportedBeans(F bean) { 1872 return getListenerContainer().isEmpty() 1873 ? Collections.<B>emptyList() 1874 : loadByForeignKeyAsList(bean,fkName,1,-1); 1875 } 1876 @Override 1877 protected void onRemove(List<B> affectedBeans) throws DaoException { 1878 switch (deleteRule) { 1879 case SET_NULL: 1880 for(B bean : affectedBeans){ 1881 for(int i=0; i<foreignKeyIds.length; ++i){ 1882 bean.setValue(foreignKeyIds[i], null); 1883 } 1884 fire(BaseTableManager.Event.UPDATE, bean); 1885 bean.resetIsModified(); 1886 } 1887 break; 1888 case SET_DEFAULT: 1889 B tmpl = createBean(); 1890 for(B bean : affectedBeans){ 1891 for(int i=0; i<foreignKeyIds.length; ++i){ 1892 bean.setValue(foreignKeyIds[i], tmpl.getValue(foreignKeyIds[i])); 1893 } 1894 fire(BaseTableManager.Event.UPDATE, bean); 1895 bean.resetIsModified(); 1896 } 1897 break; 1898 default: 1899 if(!deleteRule.isNoAction()){ 1900 BaseTableManager.Event event = BaseTableManager.Event.valueOf(deleteRule.eventOfDeleteRule); 1901 for(B bean : affectedBeans){ 1902 fire(event, bean); 1903 } 1904 } 1905 break; 1906 } 1907 } 1908 1909 /** 1910 * fire current event by {@link TableListener} 1911 * @param event 1912 * @param bean 1913 * @throws RuntimeDaoException 1914 */ 1915 private void fire(BaseTableManager.Event event,B bean)throws RuntimeDaoException { 1916 1917 ListenerContainer<B> container = getListenerContainer(); 1918 switch(event){ 1919 case UPDATE: 1920 container.beforeUpdate(bean); 1921 container.afterUpdate(bean); 1922 break; 1923 case DELETE: 1924 container.beforeDelete(bean); 1925 container.afterDelete(bean); 1926 break; 1927 case INSERT: 1928 // DO NOTHING 1929 // container.beforeInsert(bean); 1930 // container.afterInsert(bean); 1931 default: 1932 break; 1933 } 1934 } 1935 1936 @Override 1937 public String toString() { 1938 StringBuilder builder = new StringBuilder(); 1939 builder.append("DeleteRuleListener [fkName="); 1940 builder.append(fkName); 1941 builder.append(", deleteRule="); 1942 builder.append(deleteRule); 1943 builder.append(", foreignKeyIds="); 1944 builder.append(metaData.columnNamesOf(foreignKeyIds)); 1945 builder.append("]"); 1946 return builder.toString(); 1947 } 1948 } 1949 1950 //35 1951 1952 @Override 1953 public TableListener<B> registerListener(TableListener<B> listener) 1954 { 1955 this.getListenerContainer().add(listener); 1956 return listener; 1957 } 1958 1959 //36 1960 1961 @Override 1962 public void unregisterListener(TableListener<B> listener) 1963 { 1964 this.getListenerContainer().remove(listener); 1965 } 1966 1967 //37-2 1968 /** 1969 * bind foreign key listener to foreign table: <br> 1970 * DELETE RULE <br> 1971 */ 1972 public void bindForeignKeyListenerForDeleteRule(){ 1973 for(Entry<String, TableListener<BaseBean>> entry : getForeignKeyDeleteListeners().entrySet()){ 1974 TableManager<BaseBean> manager = managerOf(metaData.foreignKeys.get(entry.getKey()).foreignTable); 1975 manager.registerListener(entry.getValue()); 1976 } 1977 } 1978 1979 //37-3 1980 /** 1981 * unbind foreign key listener from all of foreign tables <br> 1982 * @see #bindForeignKeyListenerForDeleteRule() 1983 */ 1984 public void unbindForeignKeyListenerForDeleteRule(){ 1985 for(Entry<String, TableListener<BaseBean>> entry : getForeignKeyDeleteListeners().entrySet()){ 1986 TableManager<BaseBean> manager = managerOf(metaData.foreignKeys.get(entry.getKey()).foreignTable); 1987 manager.unregisterListener(entry.getValue()); 1988 } 1989 } 1990 //_____________________________________________________________________ 1991 // 1992 // UTILS 1993 //_____________________________________________________________________ 1994 //40 1995 /** 1996 * Retrieves the manager object used to get connections. 1997 * 1998 * @return the manager used 1999 */ 2000 protected Manager getManager() 2001 { 2002 return Manager.getInstance(); 2003 } 2004 2005 //41 2006 /** 2007 * Frees the connection. 2008 * 2009 * @param c the connection to release 2010 */ 2011 protected void freeConnection(Connection c) 2012 { 2013 // back to pool 2014 this.getManager().releaseConnection(c); 2015 } 2016 2017 //42 2018 /** 2019 * Gets the connection. 2020 */ 2021 protected Connection getConnection() throws DaoException 2022 { 2023 try 2024 { 2025 return this.getManager().getConnection(); 2026 } 2027 catch(SQLException e) 2028 { 2029 throw new DataAccessException(e); 2030 } 2031 } 2032 2033 private int loadBySqlForAction(String sql, Object[] argList, int[] fieldList,int startRow, int numRows,Action<B> action){ 2034 PreparedStatement ps = null; 2035 Connection connection = null; 2036 try { 2037 connection = this.getConnection(); 2038 if(debug){ 2039 log("loadBySqlForAction:" + sql ); 2040 } 2041 ps = connection.prepareStatement(sql, 2042 ResultSet.TYPE_FORWARD_ONLY, 2043 ResultSet.CONCUR_READ_ONLY); 2044 Manager.fillPrepareStatement(ps, argList); 2045 return this.loadByPreparedStatement(ps, fieldList, startRow, numRows, action); 2046 } catch (DaoException e) { 2047 throw new RuntimeDaoException(e); 2048 }catch (SQLException e) { 2049 throw new RuntimeDaoException(new DataAccessException(e)); 2050 } finally { 2051 this.getManager().close(ps); 2052 this.freeConnection(connection); 2053 } 2054 } 2055 2056 @Override 2057 public List<BaseBean> runSqlAsList(String sql, Object... argList) throws RuntimeDaoException{ 2058 return getManager().runSqlAsList(sql, argList); 2059 } 2060 2061 @Override 2062 public List<Map<String, Object>> runSqlForMap(Map<String,Class<?>> targetTypes, String sql,Object... argList) throws RuntimeDaoException{ 2063 return getManager().runSqlForMap(targetTypes, sql, argList); 2064 } 2065 2066 @Override 2067 public <T>List<T> runSqlAsList(Class<T> targetType, String sql,Object... argList) throws RuntimeDaoException{ 2068 return getManager().runSqlAsList(targetType, sql, argList); 2069 } 2070 2071 @Override 2072 public <T> T runSqlForValue(Class<T> targetType,String sql, Object... argList) throws RuntimeDaoException{ 2073 return getManager().runSqlForValue(targetType, sql, argList); 2074 } 2075 2076 @Override 2077 public <T>T runAsTransaction(Callable<T> fun) { 2078 return getManager().runAsTransaction(fun); 2079 } 2080 2081 @Override 2082 public void runAsTransaction(Runnable fun) { 2083 getManager().runAsTransaction(fun); 2084 } 2085 protected class DeleteBeanAction implements Action<B>{ 2086 private final AtomicInteger count=new AtomicInteger(0); 2087 public DeleteBeanAction() { 2088 } 2089 @Override 2090 public void call(B bean){ 2091 delete(bean); 2092 count.incrementAndGet(); 2093 } 2094 public int getCount(){ 2095 return count.get(); 2096 } 2097 } 2098 2099 //45 2100 2101 /** 2102 * @param <T> PK type 2103 * @param type 2104 * @param beans 2105 * @return return a primary key list from B array 2106 * @see #toPrimaryKeyList(Class,Collection) 2107 */ 2108 @SuppressWarnings("unchecked") 2109 protected <T> List<T> toPrimaryKeyList(Class<T>type,B... beans){ 2110 if(null == beans || beans.length ==0){ 2111 return Collections.emptyList(); 2112 } 2113 return toPrimaryKeyList(type,Arrays.asList(beans)); 2114 } 2115 2116 private class ColumnTransformer<T> implements Function<B, T>{ 2117 private int columnId; 2118 public ColumnTransformer(int columnId) { 2119 checkArgument(columnId >=0 && columnId < metaData.columnCount,"INVALID column id %s",columnId); 2120 this.columnId = columnId; 2121 } 2122 @Override 2123 public T apply(B input) { 2124 return input == null ? null : input.<T>getValue(columnId); 2125 } 2126 } 2127 /** 2128 * listener event:<br> 2129 * {@code INSERT} insert a bean<br> 2130 * {@code UPDATE} update a bean<br> 2131 * {@code DELETE} delete a bean<br> 2132 * @author guyadong 2133 * 2134 */ 2135 public static enum Event{ 2136 /** insert a bean */INSERT, 2137 /** update a bean */UPDATE, 2138 /** delete a bean */DELETE 2139 } 2140 2141 //46 2142 /** 2143 * return a primary key list from B collection<br> 2144 * throw {@link UnsupportedOperationException} if there is more than a primary key 2145 * @param <T> PK type 2146 * @param type PK type 2147 * @param beans input beans 2148 */ 2149 protected <T> List<T> toPrimaryKeyList(Class<T>type,Collection<B> beans){ 2150 if(metaData.primaryKeyNames.length != 1){ 2151 throw new UnsupportedOperationException(); 2152 } 2153 if(null == beans){ 2154 return Collections.emptyList(); 2155 } 2156 checkArgument(metaData.columnTypeOf(metaData.primaryKeyIds[0]) != type,"INVALID primary key type: " + type); 2157 return ImmutableList.copyOf(Iterables.transform(beans, new ColumnTransformer<T>(metaData.primaryKeyIds[0]))); 2158 } 2159 private Object[] toPkValues(B bean,int[] selfFkIds){ 2160 checkArgument(selfFkIds.length == metaData.primaryKeyNames.length,"MISMATCH SIZE of primary keys"); 2161 return bean.asValueArray(selfFkIds); 2162 } 2163 2164 protected List<B> listOfSelfRef(String fkName,Object... primaryKeys){ 2165 List<B> list = new ArrayList<>(); 2166 int[] selfFks = metaData.foreignKeyIdArrayOf(fkName); 2167 for(B ref = loadByPrimaryKey(primaryKeys) 2168 ; null != ref 2169 ; ref = loadByPrimaryKey(toPkValues(ref,selfFks))){ 2170 list.add(ref); 2171 Object[] refPk = ref.primaryValues(); 2172 Object[] refSelf = toPkValues(ref,selfFks); 2173 2174 if(Arrays.equals(refPk,refSelf) 2175 || (list.size() > 1 && Arrays.equals(refPk,primaryKeys))){ 2176 // cycle reference 2177 break; 2178 } 2179 } 2180 Collections.reverse(list); 2181 return list; 2182 } 2183 protected int levelOfSelfRef(String fkName,Object... primaryKeys){ 2184 int count = 0 ; 2185 int[] selfFks = metaData.foreignKeyIdArrayOf(fkName); 2186 for(B ref = loadByPrimaryKey(primaryKeys) 2187 ; null != ref 2188 ; ++count,ref = loadByPrimaryKey(toPkValues(ref,selfFks))){ 2189 Object[] refPk = ref.primaryValues(); 2190 Object[] refSelf = toPkValues(ref,selfFks); 2191 if( (Arrays.equals(refPk,refSelf)) 2192 || (count > 0 && Arrays.equals(refPk,primaryKeys))){ 2193 // cycle reference 2194 return -1; 2195 } 2196 } 2197 return count; 2198 } 2199 protected B topOfSelfRef(String fkName,Object... primaryKeys){ 2200 checkArgument(!hasNull(primaryKeys),"primaryKeys has null element"); 2201 int[] selfFks = metaData.foreignKeyIdArrayOf(fkName); 2202 2203 B ref = loadByPrimaryKey(primaryKeys); 2204 int count = 0 ; 2205 Object[] refSelf = toPkValues(ref,selfFks); 2206 2207 for(;null != ref && !hasNull(refSelf);){ 2208 Object[] refPk = ref.primaryValues(); 2209 refSelf = toPkValues(ref,selfFks); 2210 2211 if( (Arrays.equals(refPk,refSelf)) 2212 || (++count > 1 && Arrays.equals(refPk,primaryKeys))){ 2213 // cycle reference 2214 throw new IllegalStateException("cycle on field: " + "parent"); 2215 } 2216 checkState(Arrays.equals(refPk,refSelf) 2217 || (++count > 1 && Arrays.equals(refPk,primaryKeys))); 2218 ref = loadByPrimaryKeyChecked(refSelf); 2219 } 2220 return ref; 2221 } 2222 protected LinkedHashSet<B> doListOfChild(B bean, LinkedHashSet<B> set,String fkName){ 2223 if(existsByPrimaryKey(bean)){ 2224 checkState(!set.contains(bean),"cycle on foreign key: " + fkName); 2225 set.add(bean); 2226 List<B> childs = loadByForeignKeyAsList(bean, fkName, 1, -1); 2227 for(B c:childs){ 2228 doListOfChild(c,set,fkName); 2229 } 2230 } 2231 return set; 2232 } 2233 @Override 2234 public int hashCode() { 2235 final int prime = 31; 2236 int result = 1; 2237 result = prime * result + ((metaData == null) ? 0 : metaData.hashCode()); 2238 return result; 2239 } 2240 2241 @Override 2242 public boolean equals(Object obj) { 2243 if (this == obj) { 2244 return true; 2245 } 2246 if (obj == null) { 2247 return false; 2248 } 2249 if (getClass() != obj.getClass()) { 2250 return false; 2251 } 2252 BaseTableManager<?> other = (BaseTableManager<?>) obj; 2253 if (metaData == null) { 2254 if (other.metaData != null) { 2255 return false; 2256 } 2257 } else if (!metaData.equals(other.metaData)) { 2258 return false; 2259 } 2260 return true; 2261 } 2262 2263 @Override 2264 public String toString() { 2265 StringBuilder builder = new StringBuilder(); 2266 builder.append(getClass().getSimpleName() + " [metaData="); 2267 builder.append(metaData); 2268 builder.append("]"); 2269 return builder.toString(); 2270 } 2271 2272 /** 2273 * set debug flag that determine if output log message,default : false 2274 * @param debug flag for debug message output 2275 */ 2276 public static void setDebug(boolean debug) { 2277 BaseTableManager.debug = debug; 2278 } 2279 private static final ImmutableMap<String, TableManager<? extends BaseBean>> 2280 tableManagerInstances = loadTableManager(); 2281 private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> 2282 tableManagerTypeMaps = asTypeMap(tableManagerInstances); 2283 private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> 2284 tableManagerBeanTypeMaps = asBeanTypeMap(tableManagerInstances); 2285 /** 2286 * SPI(Service Provider Interface)机制加载 {@link TableManager}所有实例 2287 * @return 表名和 {@link TableManager}实例的映射对象 2288 */ 2289 @SuppressWarnings({ "rawtypes" }) 2290 private static ImmutableMap<String, TableManager<? extends BaseBean>> loadTableManager() { 2291 ServiceLoader<TableManager> providers = ServiceLoader.load(TableManager.class); 2292 Iterator<TableManager> itor = providers.iterator(); 2293 TableManager<?> node; 2294 ImmutableMap.Builder<String, TableManager<? extends BaseBean>> builder = ImmutableMap.builder(); 2295 while(itor.hasNext()){ 2296 node = itor.next(); 2297 if(node instanceof BaseTableManager){ 2298 BaseTableManager<?> manager = (BaseTableManager<?>)node; 2299 builder.put(manager.metaData.tablename, manager); 2300 } 2301 } 2302 return builder.build(); 2303 } 2304 private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> asTypeMap(Map<String, TableManager<? extends BaseBean>>input){ 2305 return Maps.uniqueIndex(input.values(), new Function<TableManager<? extends BaseBean>,Class<?>>(){ 2306 2307 @Override 2308 public Class<?> apply(TableManager<? extends BaseBean> input) { 2309 return input.getClass(); 2310 }}); 2311 } 2312 private static final ImmutableMap<Class<?>, TableManager<? extends BaseBean>> asBeanTypeMap(Map<String, TableManager<? extends BaseBean>>input){ 2313 return Maps.uniqueIndex(input.values(), new Function<TableManager<? extends BaseBean>,Class<?>>(){ 2314 2315 @Override 2316 public Class<?> apply(TableManager<? extends BaseBean> input) { 2317 return ((BaseTableManager<?>)input).metaData.beanType; 2318 }}); 2319 } 2320 public static ImmutableMap<String, TableManager<? extends BaseBean>> getTableManagers() { 2321 return tableManagerInstances; 2322 } 2323 2324 @SuppressWarnings("unchecked") 2325 public static final <M extends TableManager<?>>M 2326 getTableManager(Class<M>managerType) { 2327 TableManager<? extends BaseBean> manager = tableManagerTypeMaps.get(managerType); 2328 return checkNotNull((M) manager,"INVALID manager type %s",managerType); 2329 } 2330 @SuppressWarnings("unchecked") 2331 public static final <T extends BaseBean,M extends TableManager<T>>M 2332 getTableManagerByBeanType(Class<T>beanType) { 2333 TableManager<? extends BaseBean> manager = tableManagerBeanTypeMaps.get(beanType); 2334 return checkNotNull((M) manager,"INVALID bean type %s",beanType); 2335 } 2336 @SuppressWarnings("unchecked") 2337 public static final <M extends TableManager<?>>M 2338 getTableManager(String tablename) { 2339 TableManager<? extends BaseBean> manager = tableManagerInstances.get(tablename); 2340 return checkNotNull((M) manager,"INVALID tablename %s",tablename); 2341 } 2342 2343 private static final Map<Class<?>, TableManager<? extends BaseBean>> cacheManagers = Maps.newHashMap(); 2344 private static final Map<Class<?>, TableManager<? extends BaseBean>> cacheBeanTypeManagers = Maps.newHashMap(); 2345 private static final Map<String, TableManager<? extends BaseBean>> cacheNameManagers = Maps.newHashMap(); 2346 /** 2347 * 注册cache manager 2348 * @param cacheManager 2349 */ 2350 public synchronized static void registerCacheManager(ICacheManager cacheManager) { 2351 if(cacheManager instanceof BaseTableManager<?>){ 2352 BaseTableManager<?> manager = (BaseTableManager<?>)cacheManager; 2353 Class<? extends ICacheManager> clazz = cacheManager.getClass(); 2354 cacheManagers.put(clazz.getSuperclass(), manager); 2355 cacheNameManagers.put(manager.metaData.tablename, manager); 2356 cacheBeanTypeManagers.put(manager.metaData.beanType, manager); 2357 } 2358 } 2359 2360 public static Map<Class<?>, TableManager<? extends BaseBean>> getCacheManagers() { 2361 return Collections.unmodifiableMap(cacheManagers); 2362 } 2363 2364 /** 2365 * 根据目标类型返回对应的 {@link TableManager}实例 2366 * @param targetType 目标类型 2367 * @return {@link TableManager}实例 2368 * @throws NoSuchElementException 找不到时抛出异常 2369 */ 2370 @SuppressWarnings("unchecked") 2371 public static final <M extends TableManager<? extends BaseBean>>M 2372 getCacheManager(final Class<M>targetType) throws NoSuchElementException { 2373 checkArgument(targetType != null,"targetType is null"); 2374 TableManager<? extends BaseBean> manager; 2375 if(null != (manager = cacheManagers.get(targetType))){ 2376 return (M) manager; 2377 } 2378 return (M) Iterables.find(cacheManagers.values(), new Predicate<TableManager<?>>() { 2379 2380 @Override 2381 public boolean apply(TableManager<?> input) { 2382 return targetType.isInstance(input); 2383 } 2384 }); 2385 } 2386 @SuppressWarnings("unchecked") 2387 public static final <M extends TableManager<?>> M 2388 getCacheManagerByBeanType(Class<?> beanType) { 2389 TableManager<? extends BaseBean> manager = cacheBeanTypeManagers.get(beanType); 2390 return (M) checkNotNull(manager,"INVALID bean type %s",beanType); 2391 } 2392 @SuppressWarnings("unchecked") 2393 public static final <M extends TableManager<?>> M 2394 getCacheManager(String tablename) { 2395 TableManager<? extends BaseBean> manager = cacheNameManagers.get(tablename); 2396 return (M) checkNotNull(manager,"INVALID table name %s",tablename); 2397 } 2398 2399 public static <T extends BaseBean,M extends TableManager<? extends BaseBean>>M 2400 instanceOf(Class<M>targetType) { 2401 try { 2402 return getCacheManager(targetType); 2403 } catch (Exception e) { 2404 return getTableManager(targetType); 2405 } 2406 } 2407 2408 public static <M extends TableManager<? extends BaseBean>>M managerOf(String tablename) { 2409 try { 2410 return getCacheManager(tablename); 2411 } catch (Exception e) { 2412 return getTableManager(tablename); 2413 } 2414 } 2415 2416 public static <T extends BaseBean,M extends TableManager<T>>M managerOf(Class<T> beanType) { 2417 try { 2418 return getCacheManagerByBeanType(beanType); 2419 } catch (Exception e) { 2420 return getTableManagerByBeanType(beanType); 2421 } 2422 } 2423}