问题

mybatis的xml中的sql语句是启动时生成JDK代理类的时候就生成一次么

调用顺序链

  • 解析xml配置

    Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
  • 调用SqlSessionFactoryBuilder的方法

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

    public Configuration parse() {
    if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
    }
  • sqlSessionFactory.getConfiguration().addMapper(BookMapper.class);触发MapperRegistry.addMapper

    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);
    }
    }
    }
    }
  • 触发MapperAnnotationBuilder.parse方法

    public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
    loadXmlResource();
    configuration.addLoadedResource(resource);
    assistant.setCurrentNamespace(type.getName());
    parseCache();
    parseCacheRef();
    Method[] methods = type.getMethods();
    for (Method method : methods) {
    try {
    // issue #237
    if (!method.isBridge()) {
    parseStatement(method);
    }
    } catch (IncompleteElementException e) {
    configuration.addIncompleteMethod(new MethodResolver(this, method));
    }
    }
    }
    parsePendingMethods();
    }
  • 触发触发MapperAnnotationBuilder.loadXmlResource

    private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
    String xmlResource = type.getName().replace('.', '/') + ".xml";
    InputStream inputStream = null;
    try {
    inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
    } catch (IOException e) {
    // ignore, resource is not required
    }
    if (inputStream != null) {
    XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
    xmlParser.parse();
    }
    }
    }
    • 触发XMLMapperBuilder.parse

        public void parse() {
      if (!configuration.isResourceLoaded(resource)) {
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
      }
      parsePendingResultMaps();
      parsePendingChacheRefs();
      parsePendingStatements();
      }
  • 触发XMLMapperBuilder.configurationElement

    private void configurationElement(XNode context) {
    try {
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
    throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
    }
  • 触发XMLMapperBuilder.buildStatementFromContext

    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
    statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
    configuration.addIncompleteStatement(statementParser);
    }
    }
    }
  • 触发XMLStatementBuilder.parseStatementNode

    public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
    }
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
    // Include Fragments before parsing
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());
    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
    configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
    ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
    fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
    resultSetTypeEnum, flushCache, useCache, resultOrdered,
    keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
  • 触发MapperBuilderAssistant.addMappedStatement将解析好的statement放到configuration的map中

    public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
    }

sql的组装

  • MappedStatement.getBoundSql

    public BoundSql getBoundSql(Object parameterObject) {
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings == null || parameterMappings.isEmpty()) {
    boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
    }
    // check for nested result maps in parameter mappings (issue #30)
    for (ParameterMapping pm : boundSql.getParameterMappings()) {
    String rmId = pm.getResultMapId();
    if (rmId != null) {
    ResultMap rm = configuration.getResultMap(rmId);
    if (rm != null) {
    hasNestedResultMaps |= rm.hasNestedResultMaps();
    }
    }
    }
    return boundSql;
    }

    里头主要是把sql语句和参数组成BoundSql对象

  • 调用BaseExecutor的query方法

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  • getBoundSql调用RawSqlSource调用StaticSqlSource的getBoundSql

    public BoundSql getBoundSql(Object parameterObject) {
    return new BoundSql(configuration, sql, parameterMappings, parameterObject);
    }

    这里的sql就是mapper里头写得select * from book where id = ? limit 1

  • BaseExecutor.queryFromDatabase

    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;
    }
  • 调用SimpleExecutor.doQuery生成JDBC的statement

    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);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);
    } finally {
    closeStatement(stmt);
    }
    }
  • 调用StatementHandler来具体处理,这里是RoutingStatementHandler委托PreparedStatementHandler来处理sql的生成

    public Statement prepare(Connection connection) throws SQLException {
    return delegate.prepare(connection);
    }
  • 回调BaseStatementHandler的prepare方法

    public Statement prepare(Connection connection) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
    statement = instantiateStatement(connection);
    setStatementTimeout(statement);
    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);
    }
    }
  • 在调用PreparedStatementHandler的instantiateStatement

    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 {
    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);
    }
    }
  • 组装statement之后,填充参数

    public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
    }
  • 调用DefaultParameterHandler.setParameters方法,自此完成sql的拼装

     
    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);
    } catch (TypeException e) {
    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
    } catch (SQLException e) {
    throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
    }
    }
    }
    }
    }

