sgo

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 5, 2022 License: Apache-2.0 Imports: 18 Imported by: 0

README

sgo

Go Report Card

version

go1.19

sgo 是参考 mybatis 编写的sql标签解析,sgo仅提供对 sql 的上下文数据解析填充,并不保证对 sql 语句的语法检查。

XML 解析规则

sgo 解析 xml 文件中的sql语句,会严格检查上下文中的数据类型,字符串类型参数会自定添加 '' 单引号,其他基础数据类型不会添加,对于复杂数据结构(复合结构,泛型结构体等)会持续跟进 ,目前仅支持基础数据类型。

上下文数据

上下文数据是由用户调用时候传递接,仅接受 map 或者结构体如下:

标签详情
标签 描述 功能
<mapper> 根节点
<insert> insert语句 生成插入语句
<select> select语句 生成查询语句
<update> update语句 生成更新语句
<delete> delete语句 生成删除语句
<for> for迭代 生成IN语句,指定需要生成IN条件的字段,可以生成对应的IN条件
<if> if条件 判断是否满足属性表达式的条件,满足条件就对标签内的sql进行解析

demo

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE mapper SYSTEM "https://gitee.com/aurora-engine/sgo/blob/master/sgo.dtd">

<mapper namespace="user">
    <select id="find">
        select * from student where sss={name}
        <if expr="{arr}!=nil and {len(arr)}>0">
            and
            <for slice="{arr}" item="obj" column="id" open="("  separator="," close=")" >
                {obj}
            </for>
        </if>

        <if expr="{name}=='aaa'" >
            and abc = 1
            <if expr="1==1">
                and 1=1
                <if expr="1!=1">
                    or 1!=1
                </if>
            </if>
            or cba=1
        </if>
        or  name = {name} and 1=1
    </select>
</mapper>
xml解析
第一层

<mapper>标签是整个xml的根 namespace 属性定义了 xml的标识符,调用阶段 namespace的属性至关重要

第二层

<select>标签定义了 id 属性, id 属性是唯一标识,结合 namespace 能够定位,标签内的所有 {xx} 数据都来自于上下文数据,{xx} 将被解析成为具体的值

第三层

<if> 标签 定义了 expr 属性, expr 属性的值为一串表达式,表达式应返回一个 true 或者 false,表示 <if> 标签内的内容是否可以被解析,表达式中使用到上下文数据可以通过点直接调用属性(注意属性名不要和关键字同名)

定义 Mapper

sgo 中的 mapper 定义是基于结构体 和匿名函数字段来实现的(匿名函数字段,需要遵循一些规则):

  • 上下文参数,只能是结构体,指针结构体或者map
  • 至少有一个返回值,一个返回值只能是 error

快速入门

创建 table

创建一张表,用于测试

## 用户设计
create table comm_user(
    user_id varchar(50) primary key         comment '主键',
    user_account varchar(50)                comment '账号',
    user_email varchar(50)                  comment '邮箱',
    user_password varchar(200)              comment '密码',
    user_name varchar(50) not null          comment '昵称',
    user_age int default 0                  comment '年龄',
    user_birthday datetime                  comment '生日',
    user_head_picture varchar(100)          comment '头像',
    user_create_time timestamp              comment '用户创建时间'
) comment '用户设计';
创建 映射模型

更具 数据库表,或者sql查询结果集 创建一个结构体用于接收查询数据

// UserModel 用户模型
type UserModel struct {
	UserId          string `column:"user_id"`
	UserAccount     string `column:"user_account"`
	UserEmail       string `column:"user_email"`
	UserPassword    string `column:"user_password"`
	UserName        string `column:"user_name"`
	UserAge         int    `column:"user_age"`
	UserBirthday    string `column:"user_birthday"`
	UserHeadPicture string `column:"user_head_picture"`
	UserCreateTime  string `column:"user_create_time"`
}
创建 Mapper

更具上述的规范,创建 Mapper

// UserMapper s
type UserMapper struct {
	FindUser   func(ctx any) (UserModel, error)
	UserSelect func(ctx any) (map[string]any, error)
}
创建 XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE mapper 
        SYSTEM "https://gitee.com/aurora-engine/sgo/blob/master/sgo.dtd">

<mapper namespace="UserMapper">
    <select id="FindUser">
        select * from comm_user where user_id={id}
    </select>
    <select id="UserSelect">
        select * from comm_user where user_id={id}
    </select>
