概述
系列文章目录
【mybatis plus源码解析】mybatis plus执行原理
【mybatis plus源码解析】(二)详解自定义SQL注入器底层原理
文章目录
- 系列文章目录
- 前言
- 一、ISqlInjector SQL自动注入器接口的相关uml图
- ISqlInjector顶级接口,只做一件事
- 再来看看AbstractSqlInjector抽象类
- DefaultSqlInjector默认SQL 注入器
- 默认的注入器类治理介绍完了,里面其实做了一件事就是调用注入mapper默认实现的CRUD方法
- 二、注入方法相关的类
- 先看看UML图
- AbstractMethod抽象方法类
- 简单看个SelectById类,以这个方法为例子
- 了解完上面的类,现在回头看看初始化时注入方法,不了解的可以先看我的上篇文章。
- 总结
前言
上篇文章介绍了【mybatis plus源码解析】mybatis plus执行原理,这回接着上篇文章,建议看完上篇文章再来看这篇,这里主要介绍相关类
一、ISqlInjector SQL自动注入器接口的相关uml图
ISqlInjector顶级接口,只做一件事
再来看看AbstractSqlInjector抽象类
AbstractSqlInjector类实现了inspectInject注入方法
/**
* SQL 自动注入器
*
* @author hubin
* @since 2018-04-07
*/
public abstract class AbstractSqlInjector implements ISqlInjector {
protected final Log logger = LogFactory.getLog(this.getClass());
@Override
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
//通过反射获取实体类对象
Class<?> modelClass = ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);
if (modelClass != null) {
String className = mapperClass.toString();
//获取mapperRegistry缓存用于对比,防止覆盖
Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
//通过实体类对象反射方式获取信息(比如实体类上的注解)封装成TableInfo存储表信息对象
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
List<AbstractMethod> methodList = this.getMethodList(mapperClass, tableInfo);//获取要注入的方法集合
if (CollectionUtils.isNotEmpty(methodList)) {
// 循环注入自定义方法
methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
} else {
logger.debug(mapperClass.toString() + ", No effective injection method was found.");
}
mapperRegistryCache.add(className);
}
}
}
/**
* <p>
* 获取 注入的方法
* </p>
*
* @param mapperClass 当前mapper
* @return 注入的方法集合
* @since 3.1.2 add mapperClass
*/
public abstract List<AbstractMethod> getMethodList(Class<?> mapperClass,TableInfo tableInfo);
}
DefaultSqlInjector默认SQL 注入器
DefaultSqlInjector实现了AbstractSqlInjector类的getMethodList方法,规定哪些方法可以实现自动注入
/**
* SQL 默认注入器
*
* @author hubin
* @since 2018-04-10
*/
public class DefaultSqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
Stream.Builder<AbstractMethod> builder = Stream.<AbstractMethod>builder()
.add(new Insert())
.add(new Delete())
.add(new DeleteByMap())
.add(new Update())
.add(new SelectByMap())
.add(new SelectCount())
.add(new SelectMaps())
.add(new SelectMapsPage())
.add(new SelectObjs())
.add(new SelectList())
.add(new SelectPage());
if (tableInfo.havePK()) {
builder.add(new DeleteById())
.add(new DeleteBatchByIds())
.add(new UpdateById())
.add(new SelectById())
.add(new SelectBatchByIds());
} else {
logger.warn(String.format("%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
tableInfo.getEntityType()));
}
return builder.build().collect(toList());
}
}
默认的注入器类治理介绍完了,里面其实做了一件事就是调用注入mapper默认实现的CRUD方法
二、注入方法相关的类
先看看UML图
AbstractMethod抽象方法类
/**
* 抽象的注入方法类
*
* @author hubin
* @since 2018-04-06
*/
@SuppressWarnings("serial")
public abstract class AbstractMethod implements Constants {
protected static final Log logger = LogFactory.getLog(AbstractMethod.class);
protected Configuration configuration;
protected LanguageDriver languageDriver;
protected MapperBuilderAssistant builderAssistant;
/**
* 方法名称
* @since 3.5.0
*/
protected final String methodName;
/**
* @see AbstractMethod#AbstractMethod(java.lang.String)
* @since 3.5.0
*/
@Deprecated
public AbstractMethod() {
methodName = null;
}
/**
* @param methodName 方法名
* @since 3.5.0
*/
protected AbstractMethod(String methodName) {
Assert.notNull(methodName, "方法名不能为空");
this.methodName = methodName;
}
/**
* 注入自定义方法
*/
public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
/* 注入自定义方法 */
injectMappedStatement(mapperClass, modelClass, tableInfo);
}
/**
* 是否已经存在MappedStatement
*
* @param mappedStatement MappedStatement
* @return true or false
*/
private boolean hasMappedStatement(String mappedStatement) {
return configuration.hasStatement(mappedStatement, false);
}
/**
* SQL 更新 set 语句
*
* @param table 表信息
* @return sql set 片段
*/
protected String sqlLogicSet(TableInfo table) {
return "SET " + table.getLogicDeleteSql(false, false);
}
/**
* SQL 更新 set 语句
*
* @param logic 是否逻辑删除注入器
* @param ew 是否存在 UpdateWrapper 条件
* @param table 表信息
* @param alias 别名
* @param prefix 前缀
* @return sql
*/
protected String sqlSet(boolean logic, boolean ew, TableInfo table, boolean judgeAliasNull, final String alias,
final String prefix) {
String sqlScript = table.getAllSqlSet(logic, prefix);
if (judgeAliasNull) {
sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", alias), true);
}
if (ew) {
sqlScript += NEWLINE;
sqlScript += convertIfEwParam(U_WRAPPER_SQL_SET, false);
}
sqlScript = SqlScriptUtils.convertSet(sqlScript);
return sqlScript;
}
/**
* SQL 注释
*
* @return sql
*/
protected String sqlComment() {
return convertIfEwParam(Q_WRAPPER_SQL_COMMENT, true);
}
/**
* SQL 注释
*
* @return sql
*/
protected String sqlFirst() {
return convertIfEwParam(Q_WRAPPER_SQL_FIRST, true);
}
protected String convertIfEwParam(final String param, final boolean newLine) {
return SqlScriptUtils.convertIf(SqlScriptUtils.unSafeParam(param),
String.format("%s != null and %s != null", WRAPPER, param), newLine);
}
/**
* SQL 查询所有表字段
*
* @param table 表信息
* @param queryWrapper 是否为使用 queryWrapper 查询
* @return sql 脚本
*/
protected String sqlSelectColumns(TableInfo table, boolean queryWrapper) {
/* 假设存在用户自定义的 resultMap 映射返回 */
String selectColumns = ASTERISK;
if (table.getResultMap() == null || table.isAutoInitResultMap()) {
/* 未设置 resultMap 或者 resultMap 是自动构建的,视为属于mp的规则范围内 */
selectColumns = table.getAllSqlSelect();
}
if (!queryWrapper) {
return selectColumns;
}
return convertChooseEwSelect(selectColumns);
}
/**
* SQL 查询记录行数
*
* @return count sql 脚本
*/
protected String sqlCount() {
return convertChooseEwSelect(ASTERISK);
}
/**
* SQL 设置selectObj sql select
*
* @param table 表信息
*/
protected String sqlSelectObjsColumns(TableInfo table) {
return convertChooseEwSelect(table.getAllSqlSelect());
}
protected String convertChooseEwSelect(final String otherwise) {
return SqlScriptUtils.convertChoose(String.format("%s != null and %s != null", WRAPPER, Q_WRAPPER_SQL_SELECT),
SqlScriptUtils.unSafeParam(Q_WRAPPER_SQL_SELECT), otherwise);
}
/**
* SQL map 查询条件
*/
protected String sqlWhereByMap(TableInfo table) {
if (table.isWithLogicDelete()) {
// 逻辑删除
String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ",
" ${k} = #{v} ");
sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, "k", "v", "AND");
sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and !%s.isEmpty", COLUMN_MAP, COLUMN_MAP), true);
sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true));
sqlScript = SqlScriptUtils.convertWhere(sqlScript);
return sqlScript;
} else {
String sqlScript = SqlScriptUtils.convertChoose("v == null", " ${k} IS NULL ",
" ${k} = #{v} ");
sqlScript = SqlScriptUtils.convertForeach(sqlScript, COLUMN_MAP, "k", "v", "AND");
sqlScript = SqlScriptUtils.convertWhere(sqlScript);
sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null and !%s", COLUMN_MAP,
COLUMN_MAP_IS_EMPTY), true);
return sqlScript;
}
}
/**
* EntityWrapper方式获取select where
*
* @param newLine 是否提到下一行
* @param table 表信息
* @return String
*/
protected String sqlWhereEntityWrapper(boolean newLine, TableInfo table) {
if (table.isWithLogicDelete()) {
String sqlScript = table.getAllSqlWhere(true, true, WRAPPER_ENTITY_DOT);
sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY),
true);
sqlScript += (NEWLINE + table.getLogicDeleteSql(true, true) + NEWLINE);
String normalSqlScript = SqlScriptUtils.convertIf(String.format("AND ${%s}", WRAPPER_SQLSEGMENT),
String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
WRAPPER_NONEMPTYOFNORMAL), true);
normalSqlScript += NEWLINE;
normalSqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
WRAPPER_EMPTYOFNORMAL), true);
sqlScript += normalSqlScript;
sqlScript = SqlScriptUtils.convertChoose(String.format("%s != null", WRAPPER), sqlScript,
table.getLogicDeleteSql(false, true));
sqlScript = SqlScriptUtils.convertWhere(sqlScript);
return newLine ? NEWLINE + sqlScript : sqlScript;
} else {
String sqlScript = table.getAllSqlWhere(false, true, WRAPPER_ENTITY_DOT);
sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER_ENTITY), true);
sqlScript += NEWLINE;
sqlScript += SqlScriptUtils.convertIf(String.format(SqlScriptUtils.convertIf(" AND", String.format("%s and %s", WRAPPER_NONEMPTYOFENTITY, WRAPPER_NONEMPTYOFNORMAL), false) + " ${%s}", WRAPPER_SQLSEGMENT),
String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
WRAPPER_NONEMPTYOFWHERE), true);
sqlScript = SqlScriptUtils.convertWhere(sqlScript) + NEWLINE;
sqlScript += SqlScriptUtils.convertIf(String.format(" ${%s}", WRAPPER_SQLSEGMENT),
String.format("%s != null and %s != '' and %s", WRAPPER_SQLSEGMENT, WRAPPER_SQLSEGMENT,
WRAPPER_EMPTYOFWHERE), true);
sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", WRAPPER), true);
return newLine ? NEWLINE + sqlScript : sqlScript;
}
}
protected String sqlOrderBy(TableInfo tableInfo) {
/* 不存在排序字段,直接返回空 */
List<TableFieldInfo> orderByFields = tableInfo.getOrderByFields();
if (CollectionUtils.isEmpty(orderByFields)) {
return StringPool.EMPTY;
}
orderByFields.sort(Comparator.comparingInt(TableFieldInfo::getOrderBySort));
StringBuilder sql = new StringBuilder();
sql.append(NEWLINE).append(" ORDER BY ");
sql.append(orderByFields.stream().map(tfi -> String.format("%s %s", tfi.getColumn(),
tfi.getOrderByType())).collect(joining(",")));
/* 当wrapper中传递了orderBy属性,@orderBy注解失效 */
return SqlScriptUtils.convertIf(sql.toString(), String.format("%s == null or %s", WRAPPER,
WRAPPER_EXPRESSION_ORDER), true);
}
/**
* 过滤 TableFieldInfo 集合, join 成字符串
*/
protected String filterTableFieldInfo(List<TableFieldInfo> fieldList, Predicate<TableFieldInfo> predicate,
Function<TableFieldInfo, String> function, String joiningVal) {
Stream<TableFieldInfo> infoStream = fieldList.stream();
if (predicate != null) {
return infoStream.filter(predicate).map(function).collect(joining(joiningVal));
}
return infoStream.map(function).collect(joining(joiningVal));
}
/**
* 获取乐观锁相关
*
* @param tableInfo 表信息
* @return String
*/
protected String optlockVersion(TableInfo tableInfo) {
if (tableInfo.isWithVersion()) {
return tableInfo.getVersionFieldInfo().getVersionOli(ENTITY, ENTITY_DOT);
}
return EMPTY;
}
/**
* 查询
*/
protected MappedStatement addSelectMappedStatementForTable(Class<?> mapperClass, String id, SqlSource sqlSource,
TableInfo table) {
String resultMap = table.getResultMap();
if (null != resultMap) {
/* 返回 resultMap 映射结果集 */
return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null,
resultMap, null, NoKeyGenerator.INSTANCE, null, null);
} else {
/* 普通查询 */
return addSelectMappedStatementForOther(mapperClass, id, sqlSource, table.getEntityType());
}
}
/**
* 查询
* @since 3.5.0
*/
protected MappedStatement addSelectMappedStatementForTable(Class<?> mapperClass, SqlSource sqlSource, TableInfo table) {
return addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, table);
}
/**
* 查询
*/
protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, String id, SqlSource sqlSource,
Class<?> resultType) {
return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null,
null, resultType, NoKeyGenerator.INSTANCE, null, null);
}
/**
* 查询
*
* @since 3.5.0
*/
protected MappedStatement addSelectMappedStatementForOther(Class<?> mapperClass, SqlSource sqlSource, Class<?> resultType) {
return addSelectMappedStatementForOther(mapperClass, this.methodName, sqlSource, resultType);
}
/**
* 插入
*/
protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,
SqlSource sqlSource, KeyGenerator keyGenerator,
String keyProperty, String keyColumn) {
return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, parameterType, null,
Integer.class, keyGenerator, keyProperty, keyColumn);
}
/**
* 插入
* @since 3.5.0
*/
protected MappedStatement addInsertMappedStatement(Class<?> mapperClass, Class<?> parameterType,
SqlSource sqlSource, KeyGenerator keyGenerator,
String keyProperty, String keyColumn) {
return addInsertMappedStatement(mapperClass, parameterType, this.methodName, sqlSource, keyGenerator, keyProperty, keyColumn);
}
/**
* 删除
*/
protected MappedStatement addDeleteMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource) {
return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null,
null, Integer.class, NoKeyGenerator.INSTANCE, null, null);
}
/**
* @since 3.5.0
*/
protected MappedStatement addDeleteMappedStatement(Class<?> mapperClass, SqlSource sqlSource) {
return addDeleteMappedStatement(mapperClass, this.methodName, sqlSource);
}
/**
* 更新
*/
protected MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> parameterType, String id,
SqlSource sqlSource) {
return addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, parameterType, null,
Integer.class, NoKeyGenerator.INSTANCE, null, null);
}
/**
* 更新
*
* @since 3.5.0
*/
protected MappedStatement addUpdateMappedStatement(Class<?> mapperClass, Class<?> parameterType,
SqlSource sqlSource) {
return addUpdateMappedStatement(mapperClass, parameterType, this.methodName, sqlSource);
}
/**
* 添加 MappedStatement 到 Mybatis 容器
*/
protected MappedStatement addMappedStatement(Class<?> mapperClass, String id, SqlSource sqlSource,
SqlCommandType sqlCommandType, Class<?> parameterType,
String resultMap, Class<?> resultType, KeyGenerator keyGenerator,
String keyProperty, String keyColumn) {
String statementName = mapperClass.getName() + DOT + id;
if (hasMappedStatement(statementName)) {
logger.warn(LEFT_SQ_BRACKET + statementName + "] Has been loaded by XML or SqlProvider or Mybatis's Annotation, so ignoring this injection for [" + getClass() + RIGHT_SQ_BRACKET);
return null;
}
/* 缓存逻辑处理 */
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType,
null, null, null, parameterType, resultMap, resultType,
null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
configuration.getDatabaseId(), languageDriver, null);
}
/**
* @since 3.5.0
*/
protected MappedStatement addMappedStatement(Class<?> mapperClass, SqlSource sqlSource,
SqlCommandType sqlCommandType, Class<?> parameterType,
String resultMap, Class<?> resultType, KeyGenerator keyGenerator,
String keyProperty, String keyColumn) {
return addMappedStatement(mapperClass, this.methodName, sqlSource, sqlCommandType, parameterType, resultMap, resultType, keyGenerator, keyProperty, keyColumn);
}
/**
* 注入自定义 MappedStatement
*
* @param mapperClass mapper 接口
* @param modelClass mapper 泛型
* @param tableInfo 数据库表反射信息
* @return MappedStatement
*/
public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
/**
* 获取自定义方法名,未设置采用默认方法名
* https://gitee.com/baomidou/mybatis-plus/pulls/88
*
* @return method
* @author 义陆无忧
* @see AbstractMethod#AbstractMethod(java.lang.String)
* @deprecated 3.5.0
*/
@Deprecated
public String getMethod(SqlMethod sqlMethod) {
return StringUtils.isBlank(methodName) ? sqlMethod.getMethod() : this.methodName;
}
}
简单看个SelectById类,以这个方法为例子
/**
* 根据ID 查询一条数据
*
* @author hubin
* @since 2018-04-06
*/
public class SelectById extends AbstractMethod {
public SelectById() {
//给methodName属性赋值
super(SqlMethod.SELECT_BY_ID.getMethod());
}
/**
* @param name 方法名
* @since 3.5.0
*/
public SelectById(String name) {
//给methodName属性赋值
super(name);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
//获取SqlMethod类,这是一个枚举类,下面会有介绍
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
//生成最终的SqlSource对象
SqlSource sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(),
sqlSelectColumns(tableInfo, false),
tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(),
tableInfo.getLogicDeleteSql(true, true)), Object.class);
//将最终封装好的MappedStatement对象加入配置类对象中
return this.addSelectMappedStatementForTable(mapperClass, getMethod(sqlMethod), sqlSource, tableInfo);
}
}
断点看看
可以看到sqlMethod枚举类封装了相关sql语句,短点往下
最终生成了封装好sql语句和参数的SqlSource对象,这里生成的是StaticSqlSource静态对象
了解完上面的类,现在回头看看初始化时注入方法,不了解的可以先看我的上篇文章。
来看看parserInjector这个方法
GlobalConfigUtils.getSqlInjector(configuration)这句代码其实就是返回配置文件中设置的SqlInjector注入器,说明我们可以自定义注入器,默认注入器是DefaultSqlInjector。官方文档也正好证明这一点
最终这个方法调用了AbstractSqlInjector的inspectInject方法将CRUD相关的sql对象注入到配置中去
总结
到这里基本就介绍完了。到这里其实就能想到可以通过自定义BaseMapper,并且编写响应method对象,自定义实现ISqlInjector注入器。这样我们就能够对mybatis-plus进行扩展来实现更多的CRUD方法。后面文章我会出一片详细的
最后
以上就是可爱手机为你收集整理的【mybatis plus源码解析】(二)详解SQL注入器底层原理,mybatis plus是如何实现自动注入CRUD操作系列文章目录前言一、ISqlInjector SQL自动注入器接口的相关uml图二、注入方法相关的类总结的全部内容,希望文章能够帮你解决【mybatis plus源码解析】(二)详解SQL注入器底层原理,mybatis plus是如何实现自动注入CRUD操作系列文章目录前言一、ISqlInjector SQL自动注入器接口的相关uml图二、注入方法相关的类总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复