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后,整个流程结束。


posted @ 2019-12-13 08:35  半湖思絮  阅读(212)  评论(0编辑  收藏  举报