</mapper>

创建的 xml 文件 <mapper> namespace 属性一定要和 Mapper 结构体的名称一样,区分大小写,<select> id 属性要和 Mapper 结构体 函数字段名称匹配。

创建并使用
package main

import (
	"fmt"
	"gitee.com/aurora-engine/sgo"
)
func main() {
	ctx := map[string]any{
		"id": "3de784d9a29243cdbe77334135b8a282",
	}
	open, err := sql.Open("mysql", "root:xxx@2022@tcp(82.xx.xx.xx:xx)/xx")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	build := sgo.New(open)
	build.Source("/")
	mapper := &UserMapper{}
	build.ScanMappers(mapper)
	user, err := mapper.FindUser(ctx)
	if err != nil {
		return
	}
	fmt.Println(user)
}

创建数据库表

创建一个学生表

create table student
(
    id          int         null,
    name        varchar(20) null,
    age         int         null,
    create_time datetime    null
);

创建映射对象

对应在go代码中创建表的对应映射结构, column tag 设置字段的一一对应关系

type Student struct {
	Id         string `column:"id"`
	Name       string `column:"name"`
	Age        int    `column:"age"`
	CreateTime string `column:"create_time"`
}

创建 Mapper 和 XML

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE mapper SYSTEM "http://sgo.com">
<mapper namespace="StudentMapper">
    
</mapper>

解析: http://sgo.com 文档约束是通过 编辑器设置的,项目文件夹下的 sgo.dtd 文件导入即可。
更具 mapper xml 文件定义的 命名空间定义一个结构体类型名称一致的 Mapper 结构体(和普通结构体没什么区别只是一个叫法)

type StudentMapper struct {
	
}

前置工作已经准备就绪,xml 和 mapper 结构体里面的内容会在下面的案例中一步一步的添加进去。

Insert

Insert 插入数据

对学生表进行新增数据,我们先从定义 mapper 函数开始。

type StudentMapper struct {
	InsertOne func(any) (int, error)
}

开始定义 xml 元素,insert 中的模板参数,均来自于 mapper 函数的上下文参数中

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE mapper SYSTEM "http://sgo.com">

<mapper namespace="StudentMapper">
    <insert id="InsertOne">
        insert into student(id,name,age,create_time) value({id},{name},{age},{time})
    </insert>
</mapper>

创建 sgo 并调用执行

package main

import (
	"database/sql"
	"fmt"
	"gitee.com/aurora-engine/sgo"
	_ "github.com/go-sql-driver/mysql"
	"time"
)
type Student struct {
	Id         string `column:"id"`
	Name       string `column:"name"`
	Age        int    `column:"age"`
	CreateTime string `column:"create_time"`
}

type StudentMapper struct {
	InsertOne func(any) (int64, error)
}

func main() {
	ctx := map[string]any{
		"id":   "1",
		"name": "test1",
		"age":  19,
		"time": time.Now().Format("2006-01-02 15:04:05"),
	}
	open, err := sql.Open("mysql", "xxxx")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	if err != nil {
		return
	}
	build := sgo.New(open)
	build.Source("/")
	mapper := &StudentMapper{}
	build.ScanMappers(mapper)
	count, err := mapper.InsertOne(ctx)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(count)
}
批量插入数据

我们现在继续向 Mapper 结构体中添加定义

type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)
}

我们添加了 InsertArr func(any) (int64, error) 字段定义,在xml里面我们编写对应的sql语句,<insert>.

<mapper namespace="StudentMapper">
    <insert id="InsertOne">
        insert into student(id,name,age,create_time) value({id},{name},{age},{time})
    </insert>

    <insert id="InsertArr">
        insert into student(id,name,age,create_time) values
        <for slice="{arr}" item="obj">
            ({obj.id},{obj.name},{obj.age},{obj.time})
        </for>
    </insert>

</mapper>

arr 是上下文中的属性,obj 是作为 for 标签内的上下文数据,for 内是无法使用全局赏析问数据的。编写代码执行批量插入。

package main

import (
	"database/sql"
	"fmt"
	"gitee.com/aurora-engine/sgo"
	_ "github.com/go-sql-driver/mysql"
	"time"
)
type Student struct {
	Id         string `column:"id"`
	Name       string `column:"name"`
	Age        int    `column:"age"`
	CreateTime string `column:"create_time"`
}

type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)
}

