整体处理流程
源码版本3.5.6
从基本的查询代码引入
private static SqlSessionFactory sqlSessionFactory;
@BeforeAll
static void setUp() throws Exception {
// create an SqlSessionFactory
try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/submitted/keygen/MapperConfig.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
// populate in-memory database
BaseDataTest.runScript(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(),
"org/apache/ibatis/submitted/keygen/CreateDB.sql");
}
@Test
void shouldAssignKeyToBean() {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
try {
CountryMapper mapper = sqlSession.getMapper(CountryMapper.class);
Country country = new Country("China", "CN");
mapper.insertBean(country);
assertNotNull(country.getId());
} finally {
sqlSession.rollback();
}
}
}
代码分四步
1、创建SqlSessionFactory
2、openSession
3、getMapper
4、执行Mapper对应方法
一、创建SqlSessionFactory
SqlSessionFactoryBuilder#build -> XMLConfigBuilder#parse(
解析XML封装Configuration配置信息)
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));//解析mapper对象信息
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
-
XMLConfigBuilder#parse
执行XMLConfigBuilder#parseConfiguration
,解析配置的Mapper信息,如果配置的是source或url类型则通过
XMLMapperBuilder#parse
->XMLMapperBuilder#configurationElement
(解析入参sql返回等信息) ->XMLMapperBuilder#buildStatementFromContext
-> 创建XMLStatementBuilder
->XMLStatementBuilder#parseStatementNode
->MappedStatement.Builder
->configuration.addMappedStatement(statement)
->configuration.addMapper(boundType)
创建Mapper对应mapperProxyFactory
此阶段配置了Executor类型以及主要是定义了mapper的MappedStatement
,封装成Configuration
对象
二、openSession
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();
}
}
此阶段主要创建Transaction、Exceutor、SqlSession
三、getMapper
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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);
}
}
获取Mapper的mapperProxyFactory并创建MapperProxy代理实例。
四、执行Mapper方法
MapperMethod#exceute
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
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()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
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;
}
这个过程从MapperProxy#invoke
方法开始跟踪。
MapperProxy#invoke
-> MapperMethodInvoker#invoke
->
MapperMethod#exceute
-> SqlSession
执行select/insert/update等方法 -> Exceutor
执行select/insert/update等方法
总结
MyBatis执行过程主要环节与核心概念。在初始阶段负责解析配置生成
Configuration、MappedStatement,执行阶段创建Exceutor、MapperProxy,通过动态代理方式执行。以上是XML方式的MyBatis执行过程,Annotation注解方式的处理,其实就是判断if/else的判断处理初始阶段的处理区别。