Mybatis学习笔记-06 动态SQL

Mybatis的映射文件中支持在基础SQL上添加一些逻辑操作,动态拼接成完整的SQL后再执行,以复用SQL、简化编程。

1. 抽取重复部分 直接引用 <sql> <include>

1)建立表

use study_mybatis;
create table t_user(
    id int primary key auto_increment,
    username varchar(50),
    password varchar(20),
    gender varchar(1),
    regist_time date
)default charset = utf8;

insert into t_user values(1,'zzt','123','f','1999-4-13'),(2,'zjm','456','m','1998-5-20')

2)建立对应实体

package com.zt.Bean;

import java.util.Date;

public class User {
    private Integer id;
    private String username;
    private String password;
    private Boolean gender;
    private Date registerTime;

    public User() {
    }

    public User(Integer id, String username, String password, Boolean gender, Date registerTime) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.gender = gender;
        this.registerTime = registerTime;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Boolean getGender() {
        return gender;
    }

    public void setGender(Boolean gender) {
        this.gender = gender;
    }

    public Date getRegisterTime() {
        return registerTime;
    }

    public void setRegisterTime(Date registerTime) {
        this.registerTime = registerTime;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", gender=" + gender +
                ", registerTime=" + registerTime +
                '}';
    }
}

3)在DAO中注册方法

package com.zt.DAO;

import com.zt.Bean.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserDAO {

    List<User> queryUsers(@Param("id") Integer id);
    User queryUserById(@Param("id") Integer id);
    Integer deleteUser(@Param("id") Integer id);
    Integer updateUser(User user);
    Integer insertUser(User user);

}

4)编写Mapper.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="com.zt.DAO.UserDAO">

    <select id="queryUsers" resultType="User">
        select id,username,password,gender,regist_time as registerTime
        from t_user
    </select>

    <select id="queryUserById" resultType="User">
        select id,username,password,gender,regist_time as registerTime
        from t_user
        where id = #{id}
    </select>

    <delete id="deleteUser" parameterType="int">
        delete from t_user
        where id = #{id}
    </delete>

    <update id="updateUser" parameterType="User">
        update t_user
        set username = #{username},password = #{password},gender = #{gender},regist_time = #{registerTime}
        where id = #{id}
    </update>

    <insert id="insertUser" parameterType="User">
        <!--主键回填-->
        <selectKey order="AFTER" resultType="int" keyProperty="id">
            select last_insert_id()
        </selectKey>
        insert into t_user values(#{id},#{username},#{password},#{gender},#{registerTime})
    </insert>

</mapper>

此处我们可以发现,查询全部记录和根据id查询具有相同的SQL片段:select id,username,password,gender,regist_time as registerTime from t_user,Mybatis提供了sql和include标签用于共性片段的重复利用。

    <sql id="user_field">
        select id,username,password,gender,regist_time as registerTime
        from t_user
    </sql>

    <select id="queryUsers" resultType="User">
        <include refid="user_field"></include>
    </select>

    <select id="queryUserById" resultType="User">
        <include refid="user_field"></include>
        where id = #{id}
    </select>

5)编写测试方法

    public static void main(String[] args) {
        UserDAO mapper = MybatisUtil.getMapper(UserDAO.class);
        List<User> users = mapper.queryUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }

6)为了避免每次都需要重写一次main方法或者加注释的麻烦 我们从本文开始将使用junit进行测试

在pom.xml中加入包依赖:

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

编写测试方法,增加@Test注解

    @Test
    public void test1(){
        UserDAO mapper = MybatisUtil.getMapper(UserDAO.class);
        List<User> users = mapper.queryUsers();
        for (User user : users) {
            System.out.println(user);
        }
    }

2. 多种查询方式合并优化 <if> <where>

现在假设我们增加一个按照用户名查询的需求,首先在DAO中增加方法定义:

    User queryUserByUsername(@Param("username") String username);

在Mapper.xml中编写对应SQL:

    <select id="queryUserByUsername" resultType="User">
        <include refid="user_field"></include>
        where username = #{username}
    </select>

编写测试:

    @Test
    public void test2(){
        UserDAO mapper = MybatisUtil.getMapper(UserDAO.class);
        User user = mapper.queryUserByUsername("zzt");
        System.out.println(user);
    }

