/** * Copyright (c) 2011-2020, hubin (jobob@qq.com). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.toolkit; import com.baomidou.mybatisplus.annotations.KeySequence; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.entity.GlobalConfiguration; import com.baomidou.mybatisplus.entity.TableFieldInfo; import com.baomidou.mybatisplus.entity.TableInfo; import com.baomidou.mybatisplus.enums.IdType; import com.baomidou.mybatisplus.exceptions.MybatisPlusException; import com.baomidou.mybatisplus.mapper.IKeyGenerator; import com.baomidou.mybatisplus.mapper.SqlRunner; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.executor.keygen.NoKeyGenerator; import org.apache.ibatis.executor.keygen.SelectKeyGenerator; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.mapping.StatementType; import org.apache.ibatis.scripting.LanguageDriver; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactory; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * <p> * 实体类反射表辅助类 * </p> * * @author hubin sjy * @Date 2016-09-09 */ public class TableInfoHelper { private static final Log logger = LogFactory.getLog(TableInfoHelper.class); /** * 缓存反射类表信息 */ private static final Map<String, TableInfo> tableInfoCache = new ConcurrentHashMap<>(); /** * 默认表主键 */ private static final String DEFAULT_ID_NAME = "id"; /** * <p> * 获取实体映射表信息 * <p> * * @param clazz 反射实体类 * @return */ public static TableInfo getTableInfo(Class<?> clazz) { return tableInfoCache.get(clazz.getName()); } /** * <p> * 获取所有实体映射表信息 * <p> * @return */ public static List<TableInfo> getTableInfos() { List<TableInfo> tableInfos = new ArrayList<TableInfo>(); for (Map.Entry<String, TableInfo> entry : tableInfoCache.entrySet()) { tableInfos.add(entry.getValue()); } return tableInfos; } /** * <p> * 实体类反射获取表信息【初始化】 * <p> * * @param clazz 反射实体类 * @return */ public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) { TableInfo ti = tableInfoCache.get(clazz.getName()); if (ti != null) { return ti; } TableInfo tableInfo = new TableInfo(); GlobalConfiguration globalConfig; if (null != builderAssistant) { tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace()); tableInfo.setConfigMark(builderAssistant.getConfiguration()); globalConfig = GlobalConfiguration.getGlobalConfig(builderAssistant.getConfiguration()); } else { // 兼容测试场景 globalConfig = GlobalConfiguration.DEFAULT; } /* 表名 */ TableName table = clazz.getAnnotation(TableName.class); String tableName = clazz.getSimpleName(); if (table != null && StringUtils.isNotEmpty(table.value())) { tableName = table.value(); } else { // 开启字段下划线申明 if (globalConfig.isDbColumnUnderline()) { tableName = StringUtils.camelToUnderline(tableName); } // 大写命名判断 if (globalConfig.isCapitalMode()) { tableName = tableName.toUpperCase(); } else { // 首字母小写 tableName = StringUtils.firstToLowerCase(tableName); } } tableInfo.setTableName(tableName); // 开启了自定义 KEY 生成器 if (null != globalConfig.getKeyGenerator()) { tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class)); } /* 表结果集映射 */ if (table != null && StringUtils.isNotEmpty(table.resultMap())) { tableInfo.setResultMap(table.resultMap()); } List<TableFieldInfo> fieldList = new ArrayList<>(); List<Field> list = getAllFields(clazz); boolean existTableId = existTableId(list); for (Field field : list) { /* * 主键ID 初始化 */ if (existTableId) { if (initTableId(globalConfig, tableInfo, field, clazz)) { continue; } } else if (initFieldId(globalConfig, tableInfo, field, clazz)) { continue; } /* * 字段初始化 */ if (initTableField(globalConfig, tableInfo, fieldList, field, clazz)) { continue; } /* * 字段, 使用 camelToUnderline 转换驼峰写法为下划线分割法, 如果已指定 TableField , 便不会执行这里 */ fieldList.add(new TableFieldInfo(globalConfig, tableInfo, field)); } /* 字段列表 */ tableInfo.setFieldList(globalConfig, fieldList); /* * 未发现主键注解,提示警告信息 */ if (StringUtils.isEmpty(tableInfo.getKeyColumn())) { logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName())); } /* * 注入 */ tableInfoCache.put(clazz.getName(), tableInfo); return tableInfo; } /** * <p> * 判断主键注解是否存在 * </p> * * @param list 字段列表 * @return */ public static boolean existTableId(List<Field> list) { boolean exist = false; for (Field field : list) { TableId tableId = field.getAnnotation(TableId.class); if (tableId != null) { exist = true; break; } } return exist; } /** * <p> * 主键属性初始化 * </p> * * @param tableInfo * @param field * @param clazz * @return true 继续下一个属性判断,返回 continue; */ private static boolean initTableId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) { TableId tableId = field.getAnnotation(TableId.class); if (tableId != null) { if (StringUtils.isEmpty(tableInfo.getKeyColumn())) { /* * 主键策略( 注解 > 全局 > 默认 ) */ // 设置 Sequence 其他策略无效 if (IdType.NONE != tableId.type()) { tableInfo.setIdType(tableId.type()); } else { tableInfo.setIdType(globalConfig.getIdType()); } /* 字段 */ String column = field.getName(); if (StringUtils.isNotEmpty(tableId.value())) { column = tableId.value(); tableInfo.setKeyRelated(true); } else { // 开启字段下划线申明 if (globalConfig.isDbColumnUnderline()) { column = StringUtils.camelToUnderline(column); tableInfo.setKeyRelated(true); } // 全局大写命名 if (globalConfig.isCapitalMode()) { column = column.toUpperCase(); } } tableInfo.setKeyColumn(column); tableInfo.setKeyProperty(field.getName()); return true; } else { throwExceptionId(clazz); } } return false; } /** * <p> * 主键属性初始化 * </p> * * @param tableInfo * @param field * @param clazz * @return true 继续下一个属性判断,返回 continue; */ private static boolean initFieldId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) { String column = field.getName(); if (globalConfig.isCapitalMode()) { column = column.toUpperCase(); } if (DEFAULT_ID_NAME.equalsIgnoreCase(column)) { if (StringUtils.isEmpty(tableInfo.getKeyColumn())) { tableInfo.setIdType(globalConfig.getIdType()); tableInfo.setKeyColumn(column); tableInfo.setKeyProperty(field.getName()); return true; } else { throwExceptionId(clazz); } } return false; } /** * <p> * 发现设置多个主键注解抛出异常 * </p> */ private static void throwExceptionId(Class<?> clazz) { StringBuilder errorMsg = new StringBuilder(); errorMsg.append("There must be only one, Discover multiple @TableId annotation in "); errorMsg.append(clazz.getName()); throw new MybatisPlusException(errorMsg.toString()); } /** * <p> * 字段属性初始化 * </p> * * @param globalConfig 全局配置 * @param tableInfo 表信息 * @param fieldList 字段列表 * @param clazz 当前表对象类 * @return true 继续下一个属性判断,返回 continue; */ private static boolean initTableField(GlobalConfiguration globalConfig, TableInfo tableInfo, List<TableFieldInfo> fieldList, Field field, Class<?> clazz) { /* 获取注解属性,自定义字段 */ TableField tableField = field.getAnnotation(TableField.class); if (tableField != null) { 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(";"); String[] els = el.split(";"); if (columns.length == els.length) { for (int i = 0; i < columns.length; i++) { fieldList.add(new TableFieldInfo(globalConfig, tableInfo, columns[i], els[i], field, tableField)); } } else { String errorMsg = "Class: %s, Field: %s, 'value' 'el' Length must be consistent."; throw new MybatisPlusException(String.format(errorMsg, clazz.getName(), field.getName())); } return true; } return false; } /** * 获取该类的所有属性列表 * * @param clazz 反射类 * @return */ public static List<Field> getAllFields(Class<?> clazz) { List<Field> fieldList = ReflectionKit.getFieldList(clazz); if (CollectionUtils.isNotEmpty(fieldList)) { Iterator<Field> iterator = fieldList.iterator(); while (iterator.hasNext()) { Field field = iterator.next(); /* 过滤注解非表字段属性 */ TableField tableField = field.getAnnotation(TableField.class); if (tableField != null && !tableField.exist()) { iterator.remove(); } } } return fieldList; } /** * 初始化SqlSessionFactory (供Mybatis原生调用) * * @param sqlSessionFactory * @return */ public static void initSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { Configuration configuration = sqlSessionFactory.getConfiguration(); GlobalConfiguration globalConfig = GlobalConfiguration.getGlobalConfig(configuration); // SqlRunner SqlRunner.FACTORY = sqlSessionFactory; if (globalConfig == null) { GlobalConfiguration defaultCache = GlobalConfiguration.defaults(); defaultCache.setSqlSessionFactory(sqlSessionFactory); GlobalConfiguration.setGlobalConfig(configuration, defaultCache); } else { globalConfig.setSqlSessionFactory(sqlSessionFactory); } } /** * <p> * 自定义 KEY 生成器 * </p> */ public static KeyGenerator genKeyGenerator(TableInfo tableInfo, MapperBuilderAssistant builderAssistant, String baseStatementId, LanguageDriver languageDriver) { IKeyGenerator keyGenerator = GlobalConfiguration.getKeyGenerator(builderAssistant.getConfiguration()); if (null == keyGenerator) { throw new IllegalArgumentException("not configure IKeyGenerator implementation class."); } String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX; Class<?> resultTypeClass = tableInfo.getKeySequence().idClazz(); StatementType statementType = StatementType.PREPARED; String keyProperty = tableInfo.getKeyProperty(); String keyColumn = tableInfo.getKeyColumn(); SqlSource sqlSource = languageDriver.createSqlSource(builderAssistant.getConfiguration(), keyGenerator.executeSql(tableInfo), null); builderAssistant.addMappedStatement(id, sqlSource, statementType, SqlCommandType.SELECT, null, null, null, null, null, resultTypeClass, null, false, false, false, new NoKeyGenerator(), keyProperty, keyColumn, null, languageDriver, null); id = builderAssistant.applyCurrentNamespace(id, false); MappedStatement keyStatement = builderAssistant.getConfiguration().getMappedStatement(id, false); SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, true); builderAssistant.getConfiguration().addKeyGenerator(id, answer); return answer; } }