mybatis的statement的解析与加载(springboot)的更多相关文章

  1. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  2. 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  3. 精尽 MyBatis 源码分析 - MyBatis 初始化(一)之加载 mybatis-config.xml

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  4. 基于FBX SDK的FBX模型解析与加载 -(一)

    http://blog.csdn.net/bugrunner/article/details/7210511 1. 简介 FBX是Autodesk的一个用于跨平台的免费三维数据交换的格式(最早不是由A ...

  5. maven加载springboot project

    maven加载springboot project   1● 下载项目 2● 构建project mvn install mvn package   3● idea加载 4● run启动   ==== ...

  6. Mybatis学习(6)动态加载、一二级缓存

    一.动态加载: resultMap可以实现高级映射(使用association.collection实现一对一及一对多映射),association.collection具备延迟加载功能. 需求: 如 ...

  7. 【MyBatis源码分析】Configuration加载(下篇)

    元素设置 继续MyBatis的Configuration加载源码分析: private void parseConfiguration(XNode root) { try { Properties s ...

  8. MyBatis入门(五)---延时加载、缓存

    一.创建数据库 1.1.建立数据库 /* SQLyog Enterprise v12.09 (64 bit) MySQL - 5.7.9-log : Database - mybatis ****** ...

  9. mybatis和hibernate中的懒加载

    概念:所谓懒加载就是延时加载,延迟加载.什么时候用懒加载呢,我只能回答要用懒加载的时候就用懒加载.至于为什么要用懒加载呢,就是当我们要访问的数据量过大时,明显用缓存不太合适,因为内存容量有限 ,为了减 ...

随机推荐

  1. 01-学前入门.Net两种交换模式

    C/S:客户机(Client)/服务器模式(Server)Winfrom应用程序 B/S:浏览器(Browser)/服务器模式(Server)Internet应用模式

  2. [LOJ2542][PKUWC2018]随机游走(MinMax容斥+树形DP)

    MinMax容斥将问题转化为求x到S中任意点的最小时间. 树形DP,直接求概率比较困难,考虑只求系数.最后由于x节点作为树根无父亲,所以求出的第二个系数就是答案. https://blog.csdn. ...

  3. 【SPFA】POJ1511-Invitation Cards

    [题目大意] 给出一张有向图,以1位源点,求“从源点出发到各点的距离”和“与各点返回源点的距离和”相加得到的和. [思路] 毫无疑问是最短路径,但是这数据量就算是SPFA也绝壁会超时啊,抱着必死的心态 ...

  4. Linux下gcc与g++用法以及编写makefile

    1.         gcc与g++编译流程: 1)         编译流程: 2)         预处理:生成.i的预处理文件. Ø 只激活预处理,这个不生成文件,需要把它重定向一个输出文件. ...

  5. Lunix/Mac下根据最后修改时间复制文件和文件夹,保持原有的目录结构

    度娘知道:http://zhidao.baidu.com/link?url=DD47jm6qDgT7yxsnz9e-NC4Fqd33oRoiIwcGLkw5TL4cbf50VKY2IONbHKH0IE ...

  6. bzoj 3240 矩阵乘法+十进制快速幂

    首先,构造出从f[][i]->f[][i+1]的转移矩阵a,和从f[i][m]->f[i+1][1]的转移矩阵b, 那么从f[1][1]转移到f[n][m]就是init*(a^(m-1)* ...

  7. Git_Bug分支

    软件开发中,bug就像家常便饭一样.有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除. 当你接到一个修复一 ...

  8. Bootstrap 3之美03-独立行,文字环绕,图片自适应,隐藏元素

    本篇主要包括: ■  添加独立的一行■  文字环绕■  图片自适应■  隐藏元素 添加独立的一行 在id为body的section和id为main的section之间,添加2张图片. 我们发现,新加的 ...

  9. datagrid在MVC中的运用07-实现Master-Detail(使用PartialView)

    本文主要体验用jQuery Easyui的datagrid来实现Master-Detail主次表.谢谢Kevin的博文,助我打开了思路. 主表显示所有的Category,当点击主表的展开按钮,显示该C ...

  10. FT项目开发技术点(四)

    1.jsp页面form传递过来的值是在后台ishi通过name获得的而非ID.上传文字要用input type=text属性 <tr> <th>品牌:</th> & ...