深入理解MyBatis(二)
核心处理层 在核心处理层中,实现了包括MyBatis的初始化以及完成一次数据库操作的涉及的全部流程
MyBatis初始化 MyBatis的初始化主要工作是加载并解析mybatis-config.xml配置文件、映射配置文件以及相关的注解信息。
MyBatis的初始化入口是SqlSessionFactoryBuilder.build()方法,此方法会创建XMLConfigBuilder对象来解析mybatis-config.xml配置文件,得到Configuration对象,然后以此创建出DefaultSqlSessionFactory对象,完成初始化。
XMLConfigBuilder XMLConfigBuilder负责解析mybatis-config.xml文件,核心如下
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 47 48 49 50 51 private boolean parsed;private XPathParser parser;private String environment;private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();public Configuration parse () { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once." ); } parsed = true ; parseConfiguration(parser.evalNode("/configuration" )); return configuration; } private void parseConfiguration (XNode root) { try { Properties settings = settingsAsPropertiess(root.evalNode("settings" )); propertiesElement(root.evalNode("properties" )); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases" )); pluginElement(root.evalNode("plugins" )); objectFactoryElement(root.evalNode("objectFactory" )); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory" )); reflectionFactoryElement(root.evalNode("reflectionFactory" )); settingsElement(settings); environmentsElement(root.evalNode("environments" )); databaseIdProviderElement(root.evalNode("databaseIdProvider" )); typeHandlerElement(root.evalNode("typeHandlers" )); mapperElement(root.evalNode("mappers" )); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
XMLMapperBuilder 最重要的解析映射文件。程序入口如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public void parse () { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper" )); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
在configurationElement(parser.evalNode("/mapper"))
方法中,有着各种节点的处理方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void configurationElement (XNode context) { try { String namespace = context.getStringAttribute("namespace" ); if (namespace == null || namespace.equals("" )) { throw new BuilderException("Mapper's namespace cannot be empty" ); } builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref" )); cacheElement(context.evalNode("cache" )); parameterMapElement(context.evalNodes("/mapper/parameterMap" )); resultMapElements(context.evalNodes("/mapper/resultMap" )); sqlElement(context.evalNodes("/mapper/sql" )); buildStatementFromContext(context.evalNodes("select|insert|update|delete" )); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }
当mapper文件解析注册完成以后, 整个MyBatis的初始化也就基本上差不多了。
MyBatis初始化过程总结 其实MyBatis的初始化说白了就是把配置文件和映射文件进行解析的过程。里面如何解析一个节点的代码其实不用特别care,因为那都是一些定义好的规则, 重要的时其中用到的思想,设计模式等。
整个MyBatis初始化看成一个方法,如参是配置文件映射文件,出参是DefaultSqlSessionFactory,而DefaultSqlSessionFactory中最重要的就是Configuration,xml文件解析出来的内容都被注册到了Configuration。然后Configuration影响后续MyBatis的具体行为。
会话创建过程 我们每一次跟数据库通信,都要创建一个会话,我们使用openSession()方法创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public SqlSession openSession () { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null , false ); } private SqlSession openSessionFromDataSource (ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null ; try { final Environment environment = configuration.getEnvironment(); final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
SQL的生成、执行过程 回顾在JDBC中的过程
获取Connection
传入sql语句构建预编译对象
设置参数
执行SQL
从结果集获取数据
在MyBatis中的过程,简单来说
通过sqlSession,创建mapper代理类,执行调用SQL的方法,比如selectOne
获取BoundSql对象,根据参数生成SQL语句
从数据库连接池获取Connection,并创建代理
从Connection中构建预编译对象
设置参数
执行SQL
从结果集获取数据
将数据转换为Java对象
Anything can go right will go right