TableInfoHelper.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /**
  2. * Copyright (c) 2011-2020, hubin (jobob@qq.com).
  3. * <p>
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. * <p>
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. * <p>
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.baomidou.mybatisplus.toolkit;
  17. import java.lang.reflect.Field;
  18. import java.util.ArrayList;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.concurrent.ConcurrentHashMap;
  23. import org.apache.ibatis.builder.MapperBuilderAssistant;
  24. import org.apache.ibatis.executor.keygen.KeyGenerator;
  25. import org.apache.ibatis.executor.keygen.NoKeyGenerator;
  26. import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
  27. import org.apache.ibatis.logging.Log;
  28. import org.apache.ibatis.logging.LogFactory;
  29. import org.apache.ibatis.mapping.MappedStatement;
  30. import org.apache.ibatis.mapping.SqlCommandType;
  31. import org.apache.ibatis.mapping.SqlSource;
  32. import org.apache.ibatis.mapping.StatementType;
  33. import org.apache.ibatis.scripting.LanguageDriver;
  34. import org.apache.ibatis.session.Configuration;
  35. import org.apache.ibatis.session.SqlSessionFactory;
  36. import com.baomidou.mybatisplus.annotations.KeySequence;
  37. import com.baomidou.mybatisplus.annotations.TableField;
  38. import com.baomidou.mybatisplus.annotations.TableId;
  39. import com.baomidou.mybatisplus.annotations.TableName;
  40. import com.baomidou.mybatisplus.entity.GlobalConfiguration;
  41. import com.baomidou.mybatisplus.entity.TableFieldInfo;
  42. import com.baomidou.mybatisplus.entity.TableInfo;
  43. import com.baomidou.mybatisplus.enums.IdType;
  44. import com.baomidou.mybatisplus.exceptions.MybatisPlusException;
  45. import com.baomidou.mybatisplus.incrementer.IKeyGenerator;
  46. import com.baomidou.mybatisplus.mapper.SqlRunner;
  47. /**
  48. * <p>
  49. * 实体类反射表辅助类
  50. * </p>
  51. *
  52. * @author hubin sjy
  53. * @Date 2016-09-09
  54. */
  55. public class TableInfoHelper {
  56. private static final Log logger = LogFactory.getLog(TableInfoHelper.class);
  57. /**
  58. * 缓存反射类表信息
  59. */
  60. private static final Map<String, TableInfo> tableInfoCache = new ConcurrentHashMap<>();
  61. /**
  62. * 默认表主键
  63. */
  64. private static final String DEFAULT_ID_NAME = "id";
  65. /**
  66. * <p>
  67. * 获取实体映射表信息
  68. * <p>
  69. *
  70. * @param clazz 反射实体类
  71. * @return
  72. */
  73. public static TableInfo getTableInfo(Class<?> clazz) {
  74. return tableInfoCache.get(ClassUtils.getUserClass(clazz).getName());
  75. }
  76. /**
  77. * <p>
  78. * 获取所有实体映射表信息
  79. * <p>
  80. *
  81. * @return
  82. */
  83. public static List<TableInfo> getTableInfos() {
  84. return new ArrayList<>(tableInfoCache.values());
  85. }
  86. /**
  87. * <p>
  88. * 实体类反射获取表信息【初始化】
  89. * <p>
  90. *
  91. * @param clazz 反射实体类
  92. * @return
  93. */
  94. public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
  95. TableInfo tableInfo = tableInfoCache.get(clazz.getName());
  96. if (StringUtils.checkValNotNull(tableInfo)) {
  97. if (StringUtils.checkValNotNull(builderAssistant)) {
  98. tableInfo.setConfigMark(builderAssistant.getConfiguration());
  99. }
  100. return tableInfo;
  101. }
  102. tableInfo = new TableInfo();
  103. GlobalConfiguration globalConfig;
  104. if (null != builderAssistant) {
  105. tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
  106. tableInfo.setConfigMark(builderAssistant.getConfiguration());
  107. globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
  108. } else {
  109. // 兼容测试场景
  110. globalConfig = GlobalConfigUtils.DEFAULT;
  111. }
  112. /* 表名 */
  113. TableName table = clazz.getAnnotation(TableName.class);
  114. String tableName = clazz.getSimpleName();
  115. if (table != null && StringUtils.isNotEmpty(table.value())) {
  116. tableName = table.value();
  117. } else {
  118. // 开启字段下划线申明
  119. if (globalConfig.isDbColumnUnderline()) {
  120. tableName = StringUtils.camelToUnderline(tableName);
  121. }
  122. // 大写命名判断
  123. if (globalConfig.isCapitalMode()) {
  124. tableName = tableName.toUpperCase();
  125. } else {
  126. // 首字母小写
  127. tableName = StringUtils.firstToLowerCase(tableName);
  128. }
  129. }
  130. tableInfo.setTableName(tableName);
  131. // 开启了自定义 KEY 生成器
  132. if (null != globalConfig.getKeyGenerator()) {
  133. tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
  134. }
  135. /* 表结果集映射 */
  136. if (table != null && StringUtils.isNotEmpty(table.resultMap())) {
  137. tableInfo.setResultMap(table.resultMap());
  138. }
  139. List<TableFieldInfo> fieldList = new ArrayList<>();
  140. List<Field> list = getAllFields(clazz);
  141. // 标记是否读取到主键
  142. boolean isReadPK = false;
  143. boolean existTableId = existTableId(list);
  144. for (Field field : list) {
  145. /*
  146. * 主键ID 初始化
  147. */
  148. if (!isReadPK) {
  149. if (existTableId) {
  150. isReadPK = initTableId(globalConfig, tableInfo, field, clazz);
  151. } else {
  152. isReadPK = initFieldId(globalConfig, tableInfo, field, clazz);
  153. }
  154. if (isReadPK) {
  155. continue;
  156. }
  157. }
  158. /*
  159. * 字段初始化
  160. */
  161. if (initTableField(globalConfig, tableInfo, fieldList, field, clazz)) {
  162. continue;
  163. }
  164. /*
  165. * 字段, 使用 camelToUnderline 转换驼峰写法为下划线分割法, 如果已指定 TableField , 便不会执行这里
  166. */
  167. fieldList.add(new TableFieldInfo(globalConfig, tableInfo, field));
  168. }
  169. /* 字段列表 */
  170. tableInfo.setFieldList(globalConfig, fieldList);
  171. /*
  172. * 未发现主键注解,提示警告信息
  173. */
  174. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  175. logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));
  176. }
  177. /*
  178. * 注入
  179. */
  180. tableInfoCache.put(clazz.getName(), tableInfo);
  181. return tableInfo;
  182. }
  183. /**
  184. * <p>
  185. * 判断主键注解是否存在
  186. * </p>
  187. *
  188. * @param list 字段列表
  189. * @return
  190. */
  191. public static boolean existTableId(List<Field> list) {
  192. boolean exist = false;
  193. for (Field field : list) {
  194. TableId tableId = field.getAnnotation(TableId.class);
  195. if (tableId != null) {
  196. exist = true;
  197. break;
  198. }
  199. }
  200. return exist;
  201. }
  202. /**
  203. * <p>
  204. * 主键属性初始化
  205. * </p>
  206. *
  207. * @param tableInfo
  208. * @param field
  209. * @param clazz
  210. * @return true 继续下一个属性判断,返回 continue;
  211. */
  212. private static boolean initTableId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {
  213. TableId tableId = field.getAnnotation(TableId.class);
  214. if (tableId != null) {
  215. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  216. /*
  217. * 主键策略( 注解 > 全局 > 默认 )
  218. */
  219. // 设置 Sequence 其他策略无效
  220. if (IdType.NONE != tableId.type()) {
  221. tableInfo.setIdType(tableId.type());
  222. } else {
  223. tableInfo.setIdType(globalConfig.getIdType());
  224. }
  225. /* 字段 */
  226. String column = field.getName();
  227. if (StringUtils.isNotEmpty(tableId.value())) {
  228. column = tableId.value();
  229. tableInfo.setKeyRelated(true);
  230. } else {
  231. // 开启字段下划线申明
  232. if (globalConfig.isDbColumnUnderline()) {
  233. column = StringUtils.camelToUnderline(column);
  234. tableInfo.setKeyRelated(true);
  235. }
  236. // 全局大写命名
  237. if (globalConfig.isCapitalMode()) {
  238. column = column.toUpperCase();
  239. }
  240. }
  241. tableInfo.setKeyColumn(column);
  242. tableInfo.setKeyProperty(field.getName());
  243. return true;
  244. } else {
  245. throwExceptionId(clazz);
  246. }
  247. }
  248. return false;
  249. }
  250. /**
  251. * <p>
  252. * 主键属性初始化
  253. * </p>
  254. *
  255. * @param tableInfo
  256. * @param field
  257. * @param clazz
  258. * @return true 继续下一个属性判断,返回 continue;
  259. */
  260. private static boolean initFieldId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {
  261. String column = field.getName();
  262. if (globalConfig.isCapitalMode()) {
  263. column = column.toUpperCase();
  264. }
  265. if (DEFAULT_ID_NAME.equalsIgnoreCase(column)) {
  266. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  267. tableInfo.setIdType(globalConfig.getIdType());
  268. tableInfo.setKeyColumn(column);
  269. tableInfo.setKeyProperty(field.getName());
  270. return true;
  271. } else {
  272. throwExceptionId(clazz);
  273. }
  274. }
  275. return false;
  276. }
  277. /**
  278. * <p>
  279. * 发现设置多个主键注解抛出异常
  280. * </p>
  281. */
  282. private static void throwExceptionId(Class<?> clazz) {
  283. StringBuilder errorMsg = new StringBuilder();
  284. errorMsg.append("There must be only one, Discover multiple @TableId annotation in ");
  285. errorMsg.append(clazz.getName());
  286. throw new MybatisPlusException(errorMsg.toString());
  287. }
  288. /**
  289. * <p>
  290. * 字段属性初始化
  291. * </p>
  292. *
  293. * @param globalConfig 全局配置
  294. * @param tableInfo 表信息
  295. * @param fieldList 字段列表
  296. * @param clazz 当前表对象类
  297. * @return true 继续下一个属性判断,返回 continue;
  298. */
  299. private static boolean initTableField(GlobalConfiguration globalConfig, TableInfo tableInfo, List<TableFieldInfo> fieldList,
  300. Field field, Class<?> clazz) {
  301. /* 获取注解属性,自定义字段 */
  302. TableField tableField = field.getAnnotation(TableField.class);
  303. if (tableField != null) {
  304. String columnName = field.getName();
  305. if (StringUtils.isNotEmpty(tableField.value())) {
  306. columnName = tableField.value();
  307. }
  308. /*
  309. * el 语法支持,可以传入多个参数以逗号分开
  310. */
  311. String el = field.getName();
  312. if (StringUtils.isNotEmpty(tableField.el())) {
  313. el = tableField.el();
  314. }
  315. String[] columns = columnName.split(";");
  316. String[] els = el.split(";");
  317. if (columns.length == els.length) {
  318. for (int i = 0; i < columns.length; i++) {
  319. fieldList.add(new TableFieldInfo(globalConfig, tableInfo, columns[i], els[i], field, tableField));
  320. }
  321. } else {
  322. String errorMsg = "Class: %s, Field: %s, 'value' 'el' Length must be consistent.";
  323. throw new MybatisPlusException(String.format(errorMsg, clazz.getName(), field.getName()));
  324. }
  325. return true;
  326. }
  327. return false;
  328. }
  329. /**
  330. * 获取该类的所有属性列表
  331. *
  332. * @param clazz 反射类
  333. * @return
  334. */
  335. public static List<Field> getAllFields(Class<?> clazz) {
  336. List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
  337. if (CollectionUtils.isNotEmpty(fieldList)) {
  338. Iterator<Field> iterator = fieldList.iterator();
  339. while (iterator.hasNext()) {
  340. Field field = iterator.next();
  341. /* 过滤注解非表字段属性 */
  342. TableField tableField = field.getAnnotation(TableField.class);
  343. if (tableField != null && !tableField.exist()) {
  344. iterator.remove();
  345. }
  346. }
  347. }
  348. return fieldList;
  349. }
  350. /**
  351. * 初始化SqlSessionFactory (供Mybatis原生调用)
  352. *
  353. * @param sqlSessionFactory
  354. * @return
  355. */
  356. public static void initSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  357. Configuration configuration = sqlSessionFactory.getConfiguration();
  358. GlobalConfiguration globalConfig = GlobalConfigUtils.getGlobalConfig(configuration);
  359. // SqlRunner
  360. SqlRunner.FACTORY = sqlSessionFactory;
  361. if (globalConfig == null) {
  362. GlobalConfiguration defaultCache = GlobalConfigUtils.defaults();
  363. defaultCache.setSqlSessionFactory(sqlSessionFactory);
  364. GlobalConfigUtils.setGlobalConfig(configuration, defaultCache);
  365. } else {
  366. globalConfig.setSqlSessionFactory(sqlSessionFactory);
  367. }
  368. }
  369. /**
  370. * <p>
  371. * 自定义 KEY 生成器
  372. * </p>
  373. */
  374. public static KeyGenerator genKeyGenerator(TableInfo tableInfo, MapperBuilderAssistant builderAssistant,
  375. String baseStatementId, LanguageDriver languageDriver) {
  376. IKeyGenerator keyGenerator = GlobalConfigUtils.getKeyGenerator(builderAssistant.getConfiguration());
  377. if (null == keyGenerator) {
  378. throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");
  379. }
  380. String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  381. Class<?> resultTypeClass = tableInfo.getKeySequence().clazz();
  382. StatementType statementType = StatementType.PREPARED;
  383. String keyProperty = tableInfo.getKeyProperty();
  384. String keyColumn = tableInfo.getKeyColumn();
  385. SqlSource sqlSource = languageDriver.createSqlSource(builderAssistant.getConfiguration(),
  386. keyGenerator.executeSql(tableInfo.getKeySequence().value()), null);
  387. builderAssistant.addMappedStatement(id, sqlSource, statementType, SqlCommandType.SELECT, null, null, null,
  388. null, null, resultTypeClass, null, false, false, false,
  389. new NoKeyGenerator(), keyProperty, keyColumn, null, languageDriver, null);
  390. id = builderAssistant.applyCurrentNamespace(id, false);
  391. MappedStatement keyStatement = builderAssistant.getConfiguration().getMappedStatement(id, false);
  392. SelectKeyGenerator selectKeyGenerator = new SelectKeyGenerator(keyStatement, true);
  393. builderAssistant.getConfiguration().addKeyGenerator(id, selectKeyGenerator);
  394. return selectKeyGenerator;
  395. }
  396. }