mybatis源码解析-sql执行

本文聚焦MyBatis源码中SQL执行过程。先介绍获取SqlSession的过程,涉及不同类型的Executor执行器;接着说明获取Mapper接口代理对象的方法;还详细阐述Mapper方法执行流程,包括参数封装、解析sql、执行sql和解析结果等步骤,帮助开发者深入理解MyBatis的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,mybatis源码解析sql执行

mybatis的简单使用案例

public class App {
   
   
	public static void main(String[] args) {
   
   
		String resource = "mybatis‐config.xml";
		Reader reader;
		try {
   
   
			//将XML配置文件构建为Configuration配置类
			reader = Resources.getResourceAsReader(resource);
			// 通过加载配置文件流构建一个SqlSessionFactory DefaultSqlSessionFactory
			SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
			// 数据源 执行器 DefaultSqlSession
			SqlSession session = sqlMapper.openSession();
			try {
   
   
				// 执行查询 底层执行jdbc
				//User user = (User)session.selectOne("com.tuling.mapper.selectById", 1);
				UserMapper mapper = session.getMapper(UserMapper.class);
				System.out.println(mapper.getClass());
				User user = mapper.selectById(1L);
				System.out.println(user.getUserName());
			} catch (Exception e) {
   
   
 				e.printStackTrace();
 			}finally {
   
   
 				session.close();
			}
		} catch (IOException e) {
   
   
			e.printStackTrace();
		}
	}
}

前面两篇文章介绍了通过SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);代码解析mybatis的配置文件和mapper的配置文件,本片文章主要介绍解析完成配置文件后,mybatis进行的操作。

1.1获取SqlSession的过程

首先查看获取SqlSession的过程,查看SqlSession session = sqlMapper.openSession();方法,DefaultSqlSessionFactory实现SqlSessionFactory接口:

public SqlSession openSession() {
   
   
    	// this.configuration.getDefaultExecutorType()方法,获取默认的Executor
    	// openSessionFromDataSource方法是构建SqlSession的
        return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
    }

继续跟进this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);方法:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
   
   
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
   
   
            // 获取环境配置
            Environment environment = this.configuration.getEnvironment();
            // 根据环境配置创建TransactionFactory
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            // 创建执行器
            Executor executor = this.configuration.newExecutor(tx, execType);
            // 根据Executor创建SqlSession
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
   
   
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
   
   
            ErrorContext.instance().reset();
        }

        return var8;
    }

查看创建执行器的Executor executor = this.configuration.newExecutor(tx, execType);方法:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
   
   
        executorType = executorType == null ? this.defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Object executor;
        if (ExecutorType.BATCH == executorType) {
   
    // 批处理执行器
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
   
    // 重新使用执行器
            executor = new ReuseExecutor(this, transaction);
        } else {
   
    // 简单执行器
            executor = new SimpleExecutor(this, transaction);
        }

        if (this.cacheEnabled) {
   
   
            executor = new CachingExecutor((Executor)executor); // 普通的执行器装饰成缓存执行器
        }
		
    	// this.interceptorChain中存放的是mybatis核心配置文件配置的插件
    	// pluginAll方法中,使用了代理+装饰者模式的方式,创建Executor
        Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
        return executor;
    }

Executor分成两大类:

在这里插入图片描述

一类是CachingExecutor,另一类是普通BaseExecutor。 普通BaseExecutor又分为三种基本的Executor执行器,SimpleExecutor、ReuseExecutor、 BatchExecutor。

  • BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有 sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行 executeBatch()批处理。与JDBC批处理相同。
  • SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完 立刻关闭Statement对象。
  • ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就 使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。

CachingExecutor其实是封装了普通的Executor,和普通的区别是在查询前先会查询缓存中是否存在结果,如果存在就使用缓存中的结果,如果不存在还是使用普通的Executor进行查询,再将查询出来的结果存入缓存。

到此为止,我们已经获得了SqlSession,拿到SqlSession就可以执行各种CRUD方法了,

