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类代码:
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接口源码:
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类源码:
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相关的查询方法。
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方法...... @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处理器
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接口
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; }
然后它对应了一个实现类:
public class DefaultResultSetHandler implements ResultSetHandler { ...... }
这个类里边有几个非常重要的方法,我们下面通过源码看下:
//调用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的源码分析并不是非常的全面,因此还望读者指出不足!