zoukankan      html  css  js  c++  java
  • Mybatis学习笔记:二、Mybatis工作原理与工作流程

    上一节介绍了Mybatis的开发流程,这节分析下Mybatis工作原理与工作流程

    最原始的sql执行是这样的:

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public class TestMain {
    
    	public static void main(String[] args) throws ClassNotFoundException, SQLException {
    		/*<property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>*/
    		Class.forName("com.mysql.jdbc.Driver");
    		String url = "jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&autoReconnect=true";
    		Connection connection = DriverManager.getConnection(url, "root", "root");
    		connection.setAutoCommit(false);
    		PreparedStatement ps = connection.prepareStatement("insert into t_dept values(?, ?, ?)");
    		ps.setInt(1, 100);
    		ps.setString(2, "北京");
    		ps.setString(3, "北京");
    		
    		try {
    			ps.executeUpdate();
    			connection.commit();
    		}
    		catch (Exception e) {
    			connection.rollback();
    			e.printStackTrace();
    		} finally {
    			if (ps != null) {
    				ps.close();
    			}
    		}
    	}
    }
    

    而上一节中下段代码是怎样执行的,下面分析一下

    public static void main(String[] args) throws IOException {
    	Dept dept = new Dept();
    	dept.setDeptName("部门1");
    	dept.setLocation("北京市");
    	InputStream is = Resources.getResourceAsStream("Mybatis-config.xml");
    	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
    	SqlSession session = factory.openSession();
    	session.insert("insertDept", dept);
    	session.commit();
    	session.close();
    }

    上面代码中:

    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

    SqlSessionFactoryBuilder对象的build方法如下:

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

    构造方法调用了build的重载方法,此方法如下:

    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.
    	  }
    	}
    }

    读取流作为参数,进入到构造方法里面,剩下两个参数是null,而XMLConfigBuilder这个类,里面包含了一个XPathParser的解析器,专门读取xml文档

    public class XMLConfigBuilder extends BaseBuilder {
    
      private boolean parsed;
      private final XPathParser parser;
      private String environment;
      private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
    
      public XMLConfigBuilder(Reader reader) {
        this(reader, null, null);
      }
      ...
    }

    而build这个方法如下:

    下面parsed这个值在XMLConfigBuilder构造时默认的值就为false,而parse()后把其值变为true,意思是让每个xmlconfigbuilder只使用一次,否则抛出异常。

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

    而上面中,执行parseConfiguration()这个方法,对xml文件进行解析,解析结果交给configuration这个对象。这个对象存放了当前配置文件的相关信息。

    而此时configuration对角获取到了,需要build方法执行这个对象:(刚才提到的build重载的方法)

    return build(parser.parse());

    这个方法如下:

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

    这个构造方法如下:

    /**
     * @author Clinton Begin
     */
    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
      private final Configuration configuration;
    
      public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
      }
      ...
    }

    可以看到,这个DefaultSqlSessionFactory是SqlSessionFactory的实现类,它有这个configuration这个属性。

    因此,执行完SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);后,就获得了SqlSessionFactory 这个对象。

    -------------------

    下面看SqlSession session = factory.openSession();是怎样创建SqlSession这个对象

      @Override
      public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
      }

    上面的DefaultSqlSessionFactory的openSession方法如上,它执行了openSessionFromDataSource()这个方法,这个方法如下:

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

    configuration.getEnvironment()获取开发环境(Environment标签中的配置)

    getTransactionFactoryFromEnvironment(environment);获取到了事物工厂,并创建了一个新事物,通过tx和执行器类型(execType:simple)创建了一个执行器对象,最后构造了一个默认的sqlSession,返回它(DefaultSqlSession)

    public class DefaultSqlSession implements SqlSession {
    
      private final Configuration configuration;
      private final Executor executor;
    
      private final boolean autoCommit;
      private boolean dirty;
      private List<Cursor<?>> cursorList;
    
      public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
        this.configuration = configuration;
        this.executor = executor;
        this.dirty = false;
        this.autoCommit = autoCommit;
      }
      ...
    }

    其中dirty默认为false,autoCommit也是false.

    factory.openSession();获取到的就是DefaultSqlSession对象。

    此时这个对象属性如下:


    ---------------------------

    执行sql语句session.insert("insertDept", dept);

    此方法如下:

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

    ms属性如下:


    sqlSource中的sql存放了这个id要执行的sql语句

    @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());
    	  return handler.update(stmt);
    	} finally {
    	  closeStatement(stmt);
    	}
    }
    上面的this指的是SimpleExecutor

    stmt执行后获得:


    包含了要执行的sql语句。

    而handler.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;
    }

    返回了要执行后受影响的行数rows。

    ------------------------------

    session.commit();的调用过程如下:

      @Override
      public void commit() {
        commit(false);
      }
    
      @Override
      public void commit(boolean force) {
        try {
          executor.commit(isCommitOrRollbackRequired(force));
          dirty = false;
        } catch (Exception e) {
          throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
        } finally {
          ErrorContext.instance().reset();
        }
      }
      
      private boolean isCommitOrRollbackRequired(boolean force) {
        return (!autoCommit && dirty) || force;
      }

    因为force=false,autoCommit=false。这就决定了dirty如果是true则提交,为false则回滚。

    提交后,又重新将dirty置为false。

    这样事物提交并关闭session后,整个流程结束。


  • 相关阅读:
    torch.Tensor.view (Python method, in torch.Tensor)
    python子类调用父类的方法
    读sru代码
    Python标准库内置函数complex介绍
    numpy之ones,array,asarray
    failed: Name or service not known. wget: unable to resolve host address Ubuntu报错解决
    C++部分基础知识笔记
    《机器学习》西瓜书第十三章半监督学习
    python小笔记
    解决python中调用 imread 报错:ImportError: cannot import name imread
  • 原文地址:https://www.cnblogs.com/dulinan/p/12033036.html
Copyright © 2011-2022 走看看