一,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:
//