小总结:mybatis通过new SqlSessionFactoryBuilder().build(reader)方法加载mybatis的核心配置文件和mapper配置文件,创建一个SqlSessionFactory(默认DefaultSqlSessionFactory),然后调用SqlSessionFactory的openSession()方法,SqlSessionFactory的openSession()方法会先创建一个Sql执行器(Executor),然后根据Executor创建SqlSession(默认使用DefaultSqlSession),SqlSession中包含了configration对象,所以通过SqlSession也能拿到全局配置,SqlSession对象中有CRUD各种执行方法,调用这些方法Executor执行器会代理配置的拦截器这些方法。

1.2获取Mapper接口的代理对象

通过SqlSession的getMapper()方法,可以获取对应Mapper接口的代理对象:

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

继续跟进this.configuration.getMapper(type, this);方法:

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

继续跟进this.mapperRegistry.getMapper(type, sqlSession);方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
   
   
    	// 根据Mapper的Class,获取解析Mapper配置文件MapperProxyFactory
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
   
   
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
   
   
            try {
   
   
                // 调用mapperProxyFactory的newInstance方法创建对象
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
   
   
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

跟进mapperProxyFactory.newInstance(sqlSession);方法:

public T newInstance(SqlSession sqlSession) {
   
   
    	// 创建动态代理类MapperProxy,MapperProxy实现了InvocationHandler接口,InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口
        MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
        return this.newInstance(mapperProxy);
    }

继续跟进this.newInstance(mapperProxy);方法:

protected T newInstance(MapperProxy<T> mapperProxy) {
   
   
    	// jdk的方式创建代理对象
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{
   
   this.mapperInterface}, mapperProxy);
    }
1.3Mapper方法执行流程

下面是动态代理类MapperProxy,调用Mapper接口的所有方法都会先调用到这个代理类的 invoke方法

注意:由于Mybatis中的Mapper接口没有实现类,所以MapperProxy这个代理对象中没有委托类,也就是说MapperProxy干了代理类和委托类的事情

MapperProxy的invoke方法:

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
   
        try {
   
   
            // 如果是Object方法,则调用方法本身
            // 如果不是Object方法,则调用this.cachedInvoker(method)方法
            return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
        } catch (Throwable var5) {
   
   
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }

跟进this.cachedInvoker(method)方法:

private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
   
   
        try {
   
   
            MapperProxy.MapperMethodInvoker invoker = (MapperProxy.MapperMethodInvoker)this.methodCache.get(method);
            
            return invoker != null ? invoker : (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {
   
   
                // 如果调用接口的是默认方法(JDK8新增接口默认方法的概念)
                if (m.isDefault()) {
   
   
                    try {
   
   
                        return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
                    } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
   
   
                        throw new RuntimeException(var4);
                    }
                } else {
   
   
                    // 如果调用的普通方法(非default方法),则创建一个PlainMethodInvoker并放入缓存,其中MapperMethod保存对应接口方法的SQL以及入参和出参的数据类型等信息
                    return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
                }
            });
        } catch (RuntimeException var4) {
   
   
            Throwable cause = var4.getCause();
            throw (Throwable)(cause == null ? var4 : cause);
        }
    }

PlainMethodInvoker类是Mapper接口普通方法的调用类,它实现了MethodInvoker接口。其内部封装了MapperMethod实例。

我们跳出cachedInvoker方法回到MapperProxy::invoke方法中:

 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   
   
        try {
   
   
            // 如果是Object方法,则调用方法本身
            // 如果不是Object方法,则调用this.cachedInvoker(method)方法
            return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
        } catch (Throwable var5) {
   
   
            throw ExceptionUtil.unwrapThrowable(var5);
        }
    }

我们可以看到当cacheInvoker返回了PalinMethodInvoker实例之后,紧接着调用了这个实例的PlainMethodInvoker::invoke方法:

private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
   
   
        private final MapperMethod mapperMethod;

        public PlainMethodInvoker(MapperMethod mapperMethod) {
   
   
            this.mapperMethod = mapperMethod;
        }
		// 实例的`PlainMethodInvoker::invoke`方法
        public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
   
   
            return this.mapperMethod.execute(sqlSession, args);
        }
    }

PlainMethodInvoker::invoke方法调用的是MapperMethod::execute方法:

    public Object execute(SqlSession sqlSession, Object[] args) {
   
   
        Object result;
        Object param;
        // 判断方法类型
        switch(this.command.getType()) {
   
   
        case INSERT:
            // 参数封装
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            // 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值