
概述
Dataway 是基于 DataQL(Data Query Language)服务聚合能力,为应用提供的一个接口配置工具。使得使用者无需开发任何代码就配置一个满足需求的接口。 整个接口配置、测试、冒烟、发布,一站式都通过 Dataway 提供的 UI 界面完成。这种内嵌集成方式模式的优点是,可以使得大部分老项目都可以在无侵入的情况下直接应用 Dataway。进而改进老项目的迭代效率,大大减少企业项目研发成本。
通过 Dataway 接口配置界面,使用自己的 DataQL 查询语言,可视化的接口配置,避免了从数据存取到前端接口之间的一系列开发任务(例如:Mapper、BO、VO、DO、DAO、Service、Controller),提高后端接口的开发效率。
适用场景
取数据
- 在一些 报表、看板 纯展示类的项目中。我们做到了所有接口真正的 零 开发全配置。所有取数逻辑全部通过 DataQL + SQL 的方式满足。 在此期间遇到最大的挑战是复杂查询中需要 拼SQL,随着 DataQL 查询组件的完善,这一问题被攻克。
- 对比往期项目对于后端技术人员的需求从 3~5 人的苦逼通宵加班,直接缩减为 1 人配置化搞定 。即便是第二天要上线新的逻辑,通过 DataQL + SQL。依然可以分分钟满足需求变更。
存数据
- 在内部某个类 ERP 项目中,20多个表单页面。每个表单页面或多或少都有直接将单据数据录入到数据库的场景,每个单据的录入逻辑都有很大的不同。 我们通过 DataQL + SQL 的方式在早期用了1000 行左右的核心代码。其它数据存取逻辑全部配置化完成。
- 如今随着 DataQL 工具链的完善,其中绝大部分场景可以完全配置化无需开发了。
数据聚合
- 和 GraphQL 相同,这是设计 DataQL 的初衷。将数据库和服务等多个结果进行汇聚然后返回给前端,这是 DataQL 的使命。 Dataway 是这一过程变得更加简单和高效。
集成
Dataway 是 Hasor 生态中的一员,因此在 Spring 中使用 Dataway 首先要关联两个生态。
1. 添加依赖
在POM文件中增加依赖:
<!-- Dataway -->
<dependency>
<groupId>net.hasor</groupId>
<artifactId>hasor-spring</artifactId>
<version>4.1.7</version>
</dependency>
<dependency>
<groupId>net.hasor</groupId>
<artifactId>hasor-dataway</artifactId>
<version>4.1.7</version>
</dependency>
2. 导入数据表
导入Dataway必须的配置表到数据库中,在 net.hasor:hasor-dataway
jar包中可以找到sql文件,对应路径:
hasor-dataway-4.1.7.jar > META-INF > hasor-framework > mysql
- interface_info.sql - Dataway 中的API配置表
- interface_release.sql - Dataway API 发布历史记录表
3. 配置 Dataway
3.1 配置数据源
添加 DatawayConfig.java 配置类:
import net.hasor.core.ApiBinder;
import net.hasor.core.DimModule;
import net.hasor.db.JdbcModule;
import net.hasor.db.Level;
import net.hasor.spring.SpringModule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
@DimModule
@Component
public class DatawayConfig implements SpringModule {
// 注入现有数据源
@Autowired
private DataSource dataSource;
@Override
public void loadModule(ApiBinder apiBinder) throws Throwable {
// 设置数据源
apiBinder.installModule(new JdbcModule(Level.Full, this.dataSource));
}
}
3.2 配置参数
在 application.yml 中进行配置:
# Dataway配置
启用 Dataway 功能(默认不启用)
HASOR_DATAQL_DATAWAY: true
# 开启 ui 管理功能(注意生产环境必须要设置为 false,否则会造成严重的生产安全事故)
HASOR_DATAQL_DATAWAY_ADMIN: true
# Api工作路径,注意不要与已有的接口地址冲突
HASOR_DATAQL_DATAWAY_API_URL: /dataway/api/
# Api可视化ui的工作路径,只有开启 ui 管理功能后才有效
HASOR_DATAQL_DATAWAY_UI_URL: /interface-ui/
# SQL执行器方言设置(可选,建议设置)
HASOR_DATAQL_FX_PAGE_DIALECT: mysql
3.3 启动 Dataway
@EnableHasor() // 在Spring 中启用 Hasor
@EnableHasorWeb() // 将 hasor-web 配置到 Spring 环境中,Dataway 的 UI 是通过hasor-web 提供服务
4. 自定义返回结构
Dataway 默认接口结构:
{
"success": true,
"message": "OK",
"code": 0,
"lifeCycleTime": 22,
"executionTime": 21,
"value": ...
}
这可能不适用我们当前接口返回的参数结构,可以使用 Dataway 提供的ResultProcess 拦截器自定义返回数据的结构:
{
"success": true,
"errorCode": "string",
"errorDesc": "string",
"data": ...
}
4.1 创建处理器
创建 UnifyResultPayloadChainSpi 类:
import net.hasor.dataway.spi.ApiInfo;
import net.hasor.dataway.spi.ResultProcessChainSpi;
import java.util.HashMap;
/**
* Dataway接口统一返回结构
*/
public class UnifyResultPayloadChainSpi implements ResultProcessChainSpi {
public Object callAfter(boolean formPre, ApiInfo apiInfo, Object result) {
if (formPre) {
return result; // 如果前置拦截器处理了。那么后置拦截器就不处理。
}
return new HashMap<String, Object>() {{
put("success", true);
put("data", result);
}};
}
public Object callError(boolean formPre, ApiInfo apiInfo, Throwable e) {
return new HashMap<String, Object>() {{
put("success", false);
put("errorDesc", e.getMessage());
}};
}
}
4.2 注册处理器
在 DatawayConfig 类里面,将上面创建的处理器注册到 Dataway 中:
@Override
public void loadModule(ApiBinder apiBinder) throws Throwable {
// ... 其他代码
// 注册拦截器:自定义接口统一响应结构体
apiBinder.bindSpiListener(ResultProcessChainSpi.class, new
UnifyResultPayloadChainSpi());
}
5. 运行项目
访问接口配置页面:http://localhost:8080/tyrion/interface-ui
地址中的 /interface-ui
对应在 application.yml 文件中的HASOR_DATAQL_DATAWAY_UI_URL配置项
6. 创建接口
Dataway 工具化的提供 DataQL 配置能力,所以下面的接口查询代码会使用DataQL的语法结构,这里不对DataQL作阐述,详见:https://www.hasor.net/web/dataway/about.html
下面以创建一个分页查询的接口为例子:
①. 进入接口配置页面
- 接口地址
GET /dataway/api/v1/news/list
- 请求参数
{
"pageNo": 0,
"pageSize": 5,
"title": ""
}
- 查询代码
// SQL 执行器切换为分页模式[hint语句必须放在最前面]
hint FRAGMENT_SQL_QUERY_BY_PAGE = true
// sql查询
var querySQL = @@sql(title)<%
SELECT * FROM news_info WHERE title LIKE CONCAT('%', #{title}, '%') ORDER BY weight DESC
%>
// 创建分页查询对象
var pageQuery = querySQL(${title})
// 设置分页信息
run pageQuery.setPageInfo({
"pageSize" : ${pageSize}, // 页大小
"currentPage" : ${pageNo} // 页码,从0开始
})
// 取出分页信息
var pageInfo = pageQuery.pageInfo()
// 查询出的数据,抽取只需要的字段返回给接口调用者
var data = pageQuery.data() => [{
'id', 'title',
'link',
'weight',
'category'
}]
// 组装返回数据
return {
"pageNo": pageInfo.currentPage,
"pageSize": pageInfo.pageSize,
"total": pageInfo.totalCount,
"pages": pageInfo.totalPage,
"records": data
}
最后点击界面上的保存按钮,即可将接口保存到数据库中。
通过上面的步骤,即完成了一个分页查询的接口,没有去写Mapper、BO、VO、DO、DAO、Service、Controller 等J ava 类,大大提升了接口的开发效率。
最后
优点
- 在线配置接口,无需编写 java 代码
- 明显提升接口开发效率,无需编写 Mapper、BO、VO、DO、DAO、Service、Controller 等 Java 类
- 数据聚合方便
待解决问题
- 参数校验
- 鉴权 - 官方有提供拦截器,但是似乎不好与 shiro 进行整合
- 对其他数据库的支持不够友好,例如:Redis、MongoDB(需要自己实现)
链接
官网:https://www.hasor.net/web/dataway/index.html
知乎专栏:https://zhuanlan.zhihu.com/hasor