Mybatis 执行流程

1. 基本的使用过程

public static void testAttribute() throws IOException {
    //1,读取 mybatis-config.xml 文件
    InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");

    //2,构件 sqlSessionFactory
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);

    //3,打开 sqlSession
    SqlSession sqlSession = factory.openSession();

    //4,获取 Mapper 接口对象
    TestMapper mapper = sqlSession.getMapper(TestMapper.class);
    //mapper.attribute$Where$Test(null, "张三");
    Test test = new Test();
    test.setType("测试");
    mapper.like$Test("长", test);
}

2. 读取资源

通过 IO 来读取配置,读取配置资源

/*
 * Returns a resource on the classpath as a Stream object
 *
 * @param resource The resource to find
 * @return The resource
 * @throws java.io.IOException If the resource cannot be found or read
 */
public static InputStream getResourceAsStream(String resource) throws IOException {
  return getResourceAsStream(null, resource);
}

/*
 * Returns a resource on the classpath as a Stream object
 *
 * @param loader   The classloader used to fetch the resource
 * @param resource The resource to find
 * @return The resource
 * @throws java.io.IOException If the resource cannot be found or read
 */
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
  if (in == null) {
    throw new IOException("Could not find resource " + resource);
  }
  return in;
}

3. 解析配置,创建 SqlSessionFactory

调用 SqlSessionFactoryBuilder 的 build 方法创建 SqlSessionFactory。

3.1 解析配置

第一步先创建 XMLConfigBuilder 实例,调用 parse 方法来解析配置。

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

private void parseConfiguration(XNode root) {
  try {
    // 分步骤解析
      //issue #117 read properties first
      // 1.properties
      propertiesElement(root.evalNode("properties"));
      // 2.类型别名
      typeAliasesElement(root.evalNode("typeAliases"));
      // 3.插件
      pluginElement(root.evalNode("plugins"));
      // 4.对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      // 5.对象包装工厂
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      // 6.设置
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      // 7.环境
      environmentsElement(root.evalNode("environments"));
      // 8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 9.类型处理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 10.映射器
      mapperElement(root.evalNode("mappers"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
  }
}

3.2 解析 xxxMapper.xml 配置

XMLConfigBuilder 类中 parseConfiguration() 最后一步调用 mapperElement() 来解析 mapper 配置文件。

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {
      if ("package".equals(child.getName())) {
        // 10.4自动扫描包下所有映射器
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
          // 10.1使用类路径
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
          // 映射器比较复杂,调用XMLMapperBuilder
          // 注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url != null && mapperClass == null) {
          // 10.2使用绝对url路径
          ErrorContext.instance().resource(url);
          InputStream inputStream = Resources.getUrlAsStream(url);
          // 映射器比较复杂,调用XMLMapperBuilder
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
          // 10.3使用java类名
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          // 直接把这个映射加入配置
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

最终通过 configuration.addMapers() 方法将解析完成的 mapper 存放到一个 hashMap 中,是个代理。

configuration.addMapers():

public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

MapperRegistry.addMapper()

//用来存放
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      knownMappers.put(type, new MapperProxyFactory<T>(type));
      // It's important that the type is added before the parser is run
      // otherwise the binding may automatically be attempted by the
      // mapper parser. If the type is already known, it won't try.
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

3.3 创建 SqlSessionFactory

根据解析好的配置文件,生成 sqlSessionFactory 对象,默认使用 DefaultSqlSessionFactory 实现。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
      inputStream.close();
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

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

4. 创建 SqlSession 对象

通过 SqlSessionFactory.OperSession() 开启一个 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);
    // 然后产生一个DefaultSqlSession
    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();
  }
}

4.1. 创建 Transaction 事务

创建一个事务工厂,然后开启一个事务(根据配置文件)

private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
  if (environment == null || environment.getTransactionFactory() == null) {
    return new ManagedTransactionFactory();
  }
  return environment.getTransactionFactory();
}

默认创建的是 JdbcTransaction

@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
  return new JdbcTransaction(ds, level, autoCommit);
}

4.2 创建 Executor 执行器

 // 产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
  executorType = executorType == null ? defaultExecutorType : executorType;
  // 这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;
  // 然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
  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);
  }
  // 如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
  if (cacheEnabled) {
    executor = new CachingExecutor(executor);
  }
  // 此处调用插件,通过插件可以改变Executor行为
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

4.3 返回一个 SqlSesson 对象

return new DefaultSqlSession(configuration, executor, autoCommit);

//DefaultSqlSession 构造方法
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
  this.configuration = configuration;
  this.executor = executor;
  this.dirty = false;
  this.autoCommit = autoCommit;
}

5. 获取 mapper

创建会话后,通过会话对象session获取mapper。其中会委派给Configuration对象获取。(就是从 3.2 过程中放到的那个 hashMap 去取)

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

Configuration又会委派给映射器注册机MapperRegistry去获取。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

MapperRegistry中通过MapperProxyFactory创建代理mapper的。

@SuppressWarnings("unchecked")
// 返回代理类
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  // 获取mapper代理工厂
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  // 在map中找不到则表示没有将mapper类注册进来,抛出BindingException
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    // 使用工厂进行创建一个实例,本质上是通过代理模式创建了一个代理类,创建过程中出现异常抛出BindingException
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

MapperProxyFactory最终交由JDK动态代理类Proxy创建mapper代理类的。

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader()
    , new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession
    , mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

6. 执行 mapper 的方法

通过 mapper 调用实现方法,实际是调用代理的方法。

@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()

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

到这里实际执行的是 sqlSession 提供的方法,以 insert 为例,继续向下:

DefaultSqlSession.insert()

@Override
public int insert(String statement, Object parameter) {
  return update(statement, parameter);
}

@Override
public int update(String statement, Object parameter) {
  try {
    dirty = true;
    MappedStatement ms = configuration.getMappedStatement(statement);
    //这里继续执行 executor 代理方法
    return executor.update(ms, wrapCollection(parameter));
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

BaseExecutor.update()

@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  //清空缓存
  clearLocalCache();
  return doUpdate(ms, parameter);
}

SimpleExecutor.doUpdate()

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 最终是一个statement进行处理
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}

PreparedStatementHandler.update()

@Override
public int update(Statement statement) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  int rows = ps.getUpdateCount();
  Object parameterObject = boundSql.getParameterObject();
  KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
  keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
  return rows;
}

底层用的就是PreparedStatement的execute()方法了

参考文档

https://blog.csdn.net/qq_57756904/article/details/125105656

posted @ 2024-02-19 10:40  primaryC  阅读(88)  评论(0编辑  收藏  举报