1. mybatis 与 JDBC对比
1.1、传统JDBC动态SQL
在学习mybatis动态SQL语句之前,先来回忆一下JDBC的SQL操作。看看传统的JDBC操作是多么的难受,手动控制一些参数条件。
StringBuffer sql = new StringBuffer();
ArrayList<Object> obj = new ArrayList<>();
sql.append("select * from smbms_provider");
boolean f = false;
if(proCode != null && proCode != ""){
sql.append(" where proCode like ?");
obj.add("%" + proCode + "%");
f = true;
}
if(proName != null && proName != ""){
if(f){
sql.append(" and proName like ?");
}
else{
sql.append(" where proName like ?");
}
obj.add("%" + proName +"%");
}
-
分析SQL语句,这个SQL语句可能传入两个参数proCode 和 proName两个参数,注意这里是可能,不一定两个都有也不一定两个都没有。
-
如果第一个参数proCode有,首先得拼接一个where字段,再来拼接条件。
-
第二个参数就麻烦了,如果第一个参数proCode有,第二个参数存在就拼接 and 再拼接条件。如果第一个参数proCode没有,那么就只有一个条件需要拼接 where 再拼接条件。因为不可能出现这种where and错误SQL语句啊!
-
如果后面还有第三、第四、第五…条件,得一个个接着来。接着判断前面是否有条件?此处拼接where 还是 and?
这里就能看到传统JDBC的短板在哪了吧,是不是感觉灵活度不高?拼接SQL语句有没有像做OJ题控制输出格式,首尾不能有空格,中间有空格…反正我是做吐了,对格式控制不能说了解只能说非常了解!
1.2、mybatis动态SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL可以彻底摆脱这种痛苦。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
因此mybatis提供了四大类标签解决动态SQL语句问题,mybatis又一个强大的核心特性。
2. 搭建基本环境
-
mybatis-config.xml配置文件,基本部分略去。
<settings> <!-- 开启日志 --> <setting name="logImpl" value="STDOUT_LOGGING"/> <!-- 将经典的SQL字段映射为Java驼峰命名变量--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <typeAliases> <typeAlias type="com.pojo.Blog" alias="Blog"/> </typeAliases> <mappers> <mapper class="com.dao.BlogMapper"/> </mappers>
-
Blog实体类
@Data @AllArgsConstructor @NoArgsConstructor public class Blog { private String id; //id private String title; //标题 private String author; //作者 private Date createTime; //创建时间 private int views; //浏览量 }
-
BlogMapper接口
public interface BlogMapper { List<Blog> getBlogByIf(Map map); List<Blog> getBlogByChoose(Map map); int updateBlog(Map map); List<Blog> getBlogByForEach(@Param("arraylist") List list); }
3. if 标签
首先解决一下类似于上述JDBC代码的,痛苦SQL拼接问题。其实上述where可以单独处理,多加一个永远成立的第一个条件where 1 = 1。无论你后面是否有条件都不需要管where字段了。只需要无脑and + 条件即可。
<select id="getBlogByIf" parameterType="map" resultType="Blog">
select *from blog where 1 = 1
<if test="views != null">
and views > #{views}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
@Test
public void test_if(){
SqlSession session = mybatisUtil.getSqlSession(true);
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("author","张三"); //参数1
map.put("views",5000); //参数2
List<Blog> blogs = mapper.getBlogByIf(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
-
一个参数都不传的情况,此时数据库中所有的数据都被查出来了(共10条)
-
两个参数任意传一个
-
传两个参数
4. trim(where、set)标签
4.1、where标签
现在来解决一下where 和 and冲突的问题,上面我们使用的是where 1 = 1加一个永远成立的条件来屏蔽where关键词,之后加条件不需要考虑所加的条件是否是第一个条件。从而解决where 和 and 的冲突问题,现在mybatis提供了where标签,可以将我们的where字段释放出来!where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="getBlogByIf" parameterType="map" resultType="Blog">
select *from blog
<where>
<if test="views != null">
and views > #{views}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
4.2、set标签
set标签与where标签一样,它的诞生也是为了解决SQL语句语法错误问题,update语句在修改字段时,不知道具体需要修改几个字段,修改多个字段需要用逗号隔开?还是回到上面的问题,需要对逗号分隔符进行单独特殊处理!为了解决这种问题mybatis又将set字段释放出来,不需要手动处理" ,"问题。
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title != null"> title = #{title}, </if>
<if test="views != null"> views = #{views}, </if>
</set>
<where>
<if test="id != null"> id = #{id} </if>
</where>
</update>
4.3、trim标签
trim标签是用来定制where、set…标签的,不定制的情况下where会处理与and | or 引起的语法错误问题,set也只处理 与“,”引起的语法错误问题,当然也可以手动定制!
<trim prefix="WHERE" prefixOverrides="AND |OR "> 等价普通where
...
</trim>
<trim prefix="SET" suffixOverrides=","> 等价普通set
...
</trim>
5. choose(when、otherwise)标签
choose、when、otherwise有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
按照when的顺序匹配,一旦有一个成功就执行。之后所有的when 、otherwise都不会在执行;如果一个都不匹配就走otherwise(otherwise可以没有)。
<select id="getBlogByChoose" parameterType="map" resultType="Blog">
select *from blog
<where>
<choose>
<when test="title != null">
and title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views > #{views}
</otherwise>
</choose>
</where>
</select>
@Test
public void test_choose(){
SqlSession session = mybatisUtil.getSqlSession(true);
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Mybatis");
map.put("author","李四");
map.put("views",5000);
List<Blog> blogs = mapper.getBlogByChoose(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
6. SQL片段标签
mybatis的宗旨就是能简化就简化,将程序员从Dao层释放出来。因此mybatis也提供了SQL片段这个标签,用于抽取一些公共的代码,致力于能够达到复用的地步。在需要的时候通过include标签引入即可,像极了JSTL。
<sql id="views-author">
<if test="views != null">
and views > #{views}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="getBlogByIf" parameterType="map" resultType="Blog">
select *from blog
<where>
<include refid="views-author"/>
</where>
</select>
@Test
public void test_sql(){
SqlSession session = mybatisUtil.getSqlSession(true);
BlogMapper mapper = session.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("author","张三");
map.put("views",5000);
List<Blog> blogs = mapper.getBlogByIf(map);
for (Blog blog : blogs) {
System.out.println(blog.toString());
}
session.close();
}
-
抽取公共代码片段的时候尽量抽取独立性较高的片段,不要加入< where >、< set >等等标签。尽量只有一些 where 子条件语句、< if >之类的东西。
-
最好基于单表来定义SQL片段,不要多表抽取,那么将失去更多的复用性。
7. foreach标签
-
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
-
可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<select id="getBlogByForEach" parameterType="arraylist" resultType="Blog">
select *from blog where author in
<foreach collection="arraylist" index="index" item="item"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
@Test
public void test_foreach(){
SqlSession session = mybatisUtil.getSqlSession(true);
BlogMapper mapper = session.getMapper(BlogMapper.class);
List list = new ArrayList();
list.add("张三");
list.add("赵四");
List<Blog> blogs = mapper.getBlogByForEach(list); //参数@Param("arraylist") List list
for (Blog blog : blogs) {
System.out.println(blog);
}
session.close();
}
8. 总结
-
动态SQL就是指根据不同的条件生成不同的SQL语句
-
动态SQL本质还是SQL语句 , 只是我们可以在SQL层面去执行一个逻辑代码
-
动态SQL就是拼接SQL语句,只要按照SQL的格式,保证SQL的正确性,去组合就行了