还是上一篇的测试类,debug模式进行探究Mybatis如何封装JDBC。
InputStream inputStream = Resources.getResourceAsStream("mybatisConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = factory.openSession();
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
studentMapper.insertStudent(student);
session.commit();
session.close();
在InputStream inputStream = Resources.getResourceAsStream(“mybatisConfig.xml”);该行打上断点。
进入其中,发现:
SqlSessionFactoryBuilder该类是返回一个Sqlsession类实例,着重看build方法,发现其又调用了一个重载build方法,并把读取流当做参数传入
现在再看这个重载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.
}
}
}
XMLConfigBuilder构造方法是干什么的呢?点进去看看
有个XPath解析器,看来是读取解析xml文件的。
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);这行代码就是读取当前Mybatis的配置文件。
然后又把parser传参去build方法,再继续下一步
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
这两句先是解析核心配置文件,然后作为configuration返回
看一下configuration是什么样的:
那么这个配置文件configuration返回给谁了?
可见还得进入这又一个build方法中
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
发现一个默认的SqlSessionFactory工厂,点进去看看
可见这个默认的SqlSessionFactory就是SqlSessionFactory的一个实现,里面就有Configuration属性。
跟进openSession方法
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
可以看到configuration.getDefaultExecutorType(),点进去看到
返回的是一个默认的执行器类型对象,将其作为参数传入了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();
}
}
一部分一部分分析
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
这里首句是创建一个执行器,然后将配置文件,执行器,自动提交方式作为参数,返回一个DefaultSqlSession。
现在来看看session是什么
到这里,可以看出build方法就是读取配置文件,把配置文件信息在内存中以一个对象形式存在,这个对象就是congfiguration,加快读取配置文件速度。
综上:Mybatis框架执行流程
StudentMapper studentMapper = session.getMapper(StudentMapper.class);
studentMapper.insertStudent(student);
mybatis是如何定位sql语句的?
mybatis是如何输送数据到sql语句中?
mybatis是如何输入sql语句到数据库中
SqlSession中dirty属性的作用?
带着疑问 我们继续跟进:
跟进getMapper方法之中
看看后面的标注,在具体看看这个configuration:
加载资源已经找到了对应的XML文件,继续跟进,进入getmapper中
查看这个mapperRegistry
继续跟进
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);
}
}
可以说是很深层的getMapper方法了,进入newInstance中
继续return,
进入MapperProxy构造方法中,从构造方法中出来在进入newInstace中
一直走下去,走到一个invoke方法中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
其中method参数将sql语句带入进来
就这么一直走下去直到
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
这个configuration参数
fuck!跑晕了 什么时候把sql语句传进来了?
跑着跑着,突然看到了一个函数
精神一振,
返回一个value 不知干嘛的,又进了一个函数,出现了一个ms变量
走着走着 突然进了update方法
惊奇又困惑的发现参数parameter传进来了
同时还将dirty从false变成true
走啊走,走啊走
@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);
}
}
终于走到了JDBC里面熟悉的东西Statement,将sql语句,配置对象,简单执行器都传入
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
走下去直到
这里讲sql语句和后面的参数绑定起来,然后一路读取得到我的数据库的账户密码
连接数据库
最后看这个stmt
大体的封装就是这样了,头都晕了!
综上:MyBatis框架执行流程