mybatis(一)基本执行流程

mybatis(一)基本执行流程

一,mybatis组件

SqlSessionFactoryBuilder

  • 根据XML配置文件或者Java代码生成SqlSessionFactory

SqlSessionFactory

  • 使用它生成Sqlsession

是一定定义了生成各种类型sqlSession的接口

public interface SqlSessionFactory {
    
    

  //8个方法可以用来创建SqlSession实例
  SqlSession openSession();
  //自动提交
  SqlSession openSession(boolean autoCommit);
  //连接
  SqlSession openSession(Connection connection);
  //事务隔离级别
  SqlSession openSession(TransactionIsolationLevel level);
  //根据执行器类型生成sqlSession
  SqlSession openSession(ExecutorType execType);
  SqlSession openSession(ExecutorType execType, boolean autoCommit);
  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
  SqlSession openSession(ExecutorType execType, Connection connection);
  // 获取配置信息
  Configuration getConfiguration();
}

SqlSessionFactory只有两个子类:

img

Sqlsession

  • 可以发送SQL语句返回结果,也可以获取Mapper接口。
  • 主要作用:执行sql,管理事务,获取mapper
在这里插入图片描述

Mapper

  • 它由一个Java接口和一个XML文件(或注解)构成,需要给出对应的SQL和映射规则,它可以发送SQL并返回结果。

解释方面过程:

public class Main {
    
    
    public static void main(String[] args) throws IOException {
    
    

        // mybatis配置文件信息
        String resource = "mybatis.xml";
        // 通过输入流读取配置文件信息
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 根据配置文件信息由SqlSessionFactoryBuilder构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		// 由SqlSessionFactory构建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
    
    
            // 通过SqlSession获取mapper接口,从而进行操作
            ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);
            List<Product> productList = productMapper.selectProductList();
            for (Product product : productList) {
    
    
                System.out.printf(product.toString());
            }
        } finally {
    
    
            sqlSession.close();
        }
    }
}

二,mybatis基本执行流程

根据上面定义的流程,一步步看

2.1 构建SqlSessionFactory

根据配置文件信息由SqlSessionFactoryBuilder构建SqlSessionFactory

        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

翻看源码:

  public SqlSessionFactory build(InputStream inputStream) {
    
    
    return build(inputStream, null, null);
  }

  // 委托XMLConfigBuilder来解析xml文件封装在Configuration对象中,并根据配置文件构建SqlSessionFactory
  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.
      }
    }
  }

  //最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
  public SqlSessionFactory build(Configuration config) {
    
    
    return new DefaultSqlSessionFactory(config);	// DefaultSqlSessionFactory是SqlSessionFactory的实现类
  }

2.2 构建SqlSession

获取到sqlSessionFactory后就可以通过定义好的接口方法获取一个sqlSession,从而进行数据库交互

		// 由SqlSessionFactory构建SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

翻看源码:默认的打开一个sqlSession的方法:

  public SqlSession openSession() {
    
    
    // 构建默认类型的sqlSession
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

sqlSession有几种类型:通过一个枚举去维护

public enum ExecutorType {
    
    
    //ExecutorType.SIMPLE
    //这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
    //ExecutorType.REUSE
    //这个执行器类型会复用预处理语句。
    //ExecutorType.BATCH
    //这个执行器会批量执行所有更新语句,如果SELECT在它们中间执行还会标定它们是必须的,来保证一个简单并易于理解的行为。
  SIMPLE, REUSE, BATCH
}

2.3 利用sqlSession执行数据库交互

// 先获取mapper
ProductMapper productMapper = sqlSession.getMapper(ProductMapper.class);

获取mapper源码:

  public <T> T getMapper(Class<T> type) {
    
    
    //最后会去调用MapperRegistry.getMapper
    return configuration.<T>getMapper(type, this);
  }

  //映射注册机
  protected MapperRegistry mapperRegistry = new MapperRegistry(this);

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

  //返回mapper的代理对象
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    
    
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
    
    
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
    
    
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
    
    
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

通过JDK动态代理生成mapper的代理对象:

 mapperProxyFactory.newInstance(sqlSession);

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

  protected T newInstance(MapperProxy<T> mapperProxy) {
    
    
    //用JDK自带的动态代理生成映射器
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {
    
     mapperInterface }, mapperProxy);
  }

