这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战
我们在之前为了方便写了一获取SqlSession的工具类,我们今天来分析一下Mybatis的执行流程
String resource = "mybatsi-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student studentById = mapper.findStudentById(38);
System.out.println(studentById);
sqlSession.close();
这是我们根据id查询学生信息的代码,通过代码我们发现Myabtis的执行流程是
- 获取Mybatis主配置文件
- 通过SqlSessionFactoryBuilder().build()方法传入主配置文件构建SqlSessionFactory对象
- SqlSessionFactory对象通过openSession()获取SqlSession对象
- sqlSession.getMapper()方法获取我们xml文件得到mapper对象去执行我们的sql返回对象
到底具体怎么只执行,我们来看源码看都做了些什么
Mybatis的组件
SqlSessionFactoryBuilder
通过SqlSessionFactoryBuilder这个建造者通过XMLConfigBuilder()读取传入的inputStream的Mybatis主配置文件的流,XMLConfigBuilder.parse()解析主配置文件返回一个Configuration()对象,在调用一个build方法返回一个SqlSessionFactory()对象,还有两个参数SqlSessionFactory和properties,environment 决定加载哪种环境,包括数据源和事务管理器,比如我们的项目有 开发环境,UAT测试环境,在配置文件中<environments中有多个environment。properties是来加载一些属性的。
上边结束后我们返回一个SqlSessionFactory()对象
public SqlSessionFactory build(InputStream inputStream, String SqlSessionFactory, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
SqlSessionFactory
有了SqlSessionFactory之后我们就可以tong过他来获取SqlSession对象,SqlSessionFactory重载的多个 openSession() 方法供使用,通过参数命名我们可以知道下边的方法参数是做什么的,我们默认的openSession()方法是没有参数的,会创建包含如下属性的SqlSession对象
autoCommit
是否自动提交Connection
创建数据库会话TransactionIsolationLevel
事务隔离级别ExecutorType
执行器类型
ExecutorType有三种类型
SIMPLE
该类型的执行器没有特别的行为。它为每个语句的执行创建一个新的预处理语句REUSE
复用预处理语句BATCH
该类型的执行器会批量执行所有更新语句
public enum ExecutorType {
SIMPLE,
REUSE,
BATCH;
private ExecutorType() {
}
}
还有一个getConfiguration方法,获取Configuration对象,也就是解析xml配置文件中的<configuration标签后的数据
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean var1);
SqlSession openSession(Connection var1);
SqlSession openSession(TransactionIsolationLevel var1);
SqlSession openSession(ExecutorType var1);
SqlSession openSession(ExecutorType var1, boolean var2);
SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);
SqlSession openSession(ExecutorType var1, Connection var2);
Configuration getConfiguration();
}
看我啊接口我们再去看SqlSessionFactory的实现类 DefaultSqlSessionFactory()每一个openSession方法都要调用openSessionFromDataSource去返回SqlSession对象
- 先获取我们主配置文件的environment包函数据库配置和事务信息,
- 创建TransactionFactory事务工厂,调用newTransaction方法传入,数据库信息,隔离级别,是否自动提交。
- 新建一个构造器executor传入事务,构造器类型
- 返回一个 DefaultSqlSession对象
这是我们就获得一个SqlSession对象,我们的sql语句都是executor在帮我们执行,excutor是对于Statement的封装
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
Executor executor = this.configuration.newExecutor(tx, execType);
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;
}
SqlSession
SqlSession是Mybatis最强大的一个类,通过DefaultSqlSession的getMapper()来生成mapper的代理对象
SqlSessionManager.java
public <T> T getMapper(Class<T> type) {
return this.getConfiguration().getMapper(type, this);
}
Configuration.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry.java
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
MapperProxyFactory.java 动态代理我们的接口
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
这一通操作之后我们就拿到了代理对象去执行我们的方法,走的方法是MapperProxy的invoik方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (method.isDefault()) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
调用 mapperMethod.execute(this.sqlSession, args)判断是什么类型的sql,返回给sqlSession
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:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
我们现在看是如何实现getlist的
<E> List<E> selectList(String var1);
public <E> List<E> selectList(String statement) {
return this.selectList(statement, (Object)null);
}
这里调用的.executor.query方法。可以看到有两个实现
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset();
}
return var5;
}
走CachingExecutor的query方法,先从二级缓存中获取,,没有的话走走另一个方法去一级缓存中那,没有的话
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
没有的话走queryFromDatabase方法去数据库查数据
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
走doquery方法有四个实现,我们看SimpleExecutor的DoQuery方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
然后走StatementHandler的query方法,可以看到是statement去执行SQL语句了
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = this.boundSql.getSql();
statement.execute(sql);
return this.resultSetHandler.handleResultSets(statement);
}