1.简介

  MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录《摘自mybatis官网》。

  mybatis在我们开发中经常使用,所以搞清楚mybatis的工作流程还是比较重要的,下面就开始我们的分析。

2.Mybatis中的核心对象

2.1mybatis使用示例

public static void main(String[] args) throws IOException {
//1.创建sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
//2.创建会话
SqlSession session = sqlSessionFactory.openSession();
try {
//3.获取mapper代理对象
BlogMapper mapper = session.getMapper(BlogMapper.class);
//4.执行mapper接口方法
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}

2.2核心对象

通过上面的示例可以看出mybatis里面的几个核心对象:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession和Mapper对象,

1.SqlSessionFactoryBuilder:会话工厂构建者,用来构建SqlSessionFactory,在应用中,SqlSessionFactory作为单例对象存在,所以,创建SqlSessionFactory后,SqlSessionFactoryBuilder的任务也就完成了。所以他的生命周期为方法局部。

2.SqlSessionFactory:会话工厂类,用来创建会话,有了工厂,我们就可以创建SqlSession,而创建SqlSession只需要一个工厂就足够了,所以SqlSessionFactory为单例。  我们每次访问数据库都需要创建会话,这个过程贯穿应用的整个生命周期,所以SqlSessionFactory的生命周期为应用级别。

3.SqlSession:会话,内部持有与数据库的连接(connection),线程不安全,每次使用后需要及时关闭。生命周期为一次请求或一次事务。

4.Mapper:mapper对象实际是一个代理对象,从SqlSession中获取。BlogMapper mapper = session.getMapper(BlogMapper.class); 作用是发送sql语句操作数据,生命周期为SqlSession事务方法内。

3.工作流程

3.1 创建sqlSessionFactory对象

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
} public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//创建xml配置构建器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析配置文件返回configuration对象,使用configuration对象创建会话工厂
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
}
}
} public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}

调用了XMLConfigBuilder 的parse()方法

public class XMLConfigBuilder extends BaseBuilder {

    //...
public Configuration parse() {
if (parsed) {
//配置文件只能解析一次
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
} private void parseConfiguration(XNode root) {
try {
//解析properties标签,读取外部引入的配置文件,包括相对路径和绝对路径,
//将解析结果defaults,最后将defaults设置到XPathParser和Configuration的properties属性
propertiesElement(root.evalNode("properties"));
//解析别名,解析完成后将别名与class的映射保存到Configuration的typeAliasRegistry中
typeAliasesElement(root.evalNode("typeAliases"));
//解析<plugins>标签,比如常用的分页插件,解析为Interceptor,设置到Configuration的
//interceptorChain(持有一个拦截器List)属性中
pluginElement(root.evalNode("plugins"));
//解析<objectFactory> 和<objectWrapperFactory>标签,分别生成objectFactory和
//objectWrapperFactory,同样设置到Configuration的属性中,用来实例化对象使用。
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectionFactoryElement(root.evalNode("reflectionFactory"));
//
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
//解析 databaseIdProvider 标签,生成 DatabaseIdProvider对象,用来支持不同厂商的数据库
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析类型处理器元素,用来保存JavaType和JdbcType的映射关系,设置到Configuration的typeHandlerRegistry
typeHandlerElement(root.evalNode("typeHandlers"));
//解析<mappers>标签,只有是接口才会解析,然后判断是否已经注册,单个Mapper重复注册会抛出异常
//将解析的mapper保存到Configuration的mapperRegistry中,并解析注解信息
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
//...
}

通过parser.parse()拿到解析配置文件得到的Configuration对象,调用build()方法,创建默认的SqlSessionFactory对象并返回。

下面来看一下上面代码的运行时序图:

3.2 创建会话

调用DefaultSqlSessionFactory 的openSession()方法获取会话

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    @Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
} 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);
//创建执行器,默认使用SimpleExecutor
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();
}
}
}

下面来看一下创建会话的运行时序图:

3.3获取mapper代理对象

调用session.getMapper方法获取mapper代理对象

public class DefaultSqlSession implements SqlSession {

    //...
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
//...
}

继续调用Configuration的getMapper方法

