mybatis执行流程源码分析
Posted TomCoding
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis执行流程源码分析相关的知识,希望对你有一定的参考价值。
开始
下面是单独用mybatis查询数据的代码示例:
String resource = "mybatis-config.xml";
// 定位配置文件,包装成字节输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创建sessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建session, session中封装了一次操作sql的必备信息:Configuration,Executor等
try (SqlSession session = sqlSessionFactory.openSession()) {
// 生成BlogMapper的动态代理对象
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 查询数据,被动态代理拦截处理,通过转发到SqlSession进行后续的操作获取数据
Blog blog = mapper.selectBlog(1);
System.out.println(blog);
}
主要有四个步骤:
创建SessionFactory
打开Session
获取Mapper
执行增删改查操作
下面分析下每个步骤的源码执行过程。
1. 创建SessionFactory
创建SqlSessionFactoryBuilder用来解析配置文件
调用build方法构建SqlSessionFactory
// 这里只是创建一个对象,并调用build。具体的逻辑在build中
new SqlSessionFactoryBuilder().build(inputStream)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建XMLConfigBuilder用来处理xml配置
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parse解析xml并生成Configuration对象, 最终build方法通过parse返回的Configuration生成一个DefaultSqlSessionFactory
return build(parser.parse());
...
}
调用XMLConfigBuilder.parse()开始解析xml配置中的每个元素并填充到Configuration。(这里以层层深入的方式,通过XmlXxxBuild解析里面嵌套的各种元素及文件)
调用XMLConfigBuilder解析mybatis-config.xml并填充到Configuration
调用XMLMapperBuilder解析BlogMapper.xml并填充到Configuration
调用XMLStatementBuilder解析BlogMapper.xml中的Statement(select|update|insert|delete)并填充到Configuration
如XMLConfigBuilder.parseConfiguration会处理mybatis-config.xml中的内容
// 以下就是处理各种xml节点
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
将填充好的Configuration对象传入DefaultSqlSessionFactory构造方法并返回工厂对象
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2. 打开Session
mybatis中的session代表了一次执行会话,会包含事务,数据源,执行器等信息供后续sql操作
打开session
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
// 从配置中获取Environment并最终设置到session中
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// 获取执行器
final Executor executor = configuration.newExecutor(tx, execType);
// 创建默认的session实现
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
创建执行器Executor
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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);
}
// 根据配置,对executor缓存(装饰器模式, 持有一个具体的executor实例)
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 应用Interceptor,用户可以实现Interceptor拓展
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
3. 获取Mapper
获取Mapper本质上就是生成一个Mapper接口动态代理,最终拦截接口所有的方法调用。
session.getMapper(BlogMapper.class);
public <T> T getMapper(Class<T> type) {
// 通过configuration获取
return configuration.getMapper(type, this);
}
Configuration.getMapper获取Mapper代理对象
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 通过传入的Mapper class,从knownMappers获取对应的代理创建工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 通过代理工厂创建代理对象实例,可以看到代理对象持有sqlsession, 后续拦截Mapper接口调用时,sqlsession拥有相关的信息供crud操作
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
通过MapperProxyFactory创建代理对象。可以看到直接通过jdk动态代理生成对象, 而MapperProxy是InvocationHandler的实现,后续的Mapper接口操作都会被MapperProxy所拦截。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
// 通过jdk的动态代理创建
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
// MapperProxy持有sqlsession并实现了InvocationHandler拦截后续Mapper接口的crud操作
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
4. 执行增删改查
当我们执行Blog blog = mapper.selectBlog(1);最终会调用到上一步代理对象的MapperProxy.invok()。
selectBlog调用被MapperProxy.invok()拦截执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// Object类的方法直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 创建MapperMethodInvoker来调用
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
创建一个MapperMethodInvoker来调用
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 通过mapperMethod来调用
return mapperMethod.execute(sqlSession, args);
}
通过MapperMethod.execute调用SqlSession并根据sqlcommand执行crud方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
SqlSession执行selectOne方法,最终内部调用查询都是执行selectList方法获取结果,然后取第一条记录。
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
List<T> list = this.selectList(statement, parameter);
if (list.size() == 1) {
return list.get(0);
} else if (list.size() > 1) {
// 这里大家可能经常看到,当返回多条时抛出异常
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 获取配置中已解析好的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//通过Executor 执行调用,也就是我们在第二大步中创建Session填充的Executor(这里其实就是一个CachingExecutor(SimpleExecutor))
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
CachingExecutor.query先做缓存条件判断、没有缓存则执行具体的query操作(delegate就是SimpleExecutor)
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
delegate.query是先调用到SimpleExecutor父类的BaseExecutor.query方法, 最终BaseExecutor.query方法会调用SimpleExecutor.doQuery做具体的数据库查询操作。
BaseExecutor.query -> BaseExecutor.queryFromDatabase -> SimpleExecutor.doQuery
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 预处理
stmt = prepareStatement(handler, ms.getStatementLog());
// 查询并返回结果
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
创建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
RoutingStatementHandler的构造方法会根据StatementType创建PreparedStatementHandler并赋值给delegate属性,后续的操作都有delegate为其执行。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
PreparedStatementHandler的构造方法会调用父类BaseStatementHandler的构造方法,初始化好ParameterHandler,ResultSetHandler等
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
...
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
这里需要注意的是,不管是创建StatementHandler、ParameterHandler、ResultSetHandler都会调用interceptorChain.pluginAll执行
预处理 继续回到SimpleExecutor.query中,当我们获取到StatementHandler后,进行预处理操作。
// 我们把SimpleExecutor.query再拿出来,便于分析
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 这里query调用了内部的prepareStatement方法
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 这个就很jdbc了,先获取连接
Connection connection = getConnection(statementLog);
// 预处理sql(本质上就是获取到具体Statement)
stmt = handler.prepare(connection, transaction.getTimeout());
// 设置Statement参数
handler.parameterize(stmt);
return stmt;
}
当通过handler.prepare(connection, transaction.getTimeout());获取Statement,会通过RoutingStatementHandler.prepare里面的delegate.prepare来执行,而delegate我们这里就是PreparedStatementHandler。
PreparedStatementHandler继承了BaseStatementHandler,并没有重写prepare方法,所以会调用父类的prepare
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 实例化Statement, instantiateStatement是一个抽象方法,最终通过子类PreparedStatementHandler来完成
statement = instantiateStatement(connection);
setStatementTimeout(statement, transactionTimeout);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
PreparedStatementHandler.instantiateStatement完成Statement实例化。
protected Statement instantiateStatement(Connection connection) throws SQLException {
// 以下代码主要是判断各种属性,最终通过prepareStatement预处理sql获取Statement
String sql = boundSql.getSql();
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareStatement(sql);
} else {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
在获取到Statement之后,退回到SimpleExecutor..prepareStatement完成Statement参数设置。
执行handler.parameterize(stmt)最终调用链路又是:RoutingStatementHandler-》PreparedStatementHandler
@Override
public void parameterize(Statement statement) throws SQLException {
parameterHandler.setParameters((PreparedStatement) statement);
}
调用StatementHandler.query(stmt, resultHandler);查询与结果处理
最终调用RoutingStatementHandler-》PreparedStatementHandler
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 类似jdbc,执行查询
ps.execute();
// 用ResultSetHandler处理结果
return resultSetHandler.handleResultSets(ps);
}
resultSetHandler.handleResultSets(ps)处理返回的结果并映射转换为我们的对象
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
// 处理结果集
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
总结
通过使用mybatis来操作数据库, 使我们脱离了原来jdbc操作的细节。mybatis通过外部配置,并设计如:SessionFactory, Configuration, Session等抽象,配置好之后,使用者只需关注查询业务sql。
Configuration承载着所有的配置项,当我们从Configuration获取Executor,StatementHandler,ParameterHandler,ResultSetHandler时,都会调用Interceptor。这样使用者可以方便的在这些节点拓展引入自己的逻辑。
以上是关于mybatis执行流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章