但是如果我们每增加一种查询方式,就要多定义一个方法,如此这般,是很麻烦的,事实上,动态SQL允许我们将这些方式合并,由于不同的方式都是根据类定义中的某一个或某几个属性来的,因此我们可以直接用一个User接收参数。在动态SQL中,我们只需要让那些属性值非空的拿出来做判断条件即可。

我们先看一下单属性有值的情况,以合并按用户名和按id查询为例:

1)在DAO中增加定义

List<User> queryUser(User user);

2)编写Mapper.xml中对应方法

    <select id="queryUser" resultType="User">
        <include refid="user_field"></include>
        where
        <if test = "id != null">
            id = #{id}
        </if>
        <if test = "username != null">
            username = #{username}
        </if>
    </select>

[注]:<if>标签类似于if语句,test为条件判断的字符串,当条件成立时,if标签内的语句会拼接上前面的部分。值得注意的是,以test = "id != null"为例,字符串内的id指的是参数user内的id。

3)编写测试方法

    @Test
    public void test3(){
        UserDAO mapper = MybatisUtil.getMapper(UserDAO.class);

        User userone = new User();
        userone.setId(1);
        List<User> users = mapper.queryUser(userone);
        for (User user : users) {
            System.out.println(user);
        }

        User usertwo = new User();
        usertwo.setUsername("zjm");
        List<User> users1 = mapper.queryUser(usertwo);
        for (User user : users1) {
            System.out.println(user);
        }
    }

然而上述方法是有缺陷的,假设两个if标签均成立的话,那么语句最后的部分就会变为where id = #{id} username = #{username},显然拼接后是一个错误的SQL,也许有人会说,将第二个语句的标签变为 and username = #{username},或者变为or username = #{username}。但是我们设想,如果第一个标签不成立,那么拼接后的结果是 where or username = #{username},这显然也是错误的!!!

Mybatis提供了where标签,它有如下作用:

1. 提供一个where

2. 如果拼接后的结果在where后紧接and 或者 or,那么它会帮助我们去除。

现在我们用以id或者username的方式查询为例:

1)在DAO中定义方法

List<User> queryUser2(User user);

2)在Mapper.xml编写SQL

    <select id="queryUser2" resultType="User">
        <include refid="user_field"></include>
        <where>
            <if test = "id != null">
                id = #{id}
            </if>
            <if test = "username != null">
                or username = #{username}
            </if>
        </where>
    </select>

[注]:其实第一个if我们也可以加or,因为where会帮我们去除。

3)编写测试方法  由于所有的方法测试执行第一步都是读取主配置文件 因此我们可以抽取共性,使用@Before表示在方法的测试前执行。

    UserDAO mapper;

    @Before
    public void start(){
        mapper = MybatisUtil.getMapper(UserDAO.class);
    }

    @Test
    public void test4(){
        User user = new User();
        user.setId(1);
        user.setUsername("zjm");
        List<User> users = mapper.queryUser2(user);
        for (User user1 : users) {
            System.out.println(user1);
        }
    }

3.更新 <set>

我们前面的更新语句存在一次需要对所有属性更新的问题,但在实际应用中,我们很有可能仅仅针对某些属性更新。显然我们需要根据传入参数user的属性是否为空决定更新哪些属性。当然主键指明了更新哪条记录,一般我们不会更新。

我们来看更新的语法,update table_name set xx=xx,xx=xx where xx=xx。

看起来我们可以这样写SQL:

    <update id="updateUser" parameterType="User">
        update t_user
        set
            <if test="username != null">
                username = #{username},
            </if>
            <if test="password != null">
                password = #{password},
            </if>
            <if test="gender != null">
                gender = #{gender},
            </if>
            <if test="regist_time != null">
                regist_time = #{registerTime}
            </if>
        where id = #{id}
    </update>

现在我们想象如果我们没有给regist_time 属性赋值,那么拼接出来的SQL语句会变成update table_name set xx=xx,xx=xx, where xx=xx。在set结尾和where中间多出了",",这是有SQL语法错误的。

那么根据上面的经验,Mybatis应该也提供了类似的<set>标签,它的作用如下:

1. 提供一个set

2. 如果拼接后的结果在set的尾巴上有一个",",那么它会帮助我们去除