解析一波:

    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);

这里根据class类型获取到的代理工厂其实很重要,最后获取mapper代理对象就是通过这个工厂的newInstance方法:

      return mapperProxyFactory.newInstance(sqlSession);

所以,我们看看这个代理工厂是如何通过class类型获取到的:

所有的mapper代理工厂都被根据类型封装在一个map中,key是class类型,value是mapperProxyFactory

  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

那么mapper的配置是怎么添加到这个map中的呢?

在看XMLConfigBuilder配置解析

  //解析配置
  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);
    }
  }

mapper xml解析

	//10.映射器
//	10.1使用类路径
//	<mappers>
//	  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
//	  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
//	  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
//	</mappers>
//
//	10.2使用绝对url路径
//	<mappers>
//	  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
//	  <mapper url="file:///var/mappers/BlogMapper.xml"/>
//	  <mapper url="file:///var/mappers/PostMapper.xml"/>
//	</mappers>
//
//	10.3使用java类名
//	<mappers>
//	  <mapper class="org.mybatis.builder.AuthorMapper"/>
//	  <mapper class="org.mybatis.builder.BlogMapper"/>
//	  <mapper class="org.mybatis.builder.PostMapper"/>
//	</mappers>
//
//	10.4自动扫描包下所有映射器
//	<mappers>
//	  <package name="org.mybatis.builder"/>
//	</mappers>  
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.");
          }
        }
      }
    }
  }

将解析好的配置添加到映射map:

  //将包下所有类加入到mapper
  public void addMappers(String packageName, Class<?> superType) {
    
    
    mapperRegistry.addMappers(packageName, superType);
  }

  public void addMappers(String packageName) {
    
    
    mapperRegistry.addMappers(packageName);
  }

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

 public <T> void addMapper(Class<T> type) {
    
    
    //mapper必须是接口!才会添加
    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));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
    
    
        //如果加载过程中出现异常需要再将这个mapper从mybatis中删除
        if (!loadCompleted) {
    
    
          knownMappers.remove(type);
        }
      }
    }
  }

2.4 执行数据库操作

1.查询

  public <T> T selectOne(String statement) {
    
    
    return this.<T>selectOne(statement, null);
  }

  public <T> T selectOne(String statement, Object parameter) {
    
    
    // 内部其实调用的是selectList
    List<T> list = this.<T>selectList(statement, parameter);
    if (list.size() == 1) {
    
    
      return list.get(0);
    } else if (list.size() > 1) {
    
    
      // 查询经典错误,selectOne时出现多结果集就会报TooManyResultsException
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
    
    
      return null;
    }
  }

  public <E> List<E> selectList(String statement, Object parameter) {
    
    
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    
    
    try {
    
    
      //根据statement id找到对应的MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      //转而用执行器来查询结果,注意这里传入的ResultHandler是null
      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();
    }
  }

  //SqlSession.selectList会调用此方法
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    
    
    //得到绑定sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    //创建缓存Key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //查询,query方法是先查缓存,再调用jdbc的方法查数据库
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }


  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
      //这里看到ResultHandler传入了
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //准备语句
      stmt = prepareStatement(handler, ms.getStatementLog());
      //StatementHandler.query
      return handler.<E>query(stmt, resultHandler);
    } finally {
    
    
      closeStatement(stmt);
    }
  }

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    
    
    String sql = boundSql.getSql();
    statement.execute(sql);
    //先执行Statement.execute,然后交给ResultSetHandler.handleResultSets
    return resultSetHandler.<E>handleResultSets(statement);
  }

所以整个链路到现在为止已经比较清楚了:

  1. 读取配置,构建SqlSessionFactoryBuilder
  2. 利用SqlSessionFactoryBuilder生成SqlSessionFactory,再生成SqlSession
  3. 利用SqlSession执行数据库操作
    1. 数据库操作是通过Executor执行器去执行,内部执行原理是jdbc封装后的数据库操作

猜你喜欢

转载自blog.csdn.net/weixin_41922289/article/details/108619428