在前面 的 inject() 方法中, 调用了一个 TableInfoHelper.initTableInfo(builderAssistant, modelClass) 方法, 来获取 表信息: TableInfo

  1. /**
  2. * <p>
  3. * 实体类反射获取表信息【初始化】
  4. * <p>
  5. *
  6. * @param clazz 反射实体类
  7. * @return 数据库表反射信息
  8. */
  9. public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
  10. TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);
  11. if (tableInfo != null) {
  12. if (tableInfo.getConfigMark() == null && builderAssistant != null) {
  13. tableInfo.setConfigMark(builderAssistant.getConfiguration());
  14. }
  15. return tableInfo;
  16. }
  17.  
  18. /* 没有获取到缓存信息,则初始化 */
  19. tableInfo = new TableInfo();
  20. GlobalConfig globalConfig;
  21. if (null != builderAssistant) {
  22. tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
  23. tableInfo.setConfigMark(builderAssistant.getConfiguration());
  24. tableInfo.setUnderCamel(builderAssistant.getConfiguration().isMapUnderscoreToCamelCase());
  25. globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
  26. } else {
  27. // 兼容测试场景
  28. globalConfig = GlobalConfigUtils.defaults();
  29. }
  30.  
  31. /* 初始化表名相关 */
  32. initTableName(clazz, globalConfig, tableInfo);
  33.  
  34. /* 初始化字段相关 */
  35. initTableFields(clazz, globalConfig, tableInfo);
  36.  
  37. /* 放入缓存 */
  38. TABLE_INFO_CACHE.put(clazz, tableInfo);
  39.  
  40. /* 缓存 Lambda 映射关系 */
  41. LambdaUtils.createCache(clazz, tableInfo);
  42. return tableInfo;
  43. }

是不是还是自己人写的代码看起来爽? 这中文注释, 都不用看方法具体是干啥的.

这里的  TABLE_INFO_CACHE  是用来缓存表信息的:

  1. /**
  2. * 储存反射类表信息
  3. */
  4. private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();

第一次进这个方法的时候, 肯定是空的, 要去解析获取.

1. initTableName()

  1. /**
  2. * <p>
  3. * 初始化 表数据库类型,表名,resultMap
  4. * </p>
  5. *
  6. * @param clazz 实体类
  7. * @param globalConfig 全局配置
  8. * @param tableInfo 数据库表反射信息
  9. */
  10. public static void initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
  11. /* 数据库全局配置 */
  12. GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
  13. /* 设置数据库类型 */
  14. tableInfo.setDbType(dbConfig.getDbType());
  15.  
  16. /* 设置表名 */
  17. TableName table = clazz.getAnnotation(TableName.class);
  18. String tableName = clazz.getSimpleName();
  19. if (table != null && StringUtils.isNotEmpty(table.value())) {
  20. tableName = table.value();
  21. } else {
  22. // 开启表名下划线申明
  23. if (dbConfig.isTableUnderline()) {
  24. tableName = StringUtils.camelToUnderline(tableName);
  25. }
  26. // 大写命名判断
  27. if (dbConfig.isCapitalMode()) {
  28. tableName = tableName.toUpperCase();
  29. } else {
  30. // 首字母小写
  31. tableName = StringUtils.firstToLowerCase(tableName);
  32. }
  33. // 存在表名前缀
  34. if (null != dbConfig.getTablePrefix()) {
  35. tableName = dbConfig.getTablePrefix() + tableName;
  36. }
  37. }
  38. tableInfo.setTableName(tableName);
  39.  
  40. /* 表结果集映射 */
  41. if (table != null && StringUtils.isNotEmpty(table.resultMap())) {
  42. tableInfo.setResultMap(table.resultMap());
  43. }
  44.  
  45. /* 开启了自定义 KEY 生成器 */
  46. if (null != dbConfig.getKeyGenerator()) {
  47. tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
  48. }
  49. }

判断逻辑:

