/**
* Copyright (c) 2011-2014, hubin (jobob@qq.com).
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.baomidou.mybatisplus.mapper;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.entity.TableFieldInfo;
import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.enums.DBType;
import com.baomidou.mybatisplus.enums.FieldStrategy;
import com.baomidou.mybatisplus.enums.IdType;
import com.baomidou.mybatisplus.enums.SqlMethod;
import com.baomidou.mybatisplus.toolkit.SqlReservedWords;
import com.baomidou.mybatisplus.toolkit.TableInfoHelper;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.scripting.defaults.RawSqlSource;
import org.apache.ibatis.session.Configuration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* SQL 自动注入器
*
*
* @author hubin sjy
* @Date 2016-09-09
*/
public class AutoSqlInjector implements ISqlInjector {
private static final Log logger = LogFactory.getLog(AutoSqlInjector.class);
protected Configuration configuration;
protected LanguageDriver languageDriver;
protected MapperBuilderAssistant builderAssistant;
protected DBType dbType = DBType.MYSQL;
/**
* CRUD注入后给予标识 注入过后不再注入
*
* @param builderAssistant
* @param mapperClass
*/
public void inspectInject(MapperBuilderAssistant builderAssistant, Class> mapperClass) {
String className = mapperClass.toString();
Set mapperRegistryCache = GlobalConfiguration.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
inject(builderAssistant, mapperClass);
mapperRegistryCache.add(className);
}
}
/**
* 注入单点 crudSql
*/
public void inject(MapperBuilderAssistant builderAssistant, Class> mapperClass) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = configuration.getDefaultScriptingLanuageInstance();
GlobalConfiguration globalCache = GlobalConfiguration.GlobalConfig(configuration);
this.dbType = globalCache.getDbType();
/*
* 驼峰设置 PLUS 配置 > 原始配置
*/
if (!globalCache.isDbColumnUnderline()) {
globalCache.setDbColumnUnderline(configuration.isMapUnderscoreToCamelCase());
}
Class> modelClass = extractModelClass(mapperClass);
TableInfo table = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
/**
* 没有指定主键,默认方法不能使用
*/
if (null != table && null != table.getKeyProperty()) {
/* 插入 */
this.injectInsertOneSql(mapperClass, modelClass, table);
/* 删除 */
this.injectDeleteSql(mapperClass, modelClass, table);
this.injectDeleteByMapSql(mapperClass, table);
this.injectDeleteByIdSql(false, mapperClass, modelClass, table);
this.injectDeleteByIdSql(true, mapperClass, modelClass, table);
/* 修改 */
this.injectUpdateByIdSql(mapperClass, modelClass, table);
this.injectUpdateSql(mapperClass, modelClass, table);
/* 查询 */
this.injectSelectByIdSql(false, mapperClass, modelClass, table);
this.injectSelectByIdSql(true, mapperClass, modelClass, table);
this.injectSelectByMapSql(mapperClass, modelClass, table);
this.injectSelectOneSql(mapperClass, modelClass, table);
this.injectSelectCountSql(mapperClass, modelClass, table);
this.injectSelectListSql(SqlMethod.SELECT_LIST, mapperClass, modelClass, table);
this.injectSelectListSql(SqlMethod.SELECT_PAGE, mapperClass, modelClass, table);
/* 自定义方法 */
this.inject(configuration, builderAssistant, mapperClass, modelClass, table);
} else {
/**
* 警告
*/
logger.warn(String.format("%s ,Not found @TableId annotation, cannot use mybatis-plus curd method.",
modelClass.toString()));
}
}
/**
* 自定义方法,注入点(子类需重写该方法)
*/
public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class> mapperClass,
Class> modelClass, TableInfo table) {
// to do nothing
}
protected Class> extractModelClass(Class> mapperClass) {
Type[] types = mapperClass.getGenericInterfaces();
ParameterizedType target = null;
for (Type type : types) {
if (type instanceof ParameterizedType && BaseMapper.class.isAssignableFrom(mapperClass)) {
target = (ParameterizedType) type;
break;
}
}
Type[] parameters = target.getActualTypeArguments();
Class> modelClass = (Class>) parameters[0];
return modelClass;
}
/**
*
* 注入插入 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectInsertOneSql(Class> mapperClass, Class> modelClass, TableInfo table) {
/*
* INSERT INTO table
* xx, #{xx},
*
*/
KeyGenerator keyGenerator = new NoKeyGenerator();
StringBuilder fieldBuilder = new StringBuilder();
StringBuilder placeholderBuilder = new StringBuilder();
fieldBuilder.append("\n\n");
placeholderBuilder.append("\n\n");
String keyProperty = null;
String keyColumn = null;
if (table.getIdType() == IdType.AUTO) {
/* 自增主键 */
keyGenerator = new Jdbc3KeyGenerator();
keyProperty = table.getKeyProperty();
keyColumn = table.getKeyColumn();
} else {
/* 用户输入自定义ID */
fieldBuilder.append(table.getKeyColumn()).append(",");
placeholderBuilder.append("#{").append(table.getKeyProperty()).append("},");
}
List fieldList = table.getFieldList();
for (TableFieldInfo fieldInfo : fieldList) {
fieldBuilder.append(convertIfTagIgnored(fieldInfo, false));
fieldBuilder.append(fieldInfo.getColumn()).append(",");
fieldBuilder.append(convertIfTagIgnored(fieldInfo, true));
placeholderBuilder.append(convertIfTagIgnored(fieldInfo, false));
placeholderBuilder.append("#{").append(fieldInfo.getEl()).append("},");
placeholderBuilder.append(convertIfTagIgnored(fieldInfo, true));
}
fieldBuilder.append("\n");
placeholderBuilder.append("\n");
SqlMethod sqlMethod = SqlMethod.INSERT_ONE;
String sql = String.format(sqlMethod.getSql(), table.getTableName(), fieldBuilder.toString(),
placeholderBuilder.toString());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addInsertMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource, keyGenerator, keyProperty,
keyColumn);
}
/**
*
* 注入 entity 条件删除 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectDeleteSql(Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.DELETE;
String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlWhereEntityWrapper(table));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
}
/**
*
* 注入 map 条件删除 SQL 语句
*
*
* @param mapperClass
* @param table
*/
protected void injectDeleteByMapSql(Class> mapperClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.DELETE_BY_MAP;
String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlWhereByMap());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Map.class);
this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
}
/**
*
* 注入删除 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectDeleteByIdSql(boolean batch, Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.DELETE_BY_ID;
SqlSource sqlSource = null;
if (batch) {
sqlMethod = SqlMethod.DELETE_BATCH_BY_IDS;
StringBuilder ids = new StringBuilder();
ids.append("\n");
ids.append("#{item}");
ids.append("\n");
String sql = String.format(sqlMethod.getSql(), table.getTableName(), table.getKeyColumn(), ids.toString());
sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
} else {
String sql = String.format(sqlMethod.getSql(), table.getTableName(), table.getKeyColumn(), table.getKeyColumn());
sqlSource = new RawSqlSource(configuration, sql, Object.class);
}
this.addDeleteMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource);
}
/**
*
* 注入更新 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectUpdateByIdSql(Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.UPDATE_BY_ID;
String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(table, null), table.getKeyColumn(),
table.getKeyProperty());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
}
/**
*
* 注入批量更新 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectUpdateSql(Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.UPDATE;
String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlSet(table, "et."), sqlWhereEntityWrapper(table));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);
}
/**
*
* 注入查询 SQL 语句
*
*
* @param batch
* 是否为批量插入
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectSelectByIdSql(boolean batch, Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.SELECT_BY_ID;
SqlSource sqlSource = null;
if (batch) {
sqlMethod = SqlMethod.SELECT_BATCH_BY_IDS;
StringBuilder ids = new StringBuilder();
ids.append("\n");
ids.append("#{item}");
ids.append("\n");
sqlSource = languageDriver.createSqlSource(configuration, String.format(sqlMethod.getSql(),
sqlSelectColumns(table, false), table.getTableName(), table.getKeyColumn(), ids.toString()), modelClass);
} else {
sqlSource = new RawSqlSource(configuration, String.format(sqlMethod.getSql(), sqlSelectColumns(table, false),
table.getTableName(), table.getKeyColumn(), table.getKeyProperty()), Object.class);
}
this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
}
/**
*
* 注入 map 查询 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectSelectByMapSql(Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.SELECT_BY_MAP;
String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(), sqlWhereByMap());
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, Map.class);
this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
}
/**
*
* 注入实体查询一条记录 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectSelectOneSql(Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.SELECT_ONE;
String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, false), table.getTableName(),
sqlWhere(table, false));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
}
/**
*
* 注入EntityWrapper方式查询记录列表 SQL 语句
*
*
* @param sqlMethod
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectSelectListSql(SqlMethod sqlMethod, Class> mapperClass, Class> modelClass, TableInfo table) {
String sql = String.format(sqlMethod.getSql(), sqlSelectColumns(table, true), table.getTableName(),
sqlWhereEntityWrapper(table));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, modelClass, table);
}
/**
*
* 注入EntityWrapper查询总记录数 SQL 语句
*
*
* @param mapperClass
* @param modelClass
* @param table
*/
protected void injectSelectCountSql(Class> mapperClass, Class> modelClass, TableInfo table) {
SqlMethod sqlMethod = SqlMethod.SELECT_COUNT;
String sql = String.format(sqlMethod.getSql(), table.getTableName(), sqlWhereEntityWrapper(table));
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource, Integer.class, null);
}
/**
*
* EntityWrapper方式获取select where
*
*
* @param table
* @return String
*/
protected String sqlWhereEntityWrapper(TableInfo table) {
StringBuilder where = new StringBuilder("\n");
where.append("\n\n");
where.append("\n\n");
where.append(table.getKeyColumn()).append("=#{ew.entity.").append(table.getKeyProperty()).append("}");
where.append("\n");
List fieldList = table.getFieldList();
for (TableFieldInfo fieldInfo : fieldList) {
where.append(convertIfTag(fieldInfo, "ew.entity.", false));
where.append(" AND ").append(fieldInfo.getColumn()).append("=#{ew.entity.").append(fieldInfo.getEl()).append("}");
where.append(convertIfTag(fieldInfo, true));
}
where.append("\n\n");
where.append("\n\n${ew.sqlSegment}\n");
where.append("\n");
return where.toString();
}
/**
*
* SQL 更新 set 语句
*
*
* @param table
* @param prefix
* 前缀
* @return
*/
protected String sqlSet(TableInfo table, String prefix) {
StringBuilder set = new StringBuilder();
set.append("");
List fieldList = table.getFieldList();
for (TableFieldInfo fieldInfo : fieldList) {
set.append(convertIfTag(true, fieldInfo, prefix, false));
set.append(fieldInfo.getColumn()).append("=#{");
if (null != prefix) {
set.append(prefix);
}
set.append(fieldInfo.getEl()).append("},");
set.append(convertIfTag(true, fieldInfo, null, true));
}
set.append("\n");
return set.toString();
}
/**
*
* 获取需要转义的SQL字段
*
*
* @param convertStr
* @return
*/
protected String sqlWordConvert(String convertStr) {
DBType dbType = GlobalConfiguration.getDbType(configuration);
return SqlReservedWords.convert(dbType, convertStr);
}
/**
*
* SQL 查询所有表字段
*
*
* @param table
* @param entityWrapper
* 是否为包装类型查询
* @return
*/
protected String sqlSelectColumns(TableInfo table, boolean entityWrapper) {
StringBuilder columns = new StringBuilder();
if (null != table.getResultMap()) {
/*
* 存在 resultMap 映射返回
*/
if (entityWrapper) {
columns.append("${ew.sqlSelect}");
}
columns.append("*");
if (entityWrapper) {
columns.append("");
}
} else {
/*
* 普通查询
*/
if (entityWrapper) {
columns.append("${ew.sqlSelect}");
}
if (table.isKeyRelated()) {
columns.append(table.getKeyColumn()).append(" AS ").append(sqlWordConvert(table.getKeyProperty()));
} else {
columns.append(sqlWordConvert(table.getKeyProperty()));
}
List fieldList = table.getFieldList();
for (TableFieldInfo fieldInfo : fieldList) {
columns.append(",").append(fieldInfo.getColumn());
if (fieldInfo.isRelated()) {
columns.append(" AS ").append(sqlWordConvert(fieldInfo.getProperty()));
}
}
if (entityWrapper) {
columns.append("");
}
}
/*
* 返回所有查询字段内容
*/
return columns.toString();
}
/**
*
* SQL 查询条件
*
*
* @param table
* @param space
* 是否为空判断
* @return
*/
protected String sqlWhere(TableInfo table, boolean space) {
StringBuilder where = new StringBuilder();
if (space) {
where.append("\n");
}
where.append("\n");
where.append("\n\n");
where.append(table.getKeyColumn()).append("=#{ew.").append(table.getKeyProperty()).append("}");
where.append("\n");
List fieldList = table.getFieldList();
for (TableFieldInfo fieldInfo : fieldList) {
where.append(convertIfTag(fieldInfo, "ew.", false));
where.append(" AND ").append(fieldInfo.getColumn()).append("=#{ew.").append(fieldInfo.getEl()).append("}");
where.append(convertIfTag(fieldInfo, true));
}
where.append("\n");
if (space) {
where.append("\n");
}
return where.toString();
}
/**
*
* SQL map 查询条件
*
*/
protected String sqlWhereByMap() {
StringBuilder where = new StringBuilder();
where.append("\n");
where.append("\n");
where.append("\n");
where.append("\n");
if (DBType.MYSQL.equals(dbType)) {
where.append("\n`${k}` = #{cm[${k}]}");
} else {
where.append("\n${k} = #{cm[${k}]}");
}
where.append("\n");
where.append("\n");
where.append("\n");
where.append("\n");
return where.toString();
}
/**
*
* IF 条件转换方法
*
*
* @param ignored
* 允许忽略
* @param fieldInfo
* 字段信息
* @param prefix
* 条件前缀
* @param colse
* 是否闭合标签
* @return
*/
protected String convertIfTag(boolean ignored, TableFieldInfo fieldInfo, String prefix, boolean colse) {
/* 忽略策略 */
FieldStrategy fieldStrategy = fieldInfo.getFieldStrategy();
if (fieldStrategy == FieldStrategy.IGNORED) {
if (ignored) {
return "";
}
//TODO 考虑日期类型忽略
// 查询策略,使用全局策略
fieldStrategy = GlobalConfiguration.GlobalConfig(configuration).getFieldStrategy();
}
// 关闭标签
if (colse) {
return "";
}
/* 前缀处理 */
String property = fieldInfo.getProperty();
if (null != prefix) {
property = prefix + property;
}
// 验证逻辑
if (fieldStrategy == FieldStrategy.NOT_EMPTY) {
return String.format("\n\t", property, property);
} else {
// FieldStrategy.NOT_NULL
return String.format("\n\t", property);
}
}
protected String convertIfTagIgnored(TableFieldInfo fieldInfo, boolean colse) {
return convertIfTag(true, fieldInfo, null, colse);
}
protected String convertIfTag(TableFieldInfo fieldInfo, String prefix, boolean colse) {
return convertIfTag(false, fieldInfo, prefix, colse);
}
protected String convertIfTag(TableFieldInfo fieldInfo, boolean colse) {
return convertIfTag(fieldInfo, null, colse);
}
/*
* 查询
*/
public MappedStatement addSelectMappedStatement(Class> mapperClass, String id, SqlSource sqlSource, Class> resultType,
TableInfo table) {
if (null != table) {
String resultMap = table.getResultMap();
if (null != resultMap) {
/* 返回 resultMap 映射结果集 */
return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, resultMap, null,
new NoKeyGenerator(), null, null);
}
}
/* 普通查询 */
return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.SELECT, null, null, resultType,
new NoKeyGenerator(), null, null);
}
/*
* 插入
*/
public MappedStatement addInsertMappedStatement(Class> mapperClass, Class> modelClass, String id, SqlSource sqlSource,
KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.INSERT, modelClass, null, Integer.class,
keyGenerator, keyProperty, keyColumn);
}
/*
* 删除
*/
public MappedStatement addDeleteMappedStatement(Class> mapperClass, String id, SqlSource sqlSource) {
return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.DELETE, null, null, Integer.class,
new NoKeyGenerator(), null, null);
}
/*
* 更新
*/
public MappedStatement addUpdateMappedStatement(Class> mapperClass, Class> modelClass, String id, SqlSource sqlSource) {
return this.addMappedStatement(mapperClass, id, sqlSource, SqlCommandType.UPDATE, modelClass, null, Integer.class,
new NoKeyGenerator(), null, null);
}
public MappedStatement addMappedStatement(Class> mapperClass, String id, SqlSource sqlSource,
SqlCommandType sqlCommandType, Class> parameterClass, String resultMap, Class> resultType,
KeyGenerator keyGenerator, String keyProperty, String keyColumn) {
String statementName = mapperClass.getName() + "." + id;
if (configuration.hasStatement(statementName)) {
System.err.println("{" + statementName
+ "} Has been loaded by XML or SqlProvider, ignoring the injection of the SQL.");
return null;
}
/* 缓存逻辑处理 */
boolean isSelect = false;
if (sqlCommandType == SqlCommandType.SELECT) {
isSelect = true;
}
return builderAssistant.addMappedStatement(id, sqlSource, StatementType.PREPARED, sqlCommandType, null, null, null,
parameterClass, resultMap, resultType, null, !isSelect, isSelect, false, keyGenerator, keyProperty, keyColumn,
configuration.getDatabaseId(), languageDriver, null);
}
}