【手写Mybatis】step03:xml的解析和注册使用

一、设计

Mybatis 的核心逻辑:简单的描述成是为了给一个接口提供代理类,类中包括了对 Mapper 也就是 xml 文件中的 SQL 信息(类型、入参、出参、条件)进行解析和处理,这个处理过程就是对数据库的操作以及返回对应的结果给到接口。
在这里插入图片描述
在step02中使用到了MapperRegistry , 对包路径进行扫描注册映射器,并在 DefaultSqlSession 中进行使用。在这一个章节中主要,通过对 XML 文件的解析和处理就可以完成 Mapper 映射器的注册和 SQL 管理。把命名空间、SQL描述、映射信息维护起来。
在这里插入图片描述

二、实现

![在这里插入图片描述](https://img-blog.csdnimg.cn/209c55d644a644539766bba12e7d0a95.png

SqlSessionFactoryBuilder 是作为mybatis的入口,通过xml文件的io解析,并且返回处理

通过解析把 XML 信息注册到 Configuration 配置类中,再通过传递 Configuration 配置类到各个逻辑处理类里,包括 DefaultSqlSession 中,这样就可以在获取映射器和执行SQL的时候,从配置类中拿到对应的内容了。

2.1 代码结构


mybatis-step-03
└── src
    ├── main
    │   └── java
    │       └── com.qf
    │           ├── binding
    │           │   ├── MapperMethod.java
    │           │   ├── MapperProxy.java
    │           │   ├── MapperProxyFactory.java
    │           │   └── MapperRegistry.java
    │           ├── builder
    │           │   ├── xml
    │           │   │   └── XMLConfigBuilder.java
    │           │   └── BaseBuilder.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
        │   └── com.qf
        │       ├── dao
        │       │   └── IUserDao.java
        │       ├── po
        │       │   └── User.java
        │       └── ApiTest.java
        └── resources
            ├── mapper
            │   └──User_Mapper.xml
            └── mybatis-config-datasource.xml


2.2 构建SqlSessionFactory建造者工厂

package com.qf.mybatis.session;

import com.qf.mybatis.builder.xml.XMLConfigBuilder;
import com.qf.mybatis.session.bind.DefaultSqlSessionFactory;

import java.io.Reader;

/**
 * 建造者模式,构建sqlSession工厂
 */
public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(Reader reader){
        XMLConfigBuilder xmlConfigBuilder=new XMLConfigBuilder(reader);
        return build(xmlConfigBuilder.parse());
    }

    public SqlSessionFactory build(Configuration configuration){
        return new DefaultSqlSessionFactory(configuration);
    }
}


SqlSessionFactoryBuilder 是作为整个 Mybatis 的入口类,通过指定解析XML的IO,引导整个流程的启动

2.3 XML 解析处理

package com.qf.mybatis.builder.xml;

import com.qf.mybatis.builder.BaseBuilder;
import com.qf.mybatis.io.Resources;
import com.qf.mybatis.mapping.MappedStatement;
import com.qf.mybatis.mapping.SqlCommandType;
import com.qf.mybatis.session.Configuration;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import org.xml.sax.InputSource;

import java.io.File;
import java.io.IOException;
import java.io.Reader;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class XMLConfigBuilder extends BaseBuilder {

    private Element root;
    public XMLConfigBuilder(Reader reader) {
        //1、调用父类初始化
        super(new Configuration());
        //2、dom4j处理xml文件
        SAXReader saxReader = new SAXReader();
        try {
            Document read = saxReader.read(new InputSource(reader));
            root = read.getRootElement();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public Configuration parse() {
        try {
            //解析映射器
            mapperElement(root.element("mappers"));
        }catch (Exception e){
            throw new RuntimeException("Error parsing SQL Mapper configuration Cause:"+e,e);
        }
        return configuration;
    }

    private void mapperElement(Element mappers) throws Exception{
        List<Element> mapperList = mappers.elements("mapper");
        for (Element e : mapperList) {
            String resource = e.attributeValue("resource");
            Reader reader = Resources.getResourcesAsReader(resource);
            SAXReader saxReader = new SAXReader();
            Document document = saxReader.read(new InputSource(reader));
            Element root = document.getRootElement();

            //命名空间
            String namespace = root.attributeValue("namespace");

            //SELECT
            List<Element> select = root.elements("select");
            for (Element element : select) {
                //id,parameterType,resultType,sql
                String id = element.attributeValue("id");
                String parameterType = element.attributeValue("parameterType");
                String resultType = element.attributeValue("resultType");
                String sql = element.getText();

                //正则解析sql语句
                // ? 匹配
                Map<Integer, String> parameter = new HashMap<>();
                Pattern pattern = Pattern.compile("(#\\{(.*?)})");
                Matcher matcher = pattern.matcher(sql);
                for (int i = 1; matcher.find(); i++) {
                    String g1 = matcher.group(1);
                    String g2 = matcher.group(2);
                    parameter.put(i, g2);
                    sql = sql.replace(g1, "?");
                }

                String name = element.getName();
                String msId=namespace+"."+id;
                SqlCommandType sqlCommandType=SqlCommandType.valueOf(name.toUpperCase(Locale.ENGLISH));
                MappedStatement mappedStatement = new MappedStatement.Builder(configuration, msId, sqlCommandType, parameterType, resultType, sql, parameter).build();
                //添加解析
                configuration.addMappedStatement(mappedStatement);

                //注册mapper映射器
                configuration.addMapper(Resources.getClass(namespace));
            }
        }
    }

}



XML解析处理,解析sql语句到MappedStatement类里面,解析xml 的namespace注册到MapperRegister映射注册机。

MappedStatementMapperRegister共同组成了Configuration类。

2.4 通过配置类包装注册机和SQL语句

package com.qf.mybatis.session;

import com.qf.mybatis.bind.MapperRegister;
import com.qf.mybatis.mapping.MappedStatement;

import java.util.HashMap;
import java.util.Map;

public class Configuration {

    /**
     * 映射注册机
     */
    protected MapperRegister mapperRegistry = new MapperRegister(this);

    /**
     * 映射的语句,存在Map里
     */
    protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();

    public Configuration() {
        this.mapperRegistry=mapperRegistry;
    }

    public MapperRegister getMapperRegistry() {
        return mapperRegistry;
    }

    public void addMappedStatement(MappedStatement mappedStatement){
        mappedStatements.put(mappedStatement.getId(),mappedStatement);
    }

    public MappedStatement getMappedStatement(String id){
        return mappedStatements.get(id);
    }

    public void addMapper(Class<?> aClass){
        mapperRegistry.addMapper(aClass);
    }


    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type,sqlSession);
    }
}

