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