# 基于 tk mapper 实现增删改查的 restfull 接口

首先创建一张演示表 stu

CREATE TABLE stu
(
    id   int primary key auto_increment,
    name varchar(255) not null,
    age  int          not null
) COMMENT = '演示表 ';
1
2
3
4
5
6

使用 mybatis 代码生成工具生成,然后开始开发增删改查的示例

# Tk Mapper 单表查询简单说明

我们逆向生成代码的时候,定义了一个接口

interface MyMapper<T> extends Mapper<T>,MySqlMapper<T>
1

它继承了两个父类,先来看看 MySqlMapper<T>

interface MySqlMapper<T> extends InsertListMapper<T>,InsertUseGeneratedKeysMapper<T>{}
1

它又继承了两个父类,从类名上看,是操作数据库的,里面分别有两个方法

方法名 操作 备注
insertList(list) 数据批量插入 主键须自增
insertUseGeneratedKeys(record) 插入表数据 主键须自增

很明显,在传统 JavaWeb 开发,这两个方法使用是没有问题的,但是我们的数据库表主键设计肯定是全局唯一的,所以不可能使用自增长 id(如何设计全局唯一 ID 部分,在后续课程里有具体的讲解),所以这两个方法在我们开发过程中是不会使用的,这一点需要注意。

在来看看 Mapper<T>

public interface Mapper<T> extends BaseMapper<T>, ExampleMapper<T>, RowBoundsMapper<T>, Marker {
}
1
2
  • BaseMapper<T> 中又继承了好多的类,下面分别看看都提供了哪些方法

    方法 操作
    BaseSelectMapper T selectOne(T record) 根据实体类中的属性查询表数据,返回单个实体
    List select(T record) 根据实体类中的属性查询表数据,返回符合条件的 list
    List selectAll() 返回该表所有记录
    int selectCount(T record) 根据条件查询记录数
    T selectByPrimaryKey(Object key) 根据主键查询单挑记录
    boolean existsWithPrimaryKey(Object key) 查询主键是否存在,返回 true 或 false
    BaseInsertMapper int insert(T record) 插入一条记录,属性为空也会保存
    int insertSelective(T record) 插入一条记录,属性为空不保存,会使用默认值
    BaseUpdateMapper int updateByPrimaryKey(T record) 根据实体类更新数据库,属性有 null 会覆盖原记录
    int updateByPrimaryKeySelective(T record) 根据实体类更新数据库,属性有 null 该属性会忽略
    BaseDeleteMapper int delete(T record) 根据实体类中属性多条件删除记录
    int deleteByPrimaryKey(Object key) 根据主键删除记录
  • ExampleMapper<T>:用于自定义条件(where 语句)Exampl 是条件类,主要方法如下

    方法
    SelectByExampleMapper List<T> selectByExample(Object example) 按条件查询,返回多条记录
    SelectOneByExampleMapper T selectOneByExample(Object example) 按条件查询,返回单条记录,如果返回的是多条记录,则会发生异常
    SelectCountByExampleMapper int selectCountByExample(Object example); 按条件查询,返回符合条件的数量
    DeleteByExampleMapper int deleteByExample*(Object example) 按条件删除
    UpdateByExampleMapper int updateByExample(@Param("record") T record, @Param*("example") Object example) 根据 Example 条件更新实体 record 包含的全部属性,null 值会被更新
    UpdateByExampleSelectiveMapper int updateByExampleSelective(@Param("record") T record, @Param("example") Object example) 根据 Example 条件更新实体 record 包含的全部属性,null 值会被忽略
  • RowBoundsMapper:分页查询,配合分页插件 PageHelper 可以实现物理分页

  • Marker:则是我们上面自定义实现的顶级父类

以上方法仅限于了解,具体的用法还是建议去 官方文档阅读 (opens new window)

# 开发测试用例

编写 service

package cn.mrcode.foodiedev.service.impl;

import cn.mrcode.foodiedev.mapper.StuMapper;
import cn.mrcode.foodiedev.pojo.Stu;
import cn.mrcode.foodiedev.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@SuppressWarnings({"SpringJavaInjectionPointsAutowiringInspection"})
@Service
public class StuServiceImpl implements StuService {
    @Autowired
    private StuMapper stuMapper;

    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public Stu getStuInfo(int id) {
        return stuMapper.selectByPrimaryKey(id);
    }
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

编写 controller

package cn.mrcode.foodiedev.api.controller;

import cn.mrcode.foodiedev.service.StuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StuController {
    @Autowired
    private StuService stuService;

    @GetMapping("/getStu")
    public Object getStu(int id) {
        return stuService.getStuInfo(id);
    }
  
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

非常重要的一个步骤:需要使用 tk mapper 的方式扫描我们之前生成的那些 mapper 接口

package cn.mrcode.foodiedev;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan("cn.mrcode.foodiedev.mapper")  // 使用 tk mapper 扫描 mapper 目录
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

重启项目访问 http://localhost:8088/getStu?id=1 (先手动在 stu 表中新增一条数据,再尝试),如果返回结果则表明整合确实没有什么问题了

那么其他的几个方法,由于这里是演示,所以也是非常的简单

    @PostMapping("/saveStu")
    public Object saveStu() {
        stuService.saveStu();
        return "OK";
    }

    @PostMapping("/updateStu")
    public Object updateStu(int id) {
        stuService.updateStu(id);
        return "OK";
    }
    @PostMapping("/deleteStu")
    public Object deleteStu(int id) {
        stuService.deleteStu(id);
        return "OK";
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void saveStu() {
        Stu record = new Stu();
        record.setName("jack");
        record.setAge(19);
        stuMapper.insert(record);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void updateStu(int id) {
        Stu record = new Stu();
        record.setId(id);
        record.setName("jack-update");
        record.setAge(50);
        stuMapper.updateByPrimaryKey(record);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void deleteStu(int id) {
        stuMapper.deleteByPrimaryKey(id);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 调试 RestFull 接口

此类工具有很多,比如 PostMan 工具,自己百度一下。

不过笔者这里推荐使用 IDEA 自带的测试工具,详情看这个笔记 (opens new window)