当前位置: 首页 > news >正文

咸阳住房和城乡建设规划局网站建设和住房保障部 网站

咸阳住房和城乡建设规划局网站,建设和住房保障部 网站,企业征信系统查询官网,泰安哪里有做网站app的背景 在项目中需要同时操作Sql Server 以及 MySQL 数据库#xff0c;可能平时直接使用 BaseMapper中提供的方法习惯 了#xff0c;不用的话总感觉影响开发效率#xff0c;但是两个数据库的SQL语法稍微有点差别#xff0c;有些暴露的方法并不能直接使用#xff0c;所以便想…背景 在项目中需要同时操作Sql Server 以及 MySQL 数据库可能平时直接使用 BaseMapper中提供的方法习惯 了不用的话总感觉影响开发效率但是两个数据库的SQL语法稍微有点差别有些暴露的方法并不能直接使用所以便想提前注入相关SQL。果然偷懒是第一生产力 方案 那怎么解决这种问题呢理论上这种成熟的框架应该会提供相关的解决方案所以我当场去翻Mybatis的官方文档果然找到一个SQL注入器功能。 SQL注入器从文档中来看好像确实看不出来是什么…其实也不难理解就是将自定义的方法注入到MP中这样便可以直接调用。不要问我为什么不直接写到XML文件里都说了我想偷懒每个XML文件里面都写一遍不累的吗 在这里还是要吐槽下Mybatis的官方文档是真的简洁 ~~~ 还好提供了一个完整案例。 SQL注入器案例 SQL注入器简单使用 看完官方案例后我们可以将步骤分为以下几步 自定义方法类自定义SQL模板编写注册类注册自定义方法类定义继承类该类需要继承BaseMapper同时写入自定义方法将注册类交给Spring容器管理或者在全局配置中处理Mapper层继承自定义继承类即可使用新方法 自定义方法类 该类的主要作用就是自定义SQL模板我们可以创建多个注册到MP中。 首先需要继承AbstractMethod然后重写injectMappedStatement方法这里的SQL模板我是直接使用的SELECT_BY_ID方法的SQL模板只不过在后面加了一个limit 1虽然实际上并没有啥效果但是就是一个简单测试只要控制台能将其输出就说明我成功了手动狗头。 public class FindOneById extends AbstractMethod {public FindOneById() {this(findOneById);}public FindOneById(String methodName) {super(methodName);}Overridepublic MappedStatement injectMappedStatement(Class? mapperClass, Class? modelClass, TableInfo tableInfo) {SqlSource sqlSource new RawSqlSource(configuration, String.format(SELECT %s FROM %s WHERE %s#{%s} limit 1,sqlSelectColumns(tableInfo, false),tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty()), Object.class);return this.addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, tableInfo);} }注册类 接下来我们编写一个注入类将自定义方法注入到MP中。这里我们直接继承DefaultSqlInjector类当然你也可以继承AbstractSqlInjector类两者的区别在于AbstractSqlInjector更加灵活能直接将SQL注入到MP中DefaultSqlInjector中则已经实现了一些常用的SQL操作。 以下代码我们可以看到获取DefaultSqlInjector封装好的方法列表然后将我们自定义的方法添加进去即可同时我这里已经通过Component注解将该注册类交给容器管理了。 Component // 交给容器管理 public class Inject extends DefaultSqlInjector {Overridepublic ListAbstractMethod getMethodList(Class? mapperClass, TableInfo tableInfo) {ListAbstractMethod methodList super.getMethodList(mapperClass, tableInfo);// 自定义methodList.add(new FindOneById());return methodList;}}Mapper 想要使用自定义方法我们还需要自定义一个Mapper类供外部继承在其中写入我们的自定义方法。继承BaseMapper的原因在于能继续使用原本提供的方法。 public interface MyBaseMapperT extends BaseMapperT {T findOneById(Serializable id);}测试 上述步骤都完成后就可以进行测试了。首先编写我们的业务Mapper层继承我们自定义的MyBaseMapper Repository //持久层注解表示该类交给Springboot管理 public interface UserMapper extends MyBaseMapperUser {}然后编写测试类查看效果 SpringBootTest class SpringbootMybatisInjectorApplicationTests {Autowiredprivate UserMapper userMapper;Testvoid contextLoads() {User user userMapper.findOneById(1);System.out.println(user);}}由此可见我们的自定义SQL注入成功至此大功告成。 深度剖析 你以为就这么结束了都看了这篇文章了还不直接把SQL注入器彻底搞懂 SQL是如何注入的 那么SQL到底是如何注入的呢我们先来看SQL注入器的顶级接口ISqlInjector怎么知道它是顶级接口的在万能的IDEA里面直接通过我们自定义的注入器查看其UML图就知道了 或者我们可以直接查看MP的源码在injector目录下 如果这个时候你查看了methods目录下的内容就会发现其实这些内容就等同于我们自定义的SQL方法这些都是MP帮我们实现好的内置SQL方法。 回归正题那么ISqlInjector的作用是什么呢它只做一件事那就是检查SQL是否注入已经注入过则不再注入。 public interface ISqlInjector {// 检查SQL是否注入已经注入过则不再注入。void inspectInject(MapperBuilderAssistant builderAssistant, Class? mapperClass); }按照UML图我们接下来应该看其下一层抽象类AbstractSqlInjector。该类主要为两个方法 inspectInject实现顶层接口方法分为以下几个步骤 通过反射获取实体类对象获取mapperRegistry缓存用于对比防止覆盖封装 TableInfo 存储表信息对象循环注入自定义方法 getMethodList获取注入的方法为相关实现类提供的模板方法 public abstract class AbstractSqlInjector implements ISqlInjector {protected final Log logger LogFactory.getLog(this.getClass());public AbstractSqlInjector() {}public void inspectInject(MapperBuilderAssistant builderAssistant, Class? mapperClass) {// 通过反射获取实体类对象Class? modelClass ReflectionKit.getSuperClassGenericType(mapperClass, Mapper.class, 0);if (modelClass ! null) {String className mapperClass.toString();//获取mapperRegistry缓存用于对比防止覆盖SetString mapperRegistryCache GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());if (!mapperRegistryCache.contains(className)) {// 封装TableInfo 存储表信息对象TableInfo tableInfo TableInfoHelper.initTableInfo(builderAssistant, modelClass);ListAbstractMethod methodList this.getMethodList(mapperClass, tableInfo);if (CollectionUtils.isNotEmpty(methodList)) {// 循环注入自定义方法methodList.forEach((m) - {m.inject(builderAssistant, mapperClass, modelClass, tableInfo);});} else {this.logger.debug(mapperClass.toString() , No effective injection method was found.);}mapperRegistryCache.add(className);}}}// 获取 注入的方法public abstract ListAbstractMethod getMethodList(Class? mapperClass, TableInfo tableInfo); }最后我们来看DefaultSqlInjector作为MP的默认SQL注入器它又做了些什么呢 public class DefaultSqlInjector extends AbstractSqlInjector {public DefaultSqlInjector() {}public ListAbstractMethod getMethodList(Class? mapperClass, TableInfo tableInfo) {BuilderAbstractMethod builder Stream.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 {this.logger.warn(String.format(%s ,Not found TableId annotation, Cannot use Mybatis-Plus xxById Method., tableInfo.getEntityType()));}return (List)builder.build().collect(Collectors.toList());} }从代码可以很清晰的看出DefaultSqlInjector实现了AbstractSqlInjector抽象类的 getMethodList方法在这里将MP默认实现的CRUD方法进行注入。 虽然上面说了一堆但是我相信你还是没懂不如一起debug来看看这里主要看下数据库信息的获取以及方法的注入 获取数据库信息 实体类如下 首先我们可以看到TableInfoHelper.initTableInfo方法获取到了数据库表的相关信息那么它是如何做到的呢我们进入刚方法内查看 通过查看代码我们可以看到初始化数据库信息的方法主要为initTableName以及initTableFields initTableName的作用是获取表名信息主要通过实体上的TableName注解拿到表名所以我们这里拿到了User类名。 initTableFields的作用主要为获取主键及其他字段信息 至此数据库信息则获取成功可供注入SQL方法时使用。 方法注入 获取完表信息后可以发现通过getMethodList方法获取了所有自定义方法这些方法都是哪里来的呢都是从AbstractSqlInjector抽象类的子类中获取的比如默认的SQL注入器DefaultSqlInjector以及我们自定的Inject。 获取所有自定义方法后可以发现通过调用AbstractMethod的inject方法实现了SQL的自动注入这里也把上文获取到的数据库表对象传入用来进行SQL的封装。 injectMappedStatement方法则需要每个SQL类根据各自需求重写最后将生成好的MappedStatement对象加入到全局配置类对象中。 SQL 语句是怎么生成的 AbstractMethod 上面了解了SQL是如何注入后我们再来看下SQL是怎么生成的。我们直接看DefaultSqlInjector提供的默认方法可以发现所有的方法都继承了AbstractMethod。该类主要用于封装Mapper接口中定义的方法信息并提供了一些默认实现。通过继承 AbstractMethod 类并重写其中的方法我们可以自定义生成 SQL 语句的方式从而实现更加灵活的 SQL 操作。 该类我们目前只需要关注inject方法它主要通过injectMappedStatement方法实现了自动注入SQL的动作。injectMappedStatement是一个模板方法每个自定义SQL类都可以对其进行重写然后将封装好的sql存放到全局配置文件类中。 /*** 抽象的注入方法类*/ public abstract class AbstractMethod implements Constants {protected static final Log logger LogFactory.getLog(AbstractMethod.class);protected Configuration configuration;protected LanguageDriver languageDriver;protected MapperBuilderAssistant builderAssistant;/*** 方法名称*/protected final String methodName;/*** param methodName 方法名*/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);}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) {/* 不存在排序字段直接返回空 */ListTableFieldInfo 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(ListTableFieldInfo fieldList, PredicateTableFieldInfo predicate,FunctionTableFieldInfo, String function, String joiningVal) {StreamTableFieldInfo 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());}}/*** 查询*/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);}/*** 查询*/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);}/*** 插入*/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);}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);}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 Mybatiss 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);}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);}这里我们以SelectById类作为案例来对其分析。 首先可以看到在构造方法中将SqlMethod枚举类中定义好的方法名传入到父类中方便后续使用同时重写injectMappedStatement方法通过SQL模板构建出SQL语句并存入到全局配置类中。 public class SelectById extends AbstractMethod {public SelectById() {//给methodName属性赋值this(SqlMethod.SELECT_BY_ID.getMethod());}public SelectById(String name) {//给methodName属性赋值super(name);}public MappedStatement injectMappedStatement(Class? mapperClass, Class? modelClass, TableInfo tableInfo) {//获取SqlMethod类SqlMethod sqlMethod SqlMethod.SELECT_BY_ID;//生成SqlSource对象SqlSource sqlSource new RawSqlSource(this.configuration, String.format(sqlMethod.getSql(), this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(), tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, true)), Object.class);//将MappedStatement对象加入配置类对象中return this.addSelectMappedStatementForTable(mapperClass, this.methodName, sqlSource, tableInfo);} }SqlMethod SqlMethod就是一个枚举类存储了两个关键的元素 BaseMapper中的方法名方法名对应的sql语句模板 看到这两个元素相信大家应该已经知道SQL自动生成的本质了根据不同的方法来提供一些通用的模板项目启动后再将其加载进mappedStatement。 SqlSource SqlSource对象里面则是通过解析SQL模板、以及传入的表信息和主键信息构建出了一条SQL语句 可能会有人疑惑这里的表信息是从何而来其实这些表信息就是在SQL注入的时候获取的表信息然后传到AbstractMethod中的所以在重写injectMappedStatement方法的时候就可以使用到了。 Mapper文件被添加的过程 看完上述内容相信大家应该都知道了SQL注入器的基本原理了那么SQL注入器是在哪里添加到Mybatis中的呢如果不太清楚的话我们带着问题往下看。 首先我们回顾下Mybatis 的执行流程一般可以分为以下几个步骤 加载配置文件在应用启动时Mybatis 会读取配置文件mybatis-config.xml并解析其中的配置信息例如数据库连接信息、映射器信息等。创建 SqlSessionFactory通过SqlSessionFactoryBuilder 类加载配置文件中的信息并创建 SqlSessionFactory 对象。SqlSessionFactory 是一个重量级的对象它的作用是创建 SqlSession 对象SqlSession 是用于执行 SQL 语句的核心对象。创建 SqlSession通过 SqlSessionFactory 的 openSession 方法创建 SqlSession 对象。在执行 SQL 操作时我们需要通过 SqlSession 对象获取到对应的 Mapper 接口然后调用该接口中定义的方法来执行 SQL 语句。获取 Mapper 接口在 Mybatis 中我们通常通过 Mapper 接口的方式执行 SQL 操作。因此在获取 Mapper 接口之前我们需要先配置映射关系即在配置文件中指定 Mapper 接口所对应的 XML 文件或注解类。在创建 SqlSession 对象后我们可以通过 SqlSession 的 getMapper 方法获取到对应的 Mapper 接口。执行 SQL 语句当我们获取到 Mapper 接口后就可以通过调用其方法执行 SQL 语句了。在执行 SQL 语句前Mybatis 会将 Mapper 接口中定义的 SQL 语句转换成 MappedStatement 对象并将其中的参数信息传递给 Executor 对象执行 SQL 语句。处理 SQL 语句的执行结果在执行 SQL 语句后Mybatis 会将查询结果封装成对应的 Java 对象并返回。 看完上述流程后你觉得会在那个步骤进行添加SQL注入器的操作呢我盲猜这个步骤应该位于步骤3中那让我们从MP的入口处开始查看源码看看猜测是否正确。 MP入口 可能有些人不知道MP的具体入口从哪里看其实很简单我们可以直接去mybatis-plus-boot-starter中resources下的META-INF文件夹下查看基础的Spring boot 自动装配机制这里不过多说明 # Auto Configure org.springframework.boot.env.EnvironmentPostProcessor\com.baomidou.mybatisplus.autoconfigure.SafetyEncryptProcessor org.springframework.boot.autoconfigure.EnableAutoConfiguration\com.baomidou.mybatisplus.autoconfigure.MybatisPlusLanguageDriverAutoConfiguration,\com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration可以看到配置的自动启动类是MybatisPlusAutoConfiguration 这个类由于实现了InitializingBean接口得到了afterPropertiesSet方法在Bean初始化后会自动调用。 还有三个标注了 ConditionalOnMissingBean 注解的方法说明这些方法在没有配置对应对象时会由SpringBoot创建Bean并且保存到容器中。 所以sqlSessionFactory方法在没有配置SqlSessionFactory时会由SpringBoot创建Bean并且保存到容器中。 MybatisSqlSessionFactoryBean 我们可以发现进入sqlSessionFactory方法后就会实例化MybatisSqlSessionFactoryBean类那么该类到底做了什么呢 public class MybatisSqlSessionFactoryBean implements FactoryBeanSqlSessionFactory, InitializingBean, ApplicationListenerApplicationEvent 可以发现该类实现了三个接口FactoryBeanSqlSessionFactory, InitializingBean, ApplicationListenerApplicationEvent FactoryBean说明用到了工厂模式 InitializingBeanafterPropertiesSet 在属性设置完成时调用在Bean创建完成时调用 ApplicationListener是一个监听器监听的是ApplicationContext初始化或者刷新事件当初始化或者刷新时调用。 这里我们主要看初始化后调用的方法afterPropertiesSet可以发现在该方法中调用了buildSqlSessionFactory方法。 buildSqlSessionFactory 那么buildSqlSessionFactory方法做了些什么呢简单的说就是创建一个SqlSessionFactory实例虽然里面还有很多其他步骤但是不在本文谈论范围内。 我们直接看最重要的部分xmlMapperBuilder.parse() parse方法主要用来解析xml文件bindMapperForNamespace方法则用来解析接口文件。 addMapper是由前面MybatisConfiguration调用的。 这里会解析出对应的类型然后内部调用MybatisMapperRegistry的方法 内部最后是由MybatisMapperAnnotationBuilder去解析的 在这个方法的最后会进行基本的SQL方法注入 可以发现最后又回到了我们最初说到的AbstractSqlInjector类该类帮助我们实现了基本SQL方法的自动注入。 到这里相信大家已经对SQL注入器的原理有了一个清楚的认识了如果还不太理解的话可以从MP入口处开始根据截图的内容自行打断点熟悉下。
http://www.sczhlp.com/news/271808/

