Mybatis增删查改

增删查改

增删查改算是十分重要且基础的部分,这里来跟着文档来实现一下如何进行增删改查。

Mybatis连接数据库

需要我们在application.properties下配置好对应的内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisEmp

# 数据库用户名称
spring.datasource.username=root

# 登录密码
spring.datasource.password=123456

删除

Mapper层实现删除的接口,对于删除语句来说,其选择的注释就是@Delete,其中编写对应的删除语句即可。我们需要实现一个根据ID来删除的一个操作。

参数传递

Mapper层中我们可以编写EmpMapper接口,其中,为了更加灵活,我们可以根据传进来的ID来实现删除逻辑

1
2
3
4
5
6
@Mapper
public interface EmpMapper {
    // 删除
    @Delete("DELETE from emp where id = #{id}")
    public int deleteEmpById(Integer id);
}

使用Mapper注释可以将后续实例化的对象通过容器进行管理,后续我们只需要使用AutoWired注释即可。可以在test文件下进行测试。在测试时,每个测试都要写成一个函数,并且要加上@Test注释。

1
2
3
4
5
@Test
public void deleteByPrimaryKey() {
    int i = empMapper.deleteEmpById(17);
    System.out.println("Return Val Of Delete is " + i);
}

sql注入问题

当使用拼接sql语句去进行某些验证业务时,精心构造的参数会跳过验证,从而起到sql注入攻击的效果。假设我们使用的是sql拼接的方式进行验证,例如,我们要做登录验证。

一般,数据库会存储用户名和加密后的密码(应该不是明文密码),当我们进行登录是时,假设是在进行查询操作。

1
select count(*) where userName = 'xxx' and password = 'xxx';

当使用拼接sql时,我们可以构造出来count(*)永远大于0的操作,即password在传递时为' or '1' = '1,这样,拼接后的sql语句就是

1
select count(*) from emp where userName = 'xxx' and password = '' or '1' = '1';

1 = 1永远成立,所以这条sql相当于

1
select count(*) from emp;

sql预编译

解决sql注入可以通过预编译的方式去避免。在预编译时,传递的关键参数都是利用占位符去占用,例如上面这条sql语句就会变为

1
select count(*) from emp where userName = ? and password = ?;

当有参数传递时,就会将?替换为对应的参数,从而避免了sql拼接的问题。在上面传递参数的sql语句中,一共有两种传递参数的办法

1
2
#{id}
${id}

这两种方式都可以起到参数传递的作用,不过#{id}的方式是预编译sql,可以解决sql注入的问题。

插入数据

mysql中,插入数据的sql语句一般是

1
insert into xxx(字段名) values (xxxx);

使用的注解也是@Insert,当插入的值过多,就可以把这些值封装到一个对象里面,把需要插入的参数使用#{xx}的方式插入即可,这样可以防止sql注入,同时预编译还可以提高查询效率。

当需要进行模糊搜索时,一般会用到通配符搜索,在使用通配符的时候,有些通配符符号是无法通过#{xx}的形式去拼接的,例如,我想要搜索所有包含张的姓名

1
select name from user where name like '%张%';

那么在传递参数的时候,需要把%也传递进去,就像下面一样

1
2
3
4
5
6
7
8
9
@Mapper
public interface EmpMapper {
    @Select("select * from emp " +
            "where name like '%${name}%' " +
            "and gender = #{gender} " +
            "and entrydate between #{begin} and #{end} " +
            "order by update_time desc")
    public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}

如果要使用占位参数来编写sql,那么这个句子就变成了

1
select name from user where name like '%?%'

这样就会出现编译错误,而使用直接拼接的${}话,又会引发sql注入问题,所以可以使用sql的库函数即concat函数来拼接。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
@Mapper
public interface EmpMapper {

    @Select("select * from emp " +
            "where name like concat('%',#{name},'%') " +
            "and gender = #{gender} " +
            "and entrydate between #{begin} and #{end} " +
            "order by update_time desc")
    public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);

}

这样也可以实现预编译,把中间的#{name}替换掉,在预编译后就变成了

1
select name from user where name like concat('%', ?, '%');

XML编写

可以直接把用到的sql语句写到XML文件里面,这样当sql语句开始变得复杂的时候,就可以方便的管理一些sql语句,从而避免将sql语句直接写在java代码里面。

书写规范

其书写格式一般是

1
2
3
<mapper namespace="xxx.xxx.xxx">

</mapper>

不同的sql语句之间标签不一致,查询语句需要用<select>标签包裹,同理的,例如<insert><delete><update>

