Mybatis框架原理(基于mybatis-3.5.2.jar分析)

本文详细介绍了Mybatis框架的工作原理,从引入依赖到执行SQL的整个流程,包括XMLConfigBuilder解析配置文件,Executor执行SQL,ResultHandler处理结果集,以及MapperProxy动态代理生成Mapper实例等关键步骤。通过对源码的分析,揭示了Mybatis如何优化Mapper接口加载,以及缓存和事务的处理机制。
摘要由CSDN通过智能技术生成

mybatis是一套ORM框架,也就是把字段映射为对象的属性。
大学学过,最基础要操作一个数据库步骤:
引入JDBC包——DriverManager注册驱动——创建连接——创建Statement——CRUD操作——操作结果集——关闭连接。
为了省去上面每次操作的繁琐过程,而且相比hibernate更加灵活,方便维护。mybatis出现了。

首先pom.xml要引入mybatis。
框架的大致逻辑:

package com.stu.demo;

import com.stu.demo.pojo.User;
import org.apache.ibatis.annotations.Select;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

interface UserMapper{
   
    @Select("select * from t_user where id = #{id} and grade = #{grade}")
    List<User> selectUsers(Integer id, String grade);
}
public class Application {
   
    public static void main(String[] args) {
   
        // JDK动态代理,反射实现
        UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{
   UserMapper.class}, new InvocationHandler() {
   
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
                System.out.println("方法名字:"+method.getName());
                System.out.println("方法入参:" + Arrays.toString(args));
                // sql参数和方法参数的对应键值关系
                Map<String, Object> mapData = buildSQLData(method, args);
                Select annotions = method.getAnnotation(Select.class);
                if (annotions != null) {
   
                    String[] strs = annotions.value(); // 这个sql可以是多条
                    System.out.println("Mybatis种sql语句:" + Arrays.toString(strs));
                    // 根据键值关系赋值得到sql
                    String sql = bulidSQL(strs[0], mapData);
                    System.out.println("最终得到要在数据库执行的sql语句:" + sql);
                }
                return null;
            }
        });
        userMapper.selectUsers(1, "303班");
    }

    private static String bulidSQL(String str, Map<String, Object> mapData) {
   
        // 频繁字符操作
        StringBuilder sb = new StringBuilder();
        int length = str.length();
        for (int i = 0; i < length; i++) {
   
            char ch = str.charAt(i);
            if (ch == '#') {
   
                int nextIndex = i + 1;
                char nextChar = str.charAt(nextIndex);
                if ('{' != nextChar) {
   
                    throw new RuntimeException("sql异常:" + str);
                }
                // 找到当前}结束位置 argStr为返回的参数key,比如id
                StringBuilder argStr = new StringBuilder();
                i = parseArgStr(argStr,str,nextIndex);
                // 根据key找到对应的值。
                Object argValue = mapData.get(argStr.toString());
                if (argValue == null) {
   
                    throw new RuntimeException("argValue为null");
                }
                sb.append(argValue);
                continue;
            }
            sb.append(ch);
        }
        return sb.toString();
    }

    private static int parseArgStr(StringBuilder argStr, String str, int nextIndex) {
   
        // 从nextIndex++循环
        nextIndex++;
        for (;nextIndex < str.length(); nextIndex++) {
   
            char ch = str.charAt(nextIndex);
            if (ch != '}') {
   
                argStr.append(ch);
                continue;
            }
            if (ch == '}') {
   
                return nextIndex;
            }
        }
        // 最后都没有} 异常处理。
        throw new RuntimeException("缺少}:"+ str);
    }

    /**
     * sql参数和方法参数的对应键值关系
     * @param method
     * @param args
     * @return
     */
    public static Map<String, Object> buildSQLData(Method method, Object[] args) {
   
        Map<String, Object> mapData = new HashMap<>();
        // 参数获得
        Parameter[] parameters = method.getParameters();
        // 将我们的参数赋值给sql上变量。
        int index[] = {
   0}; // 因为里面用的时引用类型。
        // 当数据量大时,并行运行。
        // Arrays.asList(parameters).parallelStream().forEach
        // 与之对应Map<String, Object> mapData = new ConcurrentHashMap<>();
        Arrays.asList(parameters).forEach((parameter)->{
   
            String name = parameter.getName();
            System.out.println("SQL上参数的名字:" + name);
            // 得到键值关系--sql参数和方法参数的对应键值关系
            mapData.put(name, args[index[0]]);
            index[0]++;
        });
        return mapData;
    }
    public static String parseIntoSQL(String sql, Map<String, Object> mapData){
   

        return "";
    }
}

运行结果:(很清晰的看到这个过程)
在这里插入图片描述
在框架种处理更简单:通过反射直接拿到返回类型和参数类型。

UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(Application.class.getClassLoader(), new Class<?>[]{
   UserMapper.class}, new InvocationHandler() {
   
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
                    System.out.println(method.getReturnType());
                    System.out.println(method.getGenericReturnType());
                }
                return null;
            }
        });

结果:(找到了这两个,get和set进去就行了)
在这里插入图片描述
Mybatis架构设计:
在这里插入图片描述

源码包如下,ibatis包下的包:
annotations: 注解包
Arg、AutomapConstructor、CacheNamespace、CacheNamespaceRef、Case、ConstructorArgs、Delete、
DeleteProvider、Flush、Insert、InsertProvider、Lang、Many、MapKey、Mapper、One、Options、
Param、Property、Result、ResultMap、Results、ResultType、Select、SelectKey、SelectProvider、
TypeDiscriminator、Update、UpdateProvider

binding:绑定包
BindingException、MapperMethod、MapperProxy、MapperProxyFactory、MapperRegistry

builder:构建包
cache:缓冲存储包
cursor:游标包
datasource:数据源包
exceptions:异常包
executor:执行器包
io:io包
javassist:java助手
jdbc:连接包
lang:基础类型包
logging:日志包
mapping:映射包
ognl:语义解析包
parsing:解析包。用于解析mapper树结构。
plugin:依赖包
reflection:反射包
scripting:脚本包
session:session包
transaction:事务包
type:各种类型包

源码分析:

返回结果集ResultContext:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.session;

public interface ResultContext<T> {
   
    T getResultObject();

    int getResultCount();

    boolean isStopped();

    void stop();
}

返回处理类ResultHandler:

package org.apache.ibatis.session;

public interface ResultHandler<T> {
   
    void handleResult(ResultContext<? extends T> var1);
}

SqlSessionFactoryBuilder 构建类:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.apache.ibatis.session;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;

public class SqlSessionFactoryBuilder {
   
    public SqlSessionFactoryBuilder() {
   
    }

    public SqlSessionFactory build(Reader reader) {
   
        return this.build((Reader)reader, (String)null, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, String environment) {
   
        return this.build((Reader)reader, environment, (Properties)null);
    }

    public SqlSessionFactory build(Reader reader, Properties properties) {
   
        return this.build
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值