人非要经历一番不同平时的劫难才能脱胎换骨,成为真正能解决问题的人
简介初始化过程1.解析XML配置文件1.1 Config文件的解析1.2 Mapper文件的解析1.2.1 解析CURD模板1.2.2 绑定Mapper到命名空间2.创建SqlSessionFactory总结
简介
首先我们再回顾下Mybaits的基本使用。
//加载配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //SqlSession 的获取 SqlSession sqlSession = sqlSessionFactory.openSession(); try{ //执行sql User user = sqlSession.selectOne("MyMapper.selectUser", 1);//(SQL通过命名空间+SQLID 的格式定位) }finally { sqlSession .close(); }
一切都从SqlSessionFactoryBuilder说起。SqlSessionFactoryBuilder是通过builder设计模式来创建一个SqlSessionFactory 工厂。
创建SqlSessionFactory 最主要分为两步,
public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { // 1.xml配置文件解析器。 XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); Configuration config = parser.parse() // 2.根据解析到的配置,创建DefaultSqlSessionFactory return build(config); } //创建默认的sqlsesion工厂。 public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
初始化过程
1.解析XML配置文件
SqlSessionFactoryBuilder的第一步就是配置的解析。
配置文件主要分为两种:
- Conifg文件: 包括数据连接配置,全局设置配置。
- Mapper文件:用于SQL的统一管理,对SQL的配置。
1.1 Config文件的解析
Config文件的解析是由XMLConfigBuilder做的。
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration());//创建一个Configuration对象 ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
我们来看看parse()方法
public Configuration parse() { parsed = true; //从根节点开始解析 parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { // 解析 properties配置 propertiesElement(root.evalNode("properties")); // 解析 settings配置 Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); //解析environment 配置 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); // 解析 mapper 配置 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
XMLConfigBuilder 根据约定,解析config 配置文件中的各个标签。并将相关配置信息放到Configuration 对象中返回。
列如:
Properties settings = settingsAsProperties(root.evalNode("settings"));//解析settings标签 settingsElement(settings);//设置settings配置到configuration 对象。
1.2 Mapper文件的解析
在解析Config配置文件过程中,会伴随Mapper.xml文件的解析。这个解析的工作是由XMLMapperBuilder 完成的。
config中的mapper文件位置配置
<mappers> <mapper resource="mappers/UserMapper.xml"/> </mappers>
Mapper.xml
<mapper namespace="com.wqd.dao.UserMapper"> <select id="selectUser" resultType="com.wqd.model.User"> select * from user where id= #{id} </select> </mapper>
XMLMapperBuilder
public void parse() { if (!configuration.isResourceLoaded(resource)) { //1.从根标签mapper开始解析mapper文件 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); //2.绑定mapper到命名空间 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } //1.解析mapper文件 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")); //解析sql标签配置的sql 片段 sqlElement(context.evalNodes("/mapper/sql")); //解析select|insert|update|delete 标签配置的sql 模板(重点) buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } } //2.绑定命名空间 private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { } if (boundType != null) {//如果有对应的Class if (!configuration.hasMapper(boundType)) { configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType);//添加到缓存 } } } }
这里我们重点讲讲XMLMapperBuilder 中有两个重要的点
1.2.1 解析CURD模板
也就是解析select|insert|update|delete
代表的SQL 模板。这四种标签配置的SQL模板是我们操作数据库时的SQL执行语句的模板。 我们可以理解为:一个select 标签表示一类动态SQL。
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
每一个select|insert|update|delete
由XMLStatementBuilder 解析
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { //解析"select|insert|update|delete"标签 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } } }