假定有如下这样的表
DROP TABLE IF EXISTS `t_order`;
CREATE TABLE `t_order` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL COMMENT '订单号',
`order_status` int unsigned NOT NULL DEFAULT '0' COMMENT '订单状态(0.用户已创建待支付, 1.用户已支付待商户发货, 2.商户已发货待用户签收, 3.用户已签收待确认完结, 4.已完结)',
`amount` decimal(20,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '订单金额',
`desc` varchar(32) NOT NULL DEFAULT '' COMMENT '备注',
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
`deleted` int unsigned NOT NULL DEFAULT '0' COMMENT '0.未删除, 非 0.已删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`,`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单';
INSERT INTO `t_order` (`order_no`, `order_status`, `amount`, `desc`) VALUES
('1-2-221010-00001', 1, 9258.00, '请尽快发货'), ('1-2-221010-00002', 2, 6900.00, '');
DROP TABLE IF EXISTS `t_order_address`;
CREATE TABLE `t_order_address` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '订单号',
`contact` varchar(16) NOT NULL DEFAULT '' COMMENT '联系人',
`phone` varchar(16) NOT NULL DEFAULT '' COMMENT '联系电话',
`address` varchar(128) NOT NULL DEFAULT '' COMMENT '联系人地址',
`create_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`update_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
`deleted` bigint unsigned NOT NULL DEFAULT '0' COMMENT '0.未删除, 非 0.已删除',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_order_no` (`order_no`,`deleted`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单地址';
INSERT INTO `t_order_address` (`order_no`, `contact`, `phone`, `address`) VALUES
('1-2-221010-00001', '张三', '13012345678', '北京市朝阳区'), ('1-2-221010-00002', '李四', '13122223333', '广东省广州市白云区');
DROP TABLE IF EXISTS `t_order_item`;
CREATE TABLE `t_order_item` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '订单号',
`product_name` varchar(32) NOT NULL DEFAULT '' COMMENT '商品名',
`price` decimal(20,2) unsigned NOT NULL DEFAULT '0.00' COMMENT '商品价格',
`number` int unsigned NOT NULL DEFAULT '0' COMMENT '商品数量',
PRIMARY KEY (`id`),
KEY `idx_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单项(商品)';
INSERT INTO `t_order_item` (`order_no`, `product_name`, `price`, `number`) VALUES
('1-2-221010-00001', 'iPhone 14 Pro', 8999, 1), ('1-2-221010-00001', '女款外套', 129.5, 2),
('1-2-221010-00002', 'Samsung', 6999.00, 1);
DROP TABLE IF EXISTS `t_order_log`;
CREATE TABLE `t_order_log` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL DEFAULT '' COMMENT '订单号',
`operator` varchar(32) NOT NULL DEFAULT '' COMMENT '操作人',
`message` text COMMENT '操作内容',
`time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0.未删除, 1.已删除',
PRIMARY KEY (`id`),
KEY `idx_order_no` (`order_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='订单日志';
INSERT INTO `t_order_log` (`order_no`, `operator`, `message`, `time`) VALUES
('1-2-221010-00001', '张三', '创建订单', '2022-10-01 11:12:13'),
('1-2-221010-00001', '张三', '支付成功', '2022-10-01 12:13:14'),
('1-2-221010-00002', '李四', '创建订单', '2022-10-01 12:15:14'),
('1-2-221010-00002', '李四', '支付成功', '2022-10-01 12:16:14'),
('1-2-221010-00002', '李四', '已发货, 待用户收货', '2022-10-01 12:17:14');
配置如下
@Configuration
public class TableColumnConfig {
@Bean
public List<TableColumnRelation> tableRelationList() {
// 表与表之间的关联关系
return List.of(
// 订单 跟 订单地址 一对一, 用 订单号 字段关联
new TableColumnRelation("t_order", "order_no", TableRelationType.ONE_TO_ONE, "t_order_address", "order_no"),
// 订单 跟 订单项 一对多, 用 订单号 字段关联
new TableColumnRelation("t_order", "order_no", TableRelationType.ONE_TO_MANY, "t_order_item", "order_no"),
// 订单 跟 订单日志 一对多, 用 订单号 字段关联
new TableColumnRelation("t_order", "order_no", TableRelationType.ONE_TO_MANY, "t_order_log", "order_no")
);
}
// 查询别名
@Bean("queryAliasMap")
public Map<String, ReqAlias> queryAlias() {
return Map.of(
// 用这个查询别名时, 只有订单表(所有字段)
"all-order", new ReqAlias("Order"),
"order-address-item-log", orderAddressItemLogAlias()
);
}
private ReqAlias orderAddressItemLogAlias() {
// 指定 订单表 的字段, 指定 订单地址表 订单项表 订单日志表 的字段, 查询 Order 时 distinct, 并指定查询时多张表之间的关联方式
ReqResult result = new ReqResult(List.of(
"orderNo", "orderStatus", "amount", "desc", "createTime",
Map.of("address", Map.of("table", "OrderAddress", "columns", List.of("contact", "phone", "address"))),
Map.of("items", Map.of("table", "OrderItem", "columns", List.of("productName", "price", "number"))),
Map.of("logs", Map.of("table", "OrderLog", "columns", List.of("operator", "message", "time")))
), true);
List<List<String>> relationList = List.of(List.of("Order", "inner", "OrderAddress"), List.of("Order", "left", "OrderItem"));
return new ReqAlias("Order", result, relationList);
}
}
web 接口如下
@RestController
public class TableColumnController {
private final TableColumnTemplate tableColumnTemplate;
public TableColumnController(TableColumnTemplate tableColumnTemplate) {
this.tableColumnTemplate = tableColumnTemplate;
}
@GetMapping("/table-column")
public List<QueryInfo> info(String tables) {
return tableColumnTemplate.info(tables);
}
@PostMapping("/table-column")
public Object query(@RequestBody ReqInfo req) {
return tableColumnTemplate.dynamicQuery(req);
}
}
请求 GET /table-column
会返回表字段及表关联关系, 如果配置 query.has-not-return-info = true
则此接口返回空
下面是 POST /table-column
时请求参数以及最终执行的 sql, 最终会以请求参数中的 result 以及 返回结果 的示例
请求参数
{
"table" : "Order",
"param" : {
"page": [1,5]
}
}
执行 sql
SELECT COUNT(*) FROM t_order WHERE deleted = 0
SELECT .. FROM t_order WHERE deleted = 0 LIMIT 5
请求参数(默认情况下, param 必须要有 query 或 page 才能进行查询, 可以用 query.not-required-condition-or-page = true
去掉此限制
{
"table" : "Order",
"param" : {
"query": {
"conditions": [
[ "id", "gt", 0 ]
]
}
}
}
执行 sql
SELECT .. FROM t_order WHERE ( id > 0 ) AND deleted = 0
请求参数
{
"table": "Order",
"param": {
"query": {
"conditions": [
[ "id", "gt", 0 ]
]
}
},
"result": {
"columns": [
"orderNo", "orderStatus", "amount", "desc", "createTime",
{
"address": {
"table": "OrderAddress",
"columns": [ "contact", "address" ]
}
},
{
"items": {
"table": "OrderItem",
"columns": [ "productName", "price", "number" ]
}
},
{
"logs": {
"table": "OrderLog",
"columns": [ "operator", "message", "time" ]
}
}
]
}
}
会依次执行以下 sql 并将数据组装返回(关联表会批量查询避免 n+1, 数量过多会分批查询, 单次数量由 query.max-list-count
控制)
SELECT .. FROM t_order WHERE ( id > 0 ) AND deleted = 0
SELECT .. FROM t_order_address WHERE ( order_no IN ('1-2-221010-00001', '1-2-221010-00002') )
AND deleted = 0
SELECT .. FROM t_order_item WHERE order_no IN ('1-2-221010-00001', '1-2-221010-00002')
SELECT .. FROM t_order_log WHERE ( order_no IN ('1-2-221010-00001', '1-2-221010-00002') )
AND deleted = 0
请求参数
{
"table": "Order",
"param": {
"query": {
"conditions": [
[ "id", "gt", 0 ],
[ "OrderItem.productName", "ne", "" ],
[ "OrderAddress.contact", "ne", "" ]
]
},
"sort": { "createTime": "desc" },
"relation": [ [ "Order", "inner", "OrderItem" ], [ "Order", "inner", "OrderAddress" ] ],
"page": [ 1, 5 ]
},
"result": {
"columns": [
"orderNo", "orderStatus", "amount", "desc", "createTime",
{
"address": {
"table": "OrderAddress",
"columns": [ "contact", "address" ]
}
},
{
"items": {
"table": "OrderItem",
"columns": [ "productName", "price", "number" ]
}
},
{
"logs": {
"table": "OrderLog",
"columns": [ "operator", "message", "time" ]
}
}
],
"distinct": true
}
}
除 param
下的 query
sort
page
是动态外, 上面的 table
result
param.relation
部分可以由 queryAlias
去定义, 如上面定义的 order-address-item-log
别名.
下面的入参可以达到跟上面等同的查询效果, 这样一来, 后端只需要定义好 queryAlias
就可以了
{
"alias": "order-address-item-log",
"param": {
"query": {
"conditions": [
[ "id", "gt", 0 ],
[ "OrderItem.productName", "ne", "" ],
[ "OrderAddress.contact", "ne", "" ]
]
},
"sort": { "createTime": "desc" },
"page": [ 1, 5 ]
}
}
上面的查询入参会依次执行以下 sql 并将数据组装返回(关联表会批量查询避免 n+1, 数量过多会分批查询, 单次数量由 query.max-list-count
控制)
SELECT COUNT(DISTINCT `Order`.id)
FROM t_order AS `Order`
INNER JOIN t_order_item AS OrderItem ON OrderItem.order_no = `Order`.order_no
INNER JOIN t_order_address AS OrderAddress ON OrderAddress.order_no = `Order`.order_no
WHERE ( `Order`.id > 0 AND OrderItem.product_name <> '' AND OrderAddress.contact <> '' )
AND `Order`.deleted = 0 AND OrderAddress.deleted = 0
SELECT DISTINCT `Order`.order_no AS orderNo, ..
FROM t_order AS `Order`
INNER JOIN t_order_item AS OrderItem ON OrderItem.order_no = `Order`.order_no
INNER JOIN t_order_address AS OrderAddress ON OrderAddress.order_no = `Order`.order_no
WHERE ( `Order`.id > 0 AND OrderItem.product_name <> '' AND OrderAddress.contact <> '' )
AND `Order`.deleted = 0 AND OrderAddress.deleted = 0
ORDER BY `Order`.create_time DESC
LIMIT 5
SELECT .. FROM t_order_address
WHERE ( order_no IN ('1-2-221010-00001', '1-2-221010-00002') ) AND deleted = 0
SELECT .. FROM t_order_item WHERE order_no IN ('1-2-221010-00001', '1-2-221010-00002')
SELECT .. FROM t_order_log WHERE ( order_no IN ('1-2-221010-00001', '1-2-221010-00002') )
AND deleted = 0