增删查改
增删查改算是十分重要且基础的部分,这里来跟着文档来实现一下如何进行增删改查。
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语句中,一共有两种传递参数的办法
这两种方式都可以起到参数传递的作用,不过#{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"说明这个查询函数的函数名为list,resultType=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连接数据库的所有操作,加油~