001package gu.sql2java; 002 003import static com.google.common.base.Preconditions.*; 004import static com.google.common.base.MoreObjects.*; 005import static gu.sql2java.NameUtilities.*; 006import java.lang.reflect.Method; 007import java.util.Arrays; 008import java.util.Collection; 009import java.util.Collections; 010import java.util.Comparator; 011import java.util.Iterator; 012import java.util.List; 013import java.util.Map; 014import java.util.Map.Entry; 015import java.util.ServiceLoader; 016import java.util.concurrent.ExecutionException; 017 018import com.google.common.base.Function; 019import com.google.common.base.Joiner; 020import com.google.common.base.Predicate; 021import com.google.common.base.Strings; 022import com.google.common.base.Throwables; 023import com.google.common.cache.CacheBuilder; 024import com.google.common.cache.CacheLoader; 025import com.google.common.cache.LoadingCache; 026import com.google.common.collect.Collections2; 027import com.google.common.collect.ImmutableBiMap; 028import com.google.common.collect.ImmutableList; 029import com.google.common.collect.ImmutableMap; 030import com.google.common.collect.Iterables; 031import com.google.common.collect.Lists; 032import com.google.common.collect.Maps; 033import com.google.common.collect.Ordering; 034import com.google.common.primitives.Ints; 035import com.google.common.util.concurrent.UncheckedExecutionException; 036 037/** 038 * meta data used to define a table 039 * @author guyadong 040 * 041 */ 042public class RowMetaData implements IRowMetaData{ 043 protected static final String UNKNOW_TABLENAME="UNKNOWN"; 044 protected static final String UNKNOW_TABLETYPE="UNKNOWN"; 045 public final String tablename; 046 public final String tableType; 047 public final Class<? extends BaseBean> beanType; 048 public final ImmutableList<String> columnNames; 049 public final String columnFields; 050 public final String columnFullFields; 051 public final ImmutableList<String> columnJavaNames; 052 public final ImmutableList<Method> getterMethods; 053 public final ImmutableList<Method> setterMethods; 054 public final ImmutableList<Class<?>> columnTypes; 055 private final ImmutableMap<String, Integer> nameIndexsMap; 056 private final ImmutableMap<String, Integer> javaNameIndexsMap; 057 public final int[] defaultColumnIdList; 058 public final int[] sqlTypes; 059 public final ImmutableMap<String, Class<?>> typesMap; 060 061 public final int columnCount; 062 public final int[] primaryKeyIds; 063 public final String[] primaryKeyNames; 064 public final int primaryKeyCount; 065 /** lazy load */ 066 private volatile ImmutableMap<String, Object[]> junctionTablePkMap; 067 private final Map<String, String> junctionTablePkStrMap; 068 public final Class<?> lockColumnType; 069 public final String lockColumnName; 070 /** 071 * tablename-ForeignKeyMetaData map 072 */ 073 public final ImmutableMap<String,ForeignKeyMetaData> foreignKeys; 074 /** 075 * universal name-ForeignKeyMetaData map 076 */ 077 public final ImmutableMap<String, ForeignKeyMetaData> foreignKeysRn; 078 public final ImmutableMap<String, IndexMetaData> indices; 079 public final ImmutableMap<String, IndexMetaData> indicesRn; 080 public final Function<String, Integer> COLUMNID_FUN = new Function<String, Integer>(){ 081 082 @Override 083 public Integer apply(String input) { 084 return columnIDOf(input); 085 }}; 086 public final Function<Integer,String> COLUMNNAME_FUN = new Function<Integer,String>(){ 087 088 @Override 089 public String apply(Integer input) { 090 return columnNameOf(input); 091 }}; 092 /** lazy load */ 093 private volatile ImmutableList<ForeignKeyMetaData> importedKeys; 094 /** lazy load */ 095 private volatile ImmutableMap<String, ForeignKeyMetaData> importKeysMap; 096 public final int autoincrementColumnId; 097 protected RowMetaData( 098 String tablename, 099 String tableType, 100 Class<? extends BaseBean> beanType, 101 List<String> columnNames, 102 List<String> columnJavaNames, 103 List<String> getters, 104 List<String> setters, 105 Class<?>[] columnTypes, 106 int[] sqlTypes, 107 List<String> primaryKeyNames, 108 Map<String, String> junctionTablePkMap, 109 Class<?> lockColumnType, 110 String lockColumnName, 111 List<String> foreignKeys, 112 List<String> indices, 113 String autoincrement) { 114 columnJavaNames = firstNonNull(columnJavaNames,Collections.<String>emptyList()); 115 getters = firstNonNull(getters,Collections.<String>emptyList()); 116 setters = firstNonNull(setters,Collections.<String>emptyList()); 117 this.junctionTablePkStrMap = firstNonNull(junctionTablePkMap, Collections.<String,String>emptyMap()); 118 primaryKeyNames = firstNonNull(primaryKeyNames, Collections.<String>emptyList()); 119 foreignKeys = firstNonNull(foreignKeys, Collections.<String>emptyList()); 120 indices = firstNonNull(indices, Collections.<String>emptyList()); 121 autoincrement = firstNonNull(autoincrement, ""); 122 this.tablename = checkNotNull(tablename,"tablename is null"); 123 this.tableType = checkNotNull(tableType,"tableType is null"); 124 this.beanType = checkNotNull(beanType,"beanType is null"); 125 this.columnNames = ImmutableList.copyOf(checkNotNull(columnNames,"columnNames is null")); 126 this.columnJavaNames = ImmutableList.copyOf(columnJavaNames); 127 this.columnTypes = ImmutableList.copyOf(checkNotNull(columnTypes,"columnTypes is null")); 128 this.sqlTypes = Arrays.copyOf(checkNotNull(sqlTypes,"sqlTypes is null"),sqlTypes.length); 129 checkArgument(this.columnNames.size() == this.columnTypes.size() && this.columnTypes.size() == this.sqlTypes.length, 130 "MISMATCH LENGTH for input list"); 131 checkArgument(this.columnJavaNames.isEmpty() || this.columnJavaNames.size() == this.sqlTypes.length, 132 "MISMATCH LENGTH for columnJavaNames"); 133 checkArgument(getters.isEmpty() || getters.size() == this.sqlTypes.length, 134 "MISMATCH LENGTH for getters"); 135 checkArgument(setters.isEmpty() || setters.size() == this.sqlTypes.length, 136 "MISMATCH LENGTH for setters"); 137 138 ImmutableMap.Builder<String, Integer> nameIndexBuilder = ImmutableMap.builder(); 139 ImmutableMap.Builder<String, Integer> javaNameIndexBuilder = ImmutableMap.builder(); 140 ImmutableMap.Builder<String, Class<?>> nameTypeBuilder = ImmutableMap.builder(); 141 this.defaultColumnIdList = new int[sqlTypes.length]; 142 for(int i = 0; i < sqlTypes.length; ++i){ 143 defaultColumnIdList[i] = i; 144 nameIndexBuilder.put(columnNames.get(i), i); 145 nameTypeBuilder.put(columnNames.get(i), columnTypes[i]); 146 if(!columnJavaNames.isEmpty()){ 147 javaNameIndexBuilder.put(columnJavaNames.get(i), i); 148 } 149 } 150 columnFields = Joiner.on(",").join(columnNames); 151 columnFullFields = Joiner.on(",").join(Lists.transform(columnNames, new Function<String,String>(){ 152 153 @Override 154 public String apply(String input) { 155 if("UNKNOWN".equals(RowMetaData.this.tableType)){ 156 return input; 157 } 158 return RowMetaData.this.tablename + "." + input; 159 }})); 160 161 this.nameIndexsMap = nameIndexBuilder.build(); 162 this.autoincrementColumnId = firstNonNull(nameIndexsMap.get(autoincrement), -1).intValue(); 163 164 this.javaNameIndexsMap = javaNameIndexBuilder.build(); 165 this.typesMap = nameTypeBuilder.build(); 166 this.columnCount = sqlTypes.length; 167 this.primaryKeyNames = primaryKeyNames.toArray(new String[0]); 168 169 this.primaryKeyCount = primaryKeyNames.size(); 170 this.primaryKeyIds = new int[primaryKeyNames.size()]; 171 for(int i = 0 ; i < primaryKeyIds.length; ++i){ 172 String name = primaryKeyNames.get(i); 173 checkArgument(primaryKeyIds[i]>=0,"INVALID primary key name %s",name); 174 primaryKeyIds[i] = columnIDOf(name); 175 } 176 this.lockColumnType = lockColumnType; 177 this.lockColumnName = lockColumnName; 178 ImmutableMap.Builder<String,ForeignKeyMetaData> fkBuilder = ImmutableMap.builder(); 179 ImmutableMap.Builder<String,ForeignKeyMetaData> fkRnameBuilder = ImmutableMap.builder(); 180 for(String fk:foreignKeys){ 181 ForeignKeyMetaData data = new ForeignKeyMetaData(fk, tablename); 182 fkBuilder.put(data.name,data); 183 fkRnameBuilder.put(toCamelCase(data.readableName, true),data); 184 } 185 this.foreignKeys = fkBuilder.build(); 186 this.foreignKeysRn = fkRnameBuilder.build(); 187 ImmutableMap.Builder<String,IndexMetaData> indexBuilder = ImmutableMap.builder(); 188 ImmutableMap.Builder<String,IndexMetaData> indexRnameBuilder = ImmutableMap.builder(); 189 for(String fk:indices){ 190 IndexMetaData data = new IndexMetaData(fk, tablename); 191 indexBuilder.put(data.name,data); 192 indexRnameBuilder.put(data.readableName,data); 193 } 194 this.indices = indexBuilder.build(); 195 this.indicesRn = indexBuilder.build(); 196 ImmutableList.Builder<Method> getterMethodBuilder = ImmutableList.builder(); 197 ImmutableList.Builder<Method> setterMethodBuilder = ImmutableList.builder(); 198 199 for(int i = 0; i < sqlTypes.length; ++i){ 200 try { 201 if(!getters.isEmpty()){ 202 getterMethodBuilder.add(beanType.getMethod(getters.get(i))); 203 } 204 if(!setters.isEmpty()){ 205 setterMethodBuilder.add(beanType.getMethod(setters.get(i), columnTypes[i])); 206 } 207 } catch (Exception e) { 208 Throwables.throwIfUnchecked(e); 209 throw new RuntimeException(e); 210 } 211 } 212 213 this.getterMethods = getterMethodBuilder.build(); 214 this.setterMethods = setterMethodBuilder.build(); 215 } 216 /** 217 * return column name specified by column id 218 * @param columnId column id 219 * @return column name or null if columnId is invalid 220 */ 221 public String columnNameOf(int columnId){ 222 try{ 223 return columnNames.get(columnId); 224 } catch(IndexOutOfBoundsException e){ 225 return null; 226 } 227 } 228 /** 229 * return column full name(with table name,such as tablename.columnname) specified by column id 230 * @param columnId column id 231 * @return column full name or null if columnId is invalid 232 */ 233 public String fullNameOf(int columnId){ 234 try{ 235 if(tablename.startsWith(UNKNOW_TABLENAME)){ 236 return columnNames.get(columnId); 237 } 238 return tablename + "." + columnNames.get(columnId); 239 } catch(IndexOutOfBoundsException e){ 240 return null; 241 } 242 } 243 /** 244 * return column ordinal id(base 0) specified by column name 245 * @param column column name or full name,or java field name 246 * @return column ordinal id(base 0) or -1 if column name is invalid 247 */ 248 public final int columnIDOf(String column){ 249 if(null != column){ 250 String prefix = tablename + "."; 251 if(column.startsWith(prefix)){ 252 column = column.substring(prefix.length()); 253 } 254 return firstNonNull(nameIndexsMap.get(column), 255 firstNonNull(javaNameIndexsMap.get(column), -1)); 256 } 257 return -1; 258 } 259 260 /** 261 * return column ordinal id(base 0) specified by column names 262 * @param columns array of column name or full name,or java field name 263 * @return array of column ordinal id(base 0) or empty array if columns is null 264 * @see #columnIDOf(String) 265 */ 266 public final int[] columnIDsOf(String... columns){ 267 return null == columns ? new int[0] : Ints.toArray(Lists.transform(Arrays.asList(columns), COLUMNID_FUN)); 268 } 269 /** 270 * return column ordinal id(base 0) specified by column names 271 * @param columns collection of column name or full name,or java field name 272 * @return array of column ordinal id(base 0) or empty array if columns is null 273 * @see #columnIDOf(String) 274 */ 275 public final int[] columnIDsOf(Collection<String> columns){ 276 return null == columns ? new int[0] : Ints.toArray(Collections2.transform(columns, COLUMNID_FUN)); 277 } 278 /** 279 * return column names by column names 280 * @param columnIds array of column id 281 * @return array of column name or empty array if columnIds is null 282 * @see #columnNameOf(int) 283 */ 284 public final List<String> columnNamesOf(int... columnIds){ 285 return null == columnIds ? Collections.<String>emptyList() : Lists.transform(Ints.asList(columnIds), COLUMNNAME_FUN); 286 } 287 /** 288 * @param columnId column id 289 * @return java type of column,or NULL if columnId is invalid 290 */ 291 public Class<?> columnTypeOf(int columnId){ 292 try{ 293 return columnTypes.get(columnId); 294 } catch(IndexOutOfBoundsException e){ 295 return null; 296 } 297 } 298 /** 299 * @param column column name 300 * @return java type of column,or NULL if column is invalid 301 */ 302 public Class<?> columnTypeOf(String column){ 303 try{ 304 return columnTypes.get(columnIDOf(column)); 305 } catch(IndexOutOfBoundsException e){ 306 return null; 307 } 308 } 309 310 /** 311 * @param columnId column id 312 * @return SQL type of column,or throw {@link IllegalArgumentException} if columnId is invalid 313 * @see java.sql.Types 314 */ 315 public int sqlTypeOf(int columnId){ 316 try{ 317 return sqlTypes[columnId]; 318 } catch(IndexOutOfBoundsException e){ 319 throw new IllegalArgumentException(String.format("INVALID columnID %d",columnId)); 320 } 321 } 322 323 /** 324 * lazy load 325 */ 326 private final LoadingCache<String,Boolean> checkLinkedTableCache = CacheBuilder.newBuilder().build( 327 new CacheLoader<String,Boolean>(){ 328 329 @Override 330 public Boolean load(final String tablename) throws Exception { 331 return Iterables.tryFind(getJunctionTablePkMap().values(), new Predicate<Object[]>() { 332 333 @Override 334 public boolean apply(Object[] input) { 335 return tablename.equals(input[0]); 336 } 337 }).isPresent(); 338 }}); 339 340 /** 341 * check if the table specified by tablename is linked table of current table 342 * @param tablename 343 * @return true if be linked table 344 */ 345 public boolean isLinkedTable(String tablename){ 346 try { 347 return checkLinkedTableCache.get(tablename); 348 } catch (ExecutionException e) { 349 Throwables.throwIfUnchecked(e.getCause()); 350 throw new RuntimeException(e); 351 } 352 } 353 /** 354 * @return junctionTablePkMap 355 */ 356 public ImmutableMap<String, Object[]> getJunctionTablePkMap() { 357 if(junctionTablePkMap == null){ 358 synchronized (this) { 359 if(junctionTablePkMap == null){ 360 ImmutableMap.Builder<String, Object[]> builder = ImmutableMap.builder(); 361 for(Entry<String, String> entry:junctionTablePkStrMap.entrySet()){ 362 String[] values = entry.getValue().split("\\."); 363 String foreignTable = values[0]; 364 int columnId = getMedaData(foreignTable).columnIDOf(values[1]); 365 checkArgument(columnId >=0,"INVALID foreign key description %s", entry.getValue()); 366 builder.put(entry.getKey(), new Object[]{foreignTable,columnId}); 367 } 368 this.junctionTablePkMap = builder.build(); 369 } 370 } 371 } 372 return junctionTablePkMap; 373 } 374 375 /** 376 * lazy load 377 */ 378 private final LoadingCache<String,Map<String, String>> junctionMapCache = CacheBuilder.newBuilder().build( 379 new CacheLoader<String,Map<String, String>>(){ 380 381 @Override 382 public Map<String, String> load(final String linkedTableName) throws Exception { 383 Map<String, Object[]> m = Maps.filterValues(getJunctionTablePkMap(), new Predicate<Object[]>(){ 384 385 @Override 386 public boolean apply(Object[] input) { 387 return input[0].equals(linkedTableName); 388 }}); 389 return Maps.transformValues(m, new Function<Object[],String>() { 390 391 @Override 392 public String apply(Object[] input) { 393 return getMedaData((String)input[0]).fullNameOf((Integer)input[1]); 394 } 395 }); 396 }}); 397 398 public Map<String, String> junctionMapOf(String linkedTableName){ 399 try { 400 return junctionMapCache.get(linkedTableName); 401 } catch (ExecutionException e) { 402 Throwables.throwIfUnchecked(e.getCause()); 403 throw new RuntimeException(e); 404 } 405 } 406 /** lazy load */ 407 private final LoadingCache<String,ImmutableBiMap<Integer,Integer>> foreignKeyIdCache = CacheBuilder.newBuilder().build( 408 new CacheLoader<String, ImmutableBiMap<Integer,Integer>>(){ 409 410 @Override 411 public ImmutableBiMap<Integer, Integer> load(String fkName) throws Exception { 412 ForeignKeyMetaData foreignkey = foreignKeys.get(fkName); 413 checkArgument(foreignkey != null,"INVALID foreign key name:%s",fkName); 414 ImmutableBiMap.Builder<Integer, Integer> builder = ImmutableBiMap.builder(); 415 for(Entry<String, String> entry:foreignkey.columnMaps.entrySet()){ 416 builder.put(columnIDOf(entry.getKey()), getMedaData(foreignkey.foreignTable).columnIDOf(entry.getValue())); 417 } 418 return builder.build(); 419 }}); 420 421 /** 422 * 423 * @param fkName foreign key name 424 * @return map of column id TO foreign table column id 425 */ 426 public ImmutableBiMap<Integer, Integer> foreignKeyIdMapOf(String fkName){ 427 try { 428 return foreignKeyIdCache.get(fkName); 429 } catch (ExecutionException e) { 430 Throwables.throwIfUnchecked(e.getCause()); 431 throw new RuntimeException(e); 432 } 433 } 434 435 /** 436 * lazy load 437 */ 438 private final LoadingCache<String,int[]> foreignKeyIdArrayCache = CacheBuilder.newBuilder().build( 439 new CacheLoader<String,int[]>(){ 440 441 @Override 442 public int[] load(String fkName) throws Exception { 443 ForeignKeyMetaData foreignkey = checkNotNull(foreignKeys.get(fkName),"INVALID foreign key name:%s",fkName); 444 return foreignkey.foreignKeyIdArray(COLUMNID_FUN); 445 }}); 446 447 public int[] foreignKeyIdArrayOf(String fkName){ 448 try { 449 return foreignKeyIdArrayCache.get(fkName); 450 } catch (ExecutionException | UncheckedExecutionException e) { 451 Throwables.throwIfUnchecked(e.getCause()); 452 throw new RuntimeException(e); 453 } 454 } 455 public List<ForeignKeyMetaData> foreignKeysOf(final String foreignTable){ 456 Iterable<ForeignKeyMetaData> found = Iterables.filter(foreignKeys.values(), 457 new Predicate<ForeignKeyMetaData>(){ 458 @Override 459 public boolean apply(ForeignKeyMetaData input) { 460 return input.foreignTable.equals(foreignTable); 461 }}); 462 return Lists.newArrayList(found); 463 } 464 465 public ImmutableList<ForeignKeyMetaData> getImportedKeys(){ 466 if(this.importedKeys == null){ 467 synchronized (this) { 468 if(this.importedKeys == null){ 469 ImmutableList.Builder<ForeignKeyMetaData> builder = ImmutableList.builder(); 470 for(RowMetaData metaData:tableMetadata.values()){ 471 builder.addAll(Iterables.filter(metaData.foreignKeys.values(), new Predicate<ForeignKeyMetaData>() { 472 473 @Override 474 public boolean apply(ForeignKeyMetaData input) { 475 return input.foreignTable.equals(tablename); 476 } 477 })); 478 } 479 this.importedKeys = builder.build(); 480 } 481 } 482 } 483 return this.importedKeys; 484 } 485 486 public ForeignKeyMetaData getImportedKey(String fkName){ 487 if(this.importKeysMap == null){ 488 synchronized (this) { 489 if(this.importKeysMap == null){ 490 this.importKeysMap = Maps.uniqueIndex(getImportedKeys(), new Function<ForeignKeyMetaData, String>() { 491 492 @Override 493 public String apply(ForeignKeyMetaData input) { 494 return input.name; 495 } 496 }); 497 } 498 } 499 } 500 return checkNotNull(importKeysMap.get(fkName),"invalid ikIndex %s",fkName); 501 } 502 503 private volatile ImmutableList<ForeignKeyMetaData> foreignKeysForListeners; 504 /** 505 * @return 返回 所有需要输出foreign key listener的 {@link ForeignKeyMetaData}对象 506 */ 507 public ImmutableList<ForeignKeyMetaData> getForeignKeysForListener(){ 508 // double check 509 if(foreignKeysForListeners == null){ 510 synchronized (this) { 511 if(foreignKeysForListeners == null){ 512 Collection<ForeignKeyMetaData> c = Maps.filterEntries(foreignKeys, 513 new Predicate<Entry<String, ForeignKeyMetaData>>(){ 514 @Override 515 public boolean apply(Entry<String, ForeignKeyMetaData> input) { 516 ForeignKeyMetaData fk = input.getValue(); 517 return fk.updateRule.isNoAction() 518 && !Strings.isNullOrEmpty(fk.deleteRule.eventOfDeleteRule); 519 }}).values(); 520 // 排序输出 521 foreignKeysForListeners = ImmutableList.copyOf(Ordering.natural().onResultOf(new Function<ForeignKeyMetaData,String>(){ 522 @Override 523 public String apply(ForeignKeyMetaData input) { 524 return input.name; 525 }}).sortedCopy(c)); 526 } 527 } 528 } 529 return foreignKeysForListeners; 530 } 531 private volatile ImmutableMap<String, IndexMetaData> uniqueIndeices; 532 public ImmutableMap<String, IndexMetaData> getUniqueIndices(){ 533 // double check 534 if(uniqueIndeices == null){ 535 synchronized (this) { 536 if(uniqueIndeices == null){ 537 uniqueIndeices = ImmutableMap.copyOf(Maps.filterValues(indices, IndexMetaData.UNIQUE_FILTER)); 538 } 539 } 540 } 541 return uniqueIndeices; 542 } 543 544 public IndexMetaData getIndexChecked(String indexName){ 545 return checkNotNull(getUniqueIndices().get(indexName),"INVALID indexName %s",indexName); 546 } 547 /** 548 * lazy load 549 */ 550 private final LoadingCache<String,int[]> indexKeyIdArrayCache = CacheBuilder.newBuilder().build( 551 new CacheLoader<String,int[]>(){ 552 @Override 553 public int[] load(String indexName) throws Exception { 554 return getIndexChecked(indexName).getColumnIds(COLUMNID_FUN); 555 }}); 556 public int[] indexIdArray(String indexName){ 557 try { 558 return indexKeyIdArrayCache.get(indexName); 559 } catch (ExecutionException | UncheckedExecutionException e) { 560 if(null != e.getCause()){ 561 Throwables.throwIfUnchecked(e.getCause()); 562 throw new RuntimeException(e.getCause()); 563 } 564 Throwables.throwIfUnchecked(e); 565 throw new RuntimeException(e); 566 } 567 } 568 private class RowComparator<B extends BaseBean> implements Comparator<B> { 569 /** 570 * Holds the column id on which the comparison is performed. 571 */ 572 private final int columnId; 573 /** 574 * Value that will contain the information about the order of the sort: normal or reversal. 575 */ 576 private final boolean bReverse; 577 578 private RowComparator(int columnId, boolean bReverse) { 579 checkArgument(columnTypeOf(columnId) != null,"INVALID column id %s",columnId); 580 checkArgument(Comparable.class.isAssignableFrom(columnTypeOf(columnId)), 581 "type of column %s for the field is not supported Comparable",columnNames.get(columnId)); 582 this.columnId = columnId; 583 this.bReverse = bReverse; 584 } 585 586 @SuppressWarnings("unchecked") 587 @Override 588 public int compare(B o1, B o2) { 589 int iReturn = 0; 590 Object v1= o1.getValue(columnId); 591 Object v2= o2.getValue(columnId); 592 if(v1 ==null && v2 !=null){ 593 iReturn = -1; 594 }else if(v1 ==null && v2 ==null){ 595 iReturn = 0; 596 }else if(v1 !=null && v2 ==null){ 597 iReturn = 1; 598 }else{ 599 iReturn = ((Comparable<Object>)v1).compareTo(v2); 600 } 601 return bReverse ? (-1 * iReturn) : iReturn; 602 } 603 } 604 605 public <B extends BaseBean> Comparator<B> comparatorOf(int columnId,boolean bReverse){ 606 return new RowComparator<B>(columnId,bReverse); 607 } 608 609 private static final ImmutableMap<String, RowMetaData> tableMetadata = loadRowMetaData(); 610 private static final ImmutableMap<Class<?>, RowMetaData> beanTypeMetadata = Maps.uniqueIndex(tableMetadata.values(), 611 new Function<RowMetaData,Class<?>>(){ 612 613 @Override 614 public Class<?> apply(RowMetaData input) { 615 return input.beanType; 616 }});; 617 618 /** 619 * SPI(Service Provider Interface)机制加载 {@link IRowMetaData}所有实例 620 * @return 表名和 {@link RowMetaData}实例的映射对象 621 */ 622 private static ImmutableMap<String, RowMetaData> loadRowMetaData() { 623 ServiceLoader<IRowMetaData> providers = ServiceLoader.load(IRowMetaData.class); 624 Iterator<IRowMetaData> itor = providers.iterator(); 625 IRowMetaData instance; 626 ImmutableMap.Builder<String, RowMetaData> builder = ImmutableMap.builder(); 627 while(itor.hasNext()){ 628 instance = itor.next(); 629 if(instance instanceof RowMetaData){ 630 RowMetaData rowMetaData = (RowMetaData)instance; 631 builder.put(rowMetaData.tablename, rowMetaData); 632 } 633 } 634 return builder.build(); 635 } 636 /** 637 * 根据表名返回对应的 {@link RowMetaData}实例 638 * @param tablename 表名 639 * @return {@link RowMetaData}实例,找不到时抛出异常 640 */ 641 public static final RowMetaData getMedaData(String tablename) { 642 RowMetaData metaData = tableMetadata.get(tablename); 643 return checkNotNull(metaData,"INVALID TABLE NAME %s",tablename); 644 } 645 /** 646 * 根据beanType返回对应的 {@link RowMetaData}实例 647 * @param beanType 表名 648 * @return {@link RowMetaData}实例,找不到时抛出异常 649 */ 650 public static final RowMetaData getMedaData(Class<?> beanType) { 651 RowMetaData metaData = beanTypeMetadata.get(beanType); 652 return checkNotNull(metaData,"INVALID bean type %s",beanType); 653 } 654 655 656 @Override 657 public int hashCode() { 658 final int prime = 31; 659 int result = 1; 660 result = prime * result + ((columnNames == null) ? 0 : columnNames.hashCode()); 661 result = prime * result + ((tableType == null) ? 0 : tableType.hashCode()); 662 result = prime * result + ((tablename == null) ? 0 : tablename.hashCode()); 663 return result; 664 } 665 666 @Override 667 public boolean equals(Object obj) { 668 if (this == obj) { 669 return true; 670 } 671 if (obj == null) { 672 return false; 673 } 674 if (!(obj instanceof RowMetaData)) { 675 return false; 676 } 677 RowMetaData other = (RowMetaData) obj; 678 if (columnNames == null) { 679 if (other.columnNames != null) { 680 return false; 681 } 682 } else if (!columnNames.equals(other.columnNames)) { 683 return false; 684 } 685 if (tableType == null) { 686 if (other.tableType != null) { 687 return false; 688 } 689 } else if (!tableType.equals(other.tableType)) { 690 return false; 691 } 692 if (tablename == null) { 693 if (other.tablename != null) { 694 return false; 695 } 696 } else if (!tablename.equals(other.tablename)) { 697 return false; 698 } 699 return true; 700 } 701 @Override 702 public String toString() { 703 StringBuilder builder = new StringBuilder(); 704 builder.append("RowMetaData [tablename="); 705 builder.append(tablename); 706 builder.append(", tableType="); 707 builder.append(tableType); 708 builder.append(", columnNames="); 709 builder.append(columnNames); 710 builder.append("]"); 711 return builder.toString(); 712 } 713 714}