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 ...
随机推荐
- MySQL概述及入门(四)
MySql概述及入门(四) 什么是MySQL的锁? 数据库锁定机制,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则,简单说,就是不让别人动 总的来说,MySQL各 ...
- 小白的linux笔记5:关于权限那些事
在设置smb时发现,目录的权限是个影响访问的大问题,还是得研究清楚. 关于文件权限 查看当前目录下文件和文件夹的权限状态:ls -l drwxrwxr--. 4 root root 4096 ...
- P2918 [USACO08NOV]买干草Buying Hay
链接:Miku ---------------- 这就是一个完全背包的板子题 ---------------- 我们把重量当作重量,开销当作价值,那么这个题就是个求价值最小的完全背包 然而题目上说了是 ...
- Jstree在加载时和加载完成的回调方法-sunziren
1.有时候在使用jstree的时候我们想在它加载完成后立刻执行某个方法,于是我们可以用下面这个jstree自带的回调: .on('ready.jstree', function(event, obj) ...
- 13 年的 Bug 调试经验总结 【转载】
在<Learning From Your Bugs>一文中,我写了关于我是如何追踪我所遇到的一些最有趣的bug.最近,我回顾了我所有的194个条目(从13岁开始),看看有什么经验教训是我可 ...
- Kemaswill 机器学习 数据挖掘 推荐系统 Python optparser模块简介
Python optparser模块简介
- 使用DataContractJsonSerializer发序列化对象时出现的异常
最近服务器上的某个程序的错误日志中频繁出现以下异常: Deserialising: There was an error deserializing the object of type {type} ...
- JavaDay10(上)
Java learning_Day10(上) 本人学习视频用的是马士兵的,也在这里献上 <链接:https://pan.baidu.com/s/1qKNGJNh0GgvlJnitTJGqgA&g ...
- 【你不知道的javaScript 上卷 笔记7】javaScript中对象的[[Prototype]]机制
[[Prototype]]机制 [[Prototype]]是对象内部的隐试属性,指向一个内部的链接,这个链接的作用是:如果在对象上没有找到需要的属性或者方法引用,引擎就 会继续在 [[Prototyp ...
- 使用ResXmanager实现多语言[转]
1 多國語系 - 使用RESXMANAGER管理資源檔 1.1 前言 在實作多國語系網站時,針對靜態文字多會使用資源檔(Resource)來對應出各語系所需顯示的內容.由於資源檔可能會依照特定結構放式 ...