public class Configuration {
//...
//Mapper注册器,所有的mapper在解析配置文件时保存到该对象中
protected MapperRegistry mapperRegistry = new MapperRegistry(this); public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
} //...
}

继续调用mapperRegistry的getMapper方法

public class MapperRegistry {

    private final Configuration config;
//以接口的class为key,mapper代理工厂为value的map,MapperProxyFactory在加载配置文件扫描mapper所在包时创建,用来创建MapperProxy
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>(); public MapperRegistry(Configuration config) {
this.config = config;
} @SuppressWarnings("unchecked")
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 {
//调用工厂方法创建mapper接口的代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
} //...
}

调用mapper代理工厂的newInstance方法为mapper创建代理对象

public class MapperProxyFactory<T> {

    //mapper接口对应class
private final Class<T> mapperInterface;
//方法缓存,为了提升性能,会在MapperProxy中使用,所有mapper的所有方法都将缓存在该map中
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} public Class<T> getMapperInterface() {
return mapperInterface;
} public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
} @SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//创建jdk动态代理,第三个参数是mapperProxy,所以对T对象的所有的所有调用都将调用mapperProxy的invoke方法
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
} public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
} }

上面使用jdk动态代理为mapper创建了代理对象,所以对mapper的所有调用将调用mapperProxy类的invoke方法,下面来看一下mapperProxy的代码

public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
} //所有对mapper的调用都将调用该方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
//获取缓存的mapper方法,如果不存在,则创建并缓存
final MapperMethod mapperMethod = cachedMapperMethod(method);
//不像常规的动态代理,并没有调用method.invoke(target, args),因为mapper只是一个接口,并没有实现类
//这里动态代理的意义在于统一处理对mapper方法的调用
return mapperMethod.execute(sqlSession, args);
} private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
} }

到这里为什么mapper接口不需要实现类就可以操作数据库就很清楚了,对mapper接口的调用实际会调用mapperMethod.execute()方法,在mapperMethod内部调用sqlSession的增删改查方法。

下面来看一下获取mapper代理对象的运行时序图:

3.4执行mapper接口方法

上面已经说到对mapper接口的方法调用都将调用MapperProxy的invoke方法,invoke方法又会调用mapperMethod的execute()方法,下面来看一下execute()方法:

public class MapperMethod {

    private final SqlCommand command;
//方法签名
private final MethodSignature method; public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//判断sql类型,执行相应逻辑
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
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 {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
} else if (SqlCommandType.FLUSH == command.getType()) {
result = sqlSession.flushStatements();
} else {
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;
} private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
//转换参数
Object param = method.convertArgsToSqlCommandParam(args);
//方法是否RowBounds(分页)参数
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.<E>selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
return result;
} public static class SqlCommand {
//mapperInterface.getName() + "." + method.getName();
private final String name;
//sql类型:UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
private final SqlCommandType type; //...
} public static class MethodSignature{ private boolean returnsMany;
private boolean returnsMap;
private boolean returnsVoid;
private Class<?> returnType;
//方法是否有MapKey.class注解,如果有就是注解的值
private String mapKey;
//ResultHandler.class类型参数在方法参数中的索引
private Integer resultHandlerIndex;
//RowBounds.class类型参数在方法参数中的索引
private rowBoundsIndex;
//key为index,有Param注解的value为注解值,没有的也是index
private SortedMap<Integer, String> params;
//方法参数中是否有Param注解
private boolean hasNamedParameters //...
} }

在execute()方法内部根据sql的类型执行不同的增删改查逻辑,这里以select为例,mapper方法有多个返回值的情况调用executeForMany(sqlSession, args)方法,内部继续调用sqlSession的selectList()方法:

public class DefaultSqlSession implements SqlSession {

    @Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
} @Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//获取configuration中缓存的MappedStatement
MappedStatement ms = configuration.getMappedStatement(statement);
//调用执行器方法进行查询
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
}

拿到MappedStatement,调用执行器进行查询:

public abstract class BaseExecutor implements Executor {

    @Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
} @SuppressWarnings("unchecked")
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//从数据库查询数据
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
} private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//开始执行查询
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
}

query()方法调用重载的query()方法,如果没有命中缓存,则调用queryFromDatabase()从数据库查询数据,继续调用SimpleExecutor的doQuery()方法:

