從源碼看世界:Mybatis一次數據庫操做過程

Mybatis做爲你們熟知的持久層框架,可以經過XML或註解方便地實現ORM和數據庫操做,到底它是怎麼實現的呢,這裏經過源碼帶你們揭開它的神祕面紗。java

首先建立srping boot的demo項目,添加mybatis依賴、數據庫配置等,如下貼上部分代碼sql

// 初始化mybatis的SqlSessionFactory,dataSource爲數據庫配置
@Configuration
public class MyBatisConfig {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("dev", transactionFactory, dataSource);
        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration(environment);
        configuration.addMapper(StudentMapper.class);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        return sqlSessionFactory;
    }
}

// 經過註解定義mapper
public interface StudentMapper {
    @Select({
            "select * from student where id = #{id,jdbcType=INTEGER}"
    })
    @Results({
            @Result(column = "id", property = "id", jdbcType = JdbcType.BIGINT, id = true),
            @Result(column = "order_no", property = "orderNo", jdbcType = JdbcType.VARCHAR)
    })
    Student getById(@Param("id")Integer id);
}

@RestController
@SpringBootApplication
public class MybatisTestApplication {

	@Autowired
	private SqlSessionFactory sqlSessionFactory;

	// main方法省略

    // 查詢數據庫
	@GetMapping("/test")
	public String test() {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		Student student = studentMapper.getById(1);
		return student.getName();
	}
}

從demo能夠看出Mybatis的操做順序:數據庫

繼續debug源碼,看看mybatis主要經過哪些核心類進行操做,從StudentMapper.getById方法進入後,到達org.apache.ibatis.binding.MapperProxy#invoke方法:apache

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // …… 省略不相關的類操做
      // 建立mapper方法的緩存對象
      final MapperMethod mapperMethod = cachedMapperMethod(method);
      // 執行操做
      return mapperMethod.execute(sqlSession, args);
  }

進入cachedMapperMethod,發現MapperMethod的構造函數是實例化SqlCommand和MethodSignature,而SqlCommand的屬性是經過MappedStatement獲取,MethodSignature則是經過Configuration、Mapper類和方法計算返回相關信息,所以咱們重點關注下MappedStatement緩存

public final class MappedStatement {
  // 部分屬性
  private Configuration configuration;
  private String id;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private SqlCommandType sqlCommandType;
  private String[] resultSets;
  // ……
}

從MappedStatement的屬性大體能夠猜到它是記錄sql、輸入參數、輸出結果類型等信息,有了這些信息後再回到mapperMethod.execute就能夠真正發起數據庫操做了。session

以SELECT爲例,首先包裝參數,統一調用SqlSession.selectList方法:mybatis

MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

能夠看出仍是須要MappedStatement,而具體執行則經過Executor來操做。app

進入Executor方法,首先BoundSql boundSql = ms.getBoundSql(parameterObject);,從其屬性能夠看出包含sql和輸入參數框架

最終跳到org.apache.ibatis.executor.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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 到這裏就是咱們熟悉的jdbc對象了
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  // handler最終到達的方法
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    // 是否是十分熟悉了
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    // 返回結果處理器
    return resultSetHandler.handleResultSets(ps);
  }

至此,mybatis一次數據庫操做完成,結合以上代碼從新將開頭的時序圖補充完整:

所以,mybatis的原理仍是經過JDBC實現的。這時候你們是否是依然存在很多疑問,具體sql到底何時生成,mapper接口又是如何實例化,不用急,下一篇將繼續帶你們一塊兒解開這些疑問

相關文章
相關標籤/搜索