0
点赞
收藏
分享

微信扫一扫

【MyBatis】MyBatis CRUD操作、单元测试的使用、${} 与 #{} 的区别

@TOC

1. MyBatis 增删改查

1.1 增添操作

  1. 在 mapper (interface) 添加增添方法声明
//添加用户,返回受影响的行数
    public int add(UserInfo userInfo);
  1. 在 xml 中添加 < insert > 标签和增添 sql 编写
<!-- 添加用户,返回受影响的行数 -->
    <insert id="add">
        insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
    </insert>

image-20220814162712683

  1. service 层
public int add(UserInfo userInfo){
        return userMapper.add(userInfo);
    }
  1. controller 层
@RequestMapping("/add")
    public int add(UserInfo userInfo){
        userInfo.setUsername("张三");
        userInfo.setPassword("123");
        userInfo.setPhoto("default.png");
        return userService.add(userInfo);
    }
  1. 测试

image-20220814162751517

image-20220814162812115

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>

image-20220814162834671

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. 测试

image-20220814162857446

image-20220814162904930

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. 测试

image-20220814162939225

image-20220814162945976

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. 测试

image-20220814163032684

2. SpringBoot单元测试

2.1 单元测试优点

  1. 可以非常简单,直观快速的测试某一个功能是否正确
  2. 使用单元测试可以帮我们在打包的时候,发现一些问题,因为在打包之前,所有的单元测试必须通过,否则不能打包成功
  3. 使用单元测试,在测试功能的时候,可以不污染连接的数据库,也就是可以不对数据库进行任何改变的情况下,进行测试功能

2.2 单元测试的实现

2.2.1 准备工作: 确认项目中已经内置了测试框架

image-20220814163144942

2.2.2 生成单元测试的类

在 mapper 包下:

image-20220814163159536

image-20220814163204603

image-20220814163214713

image-20220814163236709

2.2.3 配置单元测试的类添加 @SpringBootTest 注解,添加单元测试的业务代码

1. 添加注解@SpringBootTest

image-20220814163312460

2.添加单元测试的业务代码

image-20220814163338153

image-20220814163346881

2.3 简单断言说明

image-20220814163510072

断⾔:如果断⾔失败,则后⾯的代码都不会执⾏。

2.4 添加 @Transactional 注解执行回滚事务

我现在的数据库

image-20220814163533857

执行修改操作:

image-20220814163610489

修改成功:

image-20220814163555967

在观察原数据库并没发生改变:

image-20220814163602572

执行删除操作:

image-20220814163634198

删除成功:

image-20220814163655017

观察原数据库也没发生改变:

image-20220814163706034

相关代码:

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);
    }

注释掉了回滚:

image-20220814163835799

image-20220814163848312

2.6 一些查询操作

2.6.1 ${} 和 #{} 的区别演示

① 根据 id 查找用户

先看 #{} 操作根据 id 查找用户

image-20220814163916496

image-20220814163924379

image-20220814163929997

执行的 JDBC 代码:(针对 int 类型的参数)

image-20220814163942536

接下来${},其他代码不变

image-20220814164003879

执行的 JDBC 代码:(针对 int 类型)

image-20220814164049854

前者体现的预处理(预查询),后者体现的即时查询

② 根据用户名查找用户

我们在看看针对 String 类型参数:

先使用 #{} 根据用户名查找 用户

image-20220814164153992

执行的 JDBC 代码:(针对 String 类型)

image-20220814164216483

接下来${},其他代码不变

image-20220814164233150

执行的 JDBC 代码:(针对 String 类型)

image-20220814164239667

报错了,他的sql语句相当于是:

image-20220814164317266

==并没有加引号==

③ 排序功能

像一些商品基本都要有这个排序功能(按价格从高到低,从低到高)我这里就按 id 来排序

先看 #{}

image-20220814164523772

报错了:

image-20220814164540026

他的 sql 相当于:

image-20220814164556950

再看 ${}

image-20220814164634304

当传递的是一个 SQL 关键字(SQL 命令)的时候,只能使用 ${},此时如果使用 #{} 就会认为传递的一个普通的值,而非 SQL 命令,所以执行就会报错
注意 ${} 会有 SQL 注入问题,我们不得不使用 ${} 时,那么一定要在业务代码中,对传递的值进行安全验证

2.6.2 总结区别

  1. 定义不同: #{} 预处理,而 ${} 是直接替换
  2. 使用不同: #{} 适用于所有类型的参数匹配,但 ${} 只适用于数值类型
  3. 安全性不同: #{} 性能高,并且没有安全问题: 但 ${} 存在 SQL 注入的安全问题
举报

相关推荐

0 条评论