/** * 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); } }