MyBatis源码解读

  1. mybatis的核心模块

image

  1. 注解注入方式(入口)这里用的是Hikari连接,其它连接方式道理一样
 @Bean(name = "dataSource")
 public DataSource dataSource() {
     System.out.println("==============  url  " +  url );

     if (maxpoolsize < 10) {
         maxpoolsize = 10;
     }
     if (maxpoolsize > 100) {
         maxpoolsize = 100;
     }
     final HikariDataSource ds = new HikariDataSource();
     ds.setMaximumPoolSize(maxpoolsize);
     ds.setDriverClassName(driverClassName);
     ds.setJdbcUrl(url);
     ds.setUsername(username);
     ds.setPassword(password);
     return ds;
 }

@Bean
 public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
     org.apache.ibatis.logging.LogFactory.useStdOutLogging();
     final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
     sessionFactory.setDataSource(dataSource);
     return sessionFactory.getObject();
 }

关键在于:

final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
return sessionFactory.getObject();

sessionFactory.setDataSource(dataSource);
是在进行数据库配置项初始化。

sessionFactory.getObject();

在mybatis-spring.jar中存在SqlSessionFactoryBean。

public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }

  return this.sqlSessionFactory;
}

 public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

  this.sqlSessionFactory = buildSqlSessionFactory();
}

getObject()中的afterPropertiesSet方法调用buildSqlSessionFactory()才是进行mybatis的初始化。

mybatis的真正的入口

sqlSessionFactoryBuilder.build(configuration);
public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}

加载config配置文件:这里给注入到DefaultSqlSessionFactory的config, openSession的时候config才会有值。openSessionFromDataSource()方法加载对应的exector,获取对应的sqlSession。

@Override
 public SqlSession openSession() {
   return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
 }

  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();
   }
 }

拿到DefaultSqlSession 对象后采用反射的方式 mapperProxyFactory.newInstance(sqlSession) 获取mapper对象,mapperRegistry中存在 Map<Class, MapperProxyFactory> knownMappers = new HashMap<>(),存放相应的代理工厂类,可以获取到相应的代理对象

@Override
 public <T> T getMapper(Class<T> type) {
   return configuration.getMapper(type, this);
 }

MapperProxy是代理类,invoke方法执行相应的sql,MapperMethod.execute(sqlSession, args);执行相应的方法


/**
 * 反射入口
 * @param proxy
 * @param method
 * @param args
 * @return
 * @throws Throwable
 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    } else if (isDefaultMethod(method)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

mapperMethod.execute(sqlSession, args):是相应的插入,更新,删除,查询方法。


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;
 }

以select查询为例,真正执行sql的是sqlSession.selectList方法

@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    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();
  }
}


  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> 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;
}

真正核心的是doQuery方法,其实也是对于jdbc的连接做了一次二次封装。

@Override
 public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
     throws SQLException {
   Statement stmt = null;
   try {
     flushStatements();
     Configuration configuration = ms.getConfiguration();
     StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
     //获取Statementhandle对象--->对jdbc的statement的封装
     Connection connection = getConnection(ms.getStatementLog());
     //获取连接
     stmt = handler.prepare(connection, transaction.getTimeout());
     handler.parameterize(stmt);
     //执行 statement.executeQuery(sql);,resultHandler 是对结果集的封装,这倆步都在handler.query()里进行处理。
     return handler.query(stmt, resultHandler);
   } finally {
     closeStatement(stmt);
   }
 }

  @Override
 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
   String sql = boundSql.getSql();
   //执行 statement.executeQuery(sql);
   statement.execute(sql); 
   //这里是在处理结果集,对结果集的封装
   return resultSetHandler.handleResultSets(statement);
 }

总结:

mybatis的实际操作过程就是jdbc的连接过程,只不过中间用到了代理模式,对mybatis的接口文件进行一个代理,解析sql语句,执行响应的jdbc流程,中间对statement,resultSet等进行了封装。


文章作者: 凌云
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 凌云 !
  目录