51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

Mybatis的原理是什么?

嗨,你好呀,我是猿java

MyBatis 是一款优秀的持久层框架,它通过简化 JDBC操作和提供灵活的 SQL映射方式,使 Java 开发人员能够更高效地进行数据库操作。那么,MyBatis的执行原理是什么?这篇文章我们将深入地分析。

  1. MyBatis 配置解析 {#1-MyBatis-配置解析} =================================

MyBatis 的配置文件通常包括全局配置文件(mybatis-config.xml)和映射文件(XXXMapper.xml)。全局配置文件主要用于配置数据源和其他全局性的信息,而映射文件则用于定义 SQL 语句。

1.1 全局配置文件解析 {#1-1-全局配置文件解析}

全局配置文件在 MyBatis 启动时被解析。SqlSessionFactoryBuilder 是 MyBatis 解析配置文件的入口点。它通过 build 方法接收一个 Reader 或 InputStream,然后调用 XMLConfigBuilder 来解析 XML 配置文件。

|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } |

XMLConfigBuilder 解析配置文件并构建出 Configuration 对象,该对象包含了 MyBatis 的所有配置信息。

1.2 映射文件解析 {#1-2-映射文件解析}

映射文件中定义了 SQL 语句,通过 XMLMapperBuilder 进行解析。每个 <mapper> 标签对应一个 MappedStatement 对象,MappedStatement 包含了 SQL 语句、输入输出参数类型、结果集映射等信息。

|------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 | public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } |

  1. SQL 语句解析 {#2-SQL-语句解析} =========================

MyBatis 支持动态 SQL,通过 <if>, <choose>, <foreach> 等标签,可以根据不同的条件构造 SQL。动态 SQL 是 MyBatis 的一大特色,通过 SqlSource 接口实现。SqlSource 的主要实现类有 StaticSqlSource, DynamicSqlSource, RawSqlSource 等。

2.1 动态 SQL 解析 {#2-1-动态-SQL-解析}

DynamicSqlSource 是处理动态 SQL 的核心类。它通过 SqlNode 树来表示 SQL 语句的结构,SqlNode 是一个接口,常用的实现类有 IfSqlNode, ChooseSqlNode, WhereSqlNode 等。每个 SqlNodeapply 方法负责将节点转换为 SQL 字符串。

|---------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class DynamicSqlSource implements SqlSource { private final Configuration configuration; private final SqlNode rootSqlNode; public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) { this.configuration = configuration; this.rootSqlNode = rootSqlNode; } @Override public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); return sqlSource.getBoundSql(parameterObject); } } |

  1. 参数设置 {#3-参数设置} =================

在获得最终的 SQL 语句后,MyBatis 需要将参数传递给 SQL 语句。ParameterHandler 接口负责这项工作,默认实现是 DefaultParameterHandler

|------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | public class DefaultParameterHandler implements ParameterHandler { private final TypeHandlerRegistry typeHandlerRegistry; private final MappedStatement mappedStatement; private final Object parameterObject; private final BoundSql boundSql; private final Configuration configuration; public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) { this.mappedStatement = mappedStatement; this.configuration = mappedStatement.getConfiguration(); this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry(); this.parameterObject = parameterObject; this.boundSql = boundSql; } @Override public void setParameters(PreparedStatement ps) throws SQLException { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) { jdbcType = configuration.getJdbcTypeForNull(); } typeHandler.setParameter(ps, i + 1, value, jdbcType); } } } } } |

  1. SQL 执行 {#4-SQL-执行} =====================

SQL 执行是 MyBatis 的核心功能之一。Executor 接口定义了执行操作的基本方法,主要的实现类有 SimpleExecutor, ReuseExecutor, BatchExecutor。这些执行器通过 StatementHandler 执行 SQL 语句。

|------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class SimpleExecutor extends BaseExecutor { public SimpleExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); } @Override public int doUpdate(MappedStatement ms, Object parameter) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.update(stmt); } finally { closeStatement(stmt); } } } |

  1. 结果处理 {#5-结果处理} =================

MyBatis 提供了强大的结果集映射功能,允许将 SQL 查询结果映射为 Java 对象。ResultSetHandler 接口负责处理结果集,DefaultResultSetHandler 是其主要实现类。

|---------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class DefaultResultSetHandler implements ResultSetHandler { private final TypeHandlerRegistry typeHandlerRegistry; private final ObjectFactory objectFactory; private final boolean useConstructorMappings; private final ReflectorFactory reflectorFactory; private final MappedStatement mappedStatement; private final RowBounds rowBounds; private final ParameterHandler parameterHandler; private final ResultHandler<?> resultHandler; private final BoundSql boundSql; public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, ParameterHandler parameterHandler, ResultHandler<?> resultHandler, BoundSql boundSql, RowBounds rowBounds) { this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry(); this.objectFactory = mappedStatement.getConfiguration().getObjectFactory(); this.useConstructorMappings = mappedStatement.getConfiguration().isUseConstructorMappings(); this.reflectorFactory = mappedStatement.getConfiguration().getReflectorFactory(); this.mappedStatement = mappedStatement; this.rowBounds = rowBounds; this.parameterHandler = parameterHandler; this.resultHandler = resultHandler; this.boundSql = boundSql; } @Override public List<Object> handleResultSets(Statement stmt) throws SQLException { final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } return collapseSingleResultList(multipleResults); } } |

  1. 总结 {#6-总结} =============

本文,我们通过核心源码分析了 MyBatis, 它是一个轻量级的 ORM框架,它通过配置文件和注解将 Java 对象与数据库记录映射起来,其核心在于通过 XML和注解配置 SQL语句,利用执行器执行 SQL,并通过结果集处理器将结果映射为 Java对象。

MyBatis的设计使得开发者可以专注于 SQL本身,而不必关心底层 JDBC操作的细节,了解和掌握其执行原理和设计模式,可以帮组我们在实际应用中更好地使用 MyBatis。

  1. 交流学习 {#7-交流学习} =================

最后,把猿哥的座右铭送给你:投资自己才是最大的财富。 如果你觉得文章有帮助,请帮忙转发给更多的好友,或关注公众号:猿java,持续输出硬核文章。

赞(2)
未经允许不得转载:工具盒子 » Mybatis的原理是什么?