public class SimpleExecutor extends BaseExecutor {

    @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();
//创建statement处理器,默认PreparedStatement
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
} private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
}

创建Statement处理器:

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建路由Statement处理器,根据statementType创建不同的statement处理器,委派模式的体现
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
//创建statement处理器拦截器链,这里先不深入看,默认没有使用拦截器,后面的文章单独分析
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}

在创建Statement处理器时返回的是RoutingStatementHandler对象,从名字就可以看出来,他不是真正工作的StatementHandler,其路由的功能,在内部会根据StatementType创建相应的StatementHandler,对RoutingStatementHandler 的所有调用都将委派给具体的StatementHandler(delegate)去处理:

public class RoutingStatementHandler implements StatementHandler {

  private final StatementHandler delegate;

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//根据statementType创建不同的策略,委派给具体的statement处理器(策略模式与委派模式的体现)
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
//默认使用PreparedStatementHandler
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
} } @Override
public Statement prepare(Connection connection) throws SQLException {
return delegate.prepare(connection);
} @Override
public void parameterize(Statement statement) throws SQLException {
delegate.parameterize(statement);
} @Override
public void batch(Statement statement) throws SQLException {
delegate.batch(statement);
} @Override
public int update(Statement statement) throws SQLException {
return delegate.update(statement);
} @Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
return delegate.<E>query(statement, resultHandler);
} @Override
public BoundSql getBoundSql() {
return delegate.getBoundSql();
} @Override
public ParameterHandler getParameterHandler() {
return delegate.getParameterHandler();
}
}

回到doQuery()方法中,继续调用StatementHandler 的query(stmt, resultHandler)方法,query方法会调用到PreparedStatementHandler 的query()方法:

public class PreparedStatementHandler extends BaseStatementHandler {

    @Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行PreparedStatement
ps.execute();
//结果集处理器处理结果集并返回结果
return resultSetHandler.<E> handleResultSets(ps);
}
}

在PreparedStatementHandler 的query方法中执行PreparedStatement,并将PreparedStatement传给resultSetHandler处理结果集并返回。

上面的执行过程还是比较复杂的,下面来看一下时序图:

以上就是对mybatis工作流程的所有分析了。

因个人能力有限,如果有错误之处,还请指出,谢谢!

最新文章

  1. select,epoll,poll比较
  2. Coursera台大机器学习课程笔记6 -- The VC Dimension
  3. 关于 DevExpress.XtraTreeList.TreeList 树形控件 的操作
  4. delphi数据类型及占用的字节数 C++ 对应数据类型
  5. Android-MediaRecorder-音频录制-警告-W/MediaRecorder(13811): mediarecorder went away with unhandled events
  6. atitit.身份认证解决方案attilax总结
  7. Oracle行内链接不会引起USER_TABLES中CHAIN_CNT值变化
  8. org.springframework.web.filter.CharacterEncodingFilter
  9. 计算机网络——超文本传送协议HTTP
  10. mybatis----增删改查
  11. JAVA有哪些数据类型?基本数据类型各占多少个字节
  12. C#中委托。
  13. 【一天一道LeetCode】#237. Delete Node in a Linked List
  14. ASP.NET Core部署在IIS上
  15. 最好的前端API备忘单整理
  16. 2017-2018-2 20172310『Java程序设计』课程 结对编程练习_四则运算_第二周
  17. RunC容器逃逸漏洞席卷业界,网易云如何做到实力修复?
  18. 使用PerfView监测.NET程序性能(二):Perfview的使用
  19. 【sping揭秘】2、关于spring配置文件
  20. React(0.13) 定义一个使用动画

热门文章

  1. Windows许可证即将过期怎么办?
  2. 使用msfvenom生成木马
  3. 千与千寻主题曲beep函数版
  4. SpringBoot系列:Spring Boot集成定时任务Quartz
  5. C函数库errno.h概况
  6. [考试反思]0725NOIP模拟测试8
  7. 【WPF on .NET Core 3.0】 Stylet演示项目 - 简易图书管理系统(3) - 使用Conductor切换页面
  8. JavaScript文档对象模型(DOM)——DOM核心操作
  9. HTML和css面试题:内容转载
  10. 深入理解计算机系统 第十章 系统级I/O