MyBatis(三)—— 动态SQL详解

在业务比较复杂的情况下,我们通常需要去拼接SQL语句来完成相关操作,这样出错率很高,不过,MyBatis有一个非常方便且强大的功能就是动态SQL,使用动态SQL,可以摆脱手动拼SQL的痛苦,接下来我们就一起来学习如何使用MyBatis动态SQL。

先来看看我们用的student表:
MyBatis(三)—— 动态SQL详解
目录结构:
MyBatis(三)—— 动态SQL详解
Student实体类:

/**
 * @ClassName Student
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Student {
    private int SID;
    private String Sname;
    private String Ssex;
    private int Age;

    public int getSID() {
        return SID;
    }

    public void setSID(int SID) {
        this.SID = SID;
    }

    public String getSname() {
        return Sname;
    }

    public void setSname(String sname) {
        Sname = sname;
    }

    public String getSsex() {
        return Ssex;
    }

    public void setSsex(String ssex) {
        Ssex = ssex;
    }

    public int getAge() {
        return Age;
    }

    public void setAge(int age) {
        Age = age;
    }

    @Override
    public String toString() {
        return "[id"+SID+" 名字"+Sname+" 性别"+Ssex+" 年龄"+Age+"]";
    }
}

mybatis-config.xml:

<?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>
    <settings>
        <!-- 打印查询语句 -->
       <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!--数据源配置-->
    <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"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>

StudentMapper接口:

public interface StudentMapper {
     public Student getStudent(Student student);
}

StudentMapper.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="tulun.dao.StudentMapper">
    <select id="getStudent" parameterType="tulun.bean.Student" resultType="tulun.bean.Student">
        select * from Student where SID = #{SID} and Sname = #{Sname}
        and Ssex = #{Ssex} and Age = #{Age}
    </select>
  
</mapper>

测试类:

/**
 * @ClassName Test
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //读取配置文件
        InputStream asStream = Resources.getResourceAsStream(resource);
        //创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);

        //创建sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //通过动态代理产生StudentMapper对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);


        //查询对象
        Student student1 = new Student();
        student1.setSID(1);
        student1.setSsex("nan");
        student1.setSname("lzq");
        student1.setAge(21);
        mapper.getStudent(student1)
    }
}

运行结果:
MyBatis(三)—— 动态SQL详解
很显然,参数student的属性完全匹配数据库第一条记录,是可以查询出结果的。

现在对代码进行修改,去掉student的ssex属性赋值操作,通过id,sname,age三个字段去匹配,理想的结果是同样可以查询出数据库的第一条记录。

测试类:

/**
 * @ClassName Test
 * @Description
 * @Author lzq
 * @Date 2019/7/26 13:53
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //读取配置文件
        InputStream asStream = Resources.getResourceAsStream(resource);
        //创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);

        //创建sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //通过动态代理产生StudentMapper对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);


        //查询对象
        Student student1 = new Student();
        student1.setSID(1);
       
        student1.setSname("lzq");
        student1.setAge(21);
        mapper.getStudent(student1)
    }
}

MyBatis(三)—— 动态SQL详解
但是结果为0,什么原因呢?因为SQL语句where条件使用的是and关键字进行连接,所有条件必须同时满足,我们可以看到此时的SQL语句:

"select * from Student where SID = 1 and Sname = lzq and Ssex = null and Age = 21"

显然这条SQL语句是查询不出任何结果的。

现在针对这种情况进行优化,判断Student对象,如果ssex属性值不为null,则SQL语句添加ssex的判断,如果password属性为null,则不添加。

我们可以使用动态SQL来完成上述操作。

使用if标签:

StudentMapper.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="tulun.dao.StudentMapper">
     <!-- if标签 -->
   <select id="getStudent" resultType="tulun.bean.Student" parameterType="tulun.bean.Student">
        select * from Student where 1=1
        <if test="SID != 0">
            and SID = #{SID}
        </if>
        <if test="Ssex != null">
            and Ssex = #{Ssex}
        </if>
        <if test="Sname != null">
            and Sname = #{Sname}
        </if>
        <if test="Age != 0">
            and Age = #{Age};
        </if>
    </select>
  
</mapper>

还是刚刚用的测试代码:
MyBatis(三)—— 动态SQL详解
在if标签中的SQL语句的where后面添加1=1的原因是:

如果我们给第一个字段赋值:

那么SQL语句如下:

"select * from Student where SID = 1 and Sname = lzq and Ssex = null and Age = 21"

要是不给第一个字段赋值呢?
"select * from Student where and Sname = lzq and Ssex = null and Age = 21"
这样会报错的,那么我们如何处理这个呢,就是像本文一样,加个1=1这个条件,或者使用where标签。

where标签:
这个一般是和if标签一起使用的:

StudentMapper.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="tulun.dao.StudentMapper">
    <!-- where标签 -->
    <select id="getStudent" resultType="tulun.bean.Student" parameterType="tulun.bean.Student">
        select * from Student
        <where>
            <if test="SID != 0">
                and SID = #{SID}
            </if>
            <if test="Ssex != null">
                and Ssex = #{Ssex}
            </if>
            <if test="Sname != null">
                and Sname = #{Sname}
            </if>
            <if test="Age != 0">
                and Age = #{Age};
            </if>
        </where>
    </select>
  
</mapper>

效果就是,如果条件一个都不满足,则不拼接where条件;如果有一个或者是多个表达式成立,where会直接拼接在SQL上,并且紧随where的表达式的and会被忽略掉;

运行结果都差不多,不展示了,

还可以进行批量操作,比如批量查询操作:

StudentMapper接口:

public interface StudentMapper {
     public List<Student> batchQueryStudent(List<Integer> list);
     public Student getStudent(Student student);
}

StudentMapper.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="tulun.dao.StudentMapper">
    <!-- where标签 -->
    <select id="getStudent" resultType="tulun.bean.Student" parameterType="tulun.bean.Student">
        select * from Student
        <where>
            <if test="SID != 0">
                and SID = #{SID}
            </if>
            <if test="Ssex != null">
                and Ssex = #{Ssex}
            </if>
            <if test="Sname != null">
                and Sname = #{Sname}
            </if>
            <if test="Age != 0">
                and Age = #{Age};
            </if>
        </where>
    </select>

  <!--批量操作 -->
    <select id="batchQueryStudent" resultType="tulun.bean.Student" parameterType="tulun.bean.Student">
        select * from Student where SID in
        <foreach collection="list" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </select>
</mapper>

测试代码:

public class Test {
    public static void main(String[] args) throws IOException {
        String resource = "mybatis-config.xml";
        //读取配置文件
        InputStream asStream = Resources.getResourceAsStream(resource);
        //创建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(asStream);

        //创建sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //通过动态代理产生StudentMapper对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);

        List<Student> list1 = mapper.batchQueryStudent(list);

        System.out.println(list1);
    }
 }

MyBatis(三)—— 动态SQL详解
顺便说一下,在配置文件里面,SQL语句的参数可以是#{} 还可以是${},这两者有什么区呢?

#{}
相当于JDBC里面的PreparedStatement ;
先编译,编译没问题在拿这个编译的结果去执行:
MyBatis(三)—— 动态SQL详解

${}
相当于JDBC里面的Statement
直接拿,然后编译执行;

所以相比之下,#{}效率更高,并且不存在SQL注入问题,比较安全;

上一篇:Asp.Net 强类型页面获取值几种方式


下一篇:MySQL笔记2