在前面 的 inject() 方法中, 調用了一個 TableInfoHelper.initTableInfo(builderAssistant, modelClass) 方法, 來獲取 表信息: TableInfo
/** * <p> * 實體類反射獲取表信息【初始化】 * <p> * * @param clazz 反射實體類 * @return 數據庫表反射信息 */ public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) { TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz); if (tableInfo != null) { if (tableInfo.getConfigMark() == null && builderAssistant != null) { tableInfo.setConfigMark(builderAssistant.getConfiguration()); } return tableInfo; } /* 沒有獲取到緩存信息,則初始化 */ tableInfo = new TableInfo(); GlobalConfig globalConfig; if (null != builderAssistant) { tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace()); tableInfo.setConfigMark(builderAssistant.getConfiguration()); tableInfo.setUnderCamel(builderAssistant.getConfiguration().isMapUnderscoreToCamelCase()); globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration()); } else { // 兼容測試場景 globalConfig = GlobalConfigUtils.defaults(); } /* 初始化表名相關 */ initTableName(clazz, globalConfig, tableInfo); /* 初始化字段相關 */ initTableFields(clazz, globalConfig, tableInfo); /* 放入緩存 */ TABLE_INFO_CACHE.put(clazz, tableInfo); /* 緩存 Lambda 映射關系 */ LambdaUtils.createCache(clazz, tableInfo); return tableInfo; }
是不是還是自己人寫的代碼看起來爽? 這中文注釋, 都不用看方法具體是干啥的.
這里的 TABLE_INFO_CACHE 是用來緩存表信息的:
/** * 儲存反射類表信息 */ private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();
第一次進這個方法的時候, 肯定是空的, 要去解析獲取.
1. initTableName()
/** * <p> * 初始化 表數據庫類型,表名,resultMap * </p> * * @param clazz 實體類 * @param globalConfig 全局配置 * @param tableInfo 數據庫表反射信息 */ public static void initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) { /* 數據庫全局配置 */ GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig(); /* 設置數據庫類型 */ tableInfo.setDbType(dbConfig.getDbType()); /* 設置表名 */ TableName table = clazz.getAnnotation(TableName.class); String tableName = clazz.getSimpleName(); if (table != null && StringUtils.isNotEmpty(table.value())) { tableName = table.value(); } else { // 開啟表名下划線申明 if (dbConfig.isTableUnderline()) { tableName = StringUtils.camelToUnderline(tableName); } // 大寫命名判斷 if (dbConfig.isCapitalMode()) { tableName = tableName.toUpperCase(); } else { // 首字母小寫 tableName = StringUtils.firstToLowerCase(tableName); } // 存在表名前綴 if (null != dbConfig.getTablePrefix()) { tableName = dbConfig.getTablePrefix() + tableName; } } tableInfo.setTableName(tableName); /* 表結果集映射 */ if (table != null && StringUtils.isNotEmpty(table.resultMap())) { tableInfo.setResultMap(table.resultMap()); } /* 開啟了自定義 KEY 生成器 */ if (null != dbConfig.getKeyGenerator()) { tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class)); } }
判斷邏輯:
1. 判斷實體類上面有沒有 TableName 注解
|-> 如果有, 則拿注解里面配置的 value 作為表名
|-> 如果沒有, 則根據類名進行解析
2. initTableFields()
/** * <p> * 初始化 表主鍵,表字段 * </p> * * @param clazz 實體類 * @param globalConfig 全局配置 * @param tableInfo 數據庫表反射信息 */ public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) { /* 數據庫全局配置 */ GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig(); List<Field> list = getAllFields(clazz); // 標記是否讀取到主鍵 boolean isReadPK = false; // 是否存在 @TableId 注解 boolean existTableId = isExistTableId(list); List<TableFieldInfo> fieldList = new ArrayList<>(); for (Field field : list) { /* * 主鍵ID 初始化 */ if (!isReadPK) { if (existTableId) { isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, clazz); } else { isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, clazz); } if (isReadPK) { continue; } } /* 有 @TableField 注解的字段初始化 */ if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field, clazz)) { continue; } /* 無 @TableField 注解的字段初始化 */ fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field)); } /* 檢查邏輯刪除字段只能有最多一個 */ Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L, String.format("annotation of @TableLogic can't more than one in class : %s.", clazz.getName())); /* 字段列表 */ tableInfo.setFieldList(fieldList); /* 未發現主鍵注解,提示警告信息 */ if (StringUtils.isEmpty(tableInfo.getKeyColumn())) { logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName())); } }
2.1 getAllFields()
/** * 獲取該類的所有屬性列表 * * @param clazz 反射類 * @return 屬性集合 */ public static List<Field> getAllFields(Class<?> clazz) { List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz)); if (CollectionUtils.isNotEmpty(fieldList)) { return fieldList.stream() .filter(i -> { /* 過濾注解非表字段屬性 */ TableField tableField = i.getAnnotation(TableField.class); return (tableField == null || tableField.exist()); }).collect(toList()); } return fieldList; }
如果字段上面加了 TableField 注解, 如果有則進行特殊處理. 如果配置了 exist=false, 則這個字段, 過濾掉, 不參與sql生成.
2.2 initTableIdWithAnnotation()
如果實體類中有 TableId 注解, 則進入此方法, 一般情況下, 最好是配一下 TableId
/** * <p> * 主鍵屬性初始化 * </p> * * @param dbConfig 全局配置信息 * @param tableInfo 表信息 * @param field 字段 * @param clazz 實體類 * @return true 繼續下一個屬性判斷,返回 continue; */ private static boolean initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo, Field field, Class<?> clazz) { TableId tableId = field.getAnnotation(TableId.class); boolean underCamel = tableInfo.isUnderCamel(); if (tableId != null) { if (StringUtils.isEmpty(tableInfo.getKeyColumn())) { /* 主鍵策略( 注解 > 全局 ) */ // 設置 Sequence 其他策略無效 if (IdType.NONE == tableId.type()) { tableInfo.setIdType(dbConfig.getIdType()); } else { tableInfo.setIdType(tableId.type()); } /* 字段 */ String column = field.getName(); if (StringUtils.isNotEmpty(tableId.value())) { column = tableId.value(); } else { // 開啟字段下划線申明 if (underCamel) { column = StringUtils.camelToUnderline(column); } // 全局大寫命名 if (dbConfig.isCapitalMode()) { column = column.toUpperCase(); } } tableInfo.setKeyRelated(checkRelated(underCamel, field.getName(), column)) .setClazz(field.getDeclaringClass()) .setKeyColumn(column) .setKeyProperty(field.getName()); return true; } else { throwExceptionId(clazz); } } return false; }
2.3 initTableFieldWithAnnotation()
這里對 TableField 進行解析賦值. 如 value 解析成 字段名稱
/** * <p> * 字段屬性初始化 * </p> * * @param dbConfig 數據庫全局配置 * @param tableInfo 表信息 * @param fieldList 字段列表 * @param clazz 當前表對象類 * @return true 繼續下一個屬性判斷,返回 continue; */ private static boolean initTableFieldWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo, List<TableFieldInfo> fieldList, Field field, Class<?> clazz) { /* 獲取注解屬性,自定義字段 */ TableField tableField = field.getAnnotation(TableField.class); if (null == tableField) { return false; } String columnName = field.getName(); if (StringUtils.isNotEmpty(tableField.value())) { columnName = tableField.value(); } /* * el 語法支持,可以傳入多個參數以逗號分開 */ String el = field.getName(); if (StringUtils.isNotEmpty(tableField.el())) { el = tableField.el(); } String[] columns = columnName.split(StringPool.SEMICOLON); String[] els = el.split(StringPool.SEMICOLON); if (columns.length == els.length) { for (int i = 0; i < columns.length; i++) { fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, columns[i], els[i], tableField)); } return true; } throw ExceptionUtils.mpe(String.format("Class: %s, Field: %s, 'value' 'el' Length must be consistent.", clazz.getName(), field.getName())); }