1. 判断实体类上面有没有 TableName 注解

  |-> 如果有, 则拿注解里面配置的 value 作为表名

  |-> 如果没有, 则根据类名进行解析

2. initTableFields()

  1. /**
  2. * <p>
  3. * 初始化 表主键,表字段
  4. * </p>
  5. *
  6. * @param clazz 实体类
  7. * @param globalConfig 全局配置
  8. * @param tableInfo 数据库表反射信息
  9. */
  10. public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
  11. /* 数据库全局配置 */
  12. GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
  13. List<Field> list = getAllFields(clazz);
  14. // 标记是否读取到主键
  15. boolean isReadPK = false;
  16. // 是否存在 @TableId 注解
  17. boolean existTableId = isExistTableId(list);
  18.  
  19. List<TableFieldInfo> fieldList = new ArrayList<>();
  20. for (Field field : list) {
  21. /*
  22. * 主键ID 初始化
  23. */
  24. if (!isReadPK) {
  25. if (existTableId) {
  26. isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, clazz);
  27. } else {
  28. isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, clazz);
  29. }
  30. if (isReadPK) {
  31. continue;
  32. }
  33. }
  34. /* 有 @TableField 注解的字段初始化 */
  35. if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field, clazz)) {
  36. continue;
  37. }
  38.  
  39. /* 无 @TableField 注解的字段初始化 */
  40. fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));
  41. }
  42.  
  43. /* 检查逻辑删除字段只能有最多一个 */
  44. Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L,
  45. String.format("annotation of @TableLogic can't more than one in class : %s.", clazz.getName()));
  46.  
  47. /* 字段列表 */
  48. tableInfo.setFieldList(fieldList);
  49.  
  50. /* 未发现主键注解,提示警告信息 */
  51. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  52. logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));
  53. }
  54. }

2.1 getAllFields()

  1. /**
  2. * 获取该类的所有属性列表
  3. *
  4. * @param clazz 反射类
  5. * @return 属性集合
  6. */
  7. public static List<Field> getAllFields(Class<?> clazz) {
  8. List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
  9. if (CollectionUtils.isNotEmpty(fieldList)) {
  10. return fieldList.stream()
  11. .filter(i -> {
  12. /* 过滤注解非表字段属性 */
  13. TableField tableField = i.getAnnotation(TableField.class);
  14. return (tableField == null || tableField.exist());
  15. }).collect(toList());
  16. }
  17. return fieldList;
  18. }

如果字段上面加了 TableField 注解, 如果有则进行特殊处理. 如果配置了 exist=false, 则这个字段, 过滤掉, 不参与sql生成.

2.2 initTableIdWithAnnotation()

如果实体类中有 TableId 注解, 则进入此方法, 一般情况下, 最好是配一下 TableId

  1. /**
  2. * <p>
  3. * 主键属性初始化
  4. * </p>
  5. *
  6. * @param dbConfig 全局配置信息
  7. * @param tableInfo 表信息
  8. * @param field 字段
  9. * @param clazz 实体类
  10. * @return true 继续下一个属性判断,返回 continue;
  11. */
  12. private static boolean initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
  13. Field field, Class<?> clazz) {
  14. TableId tableId = field.getAnnotation(TableId.class);
  15. boolean underCamel = tableInfo.isUnderCamel();
  16. if (tableId != null) {
  17. if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
  18. /* 主键策略( 注解 > 全局 ) */
  19. // 设置 Sequence 其他策略无效
  20. if (IdType.NONE == tableId.type()) {
  21. tableInfo.setIdType(dbConfig.getIdType());
  22. } else {
  23. tableInfo.setIdType(tableId.type());
  24. }
  25.  
  26. /* 字段 */
  27. String column = field.getName();
  28. if (StringUtils.isNotEmpty(tableId.value())) {
  29. column = tableId.value();
  30. } else {
  31. // 开启字段下划线申明
  32. if (underCamel) {
  33. column = StringUtils.camelToUnderline(column);
  34. }
  35. // 全局大写命名
  36. if (dbConfig.isCapitalMode()) {
  37. column = column.toUpperCase();
  38. }
  39. }
  40. tableInfo.setKeyRelated(checkRelated(underCamel, field.getName(), column))
  41. .setClazz(field.getDeclaringClass())
  42. .setKeyColumn(column)
  43. .setKeyProperty(field.getName());
  44. return true;
  45. } else {
  46. throwExceptionId(clazz);
  47. }
  48. }
  49. return false;
  50. }

