![在这里插入图片描述](https://img-blog.csdnimg.cn/2021022314170023.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80ODkyMjE1NA==,size_16,color_FFFFFF,t_70)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210223141726678.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80ODkyMjE1NA==,size_16,color_FFFFFF,t_70)
1、 创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory
= new SqlSessionFactoryBuilder().build(resourceAsStream);
- new 一个SqlSessionFactoryBuilder对象调用builder方法
- 创建一个XML解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); - 调用parser.parse()方法中的parseConfiguration(parser.evalNode("/configuration"))方法解析XML配置
- settingsElement(settings);解析每一个标签把详细信息保存在configuration中
- mapperElement(root.evalNode(“mappers”));解析Mapper.XML
- 创建mapper解析器:XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
- 调用mapperParser.parse();中的configurationElement(parser.evalNode("/mapper")); 解析Mapper.XML的所有标签
- buildStatementFromContext(context.evalNodes(“select|insert|update|delete”));中的buildStatementFromContext(list, null);方法来解析增伤改查方法
- 获得statementParser解析器调用statementParser.parseStatementNode();方法解析SQL语句获得所有标签内容
- 调用addMappedStatement方法将每一个属性封装在一个MappedStatement中一个增删改查方法对应一个MappedStatement
- 将MappedStatement也加到configuration中:configuration.addMappedStatement(statement);
- 解析完成后将封装了所有信息的全局Configuration返回:return configuration;
- 将configuration传入返回一个DefaultSqlSessionFactory:
new DefaultSqlSessionFactory(config);
总结:把配置文件信息解析并保存在Configuration对象中,最后返回包含了configuration的DefaultSqlSessionFactory
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.
}
}
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(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"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
2、 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
- 调用openSession()方法返回openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);方法
- 获得一个Executorfinal:
Executor executor = configuration.newExecutor(tx, execType);- 根据Executor在全局配置中的类型创建出SimpleExecutor或ReuseExecutor或BatchExecutor
- 如果有二级缓存配置开启就会创建:
executor = new CachingExecutor(executor); - 使用每一个拦截器(插件)包装每一个executor并返回
executor = (Executor) interceptorChain.pluginAll(executor);
- 最后返回一个包含configuration和executor的DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
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();
}
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
3、 getMapper获取接口代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
- getMapper返回configuration.getMapper(type, this);又返回mapperRegistry.getMapper(type, sqlSession);(configuration中的mapperRegistry中的KnownMappers保存了所有Mapper的代理工厂)
- 获取MapperProxyFactory:
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type); - 调用newInstance传入sqlSession:
return mapperProxyFactory.newInstance(sqlSession); - 创建mapperProxy,(实现InvocationHandler接口):
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache); - 继续调用return newInstance(mapperProxy);
- 最终创建MapperProxy代理对象:
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
4、 执行Mapper的方法
User user = userMapper.getById(3L);
- MapperProxy执行invoke(Object proxy, Method method, Object[] args)方法
- 将方法包装成Mybatis能执行的mapperMethod,并执行execute方法:
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);- command.getType()判断是增删改查的哪种方法(以查询为例子)
- 通过convertArgsToSqlCommandParam获取参数:
Object param = method.convertArgsToSqlCommandParam(args); - 继续调用参数名解析器的getNamedParams(args)方法(如果参数是一个直接返回,如果参数有多个则包装成map返回)
paramNameResolver.getNamedParams(args); - 查询多个会调用result = executeForMany(sqlSession, args);方法
- 查单个调用result = sqlSession.selectOne(command.getName(), param);方法(以查询单个为例子)
- selectOne中调用this.selectList(statement, parameter);返回list的第一个
- 先获取configuration中的MappedStatement(封装查询的详细信息)
MappedStatement ms = configuration.getMappedStatement(statement); - 调用executor的查询方法
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); - wrapCollection(parameter)将参数包装,例如collection或者list等
- 获得SQL语句参数等各种信息:
BoundSql boundSql = ms.getBoundSql(parameterObject);
- 继续调用query进行查询:
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); - 如果配置了缓存先从二级缓存里取,如果没有就利用executor进行查询:
return delegate. query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); - 继续从一级缓存里取值:
list = resultHandler == null ? (List) localCache.getObject(key) : null; - 如果没有则是真正从数据库查询:并且查出放在一级缓存中
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); - 调用BaseExecutor的doQuery方法:
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); - 创建StatementHandler 对象
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);- 根据配置的信息判断创建Statement或者PreparedStatement或者CallableStatement(默认PreparedStatement)
- 创建Statement的构造函数里默认穿创建了:parameterHandler和resultSetHandler,并都是用拦截器包装。
- 使用每一个拦截器包装statementHandler对象:
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); - 预编译SQL产生preparedStatement对象:
stmt = prepareStatement(handler, ms.getStatementLog()); - 调用ParamterHandler设置参数:
handler.parameterize(stmt); - 又调用 TypeHandler给sql语句设置预编译参数
typeHandler.setParameter(ps, i + 1, value, jdbcType); - 将处理好的Statement和resultHandler传入查询:
handler.query(stmt, resultHandler); - 查出数据利用resultSetHandler处理结果(使用TypeHandler获取value值)
- 关闭资源返回list
- 先获取configuration中的MappedStatement(封装查询的详细信息)
查询流程总结
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);
}
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;
}
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.rowBounds = rowBounds;
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.objectFactory = configuration.getObjectFactory();
if (boundSql == null) { // issue #435, get the key before calculating the statement
generateKeys(parameterObject);
boundSql = mappedStatement.getBoundSql(parameterObject);
}
this.boundSql = boundSql;
this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}
5. 流程总结
- 根据配置文件(全局,sql映射)初始化出Configuration对象
- 创建一个DefaultSqlSession对象,它里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
- DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
MapperProxy里面有(DefaultSqlSession); - 执行增删改查方法:
- 调用DefaultSqlSession的增删改查(Executor);
- 会创建一个StatementHandler对象。同时也会创建出ParameterHandler和ResultSetHandler)
- 调用StatementHandler预编译参数以及设置参数值,使用ParameterHandler来给sql设置参数
- 调用StatementHandler的增删改查方法;
- ResultSetHandler封装结果
注意:四大对象(Executor、ParameterHandler、ResultSetHandler)每个创建的时候都有一个interceptorChain.pluginAll(xxx);和插件有关
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。 插件通过动态代理机制,可以介入四大对象的任何一个方法的执行。了解mybatis运行原理才能更好开发插件。