流程图:
1.读取资源,解析配置填充Configuration配置对象字段
资源的读取:由MyBatis的IO包完成的,其中重点职责的类为ClassLoaderWrapper以及Resources
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource);
}
public static InputStream getResourceAsStream(ClassLoader loader,
String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
解析配置:解析配置核心类之一是XMLConfigBuilder,解析入口是parse()方法
// 解析mybatis-config.xml的入口方法
public Configuration parse() {
// 如果已经解析过了,报错
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true; // 标志已解析
// 在mybatis-config.xml配置文件中查找<configuration>节点,并解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// 解析配置节点
private void parseConfiguration(XNode root) {
try {
// 分步骤解析
//issue #117 read properties first
// 1.properties
propertiesElement(root.evalNode("properties"));
// 2.类型别名
typeAliasesElement(root.evalNode("typeAliases"));
// 3.插件
pluginElement(root.evalNode("plugins"));
// 4.对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
// 5.对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 6.设置
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
// 7.环境
environmentsElement(root.evalNode("environments"));
// 8.databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 9.类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// 10.映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
2.加载XXXMapper
映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,映射文件是在mybatis-config.xml中加载;可以加载多个映射文件。常见的配置的方式有两种,一种是package扫描包,一种是mapper找到配置文件的位置。
<mappers>
<package name="org.mybatis.xxx"/>
</mappers>
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 10.4自动扫描包下所有映射器
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// 10.1使用类路径
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 映射器比较复杂,调用XMLMapperBuilder
// 注意在for循环里每个mapper都重新new一个XMLMapperBuilder,来解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// 10.2使用绝对url路径
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
// 映射器比较复杂,调用XMLMapperBuilder
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 10.3使用java类名
Class<?> mapperInterface = Resources.classForName(mapperClass);
// 直接把这个映射加入配置
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
3.构造会话工厂获取SqlSessionFactory
这个过程其实是用建造者设计模式使用SqlSessionFactoryBuilder对象构建的,
读取解析配置文件并填充Configuration对象字段后,就创建DefaultSqlSessionFactory对象为SqlSessionFactory类型
SqlSessionFactory builder = new SqlSessionFactoryBuilder().build(inputStream);
public SqlSessionFactory build(InputStream inputStream,
String environment,
Properties properties) {
try {
// 读取资源,解析配置并填充Configuration
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return this.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.
}
}
}
/**
* 最后一个build方法使用了一个Configuration作为参数,并返回DefaultSqlSessionFactory
* @param config 配置
* @return 工厂
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
4.创建会话对象SqlSession
SqlSession的创建使用了工厂方法模式,由DefaultSqlSessionFactory创建DefaultSqlSession对象为SqlSession类型
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);
// 然后产生一个DefaultSqlSession
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();
}
}
5.创建Executor执行器
Executor执行器是在创建DefaultSqlSession前一点进行创建的,因为SqlSession是高层接口层,它只是关心操作并不关心实现细节的东西的。
是MyBatis的核心,负责SQL语句的生成和查询缓存的维护,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护
- SimpleExecutor – SIMPLE 就是普通的执行器。
- ReuseExecutor-执行器会重用预处理语句(PreparedStatements)
- BatchExecutor–它是批处理执行器
// 产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
// 这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// 然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
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);
}
// 如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 此处调用插件,通过插件可以改变Executor行为
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
6.获取Mapper
创建会话后,通过会话对象session获取mapper。其中会委派给Configuration对象获取。
public <T> T getMapper(Class<T> type) {
return configuration.getMapper(type, this);
}
Configuration又会委派给映射器注册机MapperRegistry去获取。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
MapperRegistry中通过MapperProxyFactory创建代理mapper的。
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 获取mapper代理工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
// 在map中找不到则表示没有将mapper类注册进来,抛出BindingException
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 使用工厂进行创建一个实例,本质上是通过代理模式创建了一个代理类,创建过程中出现异常抛出BindingException
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory最终交由JDK动态代理类Proxy创建mapper代理类的
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader()
, new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession
, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
7.调用Mapper的方法
创建mapper代理对象,执行方法后会调用如下invoke()方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
接下来会执行到MapperMethod的execute()方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
// 对用户传入的参数进行转换,下同
Object param = method.convertArgsToSqlCommandParam(args);
// 执行插入操作,rowCountResult 方法用于处理返回值
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
// 根据目标方法的返回类型进行相应的查询操作
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 执行查询操作,并返回多个结果
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 执行查询操作,并将结果封装在 Map 中返回
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 执行查询操作,并返回一个 Cursor 对象
result = executeForCursor(sqlSession, args);
} else {
// 执行查询操作,并返回一个结果
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
// 执行刷新操作
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method " +
"with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
8.封装结果集
可以封装成多种类型可以是基本数据类型,也可以是Map、List、POJO类型复杂数据类型。封装结果集的过程就和JDBC封装结果集是一样的。