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指的是SimpleExecutorstmt执行后获得:
包含了要执行的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后,整个流程结束。