hdatasvs
hdatasvs可以完成快速的增删改查操作,hdatasvs需要DatabasePlugin
基础
注册服务
main.go
package main
import (
"HASDemo/models/objs"
"HASDemo/models/services/testsvs"
"github.com/drharryhe/has/connectors/hwebconnector"
"github.com/drharryhe/has/core"
"github.com/drharryhe/has/datapackers/hjsonpacker"
"github.com/drharryhe/has/plugins/hdatabaseplugin"
"github.com/drharryhe/has/routers/hlocalrouter"
+++ "github.com/drharryhe/has/services/hdatasvs"
"github.com/drharryhe/has/services/hellosvs"
)
func main() {
// 创建gateway
gateway := core.NewAPIGateway(&core.APIGatewayOptions{
// Server配置
ServerOptions: core.ServerOptions{
// 路由配置
Router: hlocalrouter.New(),
Plugins: []core.IPlugin{
hdatabaseplugin.New(),
},
},
Connectors: []core.IAPIConnector{
hwebconnector.New(), // Web服务
},
Packers: []core.IAPIDataPacker{
hjsonpacker.New(),
},
})
// 注册服务
gateway.Server().RegisterService(&hellosvs.Service{}, nil)
gateway.Server().RegisterService(&testsvs.Service{}, nil)
+++ gateway.Server().RegisterService(&hdatasvs.Service{}, &hdatasvs.Options{
+++ Hooks: nil,
+++ Views: nil,
+++ Objects: objs.Objects(),
+++ FieldFuncMap: nil,
+++ })
// 启动服务
gateway.Start()
}
数据库结构体
service/objs/mysqlobjects.go
package objs
import (
"github.com/drharryhe/has/common/htypes"
"github.com/drharryhe/has/services/hdatasvs"
)
type MyTest struct {
hdatasvs.DataObject `data:"db:mysql;key:test"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary"`
Name string `json:"name" gorm:"size:50" data:"require:create"`
}
func Objects() []htypes.Any {
return []htypes.Any{
MyTest{},
}
}
配置文件
conf.toml
[DataService]
LimitedSlots = ''
Name = 'data'
api.json文件
api.json
{
"name": "HAS api",
"versions": [
{
"version": "v1",
"apis": [
...
{
"name": "queryData",
"desc": "数据查询",
"disabled": false,
"endpoint": {
"service": "data",
"slot": "Query"
}
},
{
"name": "createData",
"desc": "保存数据",
"disabled": false,
"endpoint": {
"service": "data",
"slot": "Create"
}
},
{
"name": "updateData",
"desc": "更新数据",
"disabled": false,
"endpoint": {
"service": "data",
"slot": "Update"
}
},
{
"name": "deleteData",
"desc": "更新数据",
"disabled": false,
"endpoint": {
"service": "data",
"slot": "Delete"
}
}
...
]
}
]
}
完成以上文件修改后,重启项目
相关信息
如果不存在数据库名为配置文件中DatabasePlugin的Name的数据库,HAS会自动创建创建一个
如果不存在main.go中hdatasvs.Options.Objects的表,则HAS会通过gorm的AutoMigrate进行建表
如果你想让数据库表名为单数,可以修改配置文件DatabasePlugin.Connections的SingularTable为true
使用
创建
通过api.json定义的接口 我们使用POST访问http://127.0.0.1:8989/v1/createData
body如下(注意application/json)
以上Request Body中key为MyTest中hdatasvs.DataObjecttag中的data的keyobjects为创建所需的参数
而在以上Reponse Body中 data为创建成功后返回的id
由于我们在Test结构体的Name字段的tag中加入了data:"require:create",这意味着在创建时,这个字段是必传的,所以当我们按照ErrorRequest中的传参进行创建时,得到了ErrorResponse的报错
查询
使用POST访问http://127.0.0.1:8989/v1/queryData
以上Request Body中key: MyTest中hdatasvs.DataObjecttag中的data的keyfilter: 筛选条件,在后续将详细说明dims: 返回结果展现字段paging: 页数和展现条数,index=0为第几页,index=1为展示多少条
以上Response Body中total: 共有几条数据 records: 搜索结果
更新
使用POST访问http://127.0.0.1:8989/v1/updateData
以上Request Body中key: MyTest中hdatasvs.DataObjecttag中的data的keyfilter: 筛选条件value: 需要更新的值
删除
使用POST访问http://127.0.0.1:8989/v1/deleteData
Request Bodykey: MyTest中hdatasvs.DataObjecttag中的data的keyobjects: 删除的筛选条件
自动分表
datasvs可以通过一些简单的配置便实现自动分表功能
结构体
// MySubTable 自动分表
type MySubTable struct {
hdatasvs.DataObject `data:"db:mysql;key:my_sub_table"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary;deny:create"`
UserID uint64 `json:"user_id" data:"key:user_id;tabNaming"` // 使用tabNaming来分表
}
func Objects() []htypes.Any {
return []htypes.Any{
MyTest{}, MyInfo{}, MySubTable{}, // 记得添加到Objects
}
}
以上便可以完成一个自动分表的功能,非常简单 之后的创建、查询、更新、删除都与正常的增删改查差不多,不同的是,在查询、更新、删除的操作时,需要添加tabNaming对应的字段作为筛选条件,如下
创建
URL: {{api}}/v1/createData
查询
URL: {{api}}/v1/queryData
更新
URL: {{api}}/v1/updateData
删除
URL: {{api}}/v1/deleteData
filter
filter用于查询筛选条件,目前的filter已经可以覆盖大多数使用场景
举例用结构体:
type MyTest struct {
hdatasvs.DataObject `data:"db:mysql;key:test"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary;deny:create"`
Name string `json:"name" gorm:"size:50" data:"require:create"`
Email string `json:"email"`
Phone string `json:"phone"`
Addr string `json:"addr"`
}
使用Postman,快速创建一些随机的数据
RequestBody:
{
"key": "test",
"object": {
"name": "{{$randomUserName}}",
"email": "{{$randomEmail}}",
"phone": "{{$randomPhoneNumber}}",
"addr": "{{$randomStreetAddress}}"
}
}