2.3 initTableFieldWithAnnotation()

这里对 TableField 进行解析赋值. 如  value 解析成 字段名称

  1. /**
  2. * <p>
  3. * 字段属性初始化
  4. * </p>
  5. *
  6. * @param dbConfig 数据库全局配置
  7. * @param tableInfo 表信息
  8. * @param fieldList 字段列表
  9. * @param clazz 当前表对象类
  10. * @return true 继续下一个属性判断,返回 continue;
  11. */
  12. private static boolean initTableFieldWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
  13. List<TableFieldInfo> fieldList, Field field, Class<?> clazz) {
  14. /* 获取注解属性,自定义字段 */
  15. TableField tableField = field.getAnnotation(TableField.class);
  16. if (null == tableField) {
  17. return false;
  18. }
  19. String columnName = field.getName();
  20. if (StringUtils.isNotEmpty(tableField.value())) {
  21. columnName = tableField.value();
  22. }
  23. /*
  24. * el 语法支持,可以传入多个参数以逗号分开
  25. */
  26. String el = field.getName();
  27. if (StringUtils.isNotEmpty(tableField.el())) {
  28. el = tableField.el();
  29. }
  30. String[] columns = columnName.split(StringPool.SEMICOLON);
  31. String[] els = el.split(StringPool.SEMICOLON);
  32. if (columns.length == els.length) {
  33. for (int i = 0; i < columns.length; i++) {
  34. fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, columns[i], els[i], tableField));
  35. }
  36. return true;
  37. }
  38. throw ExceptionUtils.mpe(String.format("Class: %s, Field: %s, 'value' 'el' Length must be consistent.",
  39. clazz.getName(), field.getName()));
  40. }

mybatis-plus - TableInfo的更多相关文章

  1. spring-cloud集成mybatis-plus

    mybatis-plus插件是对mybatis做出系列增强插件,后面简称MP,MP可免去开发者重复编写xml.mapper.service.entity等代码,通过MP提供的实体注解来完成单表的CRU ...

  2. Mybatis-Plus 代码生成器基本使用

    Mybatis-Plus 代码生成器基本使用 参考 https://mp.baomidou.com/guide/generator.html#%E4%BD%BF%E7%94%A8%E6%95%99%E ...

  3. spring boot整合mybatis+mybatis-plus

    Spring boot对于我来说是一个刚接触的新东西,学习过程中,发现这东西还是很容易上手的,Spring boot没配置时会默认使用Spring data jpa,这东西可以说一个极简洁的工具,可是 ...

  4. 使用mybatis plus自动生成controller、service、dao、mapper、entity代码

    官网:http://mp.baomidou.com(这个项目不仅仅可以用于代码生成,还有分页等其他功能,是对mybatis的一层封装) 要求:基于sql自动生成domain.controller.se ...

  5. Mybatis Plus启动注入 SQL 原理分析

    1) 问题: xxxMapper 继承了 BaseMapper<T>, BaseMapper 中提供了通用的 CRUD 方法, 方法来源于 BaseMapper, 有方法就必须有 SQL, ...

  6. 步步截图的SMM框架入门实战指引(SpringBoot、Mybatis Plus、Maven)

    前提是对spring.springmvc.mybatis有初步学习和理解,因为要全部讲这些框架的知识点太多了,自己学习,这里是实战示范(大部分人学了知识之后去实战会出现很多问题,所以出此教程) 开发环 ...

  7. Mybatis - plus 配置与运用

    Mybatis - plus mybatis-plus 官方文档  1.配置 引入对应的文件包,spring boot + mybatis 需添加依赖文件如下: <dependencies> ...

  8. Mybatis Generator通用Join的实现

    通常,我们使用Mybatis实现join表关联的时候,一般都是通过在xml或注解里写自定义sql实现. 本文通过Mybatis Generator的插件功能新增一个JoinPlugin插件,只要在配置 ...

  9. 想做时间管理大师?你可以试试Mybatis Plus代码生成器

    1. 前言 对于写Crud的老司机来说时间非常宝贵,一些样板代码写不但费时费力,而且枯燥无味.经常有小伙伴问我,胖哥你怎么天天那么有时间去搞新东西,透露一下秘诀呗. 好吧,今天就把Mybatis-pl ...

  10. SpringCloud或SpringBoot+Mybatis-Plus利用AOP+mybatis插件实现数据操作记录及更新对比

    引文 本文主要介绍如何使用Spring AOP + mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录.在更新数据时准确记录更新字段 核心:AO ...

