@TOC
1. MyBatis 增删改查
1.1 增添操作
- 在 mapper (interface)
添加增添方法声明
//添加用户,返回受影响的行数
public int add(UserInfo userInfo);
- 在 xml 中
添加 < insert > 标签和增添 sql 编写
<!-- 添加用户,返回受影响的行数 -->
<insert id="add">
insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
</insert>
- service 层
public int add(UserInfo userInfo){
return userMapper.add(userInfo);
}
- controller 层
@RequestMapping("/add")
public int add(UserInfo userInfo){
userInfo.setUsername("张三");
userInfo.setPassword("123");
userInfo.setPhoto("default.png");
return userService.add(userInfo);
}
- 测试
1.2 删除操作
1. 在 mapper(interface) 里面添加删除的代码声明
// 删除方法
public int del(@Param("id") Integer id);
2. 在 xml 中添加 < delete >标签和删除 sql 编写
<!-- 根据用户 id 删除用户 -->
<delete id="del">
delete from userinfo where id=#{id}
</delete>
3. service 层
public int del(Integer id){
return userMapper.del(id);
}
4. controller 层
@RequestMapping("/del")
public int del(Integer id){
return userService.del(id);
}
5. 测试
1.3 修改操作
1. 在 mapper(interface) 里面添加修改的代码声明
// 修改方法【根据id修改名称】
public int update(@Param("id") Integer id,
@Param("username") String username);
2. 在 xml 中添加 < update >标签和修改 sql 编写
<!-- 根据用户 id 修改用户名称 -->
<update id="update">
update userinfo set username=#{username} where id=#{id}
</update>
3. service 层
public int update(Integer id,String username){
return userMapper.update(id,username);
}
4. controller 层
@RequestMapping("/update")
public int update(Integer id,String username){
return userService.update(id,username);
}
5. 测试
1.4 查找操作
1. 在 mapper(interface) 里面添加查找的代码声明
// 根据用户名来查询用户
public UserInfo getUserByUsername(@Param("username") String username);
一般是根据 id 来查找,因为用户名也可能重复
2. 在 xml 中添加 < select >标签和查找 sql 编写
<!-- 根据用户名查询用户 -->
<select id="getUserByUsername" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username=#{username}
</select>
3. service 层
public UserInfo getUserByUsername(String username){
return userMapper.getUserByUsername(username);
}
4. controller 层
@RequestMapping("/getuserbyusername")
public UserInfo getUserById(String username){
if(username == null) return null;
return userService.getUserByUsername(username);
}
5. 测试
2. SpringBoot单元测试
2.1 单元测试优点
- 可以非常简单,直观
快速的测试某一个功能是否正确
- 使用单元测试可以帮我们在打包的时候,发现一些问题,因为在打包之前,所有的单元测试必须通过,否则不能打包成功
- 使用单元测试,在测试功能的时候,
可以不污染连接的数据库,也就是可以不对数据库进行任何改变的情况下,进行测试功能
2.2 单元测试的实现
2.2.1 准备工作: 确认项目中已经内置了测试框架
2.2.2 生成单元测试的类
在 mapper 包下:
2.2.3 配置单元测试的类添加 @SpringBootTest 注解,添加单元测试的业务代码
1. 添加注解@SpringBootTest
2.添加单元测试的业务代码
2.3 简单断言说明
断⾔:如果断⾔失败,则后⾯的代码都不会执⾏。
2.4 添加 @Transactional 注解执行回滚事务
我现在的数据库
执行修改操作:
修改成功:
在观察原数据库并没发生改变:
执行删除操作:
删除成功:
观察原数据库也没发生改变:
相关代码:
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class UserMapperTest {
@Resource
private UserMapper userMapper;
@Test
void getUserById() {
UserInfo userInfo = userMapper.getUserById(1);
System.out.println(userInfo);
}
// 在单元测试中添加此注解,表示在方法执行完之后回滚事务
@Transactional
@Test
void update() {
int result = userMapper.update(2, "张三");
Assertions.assertEquals(1, result);
}
@Transactional
@Test
void del() {
int result = userMapper.del(2);
System.out.println("受影响的行数:" + result);
Assertions.assertEquals(1, result);
}
@Transactional
@Test
void add() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("王五");
userInfo.setPassword("123");
userInfo.setPhoto("default.png");
int result = userMapper.add(userInfo);
System.out.println("添加的结果:" + result);
Assertions.assertEquals(1, result);
}
}
2.5 返回自增 id
我们不仅想看到受影响的行数,还想看到修改后的 id
1. mapper层接口:
// 添加用户,返回受影响的行数和自增 id
public int addGetId(UserInfo userInfo);
2. XML 文件:
<!-- 添加用户,返回受影响的行数和自增 id -->
<insert id="addGetId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
</insert>
-
useGeneratedKeys:
这会令 MyBatis 使⽤ JDBC 的 getGeneratedKeys ⽅法来取出由数据库内部⽣成的主键(⽐如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的⾃动递增字段),默认值:false。 -
keyColumn:
设置⽣成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第⼀列的时候,是必须设置的。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性名称。 keyProperty:
指定能够唯⼀识别对象的属性,MyBatis 会使⽤ getGeneratedKeys 的返回值或 insert 语句的 selectKey ⼦元素设置它的值,默认值:未设置(unset)。如果⽣成列不⽌⼀个,可以⽤逗号分隔多个属性名称
3. 单元测试代码:
// @Transactional
@Test
void addGetId() {
UserInfo userInfo = new UserInfo();
userInfo.setUsername("老六");
userInfo.setPassword("123");
userInfo.setPhoto("default.png");
System.out.println("添加之前的 user id: "+userInfo.getId());
int result = userMapper.addGetId(userInfo);
System.out.println("受影响的行数: "+result);
System.out.println("添加之后的 user id: "+userInfo.getId());
Assertions.assertEquals(1,result);
}
注释掉了回滚:
2.6 一些查询操作
2.6.1 ${} 和 #{} 的区别演示
① 根据 id 查找用户
先看 #{} 操作根据 id 查找用户
执行的 JDBC 代码:(针对 int 类型的参数)
接下来${},其他代码不变
执行的 JDBC 代码:(针对 int 类型)
前者体现的预处理(预查询),后者体现的即时查询
② 根据用户名查找用户
我们在看看针对 String 类型参数
:
先使用 #{} 根据用户名查找 用户
执行的 JDBC 代码:(针对 String 类型)
接下来${},其他代码不变
执行的 JDBC 代码:(针对 String 类型)
报错了,他的sql语句相当于是:
==并没有加引号==
③ 排序功能
像一些商品基本都要有这个排序功能(按价格从高到低,从低到高)
我这里就按 id 来排序
先看 #{}
报错了:
他的 sql 相当于:
再看 ${}
当传递的是一个 SQL 关键字(SQL 命令)的时候,只能使用 ${},此时如果使用 #{} 就会认为传递的一个普通的值,而非 SQL 命令,所以执行就会报错注意
${} 会有 SQL 注入问题,我们不得不使用 ${} 时,那么一定要在业务代码中,对传递的值进行安全验证
2.6.2 总结区别
- 定义不同:
#{}
预处理,而${}
是直接替换 - 使用不同:
#{}
适用于所有类型的参数匹配,但${}
只适用于数值类型 - 安全性不同:
#{}
性能高,并且没有安全问题: 但${}
存在 SQL 注入的安全问题