同时,还需要在这些标签中注明方法名和方法的返回值,假设我们要查询

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<mapper namespace="com.itheima.mapper.EmpMapper">

    <!--查询操作-->
    <select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where name like concat('%',#{name},'%')
              and gender = #{gender}
              and entrydate between #{begin} and #{end}
        order by update_time desc
    </select>
</mapper>

其中id="list"说明这个查询函数的函数名为listresultType=xxx说明其返回值类型是xxx,然后在标签中间编写sql语句即可。

需要注意,可以直接把简单的sql以注解的形式写道java代码里面,也可以写到xml文件里面。越短越简单的sql语句可以写到java代码里面。

更新数据

同样的,我们可以使用insert来向表里面插入某些数据,也可以使用update来更新一些数据,如果我们不指定所有的字段,那么未指定的字段将会被设置为null,但是这并不是我们希望看到的,有时候只需要修改一两条信息,其它的信息不用做出修改,这个时候我们就可以使用动态sql来避免传递空子段。

动态SQL

动态SQL是指前端传递过来的参数个数是不确定的,这就要求我们不可以将sql语句写死,而是根据传递结果来确定sql语句的书写,这里就用到了if标签。

语法

1
2
3
<if test="判断语句">

</if>

例如,之前的一条查询语句为

1
2
3
4
5
6
7
<select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where name like concat('%',#{name},'%')
              and gender = #{gender}
              and entrydate between #{begin} and #{end}
        order by update_time desc
</select>

我们就可以根据name gender entrydate这些字段是否为空来进行查询判断

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where
        <if test="name != null">
            name like concat('%',#{name},'%')
        </if>
        <if test="gender != null">
            and gender = #{gender}
        </if>
        <if test="begin!= null and end != null">
        and entrydate between #{begin} and #{end}
        </if>  
        order by update_time desc
</select>

当某些字段为空时,即未通过test测试,那么就不会查询该字段。

问题

但是这样就又会引发一些问题,当name字段为空时,这段SQL语句会变成

1
2
3
4
5
6
7
<select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        where
        and gender = #{gender}
        and entrydate between #{begin} and #{end}
        order by update_time desc
</select>

可以发现,,where后面多跟了一个and导致我们的语法发生了错误,当然也有解决办法,就是去使用<where>标签

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<select id="list" resultType="com.itheima.pojo.Emp">
        select * from emp
        <where>
             <!-- if做为where标签的子元素 -->
             <if test="name != null">
                 and name like concat('%',#{name},'%')
             </if>
             <if test="gender != null">
                 and gender = #{gender}
             </if>
             <if test="begin != null and end != null">
                 and entrydate between #{begin} and #{end}
             </if>
        </where>
        order by update_time desc
</select>

更新某些数据

同样的,只有在前端传递管来的值不为null时才对里面的值进行修改

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

    <!--更新操作-->
    <update id="update">
        update emp
        set
            <if test="username != null">
                username=#{username},
            </if>
            <if test="name != null">
                name=#{name},
            </if>
            <if test="gender != null">
                gender=#{gender},
            </if>
            <if test="image != null">
                image=#{image},
            </if>
            <if test="job != null">
                job=#{job},
            </if>
            <if test="entrydate != null">
                entrydate=#{entrydate},
            </if>
            <if test="deptId != null">
                dept_id=#{deptId},
            </if>
            <if test="updateTime != null">
                update_time=#{updateTime}
            </if>
        where id=#{id}
    </update>

</mapper>

可是这样做又会引起sql语句的语法错误,例如当只存在第一个字段而不存在后面那些字段时,下面的语句就是

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

    <!--更新操作-->
    <update id="update">
        update emp
        set
            username=#{username},
        where id=#{id}
    </update>

</mapper>

也就是后面多加了一个逗号,同样的,我们也可以使用<set>标签来修改

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">

    <!--更新操作-->
    <update id="update">
        update emp
        <!-- 使用set标签,代替update语句中的set关键字 -->
        <set>
            <if test="username != null">
                username=#{username},
            </if>
            <if test="name != null">
                name=#{name},
            </if>
            <if test="gender != null">
                gender=#{gender},
            </if>
            <if test="image != null">
                image=#{image},
            </if>
            <if test="job != null">
                job=#{job},
            </if>
            <if test="entrydate != null">
                entrydate=#{entrydate},
            </if>
            <if test="deptId != null">
                dept_id=#{deptId},
            </if>
            <if test="updateTime != null">
                update_time=#{updateTime}
            </if>
        </set>
        where id=#{id}
    </update>
</mapper>

sql引用

对于一些重复的sql片段,我们可以使用片段包裹的方式来进行引入

1
2
3
4
5
<sql id="name">
    select
    name, age, gender
    from user
</sql>

这个时候,如果想要引用上面这个sql语句,就可以使用include标签来进行引用

1
2
3
<include refid="name">
	where id = 1
</include>

这样就可以把重复的语句选出来。

总结

这就是Mybatis连接数据库的所有操作,加油~

Licensed under CC BY-NC-SA 4.0
花有重开日,人无再少年
使用 Hugo 构建
主题 StackJimmy 设计