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