TableInfoHelper.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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(clazz.getName());
  75. }
  76. /**
  77. * <p>
  78. * 获取所有实体映射表信息
  79. * <p>
  80. *
  81. * @return
  82. */
  83. public static List<TableInfo> getTableInfos() {
  84. List<TableInfo> tableInfos = new ArrayList<>();
  85. for (Map.Entry<String, TableInfo> entry : tableInfoCache.entrySet()) {
  86. tableInfos.add(entry.getValue());
  87. }
  88. return tableInfos;
  89. }
  90. /**
  91. * <p>
  92. * 实体类反射获取表信息【初始化】
  93. * <p>
  94. *
  95. * @param clazz 反射实体类
  96. * @return
  97. */
  98. public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
  99. TableInfo tableInfo = tableInfoCache.get(clazz.getName());
  100. if (StringUtils.checkValNotNull(tableInfo)) {
  101. if (StringUtils.checkValNotNull(builderAssistant)) {
  102. tableInfo.setConfigMark(builderAssistant.getConfiguration());
  103. }
  104. return tableInfo;
  105. }
  106. tableInfo = new TableInfo();
  107. GlobalConfiguration globalConfig;
  108. if (null != builderAssistant) {
  109. tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
  110. tableInfo.setConfigMark(builderAssistant.getConfiguration());
  111. globalConfig = GlobalConfiguration.getGlobalConfig(builderAssistant.getConfiguration());
  112. } else {
  113. // 兼容测试场景
  114. globalConfig = GlobalConfiguration.DEFAULT;
  115. }
  116. /* 表名 */
  117. TableName table = clazz.getAnnotation(TableName.class);
  118. String tableName = clazz.getSimpleName();
  119. if (table != null && StringUtils.isNotEmpty(table.value())) {
  120. tableName = table.value();
  121. } else {
  122. // 开启字段下划线申明
  123. if (globalConfig.isDbColumnUnderline()) {
  124. tableName = StringUtils.camelToUnderline(tableName);
  125. }
  126. // 大写命名判断
  127. if (globalConfig.isCapitalMode()) {
  128. tableName = tableName.toUpperCase();
  129. } else {
  130. // 首字母小写
  131. tableName = StringUtils.firstToLowerCase(tableName);
  132. }
  133. }
  134. tableInfo.setTableName(tableName);
  135. // 开启了自定义 KEY 生成器
  136. if (null != globalConfig.getKeyGenerator()) {
  137. tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
  138. }
  139. /* 表结果集映射 */
  140. if (table != null && StringUtils.isNotEmpty(table.resultMap())) {
  141. tableInfo.setResultMap(table.resultMap());
  142. }
  143. List<TableFieldInfo> fieldList = new ArrayList<>();
  144. List<Field> list = getAllFields(clazz);
  145. // 标记是否读取到主键
  146. boolean isReadPK = false;
  147. boolean existTableId = existTableId(list);
  148. for (Field field : list) {
  149. /*
  150. * 主键ID 初始化
  151. */
  152. if (!isReadPK) {
  153. if (existTableId) {
  154. if (initTableId(globalConfig, tableInfo, field, clazz)) {
  155. continue;
  156. }
  157. } else if (initFieldId(globalConfig, tableInfo, field, clazz)) {
  158. continue;
  159. }
  160. isReadPK = true;
  161. }
  162. /*
  163. * 字段初始化
  164. */
  165. if (initTableField(globalConfig, tableInfo, fieldList, field, clazz)) {
  166. continue;
  167. }
  168. /*
  169. * 字段, 使用 camelToUnderline 转换驼峰写法为下划线分割法, 如果已指定 TableField , 便不会执行这里
  170. */
  171. fieldList.add(new TableFieldInfo(globalConfig, tableInfo, field));
  172. }
  173. /* 字段列表 */
  174. tableInfo.setFieldList(globalConfig, fieldList);
  175. /*
  176. * 未发现主键注解,提示警告信息
  177. */
  178. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  179. logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));
  180. }
  181. /*
  182. * 注入
  183. */
  184. tableInfoCache.put(clazz.getName(), tableInfo);
  185. return tableInfo;
  186. }
  187. /**
  188. * <p>
  189. * 判断主键注解是否存在
  190. * </p>
  191. *
  192. * @param list 字段列表
  193. * @return
  194. */
  195. public static boolean existTableId(List<Field> list) {
  196. boolean exist = false;
  197. for (Field field : list) {
  198. TableId tableId = field.getAnnotation(TableId.class);
  199. if (tableId != null) {
  200. exist = true;
  201. break;
  202. }
  203. }
  204. return exist;
  205. }
  206. /**
  207. * <p>
  208. * 主键属性初始化
  209. * </p>
  210. *
  211. * @param tableInfo
  212. * @param field
  213. * @param clazz
  214. * @return true 继续下一个属性判断,返回 continue;
  215. */
  216. private static boolean initTableId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {
  217. TableId tableId = field.getAnnotation(TableId.class);
  218. if (tableId != null) {
  219. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  220. /*
  221. * 主键策略( 注解 > 全局 > 默认 )
  222. */
  223. // 设置 Sequence 其他策略无效
  224. if (IdType.NONE != tableId.type()) {
  225. tableInfo.setIdType(tableId.type());
  226. } else {
  227. tableInfo.setIdType(globalConfig.getIdType());
  228. }
  229. /* 字段 */
  230. String column = field.getName();
  231. if (StringUtils.isNotEmpty(tableId.value())) {
  232. column = tableId.value();
  233. tableInfo.setKeyRelated(true);
  234. } else {
  235. // 开启字段下划线申明
  236. if (globalConfig.isDbColumnUnderline()) {
  237. column = StringUtils.camelToUnderline(column);
  238. tableInfo.setKeyRelated(true);
  239. }
  240. // 全局大写命名
  241. if (globalConfig.isCapitalMode()) {
  242. column = column.toUpperCase();
  243. }
  244. }
  245. tableInfo.setKeyColumn(column);
  246. tableInfo.setKeyProperty(field.getName());
  247. return true;
  248. } else {
  249. throwExceptionId(clazz);
  250. }
  251. }
  252. return false;
  253. }
  254. /**
  255. * <p>
  256. * 主键属性初始化
  257. * </p>
  258. *
  259. * @param tableInfo
  260. * @param field
  261. * @param clazz
  262. * @return true 继续下一个属性判断,返回 continue;
  263. */
  264. private static boolean initFieldId(GlobalConfiguration globalConfig, TableInfo tableInfo, Field field, Class<?> clazz) {
  265. String column = field.getName();
  266. if (globalConfig.isCapitalMode()) {
  267. column = column.toUpperCase();
  268. }
  269. if (DEFAULT_ID_NAME.equalsIgnoreCase(column)) {
  270. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  271. tableInfo.setIdType(globalConfig.getIdType());
  272. tableInfo.setKeyColumn(column);
  273. tableInfo.setKeyProperty(field.getName());
  274. return true;
  275. } else {
  276. throwExceptionId(clazz);
  277. }
  278. }
  279. return false;
  280. }
  281. /**
  282. * <p>
  283. * 发现设置多个主键注解抛出异常
  284. * </p>
  285. */
  286. private static void throwExceptionId(Class<?> clazz) {
  287. StringBuilder errorMsg = new StringBuilder();
  288. errorMsg.append("There must be only one, Discover multiple @TableId annotation in ");
  289. errorMsg.append(clazz.getName());
  290. throw new MybatisPlusException(errorMsg.toString());
  291. }
  292. /**
  293. * <p>
  294. * 字段属性初始化
  295. * </p>
  296. *
  297. * @param globalConfig 全局配置
  298. * @param tableInfo 表信息
  299. * @param fieldList 字段列表
  300. * @param clazz 当前表对象类
  301. * @return true 继续下一个属性判断,返回 continue;
  302. */
  303. private static boolean initTableField(GlobalConfiguration globalConfig, TableInfo tableInfo, List<TableFieldInfo> fieldList,
  304. Field field, Class<?> clazz) {
  305. /* 获取注解属性,自定义字段 */
  306. TableField tableField = field.getAnnotation(TableField.class);
  307. if (tableField != null) {
  308. String columnName = field.getName();
  309. if (StringUtils.isNotEmpty(tableField.value())) {
  310. columnName = tableField.value();
  311. }
  312. /*
  313. * el 语法支持,可以传入多个参数以逗号分开
  314. */
  315. String el = field.getName();
  316. if (StringUtils.isNotEmpty(tableField.el())) {
  317. el = tableField.el();
  318. }
  319. String[] columns = columnName.split(";");
  320. String[] els = el.split(";");
  321. if (columns.length == els.length) {
  322. for (int i = 0; i < columns.length; i++) {
  323. fieldList.add(new TableFieldInfo(globalConfig, tableInfo, columns[i], els[i], field, tableField));
  324. }
  325. } else {
  326. String errorMsg = "Class: %s, Field: %s, 'value' 'el' Length must be consistent.";
  327. throw new MybatisPlusException(String.format(errorMsg, clazz.getName(), field.getName()));
  328. }
  329. return true;
  330. }
  331. return false;
  332. }
  333. /**
  334. * 获取该类的所有属性列表
  335. *
  336. * @param clazz 反射类
  337. * @return
  338. */
  339. public static List<Field> getAllFields(Class<?> clazz) {
  340. List<Field> fieldList = ReflectionKit.getFieldList(clazz);
  341. if (CollectionUtils.isNotEmpty(fieldList)) {
  342. Iterator<Field> iterator = fieldList.iterator();
  343. while (iterator.hasNext()) {
  344. Field field = iterator.next();
  345. /* 过滤注解非表字段属性 */
  346. TableField tableField = field.getAnnotation(TableField.class);
  347. if (tableField != null && !tableField.exist()) {
  348. iterator.remove();
  349. }
  350. }
  351. }
  352. return fieldList;
  353. }
  354. /**
  355. * 初始化SqlSessionFactory (供Mybatis原生调用)
  356. *
  357. * @param sqlSessionFactory
  358. * @return
  359. */
  360. public static void initSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
  361. Configuration configuration = sqlSessionFactory.getConfiguration();
  362. GlobalConfiguration globalConfig = GlobalConfiguration.getGlobalConfig(configuration);
  363. // SqlRunner
  364. SqlRunner.FACTORY = sqlSessionFactory;
  365. if (globalConfig == null) {
  366. GlobalConfiguration defaultCache = GlobalConfiguration.defaults();
  367. defaultCache.setSqlSessionFactory(sqlSessionFactory);
  368. GlobalConfiguration.setGlobalConfig(configuration, defaultCache);
  369. } else {
  370. globalConfig.setSqlSessionFactory(sqlSessionFactory);
  371. }
  372. }
  373. /**
  374. * <p>
  375. * 自定义 KEY 生成器
  376. * </p>
  377. */
  378. public static KeyGenerator genKeyGenerator(TableInfo tableInfo, MapperBuilderAssistant builderAssistant,
  379. String baseStatementId, LanguageDriver languageDriver) {
  380. IKeyGenerator keyGenerator = GlobalConfiguration.getKeyGenerator(builderAssistant.getConfiguration());
  381. if (null == keyGenerator) {
  382. throw new IllegalArgumentException("not configure IKeyGenerator implementation class.");
  383. }
  384. String id = baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  385. Class<?> resultTypeClass = tableInfo.getKeySequence().idClazz();
  386. StatementType statementType = StatementType.PREPARED;
  387. String keyProperty = tableInfo.getKeyProperty();
  388. String keyColumn = tableInfo.getKeyColumn();
  389. SqlSource sqlSource = languageDriver.createSqlSource(builderAssistant.getConfiguration(),
  390. keyGenerator.executeSql(tableInfo.getKeySequence().value()), null);
  391. builderAssistant.addMappedStatement(id, sqlSource, statementType, SqlCommandType.SELECT, null, null, null,
  392. null, null, resultTypeClass, null, false, false, false,
  393. new NoKeyGenerator(), keyProperty, keyColumn, null, languageDriver, null);
  394. id = builderAssistant.applyCurrentNamespace(id, false);
  395. MappedStatement keyStatement = builderAssistant.getConfiguration().getMappedStatement(id, false);
  396. SelectKeyGenerator selectKeyGenerator = new SelectKeyGenerator(keyStatement, true);
  397. builderAssistant.getConfiguration().addKeyGenerator(id, selectKeyGenerator);
  398. return selectKeyGenerator;
  399. }
  400. }