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