• mybatis 学习四 源码分析 mybatis如何执行的一条sql


    总体三部分,创建sessionfactory,创建session,执行sql获取结果

    1,创建sessionfactory

         这里其实主要做的事情就是将xml的所有配置信息转换成一个Configuration对象,然后用这个对象组装成factory返回。
          
         //mybatis配置文件
    String resource = "conf.xml"; InputStream is = TestMybatis.class.getClassLoader().getResourceAsStream(resource); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);

         1,xml转换成Configuration对象

              根据方法找到源码,这里可以多接受两个参数,一会有用到
               
      public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
        //environment表示要使用哪个db,properties 表示资源信息 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. } } }
              继续往里找源码,找到这里,这里就是将xml中所有配置信息转换成对象的具体方法了
               
      //将xml中的所有节点都解析成对象信息
    private void parseConfiguration(XNode root) { try { propertiesElement(root.evalNode("properties")); //issue #117 read properties first typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
              主要看三个吧
              properties的转换,从这里的逻辑可以看出来资源信息的加载顺序,在之前的章节里有说过。
               
      private void propertiesElement(XNode context) throws Exception {
        if (context != null) {
        //获取xml里面设置的 Properties defaults
    = context.getChildrenAsProperties();
        //获取指定的资源文件里的 String resource
    = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); }
        //覆盖掉了xml里配置的
    if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } Properties vars = configuration.getVariables();
        //覆盖掉了上面的
    if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); } }
              environments的转换,这里首先会看是否传进来了一个id,如果么有,那么用默认的。
        
    private void environmentsElement(XNode context) throws Exception {
        if (context != null) {
        //如果没有指定,那么直接使用默认的
    if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) {
          //获取事物factory TransactionFactory txFactory
    = transactionManagerElement(child.evalNode("transactionManager"));
          //获取datasourcefactory DataSourceFactory dsFactory
    = dataSourceElement(child.evalNode("dataSource"));
    DataSource dataSource
    = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
              其次,获取事物factory的时候,会根据配置的transactionManager属性来获取看是用JdbcTransactionFactory还是ManagedTransactionFactory,这里会涉及到事物的一些处理机制,两种类处理方法不一样的。
        
      private TransactionFactory transactionManagerElement(XNode context) throws Exception {
        if (context != null) {
        //根据type来决定要实例化哪个factory出来 String type
    = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }
              再次,获取datasourcefactory的时候,会根据配置的dataSource属性来看是使用UnpooledDataSourceFactory 还是pooledDataSourceFactory ,还是JndiDataSourceFactory。这里会涉及到数据源的获取方式,是连接池,还是单次链接,还是jndi。

        

      private DataSourceFactory dataSourceElement(XNode context) throws Exception {
        if (context != null) {
        //根据type来决定实例化哪个factory String type
    = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); }


              最重要的是mappers的转换,这里的方法就把所有的sql语句就转换过来了,将来要找的话,也是这里的源泉。

         2,将这个转换好的configuration对象组装成一个DefaultSqlSessionFactory,就可以供外部使用了。

               
        //返回sessionfactory
      public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
      }
     

    2,创建sqlsession

         1,首先从confinguration对象中,找到对应的事物factory,这里有两种facroty。     

         一种JdbcTransactionFactory,直观地讲,就是JdbcTransaction是使用的java.sql.Connection 上的commit和rollback功能,JdbcTransaction只          是相当于对java.sql.Connection事务处理进行了一次包装(wrapper),Transaction的事务管理都是通过java.sql.Connection实现的。
         
    public void commit() throws SQLException {
        if (connection != null && !connection.getAutoCommit()) {
          if (log.isDebugEnabled()) {
            log.debug("Committing JDBC Connection [" + connection + "]");
          }
        //这里就是直接使用的jdbc的提交 connection.commit(); } }
    public void rollback() throws SQLException { if (connection != null && !connection.getAutoCommit()) { if (log.isDebugEnabled()) { log.debug("Rolling back JDBC Connection [" + connection + "]"); }
        //这里就是直接使用的jdbc的回滚
      connection.rollback(); } }
         一种是ManagedTransactionFactory, ManagedTransaction让容器来管理事务Transaction的整个生命周期,意思就是说,使用ManagedTransaction的commit和rollback功能不会对事务有任何的影响,它什么都不会做,它将事务管理的权利移交给了容器来实现。
          
      //提交和回滚什么都不做,让别人来管理
    public void commit() throws SQLException { // Does nothing } public void rollback() throws SQLException { // Does nothing }

         2,然后从configuration中获取到对应的datasource,这里就可能有三种了

         一种是UnpooledDataSourceFactory,这种的话,每次调用都会产生一次连接。耗费资源。
          
      private Connection doGetConnection(Properties properties) throws SQLException {
      //初始化驱动 initializeDriver();
      //jdbc直接获取一个连接 Connection connection
    = DriverManager.getConnection(url, properties); configureConnection(connection); return connection; }
         一种是pooledDataSourceFactory,这种的话,会有一个连接池的东西在里面,使用的时候每次就从连接池里用就行,
          
      //方法内容过多,可以自己去看  这里是从一个集合中pop出来一个连接直接使用。
    private PooledConnection popConnection(String username, String password) throws SQLException {
         一种是JndiDataSourceFactory, 对于JNDI类型的数据源DataSource的获取就比较简单,MyBatis定义了一个JndiDataSourceFactory工厂来创建通过JNDI形式生成的DataSource。
          
        //从jndi上下文中直接获取数据源并返回
    if (properties.containsKey(INITIAL_CONTEXT) && properties.containsKey(DATA_SOURCE)) { Context ctx = (Context) initCtx.lookup(properties.getProperty(INITIAL_CONTEXT)); dataSource = (DataSource) ctx.lookup(properties.getProperty(DATA_SOURCE)); } else if (properties.containsKey(DATA_SOURCE)) { dataSource = (DataSource) initCtx.lookup(properties.getProperty(DATA_SOURCE)); }

         3,将得到的datasource和事物facroty一起用来,获取一个tx。

    然后用这个tx重新组装成mybatis自己的一个执行器Executor,这里需要注意,如果二级缓存的开关开启了,那么得到的会是一个CachingExecutor,将来执行查询的时候,就会使用这个去执行,会先使用二级缓存,然后在一级缓存,然后再db查询。
          
      public Executor newExecutor(Transaction transaction, ExecutorType executorType, boolean autoCommit) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
        Executor executor;
        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);
        }
      //这里查看是否二级缓存开启了,如果开启了,那么返回的是一个被装饰了的执行器 用来使用缓存
    if (cacheEnabled) { executor = new CachingExecutor(executor, autoCommit); } executor = (Executor) interceptorChain.pluginAll(executor); return executor; }

         4,得到这个执行器之后,组装成sqlsession然后返回供外部使用。

          
      private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;
        try {
        //得到db信息
    final Environment environment = configuration.getEnvironment();
        //得到事物factory
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        //根据数据源得到事物 tx
    = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        //得到执行器
    final Executor executor = configuration.newExecutor(tx, execType, autoCommit);
        //执行器和配置信息对象一起组装了session返回。
    return new DefaultSqlSession(configuration, executor); } 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(); } }

    3,执行sql查询。

         1,首先会根据sql的id去configuration对象中找到对应的sql,返回的是一个 MappedStatement,这里面有所有这个sql的信息,

          
      public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        try {
        //根据id找到对应的sql的封装对象 statement MappedStatement ms
    = configuration.getMappedStatement(statement);
        //这里的执行器有可能是缓存执行器也可能是默认执行器 List
    <E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }

         2,执行执行器的查询方法,如果二级缓存开启了那么会使用CachingExecutor的query,这里会先去这个MappedStatement里的缓存里找,如果找不到,在去执行sql,

          
     public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        Cache cache = ms.getCache();
        if (cache != null) {
          flushCacheIfRequired(ms);
          if (ms.isUseCache() && resultHandler == null) { 
            ensureNoOutParams(ms, parameterObject, boundSql);
            if (!dirty) {
              cache.getReadWriteLock().readLock().lock();
              try {
            //这里从二级缓存中找一下 @SuppressWarnings(
    "unchecked") List<E> cachedList = (List<E>) cache.getObject(key); if (cachedList != null) return cachedList; } finally { cache.getReadWriteLock().readLock().unlock(); } }
        //执行sql查询 List
    <E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks return list; } } return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); }

         3,二级缓存里如果找不到

    也不会直接执行sql,会先从一级缓存里找,除非这里专门设置了flush,如果找到了,那么直接使用,找不到,执行sql查找,然后将结果设置到缓存中。这里查到数据之后,会根据自己定义的映射关系,来组装对象并返回。
         
        //一级缓存查找
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else {
        //sqldb查找 list
    = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }
     
    这样一个简单的流程就走完了,当然还有其他很多东西,需要继续往源码深处研究。
  • 相关阅读:
    读书笔记 之《Thinking in Java》(对象、集合、异常)
    ArrayList 和 LinkedList的执行效率比较
    Hybris CronJob.
    C# 中的treeview绑定数据库(递归算法)
    identity_insert---实验性插入大批量数据和分页存储过程
    SQL Server 存储过程
    PL/SQL 在64位机上不能使用的问题解决
    登陆Oracle11g的企业管理器
    SQL在oracle和SQLserver将查询结果创建为新表的不同之处
    介绍一下内联、左联、右联
  • 原文地址:https://www.cnblogs.com/liouwei4083/p/6024038.html
Copyright © 2020-2023  润新知