ApiTable 实现增删改查
本节介绍如何使用 tio-boot 整合 ApiTable,实现一个简单的增删改查(CRUD)示例应用。通过该示例,您将了解如何创建数据库表、配置依赖、编写控制器以及测试各项功能。
创建表和插入数据
创建表
首先,创建一个名为 system_users
的用户信息表,用于存储系统用户的相关信息。以下是建表的 SQL 语句:
CREATE TABLE `system_users` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户账号',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '密码',
`nickname` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户昵称',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
`dept_id` bigint DEFAULT NULL COMMENT '部门ID',
`post_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '岗位编号数组',
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '用户邮箱',
`mobile` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '手机号码',
`sex` tinyint DEFAULT '0' COMMENT '用户性别',
`avatar` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '头像地址',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
`login_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '最后登录IP',
`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`tenant_id` bigint NOT NULL DEFAULT '0' COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_username` (`username`,`update_time`,`tenant_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=126 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户信息表';
插入测试数据
创建好表结构后,插入一些测试数据以便后续操作和测试。以下是插入数据的 SQL 语句:
INSERT INTO `system_users` VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '乒乒', '管理员', 103, '[1]', 'aoteman@126.com', '15612345678', 1, 'http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png', 0, '127.0.0.1', '2023-11-30 09:16:00', 'admin', '2021-01-05 17:03:47', NULL, '2024-03-23 08:49:55', b'0', 1);
-- 其余插入语句略
添加依赖
在项目的 pom.xml
文件中,添加以下依赖。请确保使用最新版本的相关包,以获取最新的功能和安全更新。
<dependencies>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>tio-boot</artifactId>
<version>${tio.boot.version}</version>
</dependency>
<dependency>
<groupId>com.litongjava</groupId>
<artifactId>api-table</artifactId>
<version>${tio.boot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
</dependencies>
ApiTableController
ApiTableController
创建一个控制器类 ApiTableController
,用于处理与 system_users
表相关的增删改查操作。以下是完整的控制器代码:
package com.litongjava.tio.boot.table.to.json.controller;
import java.io.IOException;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.jfinal.kit.Kv;
import com.litongjava.annotation.EnableCORS;
import com.litongjava.annotation.RequestPath;
import com.litongjava.db.TableInput;
import com.litongjava.db.TableResult;
import com.litongjava.db.activerecord.Db;
import com.litongjava.db.activerecord.Row;
import com.litongjava.model.body.RespBodyVo;
import com.litongjava.model.page.DbPage;
import com.litongjava.model.page.Page;
import com.litongjava.table.services.ApiTable;
import com.litongjava.table.utils.EasyExcelResponseUtils;
import com.litongjava.table.utils.TableInputUtils;
import com.litongjava.table.utils.TableResultUtils;
import com.litongjava.tio.boot.http.TioRequestContext;
import com.litongjava.tio.boot.utils.TioRequestParamUtils;
import com.litongjava.tio.http.common.HttpRequest;
import com.litongjava.tio.http.common.HttpResponse;
import lombok.extern.slf4j.Slf4j;
@RequestPath("/api/table")
@Slf4j
@EnableCORS
public class ApiTableController {
@RequestPath("/index")
public String index() {
return "TableJsonController";
}
@RequestPath("/{f}/create")
public RespBodyVo create(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<Kv> dbJsonBean = ApiTable.saveOrUpdate(f, kv);
if (dbJsonBean.getCode() == 1) {
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
} else {
return RespBodyVo.fail(dbJsonBean.getMsg()).code(dbJsonBean.getCode()).data(dbJsonBean.getData());
}
}
@RequestPath("/{f}/list")
public RespBodyVo list(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<List<Row>> list = ApiTable.list(f, kv);
TableResult<List<Kv>> dbJsonBean = TableResultUtils.recordsToKv(list, false);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/listAll")
public RespBodyVo listAll(String f) {
log.info("tableName:{}", f);
TableResult<List<Row>> listAll = ApiTable.listAll(f);
TableResult<List<Kv>> dbJsonBean = TableResultUtils.recordsToKv(listAll, false);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/page")
public RespBodyVo page(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
Object current = map.remove("current");
if (current != null) {
// 支持 Ant Design Pro Table
map.put("pageNo", current);
}
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<Page<Row>> page = ApiTable.page(f, kv);
TableResult<DbPage<Kv>> dbJsonBean = TableResultUtils.pageToDbPage(page, false);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/get")
public RespBodyVo get(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<Row> jsonBean = ApiTable.get(f, kv);
TableResult<Kv> dbJsonBean = TableResultUtils.recordToKv(jsonBean);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/update")
public RespBodyVo update(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<Kv> dbJsonBean = ApiTable.saveOrUpdate(f, kv);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/batchUpdate")
public RespBodyVo batchUpdate(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<Kv> dbJsonBean = ApiTable.batchUpdateByIds(f, kv);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/remove/{id}")
public RespBodyVo remove(String f, String id) {
log.info("tableName:{},id:{}", f, id);
TableResult<Boolean> dbJsonBean = ApiTable.updateFlagById(f, id, "deleted", 1);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/delete/{id}")
public RespBodyVo delete(String f, String id) {
log.info("tableName:{},id:{}", f, id);
TableResult<Boolean> dbJsonBean = ApiTable.delById(f, id);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/total")
public RespBodyVo total(String f) {
log.info("tableName:{}", f);
Long count = Db.count(f);
return RespBodyVo.ok(count);
}
/**
* 导出当前数据
*/
@RequestPath("/{f}/export-excel")
public HttpResponse exportExcel(String f, HttpRequest request) throws IOException {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
Object current = map.remove("current");
if (current != null) {
// 支持 Ant Design Pro Table
map.put("pageNo", current);
}
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
String filename = f + "_export_" + System.currentTimeMillis() + ".xlsx";
// 获取数据
List<Row> records = ApiTable.list(f, kv).getData();
HttpResponse response = TioRequestContext.getResponse();
return EasyExcelResponseUtils.exportRecords(response, filename, f, records);
}
/**
* 导出所有数据
*/
@RequestPath("/{f}/export-table-excel")
public HttpResponse exportAllExcel(String f, HttpRequest request) throws IOException, SQLException {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
map.remove("current");
map.remove("pageNo");
map.remove("pageSize");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
// 导出 Excel
String filename = f + "-all_" + System.currentTimeMillis() + ".xlsx";
// 获取数据
List<Row> records = ApiTable.listAll(f, kv).getData();
HttpResponse response = TioRequestContext.getResponse();
EasyExcelResponseUtils.exportRecords(response, filename, f, records);
log.info("finished");
return response;
}
@RequestPath("/export-all-table-excel")
public HttpResponse exportAllTableExcel(HttpRequest request) throws IOException {
String filename = "all-table_" + System.currentTimeMillis() + ".xlsx";
String[] tables = ApiTable.getAllTableNames();
LinkedHashMap<String, List<Row>> allTableData = new LinkedHashMap<>();
for (String table : tables) {
// 获取数据
List<Row> records = ApiTable.listAll(table).getData();
allTableData.put(table, records);
}
HttpResponse response = TioRequestContext.getResponse();
EasyExcelResponseUtils.exportAllTableRecords(response, filename, allTableData);
log.info("finished");
return response;
}
@RequestPath("/{f}/pageDeleted")
public RespBodyVo pageDeleted(String f, HttpRequest request) {
Map<String, Object> map = TioRequestParamUtils.getRequestMap(request);
map.remove("f");
ApiTable.transformType(f, map);
TableInput kv = TableInputUtils.camelToUnderscore(map);
log.info("tableName:{},kv:{}", f, kv);
TableResult<DbPage<Kv>> dbJsonBean = TableResultUtils.pageToDbPage(ApiTable.page(f, kv), false);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/recover")
public RespBodyVo recover(String f, String id) {
log.info("tableName:{},id:{}", f, id);
TableResult<Boolean> dbJsonBean = ApiTable.updateFlagById(f, id, "deleted", 0);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/names")
public RespBodyVo tableNames() throws IOException {
String[] data = ApiTable.tableNames().getData();
return RespBodyVo.ok(data);
}
@RequestPath("/{f}/config")
public RespBodyVo fConfig(String f, String lang) {
log.info("tableName:{}", f);
TableResult<Map<String, Object>> dbJsonBean = ApiTable.tableConfig(f, f, lang);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
@RequestPath("/{f}/columns")
public RespBodyVo proTableColumns(String f) {
TableResult<List<Map<String, Object>>> dbJsonBean = ApiTable.columns(f);
return RespBodyVo.ok(dbJsonBean.getData()).code(dbJsonBean.getCode()).msg(dbJsonBean.getMsg());
}
}
原理讲解
ApiTableController 主要通过 ApiTable 服务提供的接口,实现对数据库表的增删改查操作。以下是其主要功能和实现原理:
路由与请求处理:
- 使用自定义注解
@RequestPath
定义路由路径,映射到相应的方法。 - 通过 tio-boot 提供的 HttpRequest 和 HttpResponse 对象处理请求和响应。
- 使用自定义注解
增删改查操作:
- 创建(Create):接收前端传来的数据,转换为数据库可识别的格式,调用
ApiTable.saveOrUpdate
方法插入新记录。 - 读取(Read):
- list:根据条件查询符合的记录列表。
- listAll:查询表中所有记录。
- page:实现分页查询,支持前端分页参数。
- get:根据唯一条件(如主键)获取单条记录。
- pageDeleted:查询已逻辑删除的记录。
- 更新(Update):
- update:根据主键更新单条记录。
- batchUpdate:批量更新多条记录。
- 删除(Delete):
- remove:逻辑删除,通过更新
deleted
字段标记记录。 - delete:物理删除,直接从数据库中移除记录。
- recover:恢复已逻辑删除的记录。
- remove:逻辑删除,通过更新
- 创建(Create):接收前端传来的数据,转换为数据库可识别的格式,调用
辅助功能:
- 导出 Excel:
- exportExcel:导出当前查询结果为 Excel 文件。
- exportAllExcel:导出所有符合条件的数据为 Excel 文件。
- exportAllTableExcel:导出数据库中所有表的数据为一个 Excel 文件。
- 表信息获取:
- tableNames:获取数据库中所有表的名称。
- columns:获取指定表的字段信息。
- config:获取表的配置,用于前端展示等。
- 导出 Excel:
数据转换与处理:
- 使用
ApiTable.transformType
方法将前端传来的参数类型转换为数据库所需的类型。 - 通过
TableInputUtils.camelToUnderscore
方法将驼峰命名转换为下划线命名,以匹配数据库字段。 - 使用
TableResultUtils
工具类将数据库返回的Row
对象转换为前端所需的Kv
格式。
- 使用
日志记录:
- 使用 Slf4j 记录关键操作日志,便于调试和监控。
测试 Controller
以下是对 ApiTableController
各个接口的测试说明,包括请求 URL、请求方法、请求参数以及预期的响应结果。
测试 list
功能:查询表中的数据列表。
请求方式:GET
URL:
http://localhost:10051/table/system_users/list
请求参数:可选的查询参数,如
username=admin
等。响应示例:
{ "data": [ { "creator": "admin", "sex": 1, "deptId": "103", "mobile": "15612345678", "loginDate": "2023-11-30 09:16:00", "remark": "管理员", "updateTime": "2024-03-23 08:49:55", "avatar": "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png", "updater": null, "password": "$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm", "deleted": false, "createTime": "2021-01-05 17:03:47", "postIds": "[1]", "loginIp": "127.0.0.1", "nickname": "乒乒", "tenantId": "1", "id": "1", "email": "aoteman@126.com", "username": "admin", "status": 0 } // 其他记录... ], "msg": null, "code": 1, "ok": true }
测试 create
功能:添加一条新数据。
请求方式:POST
URL:
http://localhost:10051/table/system_users/create
请求体:
{ "username": "test01", "nickname": "Test01", "creator": "admin", "sex": 1, "deptId": "103", "mobile": "15612345678", "loginDate": "2023-11-30 09:16:00", "remark": "管理员", "avatar": "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png", "updater": null, "password": "$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm", "deleted": false, "postIds": "[1]", "loginIp": "127.0.0.1", "tenantId": "1", "email": "aoteman@126.com", "status": 0 }
响应示例:
{ "data": { "id": "361177594135064576" }, "code": 1, "msg": null, "ok": true }
数据库中的数据:
id username password nickname remark dept_id post_ids email mobile sex avatar status login_ip login_date creator create_time updater update_time deleted tenant_id 361177594135064576 test01 $2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm Test01 管理员 103 [1] aoteman@126.com 15612345678 1 http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png 0 127.0.0.1 2023-11-30 09:16:00 admin 2024-03-23 09:51:11 2024-03-23 09:51:11 0 1
测试 listAll
功能:查询表中的所有数据。
请求方式:GET
URL:
http://localhost:10051/table/system_users/listAll
响应示例:
{ "data": [ { "creator": "admin", "sex": 1, "deptId": "103", "mobile": "15612345678", "loginDate": "2023-11-30 09:16:00", "remark": "管理员", "updateTime": "2024-03-23 09:51:11", "avatar": "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png", "updater": "", "password": "$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm", "deleted": false, "createTime": "2024-03-23 09:51:11", "postIds": "[1]", "loginIp": "127.0.0.1", "nickname": "Test01", "tenantId": "1", "id": "361177594135064576", "email": "aoteman@126.com", "username": "test01", "status": 0 } // 其他记录... ], "code": 1, "msg": null, "ok": true }
测试 page
功能:分页查询数据。
请求方式:GET
URL:
http://localhost:10051/table/system_users/page
请求参数:可选的分页参数,如
current=1&pageSize=10
。响应示例:
{ "data": { "total": 17, "list": [ { "creator": "admin", "sex": 1, "deptId": "103", "mobile": "15612345678", "loginDate": "2023-11-30 09:16:00", "remark": "管理员", "updateTime": "2024-03-23 08:49:55", "avatar": "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png", "updater": null, "password": "$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm", "deleted": false, "createTime": "2021-01-05 17:03:47", "postIds": "[1]", "loginIp": "127.0.0.1", "nickname": "乒乒", "tenantId": "1", "id": "1", "email": "aoteman@126.com", "username": "admin", "status": 0 } // 其他记录... ] }, "code": 1, "msg": null, "ok": true }
测试 get
功能:根据条件获取单条数据。
请求方式:GET
URL:
http://localhost:10051/table/system_users/get?username=admin
响应示例:
{ "data": { "creator": "admin", "sex": 1, "deptId": "103", "mobile": "15612345678", "loginDate": "2023-11-30 09:16:00", "remark": "管理员", "updateTime": "2024-03-23 08:49:55", "avatar": "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png", "updater": null, "password": "$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm", "deleted": false, "createTime": "2021-01-05 17:03:47", "postIds": "[1]", "loginIp": "127.0.0.1", "nickname": "乒乒", "tenantId": "1", "id": "1", "email": "aoteman@126.com", "username": "admin", "status": 0 }, "code": 1, "msg": null, "ok": true }
测试 update
功能:根据 ID 更新数据。
请求方式:POST
URL:
http://localhost:10051/table/system_users/update
请求参数:通过路径或请求体传递参数。推荐使用请求体传递更新数据,并确保
id
存在于请求体中。请求体:
{ "id": "1", "nickname": "乒乒科技" }
响应示例:
{ "data": null, "code": 1, "msg": "更新成功", "ok": true }
测试 batchUpdate
功能:批量更新多条记录。
请求方式:POST
URL:
http://localhost:10051/table/system_users/batchUpdate
请求体:
{ "ids": ["1", "2", "3"], "nickname": "批量更新昵称", "status": 1 }
响应示例:
{ "data": null, "code": 1, "msg": "批量更新成功", "ok": true }
测试 remove
功能:逻辑删除数据。
请求方式:DELETE
URL:
http://localhost:10051/table/system_users/remove/1
响应示例:
{ "data": null, "code": 1, "msg": "逻辑删除成功", "ok": true }
测试 delete
功能:物理删除数据。
请求方式:DELETE
URL:
http://localhost:10051/table/system_users/delete/1
响应示例:
{ "data": null, "code": 1, "msg": "删除成功", "ok": true }
测试 total
功能:获取指定表的记录总数。
请求方式:GET
URL:
http://localhost:10051/table/system_users/total
响应示例:
{ "data": 17, "code": 1, "msg": null, "ok": true }
测试 export-excel
- 功能:导出当前查询的数据为 Excel 文件,不包含已删除的数据。
- 请求方式:GET
- URL:
http://localhost:10051/table/system_users/export-excel
- 响应:下载
.xlsx
文件。
测试 export-table-excel
- 功能:导出符合条件的所有数据为 Excel 文件,包括逻辑删除的数据。
- 请求方式:GET
- URL:
http://localhost:10051/table/system_users/export-table-excel
- 响应:下载
.xlsx
文件。
测试 export-all-table-excel
- 功能:导出数据库中所有表的数据为一个 Excel 文件,慎用。
- 请求方式:GET
- URL:
http://localhost:10051/table/export-all-table-excel
- 响应:下载
.xlsx
文件。
测试 pageDeleted
功能:分页查询已逻辑删除的数据。
请求方式:GET
URL:
http://localhost:10051/table/system_users/pageDeleted
响应示例:
{ "data": { "total": 1, "list": [ { "creator": "admin", "sex": 1, "deptId": "103", "mobile": "15612345678", "loginDate": "2023-11-30 09:16:00", "remark": "管理员", "updateTime": "2024-03-23 10:13:25", "avatar": "http://127.0.0.1:48080/admin-api/infra/file/4/get/37e56010ecbee472cdd821ac4b608e151e62a74d9633f15d085aee026eedeb60.png", "updater": null, "password": "$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm", "deleted": true, "createTime": "2021-01-05 17:03:47", "postIds": "[1]", "loginIp": "127.0.0.1", "nickname": "乒乒科技", "tenantId": "1", "id": "1", "email": "aoteman@126.com", "username": "admin", "status": 0 } ] }, "msg": null, "code": 1, "ok": true }
测试 recover
功能:恢复已逻辑删除的数据。
请求方式:POST
URL:
http://localhost:10051/table/system_users/recover
请求体:
{ "id": "1" }
响应示例:
{ "data": null, "msg": "恢复成功", "code": 1, "ok": true }
测试 names
功能:获取数据库中所有表名。
请求方式:GET
URL:
http://localhost:10051/table/names
响应示例:
{ "data": [ "system_users" // 其他表名... ], "code": 1, "msg": null, "ok": true }
测试 config
功能:获取指定表的配置,用于前端展示等。
请求方式:GET
URL:
http://localhost:10051/table/system_users/config?lang=en
请求参数:
lang=en
(可选,用于指定语言)
响应示例:
{ "code": 1, "data": { // 配置信息 }, "msg": null, "ok": true }
测试 columns
功能:获取指定表的字段信息。
请求方式:GET
URL:
http://localhost:10051/table/system_users/columns
响应示例:
{ "code": 1, "data": [ { "title": "Id", "dataIndex": "id", "valueType": "text" }, { "title": "Username", "dataIndex": "username", "valueType": "text" }, { "title": "Password", "dataIndex": "password", "valueType": "text" } // 其他字段信息... ], "ok": true }
原理详细解析
ApiTable 是一个基于 tio-boot 框架的通用数据表操作工具,旨在简化与数据库表的交互。通过统一的接口和工具类,开发者可以快速实现对数据库表的增删改查操作,而无需编写大量重复的代码。
核心组件
ApiTable:
- 提供了一系列静态方法,如
list
、saveOrUpdate
、delById
等,封装了常见的数据库操作。 - 支持动态表名和动态查询条件,使其具有很高的通用性。
- 提供了一系列静态方法,如
TableInput 和 TableResult:
- TableInput:用于封装查询条件和更新数据,支持灵活的数据类型转换。
- TableResult:用于封装数据库操作的结果,包括数据、状态码和消息。
TableInputUtils 和 TableResultUtils:
- TableInputUtils:提供了数据转换工具,如将驼峰命名转换为下划线命名,以匹配数据库字段。
- TableResultUtils:提供了结果转换工具,如将
Row
转换为Kv
格式,便于前端处理。
EasyExcelResponseUtils:
- 封装了导出 Excel 文件的逻辑,使用 EasyExcel 库生成和响应 Excel 文件。
数据流转
接收请求:
- 前端通过 HTTP 请求发送数据到相应的接口(如
/create
、/list
)。 - 控制器方法通过
TioRequestParamUtils.getRequestMap
获取请求参数。
- 前端通过 HTTP 请求发送数据到相应的接口(如
数据处理:
- 使用
ApiTable.transformType
方法将请求参数的类型转换为数据库所需的类型。 - 使用
TableInputUtils.camelToUnderscore
将驼峰命名的参数转换为下划线命名,以匹配数据库字段。
- 使用
调用 ApiTable 方法:
- 根据不同的操作类型(增、删、改、查),调用对应的
ApiTable
方法执行数据库操作。 ApiTable
方法返回TableResult
对象,包含操作结果和状态信息。
- 根据不同的操作类型(增、删、改、查),调用对应的
响应结果:
- 使用
RespBodyVo
封装响应结果,包括数据、状态码和消息。 - 对于导出操作,使用
EasyExcelResponseUtils
生成并返回 Excel 文件。
- 使用
日志与异常处理
- 使用 Slf4j 记录关键操作日志,有助于调试和监控。
- 对于导出操作,控制器方法可能会抛出
IOException
或SQLException
,需要进行适当的异常处理。
总结
通过 tio-boot 与 ApiTable 的整合,实现了一个高效、灵活的增删改查解决方案。该方案利用统一的接口和工具类,简化了数据库操作流程,提高了开发效率。同时,通过提供导出 Excel 和获取表信息等辅助功能,进一步增强了系统的实用性。
在实际项目中,开发者可以根据业务需求,扩展和定制 ApiTableController,以适应不同的数据表和操作场景。此外,合理配置数据库连接池和优化查询条件,可以进一步提升系统的性能和稳定性。