本文将分析Mybatis 如何获取SQL,执行SQL,与spring整合后spring做了些什么
- JDBC
mybatis实际上对JDBC进行了封装,那么先写一个JDBC的测试方法
@Test
public void testJdbc() throws SQLException, ClassNotFoundException {
String sql="SELECT * FROM my_user";
String driver="com.mysql.jdbc.Driver";//需要的数据库驱动
String url = "jdbc:mysql://localhost:3306/mybatis?serverTimezone=Asia/Shanghai";
String userName = "root";
String password = "root";
Class.forName(driver);
Connection conn =DriverManager.getConnection(url,userName, password);//建立连接
PreparedStatement pstmt=conn.prepareStatement(sql);
ResultSet rs=pstmt.executeQuery();//执行语句
System.out.println(rs);
conn.close();
}
这里不对这段代码过多阐述,下文分析mybatis时涉及的部分会再论述,这里先留个印象.
- Mybatis
接下来看看单纯的mybatis代码.代码结构如下
mybatis-config配置文件内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&autoReconnect=true&useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/mapper/UserMapper.xml"/>
<!--<mapper class="study.mybatis.mapper.MyUserMapper"/>-->
</mappers>
</configuration>
配置文件中mappers属性可以使用两种写法.一种是度配置文件,一种是读class类,如果使用xml的配置方式,由于xml中引用了MyUserMapper,所以无论是注解方式还是xml配置方式的SQL都会生效,而如果只引用MyUserMapper,则xml中的SQL无法生效.
MyUserMapper代码如下:
这里使用了两种写法,一种从mapper配置文件中获取SQL,一种从注解中配置SQL.
关于配置这里不多赘述,有需求可以去官网了解 :https://mybatis.org/mybatis-3/zh/index.html.
UserMapper.xml内容如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="study.mybatis.mapper.MyUserMapper">
<select id="selectCount" parameterType="integer" resultType="java.lang.Integer">
SELECT COUNT(0) FROM my_user
</select>
</mapper>
接下来看一段测试代码.跟着这段代码看看源码内部逻辑
@Test
public void testXml() throws IOException {
String resource="mybatis-config.xml";
InputStream inputStream= Resources.getResourceAsStream(resource);
// 加载配置文件,初始化SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession
SqlSession sqlSession=sqlSessionFactory.openSession();
// 执行代码
Integer selectCountResult=sqlSession.selectOne("study.mybatis.mapper.MyUserMapper.selectCount");
Integer selectCountByAnnotationResult=sqlSession.selectOne("study.mybatis.mapper.MyUserMapper.selectCountByAnnotation");
// 打印结果
System.out.println(selectCountResult);
System.out.println(selectCountByAnnotationResult);
//关闭sqlSession
sqlSession.close();
}
先说结论:加载配置文件流,构建SqlSessionFactory,在构建的过程中会解析mybatis-config配置,解析配置时会遍历配置里面的所有mapper,并且再遍历每个mapper里的所有SQL,并且优先xml中配置的SQL,后解析注解中的配置
首先看看SqlSessionFactory初始化的时候做了什么.
首先获取流,进入build,构建方法
中间逻辑为读取XML中的所有配置,并初始化,这里被解析的有mybatis-config.xml文件,也包括在该配置文件中配置的mapper.也就是如下部分,而这部分中可以又包含了若干的mapper.在SqlSessionFactory初始化的时候会将mapper文件一起解析.
具体逻辑就是遍历所有mapper,解析mapper中的sql.
在XMLConfigBuilder的mapperElement方法中会遍历mybatis配置文件汇总配置的所有mapper对象
而在遍历mapper对象时还会进入每个mapper中遍历里面配置的所有SQL.
在XMLMapperBuilder中的buildStatementFromContext方法会解析mapper中的所有方法,本例只有一个,所以size为1
而解析SQL方法时就会涉及到SQL的拼接.在SqlSourceBuilder中会对SQL进行处理.这里也就是mybatis配置文件中会将#{value}替换到执行SQL的逻辑,这里顺便提一句,使用${value}的话,不会被替换,而是直接拼接SQL,这也是#与$在mybatis配置(mapper)中的区别
遍历了一个mapper(xml)中的SQL后,会再次扫描注解中的SQL
在MapperAnnotationBuilder中会解析出注解中的SQL,然后走和解析Mapper一样的后续逻辑
最后将信息全部封装给mybatis的 Configuration 对象中完成了初始化逻辑,那么接下来继续看测试代码的后续逻辑
openSession()方法则是获取SqlSession
现在来看看查询方法
这里不一一截图,将调用栈给贴出来
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String)
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
org.apache.ibatis.mapping.MappedStatement#getBoundSql
执行到MappedStatement#getBoundSql时会获取需要执行的SQL语句
而这条SQL就是在SqlSessionFactory初始化时给Configuration对象赋值的.这样,执行的SQL就找到了,那么最终是如何执行的呢?
这里也把调用栈贴出来
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String)
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)
org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)
org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
org.apache.ibatis.executor.BaseExecutor#queryFromDatabase
org.apache.ibatis.executor.SimpleExecutor#doQuery
org.apache.ibatis.executor.statement.RoutingStatementHandler#query
org.apache.ibatis.executor.statement.PreparedStatementHandler#query
最后到了PreparedStatementHandler的query方法,可以看到,最后调用的是JDBC的方法,PreparedStatement的execute()方法
好的,那么这里可以看到mybatis最终调用的是JDBC的方法,执行SQL,而被执行的SQL是在SqlSessionFactory初始化的时候从配置文件及注解中加载进Configuration对象中的.
那么使用sqlSession的selectOne方法与调用mapper对象有什么区别呢?
再看下一个测试方法
@Test
public void testMapper() throws IOException {
String resource="mybatis-config.xml";
InputStream inputStream= Resources.getResourceAsStream(resource);
// 加载配置文件,初始化SqlSessionFactory
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession
SqlSession sqlSession=sqlSessionFactory.openSession();
// 获取mapper
MyUserMapper myUserMapper=sqlSession.getMapper(MyUserMapper.class);
Integer result =myUserMapper.selectCount();
System.out.println(result);
}
跟入代码,可以看到,最后执行的逻辑,依旧是sqlSession的selectOne方法
以上就是mybatis部分源码分析内容(个人理解)
mybatis与spring的整合,及源码分析,请看下节.
来源:CSDN
作者:城中行
链接:https://blog.csdn.net/qq_32140607/article/details/103585497