throw e;
} finally {
session.close();
}
return index;
}
public class SessionFactory {
private static Logger logger = LoggerFactory.getLogger(SessionFactory.class);
private static SqlSessionFactoryBuilder sqlSessionFactoryBuilder;
private static SqlSessionFactory sqlSessionFactory;
//初始化mybatis
static {
String resource = “mybatis-config.xml”;
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);
} catch (IOException e) {
logger.error(“加载配置文件错误:”,e);
e.printStackTrace();
}catch (Exception e){
logger.error(“加载配置文件错误:”,e);
}
logger.info(“init mybatis”);
}
public static synchronized SqlSession getSession() throws IOException {
SqlSession sqlSession =null;
if(null == sqlSession) {
sqlSession = (null != sqlSessionFactory)?sqlSessionFactory.openSession(): null;
}
return sqlSession;
}
}
代码中可以看到首先会通过 SessionFactory 去获取 SqlSession,里面会有一段static代码,在SessionFactory类加载时会执行mybatis-config.xml 的初始化过程。现在我们直接看重点:
通过SqlSession 的 getMapper获取DataDao数据接口的时候发现有2个实现,追溯到SessionFactory的初始化中会看到下面一段:
所以我们直接进入 DefaultSqlSession getMapper方法中,可以找到 MapperRegistry 中:
看到这里熟悉的动态代理的朋友知道这是一个获取java的动态代理类的写法(ps: 不熟悉动态代理的,可以看这里我的上一篇博客 动态代理白话解析),我们继续往下看:
继续走到下一步:dataDao.mysqlInsert(map); 执行这个方法时会调用 MapperProxy 的 invoke方法:
开源看到除了Object 中的方法不能代理,其他的都会走到 mapperMethod.execute(args):
type 字段会在mapperMethod初始化时赋值,这里我们执行的insert,继续往下走到DefaultSqlSession 的update方法 :
到这里后,接下来我们开始看 Configuration的初始化,它在SqlSessionFactory初始化时构造的
上面2段代码可以看到 Configuration 是通过加载mybatis-config.xml时进行初始化的。
现在我们回到 this.configuration.getMappedStatement(statement); 这段代码:
进去看下我们发现他其实是通过 id 去获取对应的MappedStatement ,通过上文我们可以知道id是MapperMethod的execute方法传进来的commandName,它在MapperMethod 的构造函数中的 setupFields 方法中初始化:
在本文中的值为: DataDao.mysqlInsert。类似这种,也就是接口类.方法名。
现在我们回到主流程中,查看 mappedStatements 的初始化过程,是在上文中初始化Configuration 的this.mapperElement(root.evalNode(“mappers”)); 中 :
这里会解析mapper.xml中的sql节点,本文中的示例是 这一段:
我们继续查看mappedStatements的初始化 :
这里的id我们可以看到就是节点中的namespace.id的值拼接,也就是 DataDao.mysqlInsert
现在我们继续回到主流程,看这段代码:
代码中通过executor执行update方法,现在我们看下 executor 的初始化过程,是在SqlSession初始化的过程中:
在Configuration的构造函数中可以看到这里的 executorType 默认为SIMPLE,cacheEnabled 默认为true.
现在我们继续回到主流程,进到 CachingExecutor 的 update 方法中:
这里会进入到 BaseExecutor 的 update方法中
这里会进入到 SimpleExecutor 的doUpdate中:
这里首先初始化了一个 RoutingStatementHandler ,由上文可知 statementType 默认为 PREPARED,所以 其 delegate 属性为 PreparedStatementHandler。我们回到主流程,继续看 this.prepareStatement(handler) :
会走到 BaseStatementHandler 的 prepare 方法 :
这里会进入到 PreparedStatementHandler 的 instantiateStatement 方法:
这里的 boundSql 是在 上文中的 mappedStatement 初始化的时候赋值的:
首先是在 parseStatementNode 中初始化 DynamicSqlSource,然后获取 BoundSql,接下来我们看下XMLStatementBuilder的parseDynamicTags方法:
代码中我们可以看到若节点类型既不是文本节点也不是CDATA节点就会调用不同的处理类去处理,我们比较常用的就是查询时使用的 和 节点 ,这里我们执行的是 insert , 所以直接是一个文本节点,下面就是初始化了一个MixedSqlNode 和 configuration 构造了 DynamicSqlSource,然后通过getBoundSql 方法来构造BoundSql:
由上文知:rootSqlNode是MixedSqlNode,这里会执行 MixedSqlNode 的 apply 方法,在本例中会执行 TextSqlNode 的 apply
方法:
这一段呢主要是用来处理sql中的${}占位符参数,本文中的 insert into t a b l e N a m e ( {tableName} ( tableName({columns}) values (${colValues}) 经过parse 转换后 insert into table (id,name) values (1,‘test’) , 这一段的代码就不分析了,比较繁杂,主要是通过将对应的占位符替换为对应的值。
我们回到 getBoundSql 方法 ,继续往下走,到 sqlSourceParser.parse(context.getSql(), parameterType) 这一行代码:
又看到了熟悉的代码,这一段是将#{} 占位符替换为对应的value.
我们继续回到上文主流程中的 PreparedStatementHandler 的 instantiateStatement 方法,往下走:
这里的 Jdbc3KeyGenerator 是如果你设置了useGeneratedKeys=true 用来 自动生成主键的,resultSetType 本例中也没有涉及为空,所以这里会返回一个 PreparedStatement, 到这里我们的 SimpleExecutor 的 doUpdate 的 this.prepareStatement(handler)
这段代码就分析完了。
我们回到主流程继续往下走:
这里进入到 jdbc 的 PreparedStatement 的执行过程,执行insert插入的操作。后面2段代码我就不分析了,主要是用来获取主键的。
到这里,mybatis执行一次insert sql的运行机制就解析完成了。
总结:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/1ff1a82755376305af1b570280a7df5f.jpeg)
面试结束复盘查漏补缺
每次面试都是检验自己知识与技术实力的一次机会,面试结束后建议大家及时总结复盘,查漏补缺,然后有针对性地进行学习,既能提高下一场面试的成功概率,还能增加自己的技术知识栈储备,可谓是一举两得。
以下最新总结的阿里P6资深Java必考题范围和答案,包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~
重要的事说三遍,关注+关注+关注!
更多笔记分享
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
是检验自己知识与技术实力的一次机会,面试结束后建议大家及时总结复盘,查漏补缺,然后有针对性地进行学习,既能提高下一场面试的成功概率,还能增加自己的技术知识栈储备,可谓是一举两得。
以下最新总结的阿里P6资深Java必考题范围和答案,包含最全MySQL、Redis、Java并发编程等等面试题和答案,用于参考~
重要的事说三遍,关注+关注+关注!
[外链图片转存中…(img-mD9jsURv-1713564127438)]
[外链图片转存中…(img-jSeOT5bE-1713564127440)]
更多笔记分享
[外链图片转存中…(img-bDZi500t-1713564127441)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!