func main() {
	ctx := map[string]any{
		"arr": []map[string]any{
			{
				"id":   "1",
				"name": "test1",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
			{
				"id":   "2",
				"name": "test2",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
			{
				"id":   "3",
				"name": "test3",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
		},
	}
	open, err := sql.Open("mysql", "xxxxx")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	build := sgo.New(open)
	build.Source("/")
	mapper := &StudentMapper{}
	build.ScanMappers(mapper)
	count, err := mapper.InsertArr(ctx)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(count)
}

::: tip Insert,Update,Delete,定义的返回值只能返回数据库处理记录,第一个参数返回类型不正确将会返回错误信息,Insert 相对特殊,第二个参数可以返回,自增长id。 :::

Select

查询一条记录

添加 查询定义如下:

type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)

	SelectById func(any) (Student, error)
}
<select id="SelectById">
        select * from student where id={id}
</select>
package main

import (
	"database/sql"
	"fmt"
	"gitee.com/aurora-engine/sgo"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

type Student struct {
	Id         string `column:"id"`
	Name       string `column:"name"`
	Age        int    `column:"age"`
	CreateTime string `column:"create_time"`
}

type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)

	SelectById  func(any) (Student, error)
}

func main() {
	ctx := map[string]any{
		"arr": []map[string]any{
			{
				"id":   "1",
				"name": "test1",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
			{
				"id":   "2",
				"name": "test2",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
			{
				"id":   "3",
				"name": "test3",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
		},
		"id":  "1",
		"ids": []string{"1", "2"},
	}
	open, err := sql.Open("mysql", "xxxx")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	build := sgo.New(open)
	build.Source("/")
	mapper := &StudentMapper{}
	build.ScanMappers(mapper)
	stu, err := mapper.SelectById(ctx)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(stu)
}

查询多条数据
type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)

	SelectById func(any) (Student, error)
	SelectAll   func() ([]Student, error)
}
<select id="SelectAll">
    select * from student
</select>
package main

import (
	"database/sql"
	"fmt"
	"gitee.com/aurora-engine/sgo"
	_ "github.com/go-sql-driver/mysql"
	"time"
)
type Student struct {
	Id         string `column:"id"`
	Name       string `column:"name"`
	Age        int    `column:"age"`
	CreateTime string `column:"create_time"`
}

type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)

	SelectById  func(any) (Student, error)
	SelectAll   func() ([]Student, error)
}

func main() {
	open, err := sql.Open("mysql", "xxxx")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	build := sgo.New(open)
	build.Source("/")
	mapper := &StudentMapper{}
	build.ScanMappers(mapper)
	stu, err := mapper.SelectAll()
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(stu)
}

批量查询
type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)

	SelectById func(any) (Student, error)
	SelectAll   func() ([]Student, error)
	SelectByIds func(any) ([]Student, error)
}
<select id="SelectByIds">
    select * from student where id in
    <for slice="{ids}" item="id" open="(" separator="," close=")">
        {id}
    </for>
</select>
package main

import (
	"database/sql"
	"fmt"
	"gitee.com/aurora-engine/sgo"
	_ "github.com/go-sql-driver/mysql"
	"time"
)
type Student struct {
	Id         string `column:"id"`
	Name       string `column:"name"`
	Age        int    `column:"age"`
	CreateTime string `column:"create_time"`
}

type StudentMapper struct {
	InsertOne func(any) (int64, error)
	InsertArr func(any) (int64, error)

	SelectById  func(any) (Student, error)
	SelectAll   func() ([]Student, error)
	SelectByIds func(any) ([]Student, error)
}

