问题

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. POJ 2117 Electricity 双联通分量 割点

    http://poj.org/problem?id=2117 这个妹妹我竟然到现在才见过,我真是太菜了~~~ 求去掉一个点后图中最多有多少个连通块.(原图可以本身就有多个连通块) 首先设点i去掉后它的 ...

  2. hdu 5251 包围点集最小矩形 ***

    题意:小度熊有一个桌面,小度熊剪了很多矩形放在桌面上,小度熊想知道能把这些矩形包围起来的面积最小的矩形的面积是多少. 求个凸包,矩形的边一定在凸包上,枚举边,求最大值,即为所求,多年不拍几何,直接套了 ...

  3. Windows Server 2008 R2的web服务器nginx和Apache的比较

    因为很喜欢nginx,所以也想尝试在Windows下使用nginx,前面安装配置都挺顺利,把域名解析尽量后,通过域名代理访问jboss,却异常的慢,起码有3秒的时间才显示页面,而这个页面是jboss的 ...

  4. Git提交空文件夹的技巧

    这个只能说是技巧不能说是方法,原理是在每个空文件夹新建一个.gitignore文件,然后提交. 快捷命令: find . -type d -empty -exec touch {}/.gitignor ...

  5. CentOS的update-grub2命令

    这个和Ubuntu还是有些区别,在CentOS修改成如下: grub2-mkconfig -o /boot/grub2/grub.cfg

  6. linux系统编程:线程原语

    线程原语 线程概念 线程(thread),有时被称为轻量级进程(Lightweight Process,LWP).是程序运行流的最小单元.一个标准的线程由线程ID.当前指令指针(PC),寄存器集合和堆 ...

  7. java基础学习总结——对象转型

    一.对象转型介绍 对象转型分为两种:一种叫向上转型(父类对象的引用或者叫基类对象的引用指向子类对象,这就是向上转型),另一种叫向下转型.转型的意思是:如把float类型转成int类型,把double类 ...

  8. 【转】教你用C#读写、删除、更新excel表格记录

    文章出处:http://blog.csdn.net/kuangshazi515/article/details/6585118 如下图所示,编一个程序,鼠标单击窗体视图区(右边)时,获取一对坐标(X, ...

  9. .NET:“事务、并发、并发问题、事务隔离级别、锁”小议,重点介绍:“事务隔离级别"如何影响 “锁”?

    备注 我们知道事务的重要性,我们同样知道系统会出现并发,而且,一直在准求高并发,但是多数新手(包括我自己)经常忽略并发问题(更新丢失.脏读.不可重复读.幻读),如何应对并发问题呢?和线程并发控制一样, ...

  10. [Linux] Linux 守护进程的启动方法

    reference : http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html "守护进程"(daemon)就是一直在后台 ...