mybatis-plus - TableInfo
在前面 的 inject() 方法中, 调用了一个 TableInfoHelper.initTableInfo(builderAssistant, modelClass) 方法, 来获取 表信息: TableInfo
/**
* <p>
* 实体类反射获取表信息【初始化】
* <p>
*
* @param clazz 反射实体类
* @return 数据库表反射信息
*/
public synchronized static TableInfo initTableInfo(MapperBuilderAssistant builderAssistant, Class<?> clazz) {
TableInfo tableInfo = TABLE_INFO_CACHE.get(clazz);
if (tableInfo != null) {
if (tableInfo.getConfigMark() == null && builderAssistant != null) {
tableInfo.setConfigMark(builderAssistant.getConfiguration());
}
return tableInfo;
} /* 没有获取到缓存信息,则初始化 */
tableInfo = new TableInfo();
GlobalConfig globalConfig;
if (null != builderAssistant) {
tableInfo.setCurrentNamespace(builderAssistant.getCurrentNamespace());
tableInfo.setConfigMark(builderAssistant.getConfiguration());
tableInfo.setUnderCamel(builderAssistant.getConfiguration().isMapUnderscoreToCamelCase());
globalConfig = GlobalConfigUtils.getGlobalConfig(builderAssistant.getConfiguration());
} else {
// 兼容测试场景
globalConfig = GlobalConfigUtils.defaults();
} /* 初始化表名相关 */
initTableName(clazz, globalConfig, tableInfo); /* 初始化字段相关 */
initTableFields(clazz, globalConfig, tableInfo); /* 放入缓存 */
TABLE_INFO_CACHE.put(clazz, tableInfo); /* 缓存 Lambda 映射关系 */
LambdaUtils.createCache(clazz, tableInfo);
return tableInfo;
}
是不是还是自己人写的代码看起来爽? 这中文注释, 都不用看方法具体是干啥的.
这里的 TABLE_INFO_CACHE 是用来缓存表信息的:
/**
* 储存反射类表信息
*/
private static final Map<Class<?>, TableInfo> TABLE_INFO_CACHE = new ConcurrentHashMap<>();
第一次进这个方法的时候, 肯定是空的, 要去解析获取.
1. initTableName()
/**
* <p>
* 初始化 表数据库类型,表名,resultMap
* </p>
*
* @param clazz 实体类
* @param globalConfig 全局配置
* @param tableInfo 数据库表反射信息
*/
public static void initTableName(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
/* 数据库全局配置 */
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
/* 设置数据库类型 */
tableInfo.setDbType(dbConfig.getDbType()); /* 设置表名 */
TableName table = clazz.getAnnotation(TableName.class);
String tableName = clazz.getSimpleName();
if (table != null && StringUtils.isNotEmpty(table.value())) {
tableName = table.value();
} else {
// 开启表名下划线申明
if (dbConfig.isTableUnderline()) {
tableName = StringUtils.camelToUnderline(tableName);
}
// 大写命名判断
if (dbConfig.isCapitalMode()) {
tableName = tableName.toUpperCase();
} else {
// 首字母小写
tableName = StringUtils.firstToLowerCase(tableName);
}
// 存在表名前缀
if (null != dbConfig.getTablePrefix()) {
tableName = dbConfig.getTablePrefix() + tableName;
}
}
tableInfo.setTableName(tableName); /* 表结果集映射 */
if (table != null && StringUtils.isNotEmpty(table.resultMap())) {
tableInfo.setResultMap(table.resultMap());
} /* 开启了自定义 KEY 生成器 */
if (null != dbConfig.getKeyGenerator()) {
tableInfo.setKeySequence(clazz.getAnnotation(KeySequence.class));
}
}
判断逻辑:
1. 判断实体类上面有没有 TableName 注解
|-> 如果有, 则拿注解里面配置的 value 作为表名
|-> 如果没有, 则根据类名进行解析
2. initTableFields()
/**
* <p>
* 初始化 表主键,表字段
* </p>
*
* @param clazz 实体类
* @param globalConfig 全局配置
* @param tableInfo 数据库表反射信息
*/
public static void initTableFields(Class<?> clazz, GlobalConfig globalConfig, TableInfo tableInfo) {
/* 数据库全局配置 */
GlobalConfig.DbConfig dbConfig = globalConfig.getDbConfig();
List<Field> list = getAllFields(clazz);
// 标记是否读取到主键
boolean isReadPK = false;
// 是否存在 @TableId 注解
boolean existTableId = isExistTableId(list); List<TableFieldInfo> fieldList = new ArrayList<>();
for (Field field : list) {
/*
* 主键ID 初始化
*/
if (!isReadPK) {
if (existTableId) {
isReadPK = initTableIdWithAnnotation(dbConfig, tableInfo, field, clazz);
} else {
isReadPK = initTableIdWithoutAnnotation(dbConfig, tableInfo, field, clazz);
}
if (isReadPK) {
continue;
}
}
/* 有 @TableField 注解的字段初始化 */
if (initTableFieldWithAnnotation(dbConfig, tableInfo, fieldList, field, clazz)) {
continue;
} /* 无 @TableField 注解的字段初始化 */
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field));
} /* 检查逻辑删除字段只能有最多一个 */
Assert.isTrue(fieldList.parallelStream().filter(TableFieldInfo::isLogicDelete).count() < 2L,
String.format("annotation of @TableLogic can't more than one in class : %s.", clazz.getName())); /* 字段列表 */
tableInfo.setFieldList(fieldList); /* 未发现主键注解,提示警告信息 */
if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
logger.warn(String.format("Warn: Could not find @TableId in Class: %s.", clazz.getName()));
}
}
2.1 getAllFields()
/**
* 获取该类的所有属性列表
*
* @param clazz 反射类
* @return 属性集合
*/
public static List<Field> getAllFields(Class<?> clazz) {
List<Field> fieldList = ReflectionKit.getFieldList(ClassUtils.getUserClass(clazz));
if (CollectionUtils.isNotEmpty(fieldList)) {
return fieldList.stream()
.filter(i -> {
/* 过滤注解非表字段属性 */
TableField tableField = i.getAnnotation(TableField.class);
return (tableField == null || tableField.exist());
}).collect(toList());
}
return fieldList;
}
如果字段上面加了 TableField 注解, 如果有则进行特殊处理. 如果配置了 exist=false, 则这个字段, 过滤掉, 不参与sql生成.
2.2 initTableIdWithAnnotation()
如果实体类中有 TableId 注解, 则进入此方法, 一般情况下, 最好是配一下 TableId
/**
* <p>
* 主键属性初始化
* </p>
*
* @param dbConfig 全局配置信息
* @param tableInfo 表信息
* @param field 字段
* @param clazz 实体类
* @return true 继续下一个属性判断,返回 continue;
*/
private static boolean initTableIdWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
Field field, Class<?> clazz) {
TableId tableId = field.getAnnotation(TableId.class);
boolean underCamel = tableInfo.isUnderCamel();
if (tableId != null) {
if (StringUtils.isEmpty(tableInfo.getKeyColumn())) {
/* 主键策略( 注解 > 全局 ) */
// 设置 Sequence 其他策略无效
if (IdType.NONE == tableId.type()) {
tableInfo.setIdType(dbConfig.getIdType());
} else {
tableInfo.setIdType(tableId.type());
} /* 字段 */
String column = field.getName();
if (StringUtils.isNotEmpty(tableId.value())) {
column = tableId.value();
} else {
// 开启字段下划线申明
if (underCamel) {
column = StringUtils.camelToUnderline(column);
}
// 全局大写命名
if (dbConfig.isCapitalMode()) {
column = column.toUpperCase();
}
}
tableInfo.setKeyRelated(checkRelated(underCamel, field.getName(), column))
.setClazz(field.getDeclaringClass())
.setKeyColumn(column)
.setKeyProperty(field.getName());
return true;
} else {
throwExceptionId(clazz);
}
}
return false;
}
2.3 initTableFieldWithAnnotation()
这里对 TableField 进行解析赋值. 如 value 解析成 字段名称
/**
* <p>
* 字段属性初始化
* </p>
*
* @param dbConfig 数据库全局配置
* @param tableInfo 表信息
* @param fieldList 字段列表
* @param clazz 当前表对象类
* @return true 继续下一个属性判断,返回 continue;
*/
private static boolean initTableFieldWithAnnotation(GlobalConfig.DbConfig dbConfig, TableInfo tableInfo,
List<TableFieldInfo> fieldList, Field field, Class<?> clazz) {
/* 获取注解属性,自定义字段 */
TableField tableField = field.getAnnotation(TableField.class);
if (null == tableField) {
return false;
}
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(StringPool.SEMICOLON);
String[] els = el.split(StringPool.SEMICOLON);
if (columns.length == els.length) {
for (int i = 0; i < columns.length; i++) {
fieldList.add(new TableFieldInfo(dbConfig, tableInfo, field, columns[i], els[i], tableField));
}
return true;
}
throw ExceptionUtils.mpe(String.format("Class: %s, Field: %s, 'value' 'el' Length must be consistent.",
clazz.getName(), field.getName()));
}
mybatis-plus - TableInfo的更多相关文章
- spring-cloud集成mybatis-plus
mybatis-plus插件是对mybatis做出系列增强插件,后面简称MP,MP可免去开发者重复编写xml.mapper.service.entity等代码,通过MP提供的实体注解来完成单表的CRU ...
- Mybatis-Plus 代码生成器基本使用
Mybatis-Plus 代码生成器基本使用 参考 https://mp.baomidou.com/guide/generator.html#%E4%BD%BF%E7%94%A8%E6%95%99%E ...
- spring boot整合mybatis+mybatis-plus
Spring boot对于我来说是一个刚接触的新东西,学习过程中,发现这东西还是很容易上手的,Spring boot没配置时会默认使用Spring data jpa,这东西可以说一个极简洁的工具,可是 ...
- 使用mybatis plus自动生成controller、service、dao、mapper、entity代码
官网:http://mp.baomidou.com(这个项目不仅仅可以用于代码生成,还有分页等其他功能,是对mybatis的一层封装) 要求:基于sql自动生成domain.controller.se ...
- Mybatis Plus启动注入 SQL 原理分析
1) 问题: xxxMapper 继承了 BaseMapper<T>, BaseMapper 中提供了通用的 CRUD 方法, 方法来源于 BaseMapper, 有方法就必须有 SQL, ...
- 步步截图的SMM框架入门实战指引(SpringBoot、Mybatis Plus、Maven)
前提是对spring.springmvc.mybatis有初步学习和理解,因为要全部讲这些框架的知识点太多了,自己学习,这里是实战示范(大部分人学了知识之后去实战会出现很多问题,所以出此教程) 开发环 ...
- Mybatis - plus 配置与运用
Mybatis - plus mybatis-plus 官方文档 1.配置 引入对应的文件包,spring boot + mybatis 需添加依赖文件如下: <dependencies> ...
- Mybatis Generator通用Join的实现
通常,我们使用Mybatis实现join表关联的时候,一般都是通过在xml或注解里写自定义sql实现. 本文通过Mybatis Generator的插件功能新增一个JoinPlugin插件,只要在配置 ...
- 想做时间管理大师?你可以试试Mybatis Plus代码生成器
1. 前言 对于写Crud的老司机来说时间非常宝贵,一些样板代码写不但费时费力,而且枯燥无味.经常有小伙伴问我,胖哥你怎么天天那么有时间去搞新东西,透露一下秘诀呗. 好吧,今天就把Mybatis-pl ...
- SpringCloud或SpringBoot+Mybatis-Plus利用AOP+mybatis插件实现数据操作记录及更新对比
引文 本文主要介绍如何使用Spring AOP + mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录.在更新数据时准确记录更新字段 核心:AO ...
随机推荐
- 使用 Jest 进行愉快的 JavaScript(TypeScript) 测试
一般我们不管是做前端还是后端,为了提高代码的质量,会选择一种测试驱动开发(TDD)的办法来写代码进行单元测试.Jest 是 Facebook 团队开发的一款测试框架,为的是提高开发者的"开发 ...
- 洛谷P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib 使用四种算法
洛谷P1218 [USACO1.5]特殊的质数肋骨 Superprime Rib 水题一道…… 题目描述 农民约翰的母牛总是产生最好的肋骨.你能通过农民约翰和美国农业部标记在每根肋骨上的数字认出它们. ...
- 前端-2019 history 与 location
HTML5 API解析之Window.history历史记录 1.简介 window.history是用来保存用户在一个会话期间的网站访问记录,并提供相应的方法进行追溯.其对应的成员如下: 方法:ba ...
- Uva1635 二项式递推+质因子分解+整数因子分解
题意: 给定n个数a1,a2····an,依次求出相邻两个数值和,将得到一个新数列,重复上述操作,最后结果将变为一个数,问这个数除以m的余数与那些数无关? 例如n=3,m=2时,第一次得到a1+a2, ...
- node种buffer对象数组 深拷贝浅拷贝问题
node的一个上位机和下位机通信的转发程序,用的是udp转发. 其中在发送的时候会进行一次rc4加密数据 出现问题就在这个加密数据这一块,因为这个是升级包广播发送.提前生成了升级用的广播报文,是一个b ...
- 剑指offer-面试题19-正则表达式匹配-字符串
/* 题目: 实现一个函数用来匹配包含'.'和'*'的正则表达式. '.'表示比配任意字符,‘*’表示匹配0个或多个字符串. */ /* 思路: 采用递归的方法. 基础条件:当字符串和模式串存在空的情 ...
- mybatis第二天02
MyBatis第二天内容 1.mybatis的执行原理 通过: 1.全局配置文件SqlMapConfig.xml 映射文件mapper.xml 获取得到SqlSessinFactory工厂 2.由工 ...
- Connections in Galaxy War ZOJ - 3261 离线操作+逆序并查集 并查集删边
#include<iostream> #include<cstring> #include<stdio.h> #include<map> #includ ...
- svn error: "Previous operation has not finished; run 'cleanup' if it was interrupted"
出现这种问题,有几个原因 1.本身文件确实是锁住了 2.之前clean up 过很多次,但是每次都可能以失败告终,造成work queue存在缓存队列 3.svn lock 有lock记录 以下是简单 ...
- PAT (Basic Level) Practice (中文)1043 输出PATest (20 分)
给定一个长度不超过 1 的.仅由英文字母构成的字符串.请将字符重新调整顺序,按 PATestPATest.... 这样的顺序输出,并忽略其它字符.当然,六种字符的个数不一定是一样多的,若某种字符已经输 ...