func main() {
	ctx := map[string]any{
		"arr": []map[string]any{
			{
				"id":   "1",
				"name": "test1",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
			{
				"id":   "2",
				"name": "test2",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
			{
				"id":   "3",
				"name": "test3",
				"age":  19,
				"time": time.Now().Format("2006-01-02 15:04:05"),
			},
		},
		"id":  "1",
		"ids": []string{"1", "2"},
	}
	open, err := sql.Open("mysql", "xxxxxxx")
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	build := sgo.New(open)
	build.Source("/")
	mapper := &StudentMapper{}
	build.ScanMappers(mapper)
	stu, err := mapper.SelectByIds(ctx)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	fmt.Println(stu)
}

Update

同上...

Delete

同上...

Documentation

Index

Constants

View Source
const (
	Select = "select"
	Insert = "insert"
	Update = "update"
	Delete = "delete"
	Mapper = "mapper"
	For    = "for"
	If     = "if"
)

Variables

This section is empty.

Functions

func Analysis

func Analysis(element *etree.Element, ctx map[string]any) ([]string, string, []string, []any, error)

Analysis 解析xml标签

func AnalysisExpr

func AnalysisExpr(template string) string

AnalysisExpr 翻译表达式

func AnalysisForTemplate added in v0.0.3

func AnalysisForTemplate(template string, ctx map[string]any, v any) (string, string, []any, error)

AnalysisForTemplate 解析 for 标签的 文本模板 template for标签下的文本内容 ctx 并不是全局的上下文数据,如果 for循环的 item是个 obj ,则ctx将表示 obj v 如果 for循环的 item是个 基本类型 v 将代表它

func AnalysisTemplate

func AnalysisTemplate(template string, ctx map[string]any) (string, string, []any, error)

AnalysisTemplate 模板解析器

func Args added in v0.0.10

func Args(db reflect.Value, values []reflect.Value) (ctx reflect.Value, args any, tx reflect.Value, auto bool)

Args 参数赋值处理 处理定义函数的入参,返回一个参数序列给到后面的函数调用入参

func BaseTypeKey added in v0.0.10

func BaseTypeKey(v reflect.Value) string

BaseTypeKey 通过 BaseTypeKey 得到的变量默认全包名对泛型参数进行特殊处理的,不会加上类型中的 [xxx]定义部分信息

func DatabaseType added in v0.0.11

func DatabaseType(key string, dataType ToDatabase)

DatabaseType 对外提供添加 自定义sql语句数据类型解析支持

func Debug added in v0.0.6

func Debug(msg ...any)

func Element

func Element(element *etree.Element, template string, ctx map[string]any) (string, string, []any, error)

func End added in v0.0.10

func End(auto bool, result []reflect.Value, errType, BeginCall reflect.Value)

End 错误提交及回滚

func Error added in v0.0.7

func Error(err ...any)

func ExecResultMapper added in v0.0.2

func ExecResultMapper(result []reflect.Value, exec sql.Result) (count int64, err error)

ExecResultMapper SQL执行结果赋值 规则: insert,update,delete,默认第一个返回值为 执行sql 影响的具体行数 insert 第二个返回参数是 自增长主键

func ForElement

func ForElement(element *etree.Element, template string, ctx map[string]any) (string, string, []any, error)

func GolangType added in v0.0.11

func GolangType(key string, dataType ToGolang)

GolangType 对外提供添加 自定义结果集数据类型解析支持 key 需要通过 TypeKey 函数获取一个全局唯一的标识符 dataType 需要提供 对应数据解析逻辑细节可以参考 TimeData 或者 TimeDataPointer

func IfElement

func IfElement(element *etree.Element, template string, ctx map[string]any) (string, string, []any, error)

func Info added in v0.0.6

func Info(msg ...any)

func Level added in v0.0.6

func Level(level logrus.Level)

func MapperCheck added in v0.0.2

func MapperCheck(fun reflect.Value) (bool, error)

MapperCheck 检查 不同类别的sql标签 Mapper 函数是否符合规范 规则: 入参只能有一个并且只能是 map 或者 结构体,对返回值最后一个参数必须是error

func Namespace added in v0.0.2

func Namespace(namespace string) string

func Panic added in v0.0.7

func Panic(v ...any)

func QueryResultMapper added in v0.0.2

func QueryResultMapper(value reflect.Value, result []reflect.Value)

QueryResultMapper SQL 查询结果赋值 value 对应查询结果集 result 对应mapper函数返回值

func ResultMapping added in v0.0.2

func ResultMapping(value any) map[string]string

ResultMapping 解析结构体上的column标签 生成数据库字段到结构体字段的映射匹配 value 如果传递的是结构体,则会解析对应的 column 标签 或者字段本身 value 如果是 map 则不会做任何处理,返回 nil

func Return added in v0.0.10

func Return(result []reflect.Value) (ret []reflect.Value)

Return 处理返回值排序

func SelectCheck added in v0.0.2

func SelectCheck(columns []string, resultType any) (bool, error)

func StatementElement

func StatementElement(element *etree.Element, template string, ctx map[string]any) (string, string, []any, error)

func TimeData added in v0.0.10

func TimeData(value reflect.Value, data any) error

TimeData 时间类型数据

func TimeDataPointer added in v0.0.10

func TimeDataPointer(value reflect.Value, data any) error

func ToDatabaseTime added in v0.0.11

func ToDatabaseTime(data any) (any, error)

func ToDatabaseTimePointer added in v0.0.11

func ToDatabaseTimePointer(data any) (any, error)

func TypeKey added in v0.0.10

func TypeKey(t any) string

TypeKey 通过反射得到一个类型的类型字符串, 适用于普通类型

func UnTemplate

func UnTemplate(template string) string

UnTemplate 解析 {xx} 模板 解析为三个部分 ["{","xx","}"]

Types

type Build added in v0.0.2

type Build struct {
	Log

	// SqlSource 用于保存 xml 配置的文件的根路径配置信息,Build会通过SqlSource属性去加载 xml 文件
	SqlSource string
	// NameSpaces 保存了每个 xml 配置的根元素构建出来的 Sql 对象
	NameSpaces map[string]*Sql
	// contains filtered or unexported fields
}

func New added in v0.0.2

func New(db *sql.DB) *Build

func (*Build) Load added in v0.0.15

func (build *Build) Load(files embed.FS)

Load 加载 mapper 静态文件

func (*Build) Logs added in v0.1.0

func (build *Build) Logs(log Log)

Logs 切换日志实例

func (*Build) ScanMappers added in v0.0.2

func (build *Build) ScanMappers(mappers ...any)

ScanMappers 扫描解析

func (*Build) Source added in v0.0.2

func (build *Build) Source(source string)

Source 加载 mapper文件 source 应当是项目中的 mapper 文件根路径文件夹名称

type Combine added in v0.0.2

type Combine struct {
	Value     any
	Template  string
	Separator string
	Politic
}

func (Combine) ForEach added in v0.0.2

func (c Combine) ForEach() (string, string, []any, error)

type Formatter added in v0.0.6

type Formatter struct {
	ProjectName string
	Buf         *sync.Pool
	*logrus.TextFormatter
}

func (*Formatter) Format added in v0.0.6

func (format *Formatter) Format(entry *logrus.Entry) ([]byte, error)

type Log added in v0.1.0

type Log interface {
	Info(...interface{})
	Error(...interface{})
	Debug(...interface{})
	Panic(...interface{})
	Warn(...interface{})
}

Log 自定义Log需要实现的借口

type MapperFunc added in v0.0.2

type MapperFunc func([]reflect.Value) []reflect.Value

type Other added in v0.0.15

type Other struct {
}

func (Other) ForEach added in v0.0.15

func (s Other) ForEach(value any, template string, separator string) (string, string, []any, error)

type Politic added in v0.0.2

type Politic interface {
	// ForEach value 待处理迭代的数据 ctx 上下文数据 item 上下文数据key序列
	ForEach(value any, template string, separator string) (string, string, []any, error)
}

Politic for 标签迭代实现接口扩展 标准切片之外的 List 数据支持

type Slice added in v0.0.2

type Slice struct {
}

func (Slice) ForEach added in v0.0.2

func (s Slice) ForEach(value any, template string, separator string) (string, string, []any, error)

type Sql

type Sql struct {
	// Element 表示 一个 Mapper 文件的更元素
	Element *etree.Element
	// Statement 表示每个 更元素下面的 sql语句标签
	Statement map[string]*etree.Element
}

Sql 单个xml的解析结构

func NewSql

func NewSql(root *etree.Element) *Sql

func (*Sql) LoadSqlElement

func (receiver *Sql) LoadSqlElement()

type Struct added in v0.0.2

type Struct struct {
}

func (Struct) ForEach added in v0.0.2

func (s Struct) ForEach(value any, template string, separator string) (string, string, []any, error)

type ToDatabase added in v0.0.11

type ToDatabase func(data any) (any, error)

ToDatabase mapper 中sql解析模板对应的复杂数据据类型解析器 data : 对应的数据本身 对应需要返回一个非结构体的基础数据类型(int float,bool,string) 更具需要构成的实际sql决定,后续的sql解析将自动匹配数据类

type ToGolang added in v0.0.11

type ToGolang func(value reflect.Value, data any) error

ToGolang 处理数据库从查询结果集中的复杂数据类型的赋值 value : 是在一个结构体内的字段反射,通过该函数可以对这个字段进行初始化赋值 data : 是value对应的具体参数值,可能是字符串,切片,map

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL