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