前言
本文我们将讲解Mybatis的动态sql是什么,以及如何解析,最终执行的全流程
一、MyBatis的Sql类型
1. 静态SQL
mybatis 静态sql 在编译期就已经确定其样子,就是可直接执行,或者填入参数即可执行的,如
SELECT * FROM users WHERE username = 'admin' AND password = #{password};
这种静态的sql在程序启动时,扫描到就可以生成对应的sqlsource。
2. 动态SQL
MyBatis的动态Sql是指在编写Sql语句时根据不同的条件动态地生成不同的Sql语句的技术。它可以根据不同的条件来选择不同的查询语句、修改语句、插入语句和删除语句等,减少了Sql语句的重复编写和维护成本,提高了Sql语句执行的效率。
MyBatis的动态Sql包括 if、choose、when、otherwise、trim、set、where、foreach 等标签,通过这些标签可以构建出多样化的Sql语句。因为语句最终样子不确定,需要根据入参的情况来动态生成不同样子的sql,所以叫动态sql
示例:
<select id="getUser" resultMap="UserMap"> SELECT * FROM user <where> <if test="id != null"> AND id = #{id} </if> <if test="name != null and name != ''"> AND name = #{name} </if> <if test="age != null"> AND age = #{age} </if> </where> </select> <insert id="addUser" parameterType="com.example.seeu.entity.User" > insert into user(id,username,password) values <foreach collection="list" item="item" separator=","> (#{item.id}, #{item.username},#{item.password}) </foreach> </insert>
如上图,就是一段动态Sql,其中的 if 语句会先进行判断入参字段是否为空,如果不为空,才会在Sql中添加对应的的筛选条件;同时,我们也关注下 where 标签,这也是个动态标签,会帮我们检查筛选条件,帮我们补齐多个条件之间的 AND,如果是首条件前面带AND,也会自动帮我们去除。
二、流程1 —— 文件扫描并解析
1. 扫描xml文件
第一步肯定是先寻找,并加载到所有文件,但在此之前,我们必须配置好路径,否则Mybatis是无法加载到文件的。
#mybatis的相关配置 mybatis: #mapper配置文件【重点】 mapper-locations: classpath:mapper/*.xml #开启驼峰命名 configuration: map-underscore-to-camel-case: true cache-enabled: true
下图为SqlSessionFactoryBean.java 源码的主方法 buildSqlSessionFactory(),负责了遍历与解析xml的功能。
我们这里强调两个步骤,
第一个就是命名空间绑定,这里主要是通过xml文件里的命名空间,找到其对应的Mapper接口,解析接口上的各类注解,最终将该Mapper接口及信息注册给Mybatis,需要注意的是,这里的注册与这些Mapper接口上的@Mapper注解无关,这里主要是收集Mapper实际的配置,注册进MyBatisConfiguration,最终会生成一个代理对象,并交由Mybatis管理和操作。而@Mapper则是一个桥梁,使得Spring容器也能获得该代理对象,进而在Spring的体系下,能使用上Mybatis的成果
第二个,就是对xml文件内容的解析,主要还是各个标签进行解析,我们下面会说到。
2. 动态Sql解析
handler.handleNode 里面其实也调用 parseDynamicTags 方法,实现递归,此处实际上是将这些动态内容处理完后拼接成正常语法的Sql,如果嵌套的内容为动态,那么本层也标记为动态,注意,此时#{} 内容仅判断为动态SQL,并未做任何操作,所以拼接完仍然以#{} 形式存在
我们拿个简单的例子来说明
<insert id="addUser" parameterType="com.zhanfu.springboot.demo.entity.User" > insert into user(id,username,password) values <foreach collection="list" item="item"> (#{item.id}, #{item.username},#{item.password}) </foreach> </insert>
很明显,我们能看到这里面用了foreach标签,意味着是一个动态Sql,那么它最终会被解析成什么样呢?我们来看下图
3. 保存解析结果
我们上面看到的最后,对每一个Mapper层的方法,都能解析出一个MappedStatement,这个东西最后会存在哪呢?其实它们会以Map的形式存在一个MyBatis单例配置类 Configuration 里。而这个配置类,毫无疑问也载入了容器中。