相关文章:

  • 音乐网站 源码wordpress导航改哪个php文件夹
  • 谢馥春网站建设的优势客户网站开发全流程图
  • 网站的推广新手容易上手的cms做企业网站
  • 网站可以做砍价软件吗公司商标图案大全
  • 官方网站找做化妆品套盒子网页版网游
  • 网页游戏的网站如何查到网站是谁做的
  • wordpress 站点换域名有字库wordpress
  • 工业设计创意网站wordpress英文语言包
  • 一个人做网站极速网站建设哪家便宜
  • 阳江做网站seo快递公司网站源码
  • 领诺科技网站建设建设php网站
  • 一个网站开发的权限合肥营销网站建设
  • 怎么注册网站啊织梦网站地图模板下载
  • 苏州网站建设书生制作一个介绍洛阳网站
  • 天水网站开发技术招聘毕业设计在线交流平台
  • 天津网站建设推广服务做网站必须要有前台吗
  • 电脑 手机网站建站厦门市建设局网站摇号
  • 网站模版的软件网站推广策略方法
  • 壹像素网站下步我院将建设网站信息保密
  • 全国医院网站建设展会布置效果图
  • 广州培训做网站wordpress商场插件
  • 视频网站开发价格房屋装修设计app
  • 做网站大优惠东莞企业网站制作
  • 做网站界面的软件wordpress文章字体插件
  • 英语_阅读_Robotic_待读
  • 终身授权 + 100% 源码!Java AI 开发选 JBoltAI 框架,AI 应用商用无后顾之忧
  • 2025年南京马桶维修公司权威推荐榜单:唯宝智能马桶/科勒智能马桶盖/杜拉维特马桶服务公司精选
  • qt命令行
  • 网站宽度网站建站金融模板
  • 网站如何做seo排名美食介绍网站模板