当前位置: 首页 > 科技观察

MapperXML的解析与注册

时间:2023-03-19 10:47:53 科技观察

一、前言你是如何面对功能迭代的?事实上,很多程序员在刚开始编程或者刚加入一家公司的时候,并没有太多机会去做一个新的项目。它总是在旧项目上迭代更新。在这个过程中,你可能要学习N位前辈留下的各种风格各异的代码片段。在这些纵横交错的流程中,找一个地方,加入自己的ifelse。虽然这么胡乱加了ifelse,但一开始“坏”的心态很不舒服。但是在已经压缩的工期下交付高质量的代码其实是非常困难的,所以部分研发被迫可用,跑起来就够了。不过话虽如此,其实你不可能逐渐清理一堆狗屎,让代码在你手中逐渐清晰、整齐、干净。很多时候是因为自己作为coder的经验不足,不懂系统重构,不懂设计原则,不熟悉业务背景,不清楚产品方向等原因。所以最好的办法就是提高自己的能力。每次接到一个需求,都会有一些技术上的改动。既然是狗屎山,那应该算是打怪升级了。你有越来越多的易于维护和扩展。2.目标在逐步实现Mybatis框架的过程中,首先要有一个目标导向的思路,即如何实现Mybatis的核心逻辑。其实我们可以简单的把这样一个ORM框架的目标描述为为一个接口提供一个代理类。该类包括Mapper,即xml文件中的SQL信息(类型、入参、出参、条件)。解析和处理,这个处理过程就是对数据库进行操作,并将相应的结果返回给接口。如图4-1图4-1ORM框架核心流程那么根据ORM核心流程的执行流程,我们需要继续展开分析Mapper文件,提取出对应的SQL文件就可以了上一章的基础。.而现阶段,当我们调用DAO接口方法时,我们可以在Mapper中返回相应的SQL语句来执行。为了不把整个项目展开,付哥会带着大家一步步完成这些内容,所以本章暂时不会对数据库进行操作,以后会逐步实现。扫描路径以注册映射器并在DefaultSqlSession中使用。那么,当我们将这些命名空间、SQL描述、映射信息统一维护到各个DAO对应的MapperXML文件中后,XML其实就是我们的源头。Mapper映射器的注册和SQL管理可以通过对XML文件的解析和处理来完成。这样方便我们操作和使用。如图4-2所示,图4-2XML文件解析注册处理首先需要定义SqlSessionFactoryBuilder工厂构建器模式类,通过入口IO的方式解析XML文件。目前主要集中在解析SQL部分和注册mapper来连接整个核心流程。文件解析后,会存放在Configuration配置类中。接下来你会看到这个配置类会串联到整个Mybatis进程中。所有的内容存储和阅读都离不开这个类。如果我们在DefaultSqlSession中获取Mapper并执行selectOne,我们还需要在Configuration配置类中进行读取。四、实现1、项目结构mybatis-step-03└──src├──main│└──java│└──cn.bugstack.mybatis│├──binding│├──MapperMethod.java││├──MapperProxy.java││├──MapperProxyFactory.java││└──MapperRegistry.java│├──builder││├──xml││└──XMLConfigBuilder.java││Base?──Builder.java├──io││└──Resources.java│├──mapping││├──MappedStatement.java││└──SqlCommandType.java│└──session│├──defaults│├──DefaultSqlSession。java││└──DefaultSqlSessionFactory.java│├──Configuration.java│├──SqlSession.java│├──SqlSessionFactory.java│└──SqlSessionFactoryBuilder.java└──test├──java│└──cn.bugstack.mybatis.test.dao│├──dao││└──IUserDao.java│├──po││└──User.java│└──ApiTest.java└──resources├──mapper│└──User_Mapper.xml└──mybatis-config-datasource.xml项目源码:https://t.zsxq.com/bmqNFQ7XML解析与注册类实现关系,如图4-2图4-2XML解析与注册类实现关系SqlSessionFactoryBuilder作为整个Mybatis的入口,提供一个构建器工厂,对XML解析处理进行包装,返回对应的SqlSessionFactory处理类来注册XML信息通过解析进入Configuration配置类,然后将Configuration配置类传递给各个逻辑处理类,包括DefaultSqlSession,这样在获取mapper和执行SQL的时候就可以从配置类中获取对应的内容。2.构建SqlSessionFactory构建器工厂源码,参见:cn.bugstack.mybatis.session.SqlSessionFactoryBuilderpublicclassSqlSessionFactoryBuilder{publicSqlSessionFactorybuild(Readerreader){XMLConfigBuilderxmlConfigBuilder=newXMLConfigBuilder(reader);返回生成器(读取器);returnbuilder(xmlCon)}publicSqlSessionFactorybuild(Configurationconfig){返回新的DefaultSqlSessionFactory(config);}}SqlSessionFactoryBuilder是整个Mybatis的入口类,通过指定解析XML的IO来引导整个流程的启动。从这个类开始,新增了两个处理类XMLConfigBuilder和Configuration,分别用来解析XML和衔接整个流程的对象保存操作。接下来我们分别介绍这些新引入的对象。3.XML解析处理源代码详见:cn.bugstack.mybatis.builder.xml.XMLConfigBuilderpublicclassXMLConfigBuilderextendsBaseBuilder{privateElementroot;publicXMLConfigBuilder(Readerreader){//1.调用父类初始化Configurationsuper(newConfiguration());//2.dom4j处理xmlSAXReadersaxReader=newSAXReader();尝试{Documentdocument=saxReader.read(newInputSource(reader));root=document.getRootElement();}catch(DocumentExceptione){e.printStackTrace();}}publicConfigurationparse(){try{//解析镜像mapperElement(root.element("mappers"));}catch(Exceptione){thrownewRuntimeException("解析SQLMapper配置时出错。原因:"+e,e);}返回配置;}privatevoidmapperElement(Elementmappers)throwsException{ListmapperList=mappers.elements("mapper");for(Elemente:mapperList){//解析处理,详见源码//添加解析SQLconfiguration.addMappedStatement(mappedStatement);}//注册Mappermapperconfiguration.addMapper(Resources.classForName(namespace));}}}XMLConfigBuilder的核心操作是初始化Configuration,因为Configuration的使用是最接近解析XML和存储的操作,所以放在这里比较合适,后面是具体的parse()解析操作,解析后的信息通过ConfigurationClasses存储,包括:添加解析SQL,注册Mapper映射器。整体解析配置包括:类型别名,插件,对象工厂,对象包装工厂,设置,环境,类型转换,mappers,但我们还不需要那么多,所以只做一些必要的SQL分析处理。4.通过配置类封装注册机和SQL语句**详见源码(配置项)**:cn.bugstack.mybatis.session.ConfigurationpublicclassConfiguration{/***映射注册机*/protectedMapperRegistrymapperRegistry=newMapperRegistry(this);/***映射语句,存储在Map中*/protectedfinalMapmappedStatements=newHashMap<>();publicvoidaddMapper(Classtype){mapperRegistry.添加映射器(类型);}publicvoidaddMappedStatement(MappedStatementms){mappedStatements.put(ms.getId(),ms);}}在配置类中添加mapper注册机和映射语句的存储;mapper注册机是我们上一章实现的内容,用于注册Mapper映射器锁提供的操作类。另一个MappedStatement是本章新增的SQL信息记录对象,包括记录:SQL类型、SQL语句、输入参数类型、输出参数类型等。具体可以参考源码5.DefaultSqlSession结合配置项获取信息。){MappedStatementmappedStatement=configuration.getMappedStatement(statement);return(T)("你被代理了!"+"\n方法:"+语句+"\n输入参数:"+参数+"\n要执行的SQL:"+mappedStatement.getSql());}@OverridepublicTgetMapper(Classtype){returnconfiguration.getMapper(type,this);}}DefaultSqlSession与上一章相比,傅大哥这里将MapperRegistrymapperRegistry换成Configuration配置,传递更丰富的信息内容,而不仅仅是注册表操作。之后在DefaultSqlSession#selectOne和DefaultSqlSession#getMapper这两个方法中使用配置获取相应的信息。目前selectOne方法只打印获取到的信息,后面会介绍SQL执行器查询返回结果。五、测试1.提前准备提供DAO接口和对应的Mapperxml配置publicinterfaceIUserDao{StringqueryUserInfoById(StringuId);SELECTid,userId,userHead,createTimeFROMuserwhereid=#{id}2.单元测试@Testpublicvoidtest_SqlSessionFactory()throwsIOException{//1.从SqlSessionFactoryreader=Resources.getResourceAsReader("mybatis-config-datasource.xml")获取SqlSessionReader;SqlBuildSessionFactorysqlSessionFactory=newSqlSessionFactory(newSqlSessionFactory).build(阅读器);SqlSessionsqlSession=sqlSessionFactory.openSession();//2.获取映射器对象IUserDaouserDao=sqlSession.getMapper(IUserDao.class);//3.测试验证Stringres=userDao.queryUserInfoById("10001");logger.info("Testresult:{}",res);}目前的用法和Mybatis很相似,通过加载xml配置配置文件,交给SqlSessionFactoryBuilder进行构建和解析,获取SqlSessionFactory工厂,以便顺利开启Session,完成后续操作。测试结果07:07:40.519[main]INFOcn.bugstack.mybatis.test.ApiTest-测试结果:您已被代理!方法:cn.bugstack.mybatis.test.dao.IUserDao.queryUserInfoById输入参数:[Ljava.lang.Object;@23223dd8执行的S??QL:SELECTid,userId,userHead,createTimeFROMuserwhereid=?Processfinishedwithexitcode0从测试结果可以看出,当前的代理操作已经可以打印出我们从XML中解析出来的SQL信息,我们将结合这部分处理继续完成对数据库的操作。6.总结了解ORM处理的核心流程,知道我们所处的步骤和要完成的内容。只有非常清楚代理、封装、解析、返回结果的过程,才能更好的完成整个框架的实现。SqlSessionFactoryBuilder的引入包裹了整个执行过程,包括:XML文件解析,Configuration配置类处理,使得DefaultSqlSession可以更加灵活的获取相应的信息,获取Mapper和SQL语句。另外,从整个项目构建过程中,我们可以看到其中有很多工厂模式、建造者模式、代理模式,还有很多设计原则。这些技术可以使整个项目易于维护和迭代。这也是研发人员在学习源码的过程中需要注意的地方。