TableInfoHelper.java 16 KB

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