系列
- mybatis-3.4.6 配置介绍
- mybatis-3.4.6 顶层配置解析
- mybatis-3.4.6 子配置解析
- mybatis-3.4.6 mapper解析
- mybatis-3.4.6 SQL执行流程
- mybatis-3.4.6 SqlSession执行过程
- mybatis-3.4.6 缓存介绍
- mybatis-3.4.6 自增主键
- mybatis-3.4.6 foreach 自增主键
- mybatis-3.4.6 事务管理
开篇
- 这个系列是基于mybatis-3.4.6版本的源码解析,这篇文章是梳理基于SqlSessionFactory的mybatis的整体执行流程,文章更侧重于整体流程的梳理。
- 在mybatis-3.4.6 SQL执行流程的文章中已经分析了SQL执行的流程并定位最终有SqlSession来完成最后的SQL执行过程。
- 在这篇文章中具体分析下SqlSession的执行过程。
JDBC执行Statement步骤
jdbc执行Statement的核心步骤,mybatis的流程大致如此。
// 驱动注册程序
Class.forName(com.mysql.jdbc.Driver);
// 获取连接对象
Connection conn = DriverManager.getConnection(url,user,password);
// 创建Statement对象
Statement stsm = conn.createStatement();
// 执行sql语句
stsm.executeUpdate(sql);
// 关闭连接
stsm.close();
conn.close();
- 1、驱动注册程序。
- 2、获取连接对象。
- 3、创建Statement对象。
- 4、执行sql语句。
- 5、关闭连接。
SqlSession执行过程
- DefaultSqlSession执行CachingExecutor,CachingExecutor执行SimpleExecutor,最终执行流程都通过SimpleExecutor来执行。
- SimpleExecutor核心在于获取connection,初始化statement对象,格式化查询SQL并通过PreparedStatementHandler执行statement。
- PreparedStatementHandler是真正执行SQL查询和结果处理的核心类。
- DefaultResultHandler负责处理查询结果的逻辑。
- SimpleExecutor的prepareStatement内部创建connection和statement的核心逻辑。
MapperMethod
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
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;
}
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
private Object executeForMany(SqlSession sqlSession, Object[] args) {
List result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
}
- MapperMethod的SqlCommand创建打通了Dao和Mapper.xml的映射关系,Mapper.xml在Configuration的对象中按照dao的interface+method维度保存了MappedStatement,而SqlCommand的创建就是根据dao的interface+method查询Configuration获得MappedStatement进行后续的所有操作。
- 从MapperMethod的execute可以看出所有的SQL命令本质都是通过sqlSession来完成真正的执行。
- 那么下一步核心就得研究下sqlSession的执行过程了。
SqlCommand
public class MapperMethod {
public static class SqlCommand {
private final String name;
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class> mapperInterface, Method method) {
final String methodName = method.getName();
final Class> declaringClass = method.getDeclaringClass();
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
}
} else {
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
private MappedStatement resolveMappedStatement(Class> mapperInterface, String methodName,
Class> declaringClass, Configuration configuration) {
// 根据interface + method去查找对应的MappedStatement对象
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
}
- resolveMappedStatement根据mapper.xml文件的namespace的namespace + 对应的执行的method去查找对应的MappedStatement对象。
DefaultSqlSession
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
private final boolean autoCommit;
private boolean dirty;
private List> cursorList;
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
// 查询配置xml解析后的MappedStatement对象
// statement为interface+method维度,如cn.edu.example.mapper.UserMapper.getById
MappedStatement ms = configuration.getMappedStatement(statement);
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();
}
}
}
- DefaultSqlSession从configuration根据interface+method维度获取MappedStatement。
- MappedStatement的注册本身包含interface+method和method两个维度的key。
- DefaultSqlSession内部通过executor去执行SQL查询。
Executor
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
- Executor的核心类关系如上图所示,核心接口也如上所示。
CachingExecutor
public class CachingExecutor implements Executor {
// delegate为SimpleExecutor对象
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
@Override
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public List 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 list = (List) 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);
}
}
- DefaultSqlSession通过CachingExecutor去执行,在CachingExecutor内部通过SimpleExecutor执行逻辑。
- CachingExecutor的delegate为SimpleExecutor对象,真正是通过SimpleExecutor去执行query。
SimpleExecutor
public abstract class BaseExecutor implements Executor {
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
try {
queryStack++;
list = resultHandler == null ? (List) localCache.getObject(key) : null;
// 核心逻辑先取缓存,没有再从DB进行获取
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 从mysql进行查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
return list;
}
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
}
- SimpleExecutor的query调用父类的BaseExecutor的queryFromDatabase,进而调用SimpleExecutor自身的doQuery来实现查询。
public class SimpleExecutor extends BaseExecutor {
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 创建RoutingStatementHandler对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 核心逻辑是创建mysql的stmt对象,内部包含建立connection和创建stmt对象
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行query操作
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取mysql连接对象
Connection connection = getConnection(statementLog);
// 初始化mysql的statement对象
stmt = handler.prepare(connection, transaction.getTimeout());
// 初始化mysql的statement的查询参数
handler.parameterize(stmt);
return stmt;
}
}
public class Configuration {
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;
}
- SimpleExecutor的configuration.newStatementHandler返回RoutingStatementHandler对象。
- prepareStatement的负责最核心的connection的建立以及statement的生成,所有的SQL执行是通过statement来完成操作的。
- prepareStatement核心对statement的查询语句进行了格式化,填充请求参数。
RoutingStatementHandler
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler 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());
}
}
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
// 本质上是PreparedStatementHandler对象
return delegate.query(statement, resultHandler);
}
}
- RoutingStatementHandler针对StatementHandler进行二次封装,内部是通过StatementHandler来实现query查询。
- 这里暂时通过PreparedStatementHandler来进行举例。
PreparedStatementHandler
public class PreparedStatementHandler extends BaseStatementHandler {
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
// mysql的PreparedStatement对象并执行
PreparedStatement ps = (PreparedStatement) statement;
// 执行PreparedStatement
ps.execute();
// 通过handleResultSets处理查询结果
return resultSetHandler. handleResultSets(ps);
}
public List
- PreparedStatementHandler的内部执行流程是执行statement并通过handleResultSets来处理结果。
获取connection的过程
public class SimpleExecutor extends BaseExecutor {
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
}
public abstract class BaseExecutor implements Executor {
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
}
public class JdbcTransaction implements Transaction {
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommmit = desiredAutoCommit;
}
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
protected void openConnection() throws SQLException {
// 通过JDBC的 getConnection来获取connection连接对象
connection = dataSource.getConnection();
if (level != null) {
connection.setTransactionIsolation(level.getLevel());
}
setDesiredAutoCommit(autoCommmit);
}
}
- connection的openConnection本质是通过dataSource.getConnection()来实现连接的获取。
获取statetment的过程
public abstract class BaseStatementHandler implements StatementHandler {
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
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);
}
}
}
public class PreparedStatementHandler extends BaseStatementHandler {
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
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() != null) {
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
return connection.prepareStatement(sql);
}
}
}
- statetment的获取本质是通过connection.prepareStatement去实现,中间无非增加了一些proxy的设置。
参考文章
- 源码参考
- mybatis官网介绍
- 深入理解mybatis原理
- Mybatis3.4.x技术内幕
- mybatis 3.x源码深度解析与最佳实践