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