修改Mapper.xml中的SQL:

    <update id="updateUser" parameterType="User">
        update t_user
        <set>
            <if test="username != null">
                username = #{username},
            </if>
            <if test="password != null">
                password = #{password},
            </if>
            <if test="gender != null">
                gender = #{gender},
            </if>
            <if test="registerTime != null">
                regist_time = #{registerTime}
            </if>
        </set>
        where id = #{id}
    </update>

编写测试:

    @Test
    public void test_update(){
        User user = new User(2,"zjm","520",null,null);
        Integer integer = mapper.updateUser(user);
        System.out.println(integer);
        MybatisUtil.commit();
    }

[注意]:千万要记得增删改之后都需要进行提交,否则不会持久化。

4. 通用型标签 <trim> Mybatis提供了<trim>标签让我们自行定制更为复杂的功能。

<trim>标签各属性详解:

prefix:在标签所包裹的语句块的开头添加什么

suffix:在标签所包裹的语句块的结尾添加什么

prefixOverrides:整个语句块以什么开头就去掉什么

suffixOverrides:整个语句块以什么结束就去掉什么

于是<where>标签可以用如下方式替代:

    <select id="queryUser2" resultType="User">
        <include refid="user_field"></include>
        <!--<where>
            <if test = "id != null">
                id = #{id}
            </if>
            <if test = "username != null">
                or username = #{username}
            </if>
        </where>-->
        <trim prefix="where" prefixOverrides="and|or">
            <if test = "id != null">
                id = #{id}
            </if>
            <if test = "username != null">
                or username = #{username}
            </if>
        </trim>
    </select>

<set>标签可以用如下方式替代:

    <update id="updateUser" parameterType="User">
        update t_user
        <!--<set>
            <if test="username != null">
                username = #{username},
            </if>
            <if test="password != null">
                password = #{password},
            </if>
            <if test="gender != null">
                gender = #{gender},
            </if>
            <if test="registerTime != null">
                regist_time = #{registerTime}
            </if>
        </set>-->
        <trim prefix="set" suffixOverrides=",">
            <if test="username != null">
                username = #{username},
            </if>
            <if test="password != null">
                password = #{password},
            </if>
            <if test="gender != null">
                gender = #{gender},
            </if>
            <if test="registerTime != null">
                regist_time = #{registerTime}
            </if>
        </trim>
        where id = #{id}
    </update>

5.批量操作 <foreach>   

A.批量删除为例

0)我们先插入一些数据

insert into t_user(id) values(3),(4),(5),(6)

1)在DAO中定义方法

Integer deleteUsers(List<Integer> ids);

2)在Mapper.xml定义对应SQL

我们先看看批量删除的SQL语句:delete from table_name where id in (x,x,x,x,x);也就是说,我们需要通过<foreach>生成(x,x,x,x,x)部分。

    <delete id="deleteUsers" parameterType="java.util.List">
        delete from t_user where id in
        <foreach collection="list" open="(" close=")" item="it" separator=",">
            #{it}
        </foreach>
    </delete>

collection:指明需要遍历的变量的类型

open:语句块开始需要添加什么

close:语句块结束需要添加什么

item:遍历出来的每一项叫什么

separator:每一项之间用什么分隔开

[注]:由于本方法对应的参数不是基本数据类型及其包装类,也不是string,而是复合类型List,因此我们需要显式指明。

3)编写测试方法

    @Test
    public void delete_list(){
        List<Integer> ids = Arrays.asList(3, 4, 5);
        mapper.deleteUsers(ids);
        MybatisUtil.commit();
    }

B.批量增加

1)在DAO增加方法定义

Integer insertUsers(List<User> users);

2)在Mapper.xml定义对应SQL

先看一下批量增加的SQL语句:insert into t_user values(null,x,x,x,x,x),(null,x,x,x,x,x)

<insert id="insertUsers" parameterType="java.util.List">
        insert into t_user values
        <foreach collection="list" item="Puser" separator=",">
            (null,#{Puser.username},#{Puser.password},#{Puser.gender},#{Puser.registerTime})
        </foreach>
    </insert>

3)编写测试

    @Test
    public void insert_list(){
        List<User> users = Arrays.asList(new User(null, "张三", "111", "m", new Date()),
                new User(null, "李四", "222", "f", new Date()));
        mapper.insertUsers(users);
        MybatisUtil.commit();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值