JavaEE 企业级分布式高级架构师(一)MyBatis框架学习笔记(4)

手写框架篇

手写MyBatis框架分析

  • 回顾JDBC
    在这里插入图片描述

读取配置文件

有两类配置文件需要加载:全局配置文件和映射文件 XML文件

全局配置文件加载

XMLConfigBuilder:专门用来解析全局配置文件的

  • 根据配置文件路径,读取配置文件(InputStream流)
  • 创建 Document 对象(没有对 MyBatis 语义进行解析)
  • 根据 MyBatis 语义去解析 Document 对象,将解析结果封装到一个对象(Configuration)
    • 解析数据源信息(dom4j + path)
      • 将数据源信息封装到 Configuration 对象中
    • 解析映射文件信息
映射文件加载

XMLMapperBuilder:专门用来解析映射文件的

  • 根据映射文件路径,读取映射文件(InputStream流)
  • 创建 Document 对象(没有对 MyBatis 语义进行解析)
  • 根据 MyBatis 语义去解析 Document 对象,将解析结果封装到一个对象集合(Map<String, MappedStatement>)
    • 先将未解析的SQL语句封装到一个对象中(SqlSource)

执行JDBC

问题:SqlSession如何得到 Configuration ?
jdbc程序太重,需要设计更简洁的面向用户的接口(SqlSession)

  • SqlSession接口定义(selectOne | selectList | insert | update | delete)
  • SqlSession实现jdbc流程如下:
准备SQL语句
  • 从 Configuration 对象中根据 statementId 获取 Map<String, MappedStatement>中的指定MappedStatement对象(??)
    • 调用 SqlSource 的 getBoundSql 方法,获取 BoundSql (解析之后的SQL语句,入参信息集合【入参名称、入参类型】,入参对象)
  • 从 MappedStatement 对象中获取 SQL 语句
创建Connection
  • 从 Configuration 对象中获取 DataSource,通过 DataSource 创建 Connection
执行SqlSession
  • 从 Configuration 对象中根据 statementId 获取 Map<String, MappedStatement>中的指定MappedStatement 对象
  • 从 MappedStatement 对象中获取 statementType 信息,目的是创建对应的 Statement 对象
  • 设置参数(??)
  • 执行statement对象
  • 处理 ResultSet(??)

手写MyBatis框架

回顾JDBC

在这里插入图片描述

MyBatis框架V1版本

  • properties文件
# 数据库连接配置
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://192.168.254.128:3306/ssm?characterEncoding=utf-8
db.username=root
db.password=123456

# 根据用户id查询用户信息
db.sql.selectUserById=select * from user where id = ?
db.sql.selectUserById.resultType=com.yw.mybatis.example.po.User
# 根据用户名称查询用户信息
db.sql.selectUserByUsername=select * from user where username = ?
db.sql.selectUserByUsername.resultType=com.yw.mybatis.example.po.User

# 根据用户名称和性别查询用户信息
db.sql.selectUserByCriteria=select * from user where username = ? and sex = ?
db.sql.selectUserByCriteria.columnNames=username,sex
db.sql.selectUserByCriteria.resultType=com.yw.mybatis.example.po.User
  • 代码实现:
/**
 * 解决硬编码问题:properties文件
 */
public class MyBatisV1 {
    private Properties properties = new Properties();

    @Test
    public void test() {
        // 加载配置文件
        loadProperties("mybatis.properties");

        // 测试
        System.out.println("selectUserById");
        System.out.println(selectList("selectUserById", 1));

        System.out.println("selectUserByUsername");
        System.out.println(selectList("selectUserByUsername", "李四"));

        System.out.println("selectUserByCriteria");
        Map<String, Object> param = new HashMap<>(2);
        param.put("username", "张三");
        param.put("sex", "男");
        System.out.println(selectList("selectUserByCriteria", param));
    }