配置类包含了MapperRegistryMappedStatement

2.5 DefaultSqlSession结合配置项获取信息

package com.qf.mybatis.session.bind;

import com.qf.mybatis.mapping.MappedStatement;
import com.qf.mybatis.session.Configuration;
import com.qf.mybatis.session.SqlSession;

public class DefaultSqlSession implements SqlSession {
    public Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration=configuration;
    }

    /**
     * 目前是返回方法
     * @param statement
     * @param <T>
     * @return
     */

    public <T> T selectOne(String statement) {
        return (T) ("你被代理了"+statement);
    }

    public <T> T selectOne(String statement, Object param) {
        MappedStatement mappedStatement = configuration.getMappedStatement(statement);
        return (T) ("你被代理了"+"方法:"+mappedStatement.getId()+"入参:"+param+mappedStatement.getSql());
    }

    public <T> T getMapper(Class<T> type) {
        return configuration.getMapper(type,this);
    }

    @Override
    public Configuration getConfiguration() {
        return configuration;
    }
}

mapperRegistry 替换为 Configuration

3、测试

定义的dao接口和对应的xml文件

package com.qf.mybatis.dao;


public interface IUserDao {
    String queryUserInfoById(String id);
}


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qf.mybatis.dao.IUserDao">

    <select id="queryUserInfoById" parameterType="java.lang.Long" resultType="com.qf.mybatis.po.User">
        SELECT id, userId, userHead, createTime
        FROM user
        where id = #{id}
    </select>

</mapper>


ApiTest

package com.qf.mybatis.test;

import com.qf.mybatis.dao.IUserDao;
import com.qf.mybatis.io.Resources;
import com.qf.mybatis.po.User;
import com.qf.mybatis.session.SqlSession;
import com.qf.mybatis.session.SqlSessionFactory;
import com.qf.mybatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.Reader;

public class ApiTest {

    private Logger logger= LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test_SqlSessionFactory() throws IOException {
        //1、sqlsession工厂里面拿取sqlsession
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        Reader reader = Resources.getResourcesAsReader("mybatis-config-datasource.xml");
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //2、获取映射器
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);

        //3、测试结果
        String user = userDao.queryUserInfoById("11L");
        logger.info("结果:{}",user);
    }
}



结果:

"C:\Program Files\Java\jdk1.8.0_231\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:D:\idea\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar=54097:D:\idea\IntelliJ IDEA 2019.1.1\bin" -Dfile.encoding=UTF-8 -classpath "D:\idea\IntelliJ IDEA 2019.1.1\lib\idea_rt.jar;D:\idea\IntelliJ IDEA 2019.1.1\plugins\junit\lib\junit-rt.jar;D:\idea\IntelliJ IDEA 2019.1.1\plugins\junit\lib\junit5-rt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_231\jre\lib\rt.jar;E:\zhoutao\mybatis-write\mybatis\step-03-xmlparse\target\test-classes;E:\zhoutao\mybatis-write\mybatis\step-03-xmlparse\target\classes;D:\maven\myrepository\com\alibaba\fastjson\1.2.75\fastjson-1.2.75.jar;D:\maven\myrepository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;D:\maven\myrepository\junit\junit\4.7\junit-4.7.jar;D:\maven\myrepository\cn\hutool\hutool-all\5.5.0\hutool-all-5.5.0.jar;D:\maven\myrepository\org\slf4j\slf4j-api\1.7.5\slf4j-api-1.7.5.jar;D:\maven\myrepository\org\slf4j\jcl-over-slf4j\1.7.5\jcl-over-slf4j-1.7.5.jar;D:\maven\myrepository\ch\qos\logback\logback-classic\1.0.9\logback-classic-1.0.9.jar;D:\maven\myrepository\ch\qos\logback\logback-core\1.0.9\logback-core-1.0.9.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 com.qf.mybatis.test.ApiTest,test_SqlSessionFactory
23:34:42.299 [main] INFO  com.qf.mybatis.test.ApiTest - 结果:你被代理了方法:com.qf.mybatis.dao.IUserDao.queryUserInfoById入参:[Ljava.lang.Object;@2aafb23c
        SELECT id, userId, userHead, createTime
        FROM user
        where id = ?
    

Process finished with exit code 0

目前的使用方式就和 Mybatis 非常像了,通过加载 xml 配置文件,交给 SqlSessionFactoryBuilder 进行构建解析,并获取 SqlSessionFactory 工厂。这样就可以顺利的开启 Session 以及完成后续的操作。看到了sql语句的输出

4、总结

SqlSessionFactoryBuilder 的引入包装了整个执行过程,包括:XML 文件的解析、Configuration 配置类的处理,让 DefaultSqlSession 可以更加灵活的拿到对应的信息,获取 Mapper 和 SQL 语句。。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

舒克日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值