mybatis源码分析(上)

在了解mybatis之前,我们先来看看原始jdbc操作数据库需要哪些步骤:

		Class.forName("com.mysql.jdbc.Driver"); //1.加载驱动
       Connection con = DriverManager.getConnection(
           "jdbc:mysql://localhost:3306/kkb","root","123"
        );                                                            //2.获取数据库链接
       con.setAutoCommit(false)
      PreparedStatement ps = con.preparedStatement(sql); //   3.得到数据库操作对象
      ps.setInt();                                              // 4.设置参数
      ps.setString();
     try{
    	int num=  ps.executeUpdate();
    	ResultSet rs=   ps.executeQuery();      //5.执行sql,得到结果 
    	ps.executeBatch();
    	con.commit();                                        //6. 提交事物
    }catch(SQLException ex){
       con.rollback();
   }
   con.close();                                            //7.关闭链接

mybatis作为一个数据持久层框架,说白了就是对上面几个步骤的封装,然我们只需要关心sql的编写,其它的事,都是一些公共的操作,我们只需要简单的配置,它就可以帮我们完成了。

下面对源码的解析,大体的方向也就是看mybatis对应上述的步骤,都是怎么封装的。

1.加载配置文件,生成Configuration对象

Configuration对象是mybatis配置在内存中的载体。
这里的源码,多余的内容被我删了,只留下了主要的处理逻辑。

//SqlSessionFactoryBuilder.build()方法,该方法返回SqlSessionFactory(工厂模式)
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
    	//reader 配置文件的数据流
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    }
  }

  public Configuration parse() {//解析配置文件的方法
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
   //具体解析配置,然后设置到Configuration对象中
   //这里采用了建造者模式,一步步完善Configuration的属性
  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(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"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
2.创建执行器对象Executor

Executor是个接口,它有两个实现类BaseExecutor和CachingExecutor

  • BaseExecutor(适配器 模式)有三个子类:
    * SimpleExecutor 普通执行器
    * ReuseExecutor 可重用执行器
    * BatchExecutor 批量执行器
    具体看下面源码,会根据不同情况创建不同的Executor.
    通过第一步的SqlSessionFactory.openSession()就会创建一个SqlSession,
    下面看看openSession()干了啥 (采用的DefaultSqlSessionFactory的openSession())
  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);
    } 
  }
  //这个地方判断执行器类型:BATCH 批量; REUSE 可重用;simple 普通执行器
 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);
    }
    //是否开启了缓存   默认开启
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    //加入了拦截器逻辑
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
//最后返回了DefaultSqlSession  调用构造函数填充属性
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }
3.创建StatmentHandler对象,设置参数会用ParmeterHandler,结果映射会用到ResultHandler

StatmentHandler是个接口 下面有两个子类BaseStatmentHandler和RoutingStatementHandler;
SimpleStatementHandler,PreparedStatementHandler,CallableStatementHandler继承了BaseStatmentHandler。
对比本文最开始列出来的几个操作数据的步骤,1加载驱动 2获取数据库连接(mybatis会初始化数据库连接池),因为不是核心,我这就不去贴源码了。
这个statment对象是在什么时候创建的呢?看看执行器对象SimpleExecutor就知道了

执行查询主要分为三步

//用执行器对象执行某个数据库操作的时候,就会创建statmentHandler对象 ,下面只列出了query操作

 @Override
  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();
      //1. 创建StatementHandler 对象
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //2. sql预编译后,返回PrepareStatment
      stmt = prepareStatement(handler, ms.getStatementLog());
      //3. 通过statment对象执行真正的数据库查询操作
      return handler.<E>query(stmt, resultHandler);
    }
  }

第一步 创建mybatis 的StatmentHandler对象

 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;
 }
 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根据不同的类型创建不同的StatmentHandler对象
   switch (ms.getStatementType()) {
     case STATEMENT://sql没有参数
       delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
       break;
     case PREPARED://sql有参数
       delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
       break;
     case CALLABLE://采用 call()
       delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
       break;
     default:
       throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
   }
 }

第二.1步 创建数据库对象PrepareStatment对象 相当于最上面的 3.得到数据库操作对象。

    private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    //2.1 预编译后 返回 prepareStatement
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 2.2设置sql参数 (源码在下一步)
    handler.parameterize(stmt);
    return stmt;
  }
@Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
    //这个方法 就是通过数据库连接创建真正的数据 PrepareStatment对象了
      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);
    }
  }
 @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 {
      //这里就是调用本文最上面步骤3的代码了 预编译
        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);
    } 

第二.2步 给PrepareStatment对象赋值 相当于最上面的 4.设置参数
调用这个方法给参数赋值handler.parameterize(stmt);

@Override
public void parameterize(Statement statement) throws SQLException {
//赋值 调用的是 parameterHandler
  parameterHandler.setParameters((PreparedStatement) statement);
  }
  
//主要的逻辑就是将java参数类型转成数据库类型   不同的类型调用不同的Typehandler赋值
@Override
public void setParameters(PreparedStatement ps) {
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    for (int i = 0; i < parameterMappings.size(); i++) {
      ParameterMapping parameterMapping = parameterMappings.get(i);
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } 
      }
    }
  }
}
}
//关注最下面的typeHandler.setParameter(ps, i + 1, value, jdbcType)方法即可
//如果是数组调用的就是
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
  ps.setArray(i, (Array) parameter);
} 

第三步 执行查询 将结果映射成java对象

   //执行查询
     @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  //这个地方也很熟悉上,对应最上面的第五步,执行sql
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }
  }
  //TODO resultSetHandler 源码
  
mybatis面向接口编程,采用的jdk动态代理

看看 session.getMapper(),获取操作数据库的dao对象了(代理模式)

 @SuppressWarnings("unchecked")
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    try {
    //一看这个名字,就猜到是代理工厂了
      return mapperProxyFactory.newInstance(sqlSession);
    } 
 @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
  //这个代码就很熟悉了啥 采用jdk动态代理,对mapper接口进行代理
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    //调用上面紧挨着的方法
    return newInstance(mapperProxy);
  }
  }

到这里,已经大体分析完了mybatis的整个查询过程,具体某些方法的执行细节,就需要大家自己去debug看了,其它update,delete操作也和这个流程差不多。后面会分析mybatis的缓存和事务。

发布了42 篇原创文章 · 获赞 29 · 访问量 2549

猜你喜欢

转载自blog.csdn.net/qq_32314335/article/details/103411535