问题:为什么需要动态SQL
INSERT INTO student (id,name,sex,age) VALUES (#{id},#{name},#{sex},#{age})
假设在测试程序中输入没有name属性的值,那他就会因缺少值而报错
因此,是否可以让上面的SQL语句在缺少name属性时,自动变成
INSERT INTO student (id,sex,age) VALUES (#{id},#{sex},#{age})
这种想要达到随着参数的变化,让SQL也变化,就需要使用动态SQL
动态SQL
动态if标签:符合if条件的分段SQL可以被加入的整体的SQL中去执行
例如<if test="name != null">,name</if>
如果name是null,这个if标签的文本就不会被拼接到SQL中,SQL语句就会变成 INSERT INTO student (id,sex,age) VALUES (#{id},#{sex},#{age})
注意:动态if条件可以使用在增删改查任意一个标签中,只要是需要if判断
动态Where条件,一般需要与if结合使用,使用Where 标签,可以省略了where 1=1
where标签,假设被where标签包含的部分没有SQL段,我们的SQL就不会拼接where,只剩select * from student
如果被where标签包含的部分存在SQL段,mybatis会自动地在主题的SQL后面先拼接where,然后去除紧跟where后的第一个and字符串
例如:select * from student where and id = #{id} and name = #{name},
就会去掉第一个and,变成select * from student where id = #{id} and name = #{name}
注意:动态where条件可以使用在删改查任意一个标签中,只要这个标签有where条件,就可以使用
choose(when,otherwise)条件 ,类似if()else if()else()
查询student表,根据年龄,如果年龄小于15,只查询姓名字段
如果年龄大于20,查询姓名和性别字段
如果不是以上的两个条件,查询姓名、性别、班级字段
在mybatis中, <(小于)使用lt, >(大于)使用gt, <=(小于等于)使用lte, >=(大于等于)使用gte
注意: choose(when,otherwise)条件可以使用在增删改查任意一个标签中,只要需要if()else if()else() 的逻辑,就可以使用
set标签,在更新SQL中,对于set部分的语句拼接时,可以自动去掉多余的逗号
在update中,如果有的值是null,就不要去set值。因为可能会出现以下的情况
update student set
name = #{name},
age = #{age},
where id = #{id}
这个SQL多了一个逗号。处理这种问题,mybatis使用set标签解决,用set标签替代set,把需要改的字段括起来,这样就可以自动去掉多余的逗号
forEach标签,迭代数组或集合类型的参数,把所有元素组装成一个字符串
要传递一个id数组,根据id数组查询student,使用foreach标签
collection=“array或list”,array用来对应参数为数组,list对应参数为集合,
item是为当前元素的变量名称,separator是以什么符号来分隔元素,
open和close分别是以什么符号来作为整体字符串的开头和结尾。
注意:forEach标签可以使用在增删改查任意一个标签中,只要需要迭代数组或集合类型的参数,就可以使用
include标签,可以事先使用SQL标签预置SQL段,在需要的地方用include标签引用这些SQL
sql标签中,我们写SQL段时候,可以使用之前学习的set,where,if,choose(when),trim,foreach
使用sql标签预置SQL段,使用include标签引用SQL,一般是在经常重复的SQL段常用
可以把经常会用到的SQL段提取出来,放到SQL标签里,其他的映射器需要使用的时候直接使用include标签引用
主配置文件
<!-- 数据源环境配置 -->
<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/demo?useUnicode=true&characterEncoding=UTF-8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 主配置文件引入映射器 -->
<mappers>
<mapper resource="com/test/day39/DynamicSqlMapper.xml" />
</mappers>
xml映射器-if
<!-- 动态if标签在增删改上的应用 -->
<insert id="insert" parameterType="Student">
<!-- 对比原例:insert into student(id, name, sex, age) values (#{id},#{name},#{sex},#{age}); -->
INSERT INTO student
(
id
<if test="name != null">,name</if>
<if test="sex != null">,sex</if>
<if test="age != null">,age</if>
)
VALUES
(
#{id}
<if test="name != null">,#{name}</if>
<if test="sex != null">,#{sex}</if>
<if test="age != null">,#{age}</if>
)
</insert>
<!-- 动态if在查询上的应用:根据传递的字符串判断到底执行哪一段SQL。str指的就是测试程序输入进来的值 -->
<select id="select" resultType="Map" parameterType="String">
<if test="str == 'abc'">
select * from student
</if>
<if test="str == 'ss'">
select * from user
</if>
</select>
<!-- 注意:关于 1=1 的说明 -->
<select id="select" resultType="Map" parameterType="String">
select * from dept where 1 = 1
<if test="deptId != null">
and dept_id=#{deptId}
</if>
<if test="deptName != null">
and dept_name=#{deptName}
</if>
</select>
为什么上面where后面要存在一个 1=1 的恒等式?
上面的式子,将判断横放就是如下式子
select * from dept where 1 = 1
<if test="deptId != null">and dept_id=#{deptId} </if>
<if test="deptName != null">and dept_name=#{deptName} </if>
假设deptId是null,SQL语句就是select * from dept where and dept_name=#{deptName}
这样会造成的一个问题就是,where 后面直接跟着一个 and,从SQL语法上来说,这个语句就是错的,
因此,我们需要在where后面紧跟一个 and,用来避免语法上的错误
select * from dept where 1 = 1 and dept_id=#{deptId} and dept_name=#{deptName}
xml映射器-where、choose、set、forEach、include
<?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">
<!-- 接口与xml映射器混合使用 -->
<mapper namespace="com.test.day39.DynamicSqlMapper">
<!-- 动态where标签的应用 -->
<select id="selectStu" resultType="Map" parameterType="Student">
select * from student
<where>
<if test="id != null">and id = #{id} </if>
<if test="name != null">and name = #{name}</if>
</where>
</select>
<!-- 动态choose标签的应用 -->
<select id="selectStuByAge" resultType="Map" parameterType="Integer">
select
<choose>
<when test="age lt 15">name</when>
<when test="age gt 20">name,sex</when>
<otherwise>name,sex,class</otherwise>
</choose>
from student where age = #{age}
</select>
<!-- 动态set标签的应用 -->
<update id="updateStu" parameterType="Student">
update student
<set>
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age},</if>
<if test="sex != null">sex = #{sex}</if>
</set>
where id = #{id}
</update>
<!-- 动态forEach标签的应用:下面的语句等于select * from student where id in (1,2,3) -->
<select id="selectStuByIds" resultType="Map" parameterType="Integer[]">
select * from student where id in
<foreach collection="array" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<!-- foreach标签可以完成批量操作。比如批量插入,下面的语句等于insert into student (id,name) values (1,''),(2,''),(3,'') -->
<insert id="insertStus" parameterType="Student">
insert into student (id,name) values
<foreach collection="list" item="stu" open="" separator="," close="">
(#{stu.id},#{stu.name})
</foreach>
</insert>
<!-- 动态include标签的应用-1 -->
<sql id="columns">
id,name,sex,class
</sql>
<select id="selectStuById" resultType="Map" parameterType="Integer">
select
<include refid="columns"/>
from student where id = #{id}
</select>
<!-- 动态include标签的应用-2 -->
<sql id="updateVal">
<!-- trim suffixOverrides="," 这样可以去掉被trim标签所包含的最后一个逗号 -->
<trim suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age},</if>
<if test="sex != null">sex = #{sex},</if>
</trim>
</sql>
<update id="updateStu1" parameterType="Student">
update student set
<include refid="updateVal"/>
where id = #{id}
</update>
</mapper>
接口- if
package com.test.day38;
//接口和映射器混合使用:方法名与xml映射器中的id值对应
public interface DynamicSqlMapper {
public int insert(Student stu);//动态if
public List<Map> select(String s);//动态if
}
接口- where、choose、set、forEach、include
package com.test.day38;
//接口和映射器混合使用:方法名与xml映射器中的id值对应
public interface DynamicSqlMapper {
public List<Map> selectStu(Student stu);//动态where
public List<Map> selectStuByAge(Integer age);//动态choose
public int updateStu(Student stu);//动态set
public List<Map> selectStuByIds(Integer[] ids);//动态foreach
public int insertStus(List<Student> stus);//动态foreach
public Map selectStuById(Integer id);//动态include
public int updateStu1(Student stu);//动态include
}
调用程序:接口和映射器混合使用
public class XmlAndInMapperTest {
public static void main(String[] args) {
Reader reader = null;
SqlSessionFactory sqlSessionFactory = null;
SqlSession session = null;
try{
//加载主配置文件,主配置文件中有数据库连接相关信息,还有映射器的相关信息
//参数是主配置文件的路径,我们的主配置文件是在跟路径下,所以直接写文件名就可以
reader = Resources.getResourceAsReader("myBatis-config.xml");
//创建SqlSessionFactory工厂
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//得到SqlSession对象,这个对象是操作映射器的对象
session = sqlSessionFactory.openSession();
//操作接口与xml混合映射器:getMapper(接口的class).调用的方法
Student stu = new Student();
stu.setId(43);
// stu.setName("测试动态SQL");
// stu.setAge(16);
// stu.setSex("男");
int res = session.getMapper(DynamicSqlMapper.class).insert(stu);//动态if的测试
System.out.println(res);
System.out.println(session.getMapper(DynamicSqlMapper.class).select("ss"));//动态if的测试
// Student stu1 = new Student();
// stu1.setId(1);
// stu1.setName("张三");
// System.out.println(session.getMapper(DynamicSqlMapper.class).selectStu(stu1));//动态where
// System.out.println(session.getMapper(DynamicSqlMapper.class).selectStuByAge(21));//动态choose
// Student stu2 = new Student();
// stu2.setId(11);
// stu2.setName("测试set标签");
// stu2.setAge(55);
// int res = session.getMapper(DynamicSqlMapper.class).updateStu(stu2);//动态set
// System.out.println(res);
// Integer[] ids = new Integer[]{1,2,3};
// System.out.println(session.getMapper(DynamicSqlMapper.class).selectStuByIds(ids));//动态foreach
// List<Student> list = new ArrayList<Student>();
// Student s1 = new Student();
// s1.setId(44);
// s1.setName("批量操作1");
// Student s2 = new Student();
// s2.setId(45);
// s2.setName("批量操作2");
// list.add(s1);
// list.add(s2);
// System.out.println(session.getMapper(DynamicSqlMapper.class).insertStus(list));//动态foreach
// System.out.println(session.getMapper(DynamicSqlMapper.class).selectStuById(1));//动态include
// Student stu2 = new Student();
// stu2.setId(41);
// stu2.setName("测试include标签");
// stu2.setAge(55);
// int res = session.getMapper(DynamicSqlMapper.class).updateStu1(stu2);//动态include
// System.out.println(res);
//提交session
session.commit();
}catch(Exception e){
//如果发生异常回滚session
session.rollback();
}finally{
//关闭资源
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
session.close();
}
}
}
测试用的实体类
package com.test.day38;
import java.io.Serializable;
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
public Integer id;
public String name;
public Integer age;
public String sex;
public String classes;
public Integer servlet;
public Integer jsp;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getClasses() {
return classes;
}
public void setClasses(String classes) {
this.classes = classes;
}
public Integer getServlet() {
return servlet;
}
public void setServlet(Integer servlet) {
this.servlet = servlet;
}
public Integer getJsp() {
return jsp;
}
public void setJsp(Integer jsp) {
this.jsp = jsp;
}
@Override
public String toString() {//打印的时候用,System.out.println(stus)等于System.out.println(stus.toString())
return "[id=" + id + ",name=" + name + ",age=" + age + ",sex=" + sex + ",classes="
+ classes + ",servlet=" + servlet + ",jsp=" + jsp + "]";
}
}