# gobatis **Repository Path**: dombine/gobatis ## Basic Information - **Project Name**: gobatis - **Description**: Golang 关系数据库自动映射框架 - **Primary Language**: Go - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://go-aurora-engine.github.io/orm/sgo.html - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 22 - **Created**: 2023-01-30 - **Last Updated**: 2024-04-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # GoBatis [![Go Report Card](https://goreportcard.com/badge/gitee.com/aurora-engine/sgo)](https://goreportcard.com/report/gitee.com/aurora-engine/sgo)
## version ```shell go1.21 ``` `GoBatis` 是 `MyBatis` go语言实现,`GoBatis`提供对 mapper 的上下文数据解析填充,并不保证对 sql 语句的语法检查,支持自定义数据映射. ## XML 解析规则 `gobatis` 解析 xml 文件中的sql语句,会严格检查上下文中的数据类型,字符串类型参数会自定添加 ` '' ` 单引号,其他基础数据类型不会添加,对于复杂数据结构(复合结构,泛型结构体等)会持续跟进 ,目前仅支持基础数据类型。 ### 上下文数据 上下文数据是由用户调用时候传递接,仅接受 map 或者结构体. ### 标签详情 | 标签 | 描述 | 功能 | |:-----------|:---------|:---------------------------------------------------------------| | `` | 根节点 | | | `` | insert语句 | 生成插入语句 | | ` select * from student ``` #### 执行 ```go func TestQueryAll(t *testing.T) { var stus []model.Student if stus, err = studentMapper.QueryAll(); err != nil { t.Error(err.Error()) return } t.Log(stus) } ``` ## 分页查询 添加分页 mapper 字段 `QueryPage`,作为测试我们不进行参数传递,它返回3个参数,第一个参数是分页数据,第二个参数,是`sql` 条件所统计的总数, 查询mapper不返回 `int64` 的参数就不会自动统计数量 ```go type StudentMapper struct { AddOne func(student model.Student) error InsertId func(student model.Student) (int64, int64, error) Adds func(ctx any) error QueryAll func() ([]model.Student, error) QueryPage func() ([]model.Student, int64, error) } ``` ```xml insert into student (name, age, create_time) values ({Name},{Age},{CreateTime}); insert into student (name, age, create_time) values ({Name},{Age},{CreateTime}); insert into student (name, age, create_time) values ({stu.Name},{stu.Age},{stu.CreateTime}) ``` #### 执行 ```go func TestQueryPage(t *testing.T) { var stus []model.Student var count int64 if stus, count, err = studentMapper.QueryPage(); err != nil { t.Error(err.Error()) return } t.Log("rows:", stus, "count:", count) } ``` ## 事务支持 定义一个数据修改操作,通过外部传递一个事务 `tx` 由它来完成数据库操作后的提交或是回滚,我们定义一个 `Update` 第二个参数传递事务 ```go type StudentMapper struct { AddOne func(student model.Student) error InsertId func(student model.Student) (int64, int64, error) Adds func(ctx any) error QueryAll func() ([]model.Student, error) QueryPage func() ([]model.Student, int64, error) Update func(student model.Student, tx *sql.Tx) (int64, error) } ``` 编写sql语句,修改年龄大于5的数据姓名修改为AAA ```xml update student set name={Name} where age>{Age} ``` #### 运行测试 ```go func TestUpdate(t *testing.T) { var begin *sql.Tx var count int64 begin, err = open.Begin() if err != nil { t.Error(err.Error()) return } u := model.Student{ Name: "AAA", Age: 5, } count, err = studentMapper.Update(u, begin) if err != nil { t.Error(err.Error()) return } begin.Commit() t.Log(count) } ``` ## if 标签的使用 编写 xml,`QueryIf` 查询中使用了`where`标签,在`where`标签中,使用if来对上下文参数进行判断,如果存在 if标签将被解析到语句中 ```xml ``` 定义mapper字段 ```go type StudentMapper struct { AddOne func(student model.Student) error InsertId func(student model.Student) (int64, int64, error) Adds func(ctx any) error QueryAll func() ([]model.Student, error) QueryPage func() ([]model.Student, int64, error) Update func(student model.Student, tx *sql.Tx) (int64, error) QueryIf func(any) (model.Student, error) } ``` 运行测试 ```go func TestIf(t *testing.T) { var stu model.Student args := map[string]any{ "id": 1, "name": "test_0", } if stu, err = studentMapper.QueryIf(args); err != nil { t.Error(err.Error()) return } t.Log(stu) } ``` ## 自定义映射数据 `GoBatis` 提供了上下文参数中复杂数据类型如何解析对应到 SQL 中对应的参数以及 SQL 中的查询结果集如何映射到自定义的复杂数据类型中。 ### Go参数解析到 SQL ```go // ToDatabase mapper 中sql解析模板对应的复杂数据据类型解析器 // data : 对应的数据本身 // 对应需要返回一个非结构体的基础数据类型(int float,bool,string) 更具需要构成的实际sql决定,后续的sql解析将自动匹配数据类 type ToDatabase func(data any) (any, error) // DatabaseType 对外提供添加 自定义sql语句数据类型解析支持 func DatabaseType(key string, dataType ToDatabase) ``` 需要注册一个 `ToDatabase` 的解析器,`GoBatis`中对时间类型做了内置支持如下 ```go func ToDatabaseTime(data any) (any, error) { t := data.(time.Time) return t.Format("2006-01-02 15:04:05"), nil } func ToDatabaseTimePointer(data any) (any, error) { t := data.(*time.Time) return t.Format("2006-01-02 15:04:05"), nil } ``` ### SQL结果集解析到Go ```go // ToGolang 处理数据库从查询结果集中的复杂数据类型的赋值 // value : 是在一个结构体内的字段反射,通过该函数可以对这个字段进行初始化赋值 // data : 是value对应的具体参数值,可能是字符串,切片,map type ToGolang func(value reflect.Value, data any) error // GolangType 对外提供添加 自定义结果集数据类型解析支持 // key 需要通过 TypeKey 函数获取一个全局唯一的标识符 // dataType 需要提供 对应数据解析逻辑细节可以参考 TimeData 或者 TimeDataPointer func GolangType(key string, dataType ToGolang) ``` 需要注册一个 `ToGolang` 的解析器,`GoBatis`中对时间类型做了内置支持如下 ```go // TimeData 时间类型数据 func TimeData(value reflect.Value, data any) error { t := data.(string) parse, err := time.Parse("2006-04-02 15:04:05", t) if err != nil { return err } value.Set(reflect.ValueOf(parse)) return nil } func TimeDataPointer(value reflect.Value, data any) error { t := data.(string) parse, err := time.Parse("2006-04-02 15:04:05", t) if err != nil { return err } if value.CanSet() { value.Set(reflect.ValueOf(&parse)) } return nil } ```