动态sql就是根据不同的条件生成不同的sql语句
但是为什么要使用它?原因很简单,当我们百度的时候,输入一个人名,可能会出现多个人,但是如果知道他的演员,我们在输入的人名后面加上演员,就可以直接搜索到,不需要再去筛选。
实际在这里就使用了动态SQL,当我们只输入人名的时候,sql语句可能是
select * from table where name=#{name}
如果再加入演员的话,sql语句可能变成
select * from table where name=#{name} and job=#{actor}
在这里,我们的sql语句都使用了动态拼接。
name=#{name}
name=#{name} and job=#{job}
在Mybatis中,动态SQL已经成为了它最强大的特性之一,而Mybatis3中采用了功能强大的OGNL表达式来完成动态sql
简单了解OGNL表达式:
对象导航图语言(Object Graph Navigation Language),简称OGNL,是应用于Java中的一个开源的表达式语言(Expression Language),它被集成在Struts2等框架中,作用是对数据进行访问,它拥有类型转换、访问对象方法、操作集合对象等功能。
知识点主要如下:(每个元素都有<>包裹,csdn打不出来)
- if:判断语句,用于单条件分支判断
- choose(when、otherwise):用于多条件分支判断
- where、trim、set:辅助元素,用于处理SQL拼接、特殊字段问题
- foreach:循环语句,常用于in语句等列举条件中
- bind:从OGNL表达式中创建一个变量,且绑定到上下文,常用于模糊查询的SQL中
一、if
在Mybatis中,if元素是常用的判断语句,主要用于实现简单的条件选择。在实际应用中,我们可能通过多个条件来精确地查询某个数据。
如何使用:
1、搭建一个数据库
2、搭建配置文件
<!--加入日志工厂设置-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
3、编写工具类
4、创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private int id;
private String name;
private double price;
private int bookCount;
private String author;
}
5、创建dao接口
public interface BookMapper {
//if查询
List<Book> query(Book book);
//choose查询
List<Book> query2(Book book);
//where+if
List<Book> query3(Book book);
//where+trim
List<Book> query4(Book book);
//update
int set(Book book);
//foreach遍历
List<Book> query5(List list);
//bind
List<Book> query6(Book book);
}
实现接口
<select id="query" parameterType="book" resultType="book">
select *
from mydatabase.book
where 1=1
<if test="name!=null and name!=''">
and name=#{name}<!--也可以加入like %name%模糊查询-->
</if>
<if test="price!=null and price!=''">
and price=#{price}
</if>
<if test="bookCount!=null and bookCount!=''">
and bookCount=#{bookCount}
</if>
<if test="author!=null and author!=''">
and author=#{author}
</if>
</select>
6、注册Mapper
<mappers>
<mapper class="com.dao.BookMapper"/>
</mappers>
7、测试
通过实体类的set方法,去传入我们要查询的值,这样可以精确查找
二、choose、when、otherwise
为什么使用它们?
在使用if元素时,只要test属性中的表达式为true,就会执行元素中的条件语句,但是在实际应用当中,有时只需要从多个选项中选择一个执行。
例如:
- 如果name不为空,则值根据name进行筛选;
- 如果name为空,price不为空,则只根据price进行筛选;
- 如果name和price和其他的属性字段都为空,则要求查询出所有的信息!
<!--choose-->
<select id="query2" resultType="book" parameterType="book">
select *
from mydatabase.book
where 1=1
<choose>
<when test="name!=null and name!=''">
and name like concat('%',#{name},'%')
</when>
<when test="price!=null and price!=''">
and price=#{price}
</when>
<when test="author!=null and author!=''">
and author like concat('%',#{author},'%')
</when>
<otherwise>
and bookCount is not null
</otherwise>
</choose>
</select>
测试:
@Test
public void test2(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
Book book = new Book();
book.setAuthor("白");
for (Book book1 : mapper.query2(book)) {
System.out.println(book1);
}
}
@Test
public void test2(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
Book book = new Book();
book.setAuthor("白");
book.setName("无");
for (Book book1 : mapper.query2(book)) {
System.out.println(book1);
}
}
结果:
可以观察得出,choose是选择其中的一个sql字段去查询,而不是像if元素一样,只要在set中传入了值,就将传入值的if元素中的sql字段全部拼接在一起,达到精确查询。
在上述代码中,使用了choose元素进行sql拼接,
如果第一个when元素中的条件为真,则只动态拼接第一个when元素内的sql片段,否则继续向下判断。
如果前面所有when元素中的条件都不为真,则只拼接otherwise元素内的sql片段。
三、where、trim
前面的查询中,映射文件中编写的sql后面都加入了1=1的条件判断语句,是为了保证当条件不成立时拼接起来的sql语句在执行时不报错,即 使得sql不出现语法错误。
那么在Mybatis中,有没有办法不用加入1=1这样的条件呢?针对这种情况,Mybatis中提供了where元素
1、where+if
<!--where+if-->
<select id="query3" parameterType="book" resultType="book">
select *
from mydatabase.book
<where>
<if test="name!=null and name!=''">
and name=#{name}
</if>
<if test="author!=null and author!=''">
and author=#{author}
</if>
</where>
</select>
@Test
public void test3(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
Book book = new Book();
book.setName("无题");
book.setAuthor("李白");
for (Book book1 : mapper.query3(book)) {
System.out.println(book1);
}
}
结果:
2、where+trim(重点)
<trim prefix="str1" prefixOverrides="str2"/>
trim中的prefix="str1"表示,语句的前缀,它会在拼接的时候,str1自动加到if中的sql语句前面
prefixOverrides="str2"表示,去除语句的等于str2的特殊字符串,在拼接的sql语句的时候,将str2去除。
这个元素,一般是用来定制查询的作用!
<!--where+trim-->
<select id="query4" resultType="book" parameterType="book">
select *
from mydatabase.book
<where>
<trim prefix="where" prefixOverrides="and"/>
<if test="name!=null and name!=''">
and name=#{name}
</if>
<if test="author!=null and author!=''">
and author=#{author}
</if>
</where>
</select>
@Test
public void test4(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
Book book = new Book();
book.setName("无题");
book.setAuthor("李白");
for (Book book1 : mapper.query4(book)) {
System.out.println(book1);
}
}
结果:
1=1,where+if,where+trim的查询结果均相同,什么时候该用什么,全看自己的选择了,一般用where+trim比较好,它可以定制sql语句,但是1=1一般比较简单,全看自己的取舍了。
四、set
一般,我们在数据库的操作中,总是需要更改一些信息,Mybatis给我们提供了set元素,可以动态的拼接更新信息的操作
foreach:
- item:循环中当前的元素
- index:当前元素在集合中的位置下标
- collection:可以是一个array、list、collection、map集合的键、pojo包装类中的数组或集合类型的属性名
- open和close:以什么符号将这些元素包装起来
- separator:各个元素的间隔符
<update id="set" parameterType="book">
update mydatabase.book
<set>
<if test="name!=null and name!=''">
name=#{name},
</if>
<if test="price!=null and price!=''">
price=#{price},
</if>
<if test="bookCount!=null and bookCount!=''">
bookCount=#{bookCount},
</if>
<if test="author!=null and author!=''">
author=#{author},
</if>
</set>
where id=#{id}
</update>
@Test
public void test5(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
Book book = new Book();
book.setAuthor("张若虚");
book.setName("春江花月夜");
book.setId(2);
mapper.set(book);
for (Book book1 : mapper.query(new Book())) {
System.out.println(book1);
}
}
结果:
Mybatis中的set元素,主要作用是动态的包含sql语句前输出一个set关键字,并且将sql语句中的最后一个多余的逗号去除!
在编写sql语句的时候,记得在所有的sql语句后面加入逗号’,’,如果不加会报错
而且在传入值的时候,不能都为空,如果全为空,也会报错。
五、foreach
foreach元素一般用来遍历数组和集合,假设有上千条数据,我们可以可以用foreach全部查询出来
<!--foreach-->
<select id="query5" parameterType="list" resultType="book">
select * from mydatabase.book where id in
<foreach collection="list" index="index" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
@Test
public void test6(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
for (Book book : mapper.query5(list)) {
System.out.println(book);
}
}
结果:
注意:
collection属性:
- 如果传入的是单参数且参数类型是一个数组或者List的时候,collection属性值分别为array、list(或collection)
- 如果传入的参数有多个,就需要把它们封装成一个Map,单参数也可以封装成Map集合,这是collection的属性值就为Map的键
- 如果传入的参数是pojo包装类,collection属性值就为该包装类中需要遍历的数组或集合的属性名
六、bind(了解即可)
在进行模糊查询的时候
如果使用${}进行字符串拼接,则无法防止sql注入问题
如果使用concat函数进行拼接,则只针对mysql数据库有效,对别的数据库无效。
映射文件中的sql就要根据不同的情况提供不同形式的实现,显然是不可能的,非常的麻烦,且不利于项目的移植。
所以,Mybatis提供了bind元素来解决这个问题,完全不需要使用数据库语言,只要使用Mybatis的语言即可与所需参数连接!
<!--bind-->
<select id="query6" resultType="book" parameterType="book">
<bind name="_name" value="'%'+name+'%'"/>
select *
from mydatabase.book
where name like #{_name}
</select>
@Test
public void test7(){
BookMapper mapper = MybatisUtil.getSqlSession().getMapper(BookMapper.class);
Book book = new Book();
book.setName("春");
for (Book book1 : mapper.query6(book)) {
System.out.println(book1);
}
}
结果:
bind实际就是运用了一个ref(引用),将sql语段拼接进去