随机推荐

  1. PMP--1.6 项目经理

    本节都是理论的东西,可以在管理没有思路的或者管理陷入困境的时候当做提升或解决问题的思路来看. 一.项目经理 1. 项目经理.职能经理与运营经理的区别 (1)职能经理专注于对某个职能领域或业务部门的管理 ...

  2. day19 几个模块的学习

    # 模块本质上就是一个 .py 文件# 数据类型# 列表.元组# 字典# 集合.frozenset# 字符串# 堆栈:特点:先进后出# 队列:先进先出 FIFO # from collections ...

  3. 0.96寸OLED显示屏驱动手册(SSD1306)

    MCU IIC接口 IIC通信接口由从地址位SA0,IIC总线数据信号SDA(输出SDAout/D2和输入SDAin /D1)和IIC总线时钟信号SCL(D0).不管是数据线还是时钟线都需要连接上拉电 ...

  4. java如何解决线程安全问题

    方式一:同步代码块 synchroized(同步监视器的对象){需要被同步的代码} package threadtest; //使用同步代码块实现Runable接口的线程 public class R ...

  5. 大二网课ing学习周记

    行稳致远,久久为功! 一个概念套着一个概念哦!码就是关键字,标识属性是也! 1.什么是主码和外码,请举例说明. 关键字也叫码! 首先,在一个关系中,能惟一标识元组的属性or最小属性集称为关系的候选码. ...

  6. 吴裕雄--天生自然HADOOP操作实验学习笔记:tf-idf算法

    实验目的 通过实验了解tf-idf算法原理 通过实验了解mapreduce的更多组件 学会自定义分区,读写缓存文件 了解mapreduce程序的设计方法 实验原理 1.TF-IDF简介 TF-IDF( ...

  7. 小程序公共模板template与公共js数据utils的引用实例

    在小程序项目开发中,经常会遇到公共模板与公共js数据的调用,这里结合自己的项目为这一需求做一简单介绍 目录截图 现在是有一个评论版块需要在几个页面里共用 先将评论版块的wxml剔出来放在templat ...

  8. 关于Spring注入参数到static静态参数失败问题处理。解决Autowired annotation is not supported on static fields的问题

    直接贴代码 把注入参数的注解加到set方法上面去即可. 因为这是一个工具类用到的config,所以一开始没有加@Component,还是依然为空,加上之后就正常能注入了

  9. Python之pptx实现添加内容与删除(移动)页操作

    问题背景 大量表格数据需要生成指定格式的ppt文件,内容以文字和表格为主,首尾页与内容有固定格式.博主不熟悉VBA操作,希望通过模板用Python完成自动化. 基本思路 使用xlrd模块读取xlsx文 ...

  10. JS 自动关闭页面

    <script language=javascript> this.window.opener = null; window.close(); </script>