1 回顾JDBC的执行原理和流程
MyBatis是一个Dao层映射框架,底层还是用的JDBC来访问数据库,在学习MyBatis之前有必要先回顾一下JDBC的执行过程:
在这里重点说一下预编译SQL部分的Statement也就是预编译器,通过该组件来发送对应的SQL与参数。它有三种类型:分别是简单Statement(Statement),预处理Statement(PrepareStatement)和存储过程Statement(CallableStatement)。后者继承自前者,也就是说简单执行器的所有功能,预处理执行器和存储过程执行器都有。
预编译器的主要作用:设置和执行SQL语句
2 Mybatis执行流程
2.1 内部基本架构
2.2 执行流程
3 各阶段分析
3.1 阶段一:接口代理
大家都知道Mybatis有两种使用方式,分别为注解和XML形式,因此两种形式必然有与之匹配的配置,我们看下Mybatis源码的builder包:
果然在这个包下有annotation和xml两个子包:
XMLStatementBuilder类代码:
代码语言:javascript复制public class XMLStatementBuilder extends BaseBuilder {
//用来构建Mapper
private final MapperBuilderAssistant builderAssistant;
//表示全局配置
private final XNode context;
//数据库ID
private final String requiredDatabaseId;
public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context) {
this(configuration, builderAssistant, context, null);
}
public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
super(configuration);
this.builderAssistant = builderAssistant;
this.context = context;
this.requiredDatabaseId = databaseId;
}
......
//获取驱动
private LanguageDriver getLanguageDriver(String lang) {
Class<?> langClass = null;
if (lang != null) {
langClass = resolveClass(lang);
}
return builderAssistant.getLanguageDriver(langClass);
}
}
3.2 阶段二:SQL会话处理
三者关系:
SqlSessionFactory接口源码:
代码语言:javascript复制public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
DefaultSqlSessionFactory类源码:
代码语言:javascript复制public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
@Override
public SqlSession openSession(boolean autoCommit) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
}
@Override
public SqlSession openSession(ExecutorType execType) {
return openSessionFromDataSource(execType, null, false);
}
@Override
public SqlSession openSession(TransactionIsolationLevel level) {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
return openSessionFromDataSource(execType, level, false);
}
@Override
public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
return openSessionFromDataSource(execType, null, autoCommit);
}
@Override
public SqlSession openSession(Connection connection) {
return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
}
@Override
public SqlSession openSession(ExecutorType execType, Connection connection) {
return openSessionFromConnection(execType, connection);
}
@Override
public Configuration getConfiguration() {
return configuration;
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
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();
}
}
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " e, e);
} finally {
ErrorContext.instance().reset();
}
}
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
private void closeTransaction(Transaction tx) {
if (tx != null) {
try {
tx.close();
} catch (SQLException ignore) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
SqlSessionManager源码:
SqlSessionManager本身实现了SqlSessionFactory, SqlSession两个接口,所以本身能够构建Sqlsession和使用Sqlsesion声明的CURD相关的查询方法。
代码语言:javascript复制public class SqlSessionManager implements SqlSessionFactory, SqlSession {
private final SqlSessionFactory sqlSessionFactory;
private final SqlSession sqlSessionProxy;
private final ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor());
}
public static SqlSessionManager newInstance(Reader reader) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, null, null));
}
public static SqlSessionManager newInstance(Reader reader, String environment) {
return new SqlSessionManager(new SqlSessionFactoryBuilder().build(reader, environment, null));
}
......
@Override
public SqlSession openSession(Connection connection) {
return sqlSessionFactory.openSession(connection);
}
@Override
public Configuration getConfiguration() {
return sqlSessionFactory.getConfiguration();
}
@Override
public <T> T selectOne(String statement) {
return sqlSessionProxy.<T> selectOne(statement);
}
相关CRUD方法......
代码语言:javascript复制 @Override
public Connection getConnection() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot get connection. No managed session is started.");
}
return sqlSession.getConnection();
}
@Override
public void clearCache() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot clear the cache. No managed session is started.");
}
sqlSession.clearCache();
}
@Override
public void commit() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot commit. No managed session is started.");
}
sqlSession.commit();
}
@Override
public void commit(boolean force) {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot commit. No managed session is started.");
}
sqlSession.commit(force);
}
@Override
public void rollback() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");
}
sqlSession.rollback();
}
@Override
public void rollback(boolean force) {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");
}
sqlSession.rollback(force);
}
@Override
public List<BatchResult> flushStatements() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot rollback. No managed session is started.");
}
return sqlSession.flushStatements();
}
@Override
public void close() {
final SqlSession sqlSession = localSqlSession.get();
if (sqlSession == null) {
throw new SqlSessionException("Error: Cannot close. No managed session is started.");
}
try {
sqlSession.close();
} finally {
localSqlSession.set(null);
}
}
private class SqlSessionInterceptor implements InvocationHandler {
public SqlSessionInterceptor() {
// Prevent Synthetic Access
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get();
if (sqlSession != null) {
try {
return method.invoke(sqlSession, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
} else {
final SqlSession autoSqlSession = openSession();
try {
final Object result = method.invoke(autoSqlSession, args);
autoSqlSession.commit();
return result;
} catch (Throwable t) {
autoSqlSession.rollback();
throw ExceptionUtil.unwrapThrowable(t);
} finally {
autoSqlSession.close();
}
}
}
}
}
3.3 阶段三:调用执行器
执行器相关部分请看这篇文章:https://blog.csdn.net/Mr_YanMingXin/article/details/118719270
3.4 阶段四:获取JDBC处理器
代码语言:javascript复制public abstract class AbstractSQL<T> {
private static final String AND = ") nAND (";
private static final String OR = ") nOR (";
private final SQLStatement sql = new SQLStatement();
public abstract T getSelf();
public T UPDATE(String table) {
sql().statementType = SQLStatement.StatementType.UPDATE;
sql().tables.add(table);
return getSelf();
}
private static class SQLStatement {
public enum StatementType {
DELETE, INSERT, SELECT, UPDATE
}
private String selectSQL(SafeAppendable builder) {
if (distinct) {
sqlClause(builder, "SELECT DISTINCT", select, "", "", ", ");
} else {
sqlClause(builder, "SELECT", select, "", "", ", ");
}
sqlClause(builder, "FROM", tables, "", "", ", ");
joins(builder);
sqlClause(builder, "WHERE", where, "(", ")", " AND ");
sqlClause(builder, "GROUP BY", groupBy, "", "", ", ");
sqlClause(builder, "HAVING", having, "(", ")", " AND ");
sqlClause(builder, "ORDER BY", orderBy, "", "", ", ");
return builder.toString();
}
......
switch (statementType) {
case DELETE:
answer = deleteSQL(builder);
break;
case INSERT:
answer = insertSQL(builder);
break;
case SELECT:
answer = selectSQL(builder);
break;
case UPDATE:
answer = updateSQL(builder);
break;
default:
answer = null;
}
return answer;
}
}
}
3.5 阶段五:结果集映射
首先是有一个ResultSetHandler接口
代码语言:javascript复制public interface ResultSetHandler {
<E> List<E> handleResultSets(Statement stmt) throws SQLException;
<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
void handleOutputParameters(CallableStatement cs) throws SQLException;
}
然后它对应了一个实现类:
代码语言:javascript复制public class DefaultResultSetHandler implements ResultSetHandler {
......
}
这个类里边有几个非常重要的方法,我们下面通过源码看下:
代码语言:javascript复制//调用getFirstResultSet 得到结果集的包装类
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
ResultSet rs = stmt.getResultSet();
while (rs == null) {
// move forward to get the first resultset in case the driver
// doesn't return the resultset as the first result (HSQLDB 2.1)
if (stmt.getMoreResults()) {
rs = stmt.getResultSet();
} else {
if (stmt.getUpdateCount() == -1) {
// no more results. Must be no resultset
break;
}
}
}
return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
//获取结果集映射集合
private ResultSetWrapper getNextResultSet(Statement stmt) throws SQLException {
// Making this method tolerant of bad JDBC drivers
try {
if (stmt.getConnection().getMetaData().supportsMultipleResultSets()) {
// Crazy Standard JDBC way of determining if there are more results
if (!(!stmt.getMoreResults() && stmt.getUpdateCount() == -1)) {
ResultSet rs = stmt.getResultSet();
if (rs == null) {
return getNextResultSet(stmt);
} else {
return new ResultSetWrapper(rs, configuration);
}
}
}
} catch (Exception e) {
// Intentionally ignored.
}
return null;
}
//遍历结果集映射 处理结果集 生成java对象
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
4 总结
Mybatis其实就是对JDBC的大量重复代码进行了封装,也对ORM(对象关系映射)进行了实现,可以说是一款很优秀的ORM框架,除了简单的CRUD之外,还支持事务和分页以及批量增加等等,因此Mybatis的源码很值得探究,由于作者能力有限,这篇文章对Mybatis的源码分析并不是非常的全面,因此还望读者指出不足!