查询插入结果
{
"data": {
"total": 15,
"records": [
{
"id": 1,
"name": "Sheridan54",
"email": "Hertha_Bauch55@hotmail.com",
"phone": "745-403-3238",
"addr": "64344 Moore Oval"
},
{
"id": 2,
"name": "Deven90",
"email": "Tess.Williamson@yahoo.com",
"phone": "490-391-5684",
"addr": "45968 Donnelly Row"
},
{
"id": 3,
"name": "Delphine_Kreiger",
"email": "Dion83@gmail.com",
"phone": "272-386-2509",
"addr": "8557 Leffler Club"
},
{
"id": 4,
"name": "Claire64",
"email": "Leanne.McGlynn@yahoo.com",
"phone": "329-763-3795",
"addr": "0886 Sanford Trail"
},
{
"id": 5,
"name": "Ottilie_Romaguera77",
"email": "Rudy.Hintz@yahoo.com",
"phone": "686-974-3857",
"addr": "3764 Keeley Hollow"
},
{
"id": 6,
"name": "Nayeli67",
"email": "Camila_Mann@gmail.com",
"phone": "338-512-6097",
"addr": "348 Gerhold Fields"
},
{
"id": 7,
"name": "Wiley_Schroeder64",
"email": "Davon_Hilll@gmail.com",
"phone": "637-857-6181",
"addr": "9220 Georgiana Wells"
},
{
"id": 8,
"name": "Cristobal65",
"email": "Violet_Lehner@gmail.com",
"phone": "371-271-3972",
"addr": "448 Letha Roads"
},
{
"id": 9,
"name": "Eugene.Bruen48",
"email": "Chloe.Wilderman41@gmail.com",
"phone": "956-534-5654",
"addr": "35494 Braun Light"
},
{
"id": 10,
"name": "Cassidy78",
"email": "Ofelia.Bernier31@hotmail.com",
"phone": "241-672-0111",
"addr": "17718 Noemy Brooks"
},
{
"addr": "84200 Santa Plains",
"id": 11,
"name": "Chauncey_Harris38",
"email": "Jamel_Fay@hotmail.com",
"phone": "705-792-6278"
},
{
"addr": "4017 Reese Road",
"id": 12,
"name": "Trent_Kunde34",
"email": "Bernardo17@hotmail.com",
"phone": "619-853-1497"
},
{
"id": 13,
"name": "Judy.Hirthe46",
"email": "Ursula.Hand@gmail.com",
"phone": "368-547-0844",
"addr": "13090 Johnson Lakes"
},
{
"addr": "763 Madison Springs",
"id": 14,
"name": "Ruby_Hills79",
"email": "Angel_Blick@yahoo.com",
"phone": "836-458-7994"
},
{
"addr": "9247 Cristal Place",
"id": 15,
"name": "Ruthe.Marvin51",
"email": "Yadira4@yahoo.com",
"phone": "774-616-0595"
}
]
},
"error": {
"code": 0,
"desc": "",
"cause": ""
}
}
根据datasvs中对QueryRequest的定义
type QueryRequest struct {
core.SlotRequestBase
Key *string `json:"key" param:"require"`
Filter *rawFilter `json:"filter" param:"require"`
Dims *[]string `json:"dims" param:"require;type:StringArray"`
Ordering *[]string `json:"ordering" param:"type:StringArray"`
Paging *[]int `json:"paging" param:"type:NumberRange"`
Records []htypes.Any `param:"-"`
}
type rawFilter struct {
Or bool `json:"or"`
Conditions []string `json:"conditions"`
Filters []rawFilter `json:"filters"`
}
conditions
conditions 用于筛选条件的确认 如要查询id为3的数据,那么请求体应为:
HAS支持以下筛选条件
| 条件 | 含义 |
|---|---|
| == | 等于 |
| >= | 大于等于 |
| <= | 小于等于 |
| < | 小于 |
| > | 大于 |
| != | 不等于 |
| !IN | 不在...内 |
| IN | 在...内 |
| !BETWEEN | 不在...之间 |
| BETWEEN | 在...之间 |
| !LIKE | 不类似于 |
| LIKE | 类似于 |
| HAS_PREFIX | 含有前缀 |
| !HAS_PREFIX | 不含有前缀 |
| HAS_SUFFIX | 含有后缀 |
| !HAS_SUFFIX | 不含有后缀 |
or
or字段用于多条件查询
如,我们需要查询id为1或者2的数据,请求体如下
当or为true时,filters内的关系为或关系(根据QueryRequest的结构体可以知道filters其实就是filter的一个数组)
当or为false时(默认为false),filters内关系为且关系
通过使用or和filters的组合,可以实现部分复杂的查询条件
ordering
ordering可以实现排序
如,用name来排序
view
view主要用于链表查询
有至少2个表 models/objs/mysqlobjects.go
type MyInfo struct {
hdatasvs.DataObject `data:"db:mysql;key:test"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary;deny:create"`
TestID uint64 `json:"test_id"`
Test2ID uint64 `json:"test2_id"`
User string `json:"user" gorm:"size:20" data:"require:create"`
Pwd string `json:"pwd" gorm:"size:50" data:"require:create"`
}
type MyTest struct {
hdatasvs.DataObject `data:"db:mysql;key:test"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary;deny:create"`
Name string `json:"name" gorm:"size:50" data:"require:create"`
Email string `json:"email"`
Phone string `json:"phone"`
Addr string `json:"addr"`
}
type MyTest2 struct {
hdatasvs.DataObject `data:"db:mysql;key:test2"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary;deny:create"`
Name string `json:"name"`
}
func Objects() []htypes.Any {
return []htypes.Any{
MyTest{}, MyInfo{}, MyTest2{}
}
}
创建后添加几个数据,便于演示 创建models/objs/views.go
package objs
import (
"github.com/drharryhe/has/common/htypes"
"github.com/drharryhe/has/services/hdatasvs"
)
type ViewTestInfo struct {
hdatasvs.DataView `data:"key:test_info;from:test;join:info@test.id=info.test_id"`
ID uint64 `json:"id" data:"key:id;field:test.id"`
User string `json:"user" data:"key:user;field:info.user"`
Name string `json:"name" data:"key:name;field:test.name"`
Phone string `json:"phone" data:"key:phone;field:test.phone"`
}
// 多表
type ViewAll struct {
hdatasvs.DataView `data:"key:all;from:info;join:test@test.id=info.test_id,test2@test2.id=info.test2_id"`
InfoID uint64 `json:"info_id" data:"key:info_id;field:info.id"`
User string `json:"user" data:"key:user;field:info.user"`
Name string `json:"name" data:"key:name;field:test2.name"`
Addr string `json:"addr" data:"key:addr;field:test.addr"`
}
func Views() []htypes.Any {
return []htypes.Any{
ViewTestInfo{}, ViewAll{},
}
}
修改main.go
gateway.Server().RegisterService(&hdatasvs.Service{}, &hdatasvs.Options{
Hooks: nil,
=== Views: objs.Views(),
Objects: objs.Objects(),
FieldFuncMap: nil,
})
在api.json添加view接口
{
"name": "HAS api",
"versions": [
{
"version": "v1",
"apis": [
...
{
"name": "view",
"desc": "数据查询",
"disabled": false,
"endpoint": {
"service": "data",
"slot": "View"
}
}
...
]
}
]
}
重启服务,访问view接口(此处我们只添加了2条info,因此只有前两条有info的数据)http://127.0.0.1:8989/v1/view
hdatasvs struct tag
通过继承了
hdatasvs.DataObject的struct的tag,可以对增删改查进行约束
data tag K-V对照表
注意
本章所有的K-V都是在data``tag下,使用如下:
type MyInfo struct {
hdatasvs.DataObject `data:"db:mysql;key:info"`
ID uint64 `json:"id" gorm:"primaryKey" data:"primary;deny:create"`
TestID uint64 `json:"test_id"`
User string `json:"user" gorm:"size:20" data:"require:create"`
Pwd string `json:"pwd" gorm:"size:50" data:"require:create"`
}
object
| Key | 说明 | 使用 | Value |
|---|---|---|---|
| primary | 主键,此主键并不影响gorm的主键定义,主要用于删除时使用 | data:"primary“ | - |
| deny | 忽略,如可以忽略create,则此字段不允许通过hdatasvs创建 | data:"deny:create" | create update query (可多选,逗号分隔,) |
| require | 必传,表示此字段在前端传参时不能为空 | data:"require:create" | create update delete query (可多选,逗号分隔,) |
| db | 数据库,hdatasvs可以支持多个数据库,因此需要db来确认结构体来自于哪一个数据库,此处填写的db来自于配置文件中DatabasePlugin填写的Key,需要写在hdatasvs.DataObject的tag里 | hdatasvs.DataObject `data:"db:mysql;key:info"` | 自定义 |
| key | 用于前端查询的key,可以有效的避免表名暴露,需要写在hdatasvs.DataObject的tag里 | hdatasvs.DataObject `data:"db:mysql;key:info"` | 自定义 |
| tabNaming | 作用于字段上,用于自动分表,需要注意只用于一个字段上 | UserID uint64 `json:"user_id" data:"tabNaming"` | - |
| autoInit | 自动初始化函数,目前的自动初始化函数只有一个$now,字段类型可以是*time.Time也可以是string | data:"autoInit:$now" | $now: 现在时间的字符串 |
| col | 对应表中的列名 | data:"col:user" | 自定义 |
view
- hdatasvs.DataView
| Key | 说明 | 使用 |
|---|---|---|
| key(在hdatasvs.DataView) | 前端查询用的key | hdatasvs.DataView `data:"key:test_info;from:test;join:info@test.id=info.test_id"` |
| key(在字段上) | 可理解为AS关键字,用于前端查询 | ID uint64 `json:"id" data:"key:id;field:test.id"` |
| from、join | 参考SQL中的LEFT JOIN | hdatasvs.DataView `data:"key:test_info;from:test;join:info@test.id=info.test_id"` |
| @ | 可理解为SQL中的ON关键字 | hdatasvs.DataView `data:"key:test_info;from:test;join:info@test.id=info.test_id"` |
| field | 字段对应的表字段 | ID uint64 `json:"id" data:"key:id;field:test.id"` |