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