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