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