    private void loadProperties(String location) {
        try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(location)) {
            properties.load(is);
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    private <T> List<T> selectList(String id, Object param) {
        List<T> results = new ArrayList<>();
        try {
//            // 1、加载驱动
//            Class.forName(JDBC_DRIVER);

            // 解决连接获取时的硬编码问题和频繁连接的问题
            BasicDataSource dataSource = new BasicDataSource();
            dataSource.setDriverClassName(properties.getProperty("db.driver"));
            dataSource.setUrl(properties.getProperty("db.url"));
            dataSource.setUsername(properties.getProperty("db.username"));
            dataSource.setPassword(properties.getProperty("db.password"));

            // 2、得到连接
//            try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
            String statementId = "db.sql." + id;
            try (Connection conn = dataSource.getConnection();
                 // 3、获取预处理statement
                 PreparedStatement ps = conn.prepareStatement(properties.getProperty(statementId))) {
                // 4、设置参数
//                ps.setObject(1, param);
                if (param instanceof Integer || param instanceof String) {
                    ps.setObject(1, param);
                } else if (param instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) param;
                    String columnNames = properties.getProperty(statementId + ".columnNames");
                    String[] nameArray = columnNames.split(",");
                    for (int i = 0; i < nameArray.length; i++) {
                        ps.setObject(i + 1, map.get(nameArray[i]));
                    }
                } else {
                    // TODO
                }

                // 5、执行SQL
                try (ResultSet rs = ps.executeQuery()) {
                    // 6、遍历结果集
                    String resultType = properties.getProperty(statementId + ".resultType");
                    Class<?> resultTypeClazz = Class.forName(resultType);
                    while (rs.next()) {
                        Object result = resultTypeClazz.newInstance();
                        ResultSetMetaData metaData = rs.getMetaData();
                        int columnCount = metaData.getColumnCount();
                        for (int i = 0; i < columnCount; i++) {
                            String columnName = metaData.getColumnName(i + 1);
                            Field field = resultTypeClazz.getDeclaredField(columnName);
                            field.setAccessible(true);
                            field.set(result, rs.getObject(columnName));
                        }
                        results.add((T) result);
                    }
                }
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return results;
    }
}

MyBatis框架V2版本

  • properties配置文件升级为XML配置文件;使用面向过程思维去优化代码;使用面向对象思维去理解配置文件封装的类的作用。
  • 需求分析:

在这里插入图片描述

在这里插入图片描述

  • 主要的类和接口:
    在这里插入图片描述
  • Debug 查看解析后的SqlSource信息

在这里插入图片描述

配置文件解析阶段
全局配置文件解析
  • 利用dom4j从 <configuration> 根标签开始层层解析,将所有的全局配置信息封装到一个 Configuration 对象中。
  • Configuration 对象主要封装了DataSource数据源信息,以及解析Mapper映射文件时生成的一个一个的MappedStatement对象集合(key是statementId,value是MappedStatement对象)。
Mapper映射文件解析
  • MappedStatement:封装了select|update|delete|insert标签信息,主要包括 statementId、statementType、resultType、SqlSource等。

  • SqlSource:它是一个接口,其封装了statement标签中的整个SQL信息,并对外提供安全可靠的访问方法(获取JDBC可以正常执行的SQL语句)。BoundSql getBoundSql(Object param)

    • SqlSource的实现类根据处理#{}和${}的不同,分为多个:
    • DynamicSqlSource:主要是封装动态SQL标签解析之后的SQL语句和带有${}的SQL语句
    • RawSqlSource:主要封装带有#{}的SQL语句
    • StaticSqlSource:是BoundSql中要存储SQL语句的一个载体,上面两个SqlSource的SQL语句,最终都会存储到该SqlSource实现类中。
  • BoundSql:主要封装了解析之后JDBC可以直接执行的SQL语句,以及#{}解析出来的参数信息List <ParameterMapping>

  • SqlNode:它是一个接口,其封装的是不同的SQL片段的信息,并提供对封装数据的操作void apply(DynamicContext context)

    • SqlSource与SqlNode的关系:一个SqlSource包含多个SqlNode。
    • SqlNode的实现类:
    • TextSqlNode:封装了带有${}的 SQL文本。
    • StaticTextSqlNode:封装了不带有${}的 SQL文本。
    • IfSqlNode:封装了带有if标签的动态标签。
    • MixedSqlNode:封装的是一组SqlNode,采用的是组合模式
    • 对SQL片段的解析过程,用到了责任链模式,最终会形成一个基于SqlNode的树状结构。
  • DynamicContext:它是对SqlNode执行过程中需要的信息进行统一的封装传递,对于解析之后的SQL语句进行字符串拼接。

执行阶段
  • 根据statementId从Configuration对象中获取mappedStatement对象,并从 mappedStatement 得到SqlSource对象,sqlSource.getBoundSql会触发 SqlSource 和 SqlNode 的解析处理流程。
  • 进过解析处理会返回 BoundSql 对象,从 BoundSql 中获取 JDBC 可以正常执行的SQL,同时从 BoundSql 中获取入参信息,利用合适的 Statement 给SQL设置参数,最后执行SQL。
代码图解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

MyBatis框架V3版本

MyBatis架构流程图

在这里插入图片描述

全局配置文件和映射文件中的四个解析类
  • XMLConfigBuilder:解析全局配置文件
  • XMLMapperBuilder:解析Mapper映射文件
  • XMLStatementBuilder:解析 select|insert|update|delete 标签的
  • XMLScriptBuilder:解析 SqlSource
SqlSession执行流程中的四大组件
  • Executor接口

    • CachingExecutor:二级缓存执行器
    • BaseExecutor(抽象类):一级缓存执行器
      • SimpleExecutor:普通的JDBC执行
      • BatchExecutor:处理批量操作
      • ReuseExecutor:statement可重用
  • StatementHandler接口

    • PreparedStatementHandler:预编译PreparedStatement的处理器
    • SimpleStatementHandler:处理简单JDBC的Statement的处理器
    • CallableStatementHandler:支持存储过程的Statement的处理器
    • RoutingStatementHandler:用于路由选择上面三种Statement处理器
  • ParameterHandler接口

    • DefaultParameterHandler
  • ResultSetHandler接口

  • 代码github地址:https://github.com/shouwangyw/ssm/tree/main/mybatis-framework-custom

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

讲文明的喜羊羊拒绝pua

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

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

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

打赏作者

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

抵扣说明:

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

余额充值