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.gohdatasvs.Options.Objects的表,则HAS会通过gorm的AutoMigrateopen in new window进行建表
如果你想让数据库表名为单数,可以修改配置文件DatabasePlugin.ConnectionsSingularTabletrue

使用

创建

通过api.json定义的接口 我们使用POST访问http://127.0.0.1:8989/v1/createData
body如下(注意application/json)

以上Request Body
keyMyTesthdatasvs.DataObjecttag中的data的key
objects为创建所需的参数

而在以上Reponse Bodydata为创建成功后返回的id

由于我们在Test结构体的Name字段的tag中加入了data:"require:create",这意味着在创建时,这个字段是必传的,所以当我们按照ErrorRequest中的传参进行创建时,得到了ErrorResponse的报错

查询

使用POST访问http://127.0.0.1:8989/v1/queryData

以上Request Body中
key: MyTesthdatasvs.DataObjecttag中的data的key
filter: 筛选条件,在后续将详细说明
dims: 返回结果展现字段
paging: 页数和展现条数,index=0为第几页,index=1为展示多少条

以上Response Body中
total: 共有几条数据 records: 搜索结果

更新

使用POST访问http://127.0.0.1:8989/v1/updateData

以上Request Body中
key: MyTesthdatasvs.DataObjecttag中的data的key
filter: 筛选条件
value: 需要更新的值

删除

使用POST访问http://127.0.0.1:8989/v1/deleteData

Request Body
key: MyTesthdatasvs.DataObjecttag中的data的key
objects: 删除的筛选条件

自动分表

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}}"
    }
}

Postman Request

查询插入结果

{
    "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的数据,请求体如下

ortrue时,filters内的关系为关系(根据QueryRequest的结构体可以知道filters其实就是filter的一个数组)
orfalse时(默认为false),filters内关系为关系
通过使用orfilters的组合,可以实现部分复杂的查询条件

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.DataObjectstructtag,可以对增删改查进行约束

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主键,此主键并不影响gormopen in new window的主键定义,主要用于删除时使用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也可以是stringdata:"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 JOINopen in new windowhdatasvs.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"`