mirror of
https://github.com/gohouse/gorose.git
synced 2025-12-24 12:47:55 +08:00
raw query & execute
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,3 +20,4 @@ glide.lock
|
||||
*.sqlite
|
||||
*.log
|
||||
filter.sh
|
||||
tmp/
|
||||
|
||||
467
README.md
467
README.md
@@ -1,467 +0,0 @@
|
||||
# GoRose ORM
|
||||
|
||||
[](https://godoc.org/github.com/gohouse/gorose/v2)
|
||||
[](https://goreportcard.com/report/github.com/gohouse/gorose/v2)
|
||||
[](https://github.com/gohouse/gorose/v2/releases/latest)
|
||||
[](https://gitter.im/gorose/wechat)
|
||||

|
||||

|
||||
<a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5JJOG9E">
|
||||
<img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="gorose-orm" title="gorose-orm"></a>
|
||||
|
||||
```
|
||||
_______ ______ .______ ______ _______. _______
|
||||
/ _____| / __ \ | _ \ / __ \ / || ____|
|
||||
| | __ | | | | | |_) | | | | | | (----`| |__
|
||||
| | |_ | | | | | | / | | | | \ \ | __|
|
||||
| |__| | | `--' | | |\ \----.| `--' | .----) | | |____
|
||||
\______| \______/ | _| `._____| \______/ |_______/ |_______|
|
||||
```
|
||||
|
||||
## 翻译(translation)
|
||||
[English readme](README_en.md) |
|
||||
[中文 readme](README.md)
|
||||
|
||||
## 文档
|
||||
[最新版2.x文档](https://www.kancloud.cn/fizz/gorose-2/1135835) |
|
||||
[1.x文档](https://www.kancloud.cn/fizz/gorose/769179) |
|
||||
[0.x文档](https://gohouse.github.io/gorose/dist/en/index.html)
|
||||
|
||||
|
||||
## 简介
|
||||
gorose是一个golang orm框架, 借鉴自laravel的eloquent.
|
||||
gorose 2.0 采用模块化架构, 通过interface的api通信,严格的上层依赖下层.每一个模块都可以拆卸, 甚至可以自定义为自己喜欢的样子.
|
||||
模块关系图如下: 
|
||||
|
||||
## 安装
|
||||
- go.mod
|
||||
```bash
|
||||
require github.com/gohouse/gorose/v2 v2.1.10
|
||||
```
|
||||
> 重要的事情说三遍!
|
||||
重要的事情说三遍!
|
||||
重要的事情说三遍!
|
||||
使用的时候必须`import "github.com/gohouse/gorose/v2"`方可正常使用.
|
||||
千万不要漏掉末尾的`v2`,这个是vgo的规定
|
||||
|
||||
> 如果使用最新更新,没有tag的话,可以使用`require github.com/gohouse/gorose/v2 master`,执行`go mod tidy`后,会自动获取最新提交的版本hash最为版本号,最终效果如:`github.com/gohouse/gorose/v2 v2.1.6-0.20200403045240-167d9094d7bd`
|
||||
|
||||
- docker
|
||||
```bash
|
||||
docker run -it --rm ababy/gorose sh -c "go run main.go"
|
||||
```
|
||||
> docker 镜像: [ababy/gorose](https://cloud.docker.com/u/ababy/repository/docker/ababy/gorose), docker镜像包含了gorose所必须的包和运行环境, [查看`Dockerfile`](https://github.com/docker-box/gorose/blob/master/master/golang/Dockerfile)
|
||||
|
||||
- go get
|
||||
```bash
|
||||
go get -u github.com/gohouse/gorose/v2
|
||||
```
|
||||
|
||||
## 支持驱动
|
||||
- mysql : https://github.com/go-sql-driver/mysql
|
||||
- sqlite3 : https://github.com/mattn/go-sqlite3
|
||||
- postgres : https://github.com/lib/pq
|
||||
- oracle : https://github.com/mattn/go-oci8
|
||||
- mssql : https://github.com/denisenkom/go-mssqldb
|
||||
- clickhouse : https://github.com/kshvakov/clickhouse
|
||||
|
||||
## api预览
|
||||
```go
|
||||
db.Table().Fields().Where().GroupBy().Having().OrderBy().Limit().Select()
|
||||
db.Table().Data().Insert()
|
||||
db.Table().Data().Where().Update()
|
||||
db.Table().Where().Delete()
|
||||
```
|
||||
|
||||
## 简单用法示例
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
var err error
|
||||
var engin *gorose.Engin
|
||||
func init() {
|
||||
// 全局初始化数据库,并复用
|
||||
// 这里的engin需要全局保存,可以用全局变量,也可以用单例
|
||||
// 配置&gorose.Config{}是单一数据库配置
|
||||
// 如果配置读写分离集群,则使用&gorose.ConfigCluster{}
|
||||
engin, err = gorose.Open(&gorose.Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
|
||||
// mysql示例, 记得导入mysql驱动 github.com/go-sql-driver/mysql
|
||||
// engin, err = gorose.Open(&gorose.Config{Driver: "mysql", Dsn: "root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true"})
|
||||
}
|
||||
func DB() gorose.IOrm {
|
||||
return engin.NewOrm()
|
||||
}
|
||||
func main() {
|
||||
// 原生sql, 直接返回结果集
|
||||
res,err := DB().Query("select * from users where uid>? limit 2", 1)
|
||||
fmt.Println(res)
|
||||
affected_rows,err := DB().Execute("delete from users where uid=?", 1)
|
||||
fmt.Println(affected_rows, err)
|
||||
|
||||
// orm链式操作,查询单条数据
|
||||
res, err = DB().Table("users").First()
|
||||
// res 类型为 map[string]interface{}
|
||||
fmt.Println(res)
|
||||
|
||||
// orm链式操作,查询多条数据
|
||||
res2, _ := DB().Table("users").Get()
|
||||
// res2 类型为 []map[string]interface{}
|
||||
fmt.Println(res2)
|
||||
}
|
||||
```
|
||||
|
||||
## 使用建议
|
||||
gorose提供数据对象绑定(map, struct), 同时支持字符串表名和map数据返回. 提供了很大的灵活性
|
||||
建议优先采用数据绑定的方式来完成查询操作, 做到数据源类型可控
|
||||
gorose提供了默认的 `gorose.Map` 和 `gorose.Data` 类型, 用来方便初始化绑定和data
|
||||
|
||||
## 配置和链接初始化
|
||||
简单配置
|
||||
```go
|
||||
var configSimple = &gorose.Config{
|
||||
Driver: "sqlite3",
|
||||
Dsn: "./db.sqlite",
|
||||
}
|
||||
```
|
||||
更多配置, 可以配置集群,甚至可以同时配置不同数据库在一个集群中, 数据库会随机选择集群的数据库来完成对应的读写操作, 其中master是写库, slave是读库, 需要自己做好主从复制, 这里只负责读写
|
||||
```go
|
||||
var config1 = gorose.Config{Dsn: "./db.sqlite"}
|
||||
var config2 = gorose.Config{Dsn: "./db2.sqlite"}
|
||||
var config3 = gorose.Config{Dsn: "./db3.sqlite"}
|
||||
var config4 = gorose.Config{Dsn: "./db4.sqlite"}
|
||||
var configCluster = &gorose.ConfigCluster{
|
||||
Master: []gorose.Config{config3, config4},
|
||||
Slave: []gorose.Config{config1, config2},
|
||||
Driver: "sqlite3",
|
||||
}
|
||||
```
|
||||
初始化使用
|
||||
```go
|
||||
var engin *gorose.Engin
|
||||
engin, err := Open(config)
|
||||
//engin, err := Open(configCluster)
|
||||
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
```
|
||||
|
||||
## 原生sql操作(增删改查), session的使用
|
||||
创建用户表 `users`
|
||||
```sql
|
||||
DROP TABLE IF EXISTS "users";
|
||||
CREATE TABLE "users" (
|
||||
"uid" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"age" integer NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "users" VALUES (1, 'gorose', 18);
|
||||
INSERT INTO "users" VALUES (2, 'goroom', 18);
|
||||
INSERT INTO "users" VALUES (3, 'fizzday', 18);
|
||||
```
|
||||
定义表struct
|
||||
```go
|
||||
type Users struct {
|
||||
Uid int `gorose:"uid"`
|
||||
Name string `gorose:"name"`
|
||||
Age int `gorose:"age"`
|
||||
}
|
||||
// 设置表名, 如果没有设置, 默认使用struct的名字
|
||||
func (u *Users) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
```
|
||||
原生查询操作
|
||||
除了上边的直接返回结果集外, 还支持绑定结果到给定对象上
|
||||
```go
|
||||
// 这里是要绑定的结构体对象
|
||||
// 如果你没有定义结构体, 则可以直接使用map, map示例
|
||||
// var u = gorose.Data{}
|
||||
// var u = gorose.Map{} 这两个都是可以的
|
||||
var u Users
|
||||
session := engin.NewSession()
|
||||
// 这里Bind()是为了存放结果的, 如果你使用的是NewOrm()初始化,则可以直接使用 NewOrm().Table().Query()
|
||||
_,err := session.Bind(&u).Query("select * from users where uid=? limit 2", 1)
|
||||
fmt.Println(err)
|
||||
fmt.Println(u)
|
||||
fmt.Println(session.LastSql())
|
||||
```
|
||||
> struct字段顺序需要跟`select *`内的表结构字段顺序一致(也可以手动指定要查询的字段), 具体原因参考 [https://github.com/gohouse/gorose/issues/136](https://github.com/gohouse/gorose/issues/136)
|
||||
|
||||
原生增删改操作
|
||||
```go
|
||||
session.Execute("insert into users(name,age) values(?,?)(?,?)", "gorose",18,"fizzday",19)
|
||||
session.Execute("update users set name=? where uid=?","gorose",1)
|
||||
session.Execute("delete from users where uid=?", 1)
|
||||
```
|
||||
## 对象关系映射, orm的使用
|
||||
- 1. 基本链式使用
|
||||
```go
|
||||
var u Users
|
||||
db := engin.NewOrm()
|
||||
err := db.Table(&u).Fields("name").AddFields("uid","age").Distinct().Where("uid",">",0).OrWhere("age",18).
|
||||
Group("age").Having("age>1").OrderBy("uid desc").Limit(10).Offset(1).Select()
|
||||
```
|
||||
也可以使用`xxx.Limit().Page()`,这个是固定用法,`Page()`必须在`Limit()`后边
|
||||
- 2. 如果不想定义struct, 又想绑定指定类型的map结果, 则可以定义map类型, 如
|
||||
```go
|
||||
type user gorose.Map
|
||||
// 或者 以下的type定义, 都是可以正常解析的
|
||||
type user2 map[string]interface{}
|
||||
type users3 []user
|
||||
type users4 []map[string]string
|
||||
type users5 []gorose.Map
|
||||
type users6 []gorose.Data
|
||||
```
|
||||
- 2.1 开始使用map绑定
|
||||
```go
|
||||
db.Table(&user).Select()
|
||||
db.Table(&users4).Limit(5).Select()
|
||||
```
|
||||
> 注意: 如果使用的不是slice数据结构, 则只能获取到一条数据
|
||||
|
||||
---
|
||||
这里使用的 gorose.Data , 实际上就是 `map[string]interface{}` 类型.
|
||||
而 `gorose.Map`, 实际上是 `t.MapStringT` 类型, 这里出现了一个 `t` 包, 是一个golang基本数据类型的相互转换包, 请看详细介绍 http://github.com/gohouse/t
|
||||
|
||||
- 3. laravel的`First()`,`Get()`, 用来返回结果集
|
||||
也就是说, 你甚至可以不用传入各种绑定的struct和map, 直接传入表名, 返回两个参数, 一个是 `[]gorose.Map`结果集, 第二个是`error`,堪称简单粗暴
|
||||
用法就是把上边的 `Select()` 方法换成 Get,First 即可, 只不过, `Select()` 只返回一个参数
|
||||
|
||||
- 4. orm的增删改查
|
||||
```go
|
||||
db.Table(&user2).Limit(10.Select()
|
||||
db.Table(&user2).Where("uid", 1).Data(gorose.Data{"name","gorose"}).Update()
|
||||
db.Table(&user2).Data(gorose.Data{"name","gorose33"}).Insert()
|
||||
db.Table(&user2).Data([]gorose.Data{{"name","gorose33"},"name","gorose44"}).Insert()
|
||||
db.Table(&user2).Where("uid", 1).Delete()
|
||||
```
|
||||
|
||||
## 最终sql构造器, builder构造不同数据库的sql
|
||||
目前支持 mysql, sqlite3, postgres, oracle, mssql, clickhouse等符合 `database/sql` 接口支持的数据库驱动
|
||||
这一部分, 用户基本无感知, 分理出来, 主要是为了开发者可以自由添加和修改相关驱动以达到个性化的需求
|
||||
|
||||
## binder, 数据绑定对象
|
||||
这一部分也是用户无感知的, 主要是传入的绑定对象解析和数据绑定, 同样是为了开发者个性化定制而独立出来的
|
||||
|
||||
## 模块化
|
||||
gorose2.0 完全模块化, 每一个模块都封装了interface接口api, 模块间调用, 都是通过接口, 上层依赖下层
|
||||
|
||||
- 主模块
|
||||
- engin
|
||||
gorose 初始化配置模块, 可以全局保存并复用
|
||||
- session
|
||||
真正操作数据库底层模块, 所有的操作, 最终都会走到这里来获取或修改数据
|
||||
- orm
|
||||
对象关系映射模块, 所有的orm操作, 都在这里完成
|
||||
- builder
|
||||
构建终极执行的sql模块, 可以构建任何数据库的sql, 但要符合`database/sql`包的接口
|
||||
- 子模块
|
||||
- driver
|
||||
数据库驱动模块, 被engin和builder依赖, 根据驱动来搞事情
|
||||
- binder
|
||||
结果集绑定模块, 所有的返回结果集都在这里
|
||||
|
||||
以上主模块, 都相对独立, 可以个性化定制和替换, 只要实现相应模块的接口即可.
|
||||
|
||||
## 最佳实践
|
||||
sql
|
||||
```sql
|
||||
DROP TABLE IF EXISTS "users";
|
||||
CREATE TABLE "users" (
|
||||
"uid" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"age" integer NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "users" VALUES (1, 'gorose', 18);
|
||||
INSERT INTO "users" VALUES (2, 'goroom', 18);
|
||||
INSERT INTO "users" VALUES (3, 'fizzday', 18);
|
||||
```
|
||||
实战代码
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type Users struct {
|
||||
Uid int64 `gorose:"uid"`
|
||||
Name string `gorose:"name"`
|
||||
Age int64 `gorose:"age"`
|
||||
Xxx interface{} `gorose:"-"` // 这个字段在orm中会忽略
|
||||
}
|
||||
|
||||
func (u *Users) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
var err error
|
||||
var engin *gorose.Engin
|
||||
|
||||
func init() {
|
||||
// 全局初始化数据库,并复用
|
||||
// 这里的engin需要全局保存,可以用全局变量,也可以用单例
|
||||
// 配置&gorose.Config{}是单一数据库配置
|
||||
// 如果配置读写分离集群,则使用&gorose.ConfigCluster{}
|
||||
engin, err = gorose.Open(&gorose.Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
|
||||
}
|
||||
func DB() gorose.IOrm {
|
||||
return engin.NewOrm()
|
||||
}
|
||||
func main() {
|
||||
// 这里定义一个变量db, 是为了复用db对象, 可以在最后使用 db.LastSql() 获取最后执行的sql
|
||||
// 如果不复用 db, 而是直接使用 DB(), 则会新建一个orm对象, 每一次都是全新的对象
|
||||
// 所以复用 db, 一定要在当前会话周期内
|
||||
db := DB()
|
||||
|
||||
// 查询一条
|
||||
var u Users
|
||||
// 查询数据并绑定到 user{} 上
|
||||
err = db.Table(&u).Fields("uid,name,age").Where("age",">",0).OrderBy("uid desc").Select()
|
||||
if err!=nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(u, u.Name)
|
||||
fmt.Println(db.LastSql())
|
||||
|
||||
// 查询多条
|
||||
// 查询数据并绑定到 []Users 上, 这里复用了 db 及上下文条件参数
|
||||
// 如果不想复用,则可以使用DB()就会开启全新会话,或者使用db.Reset()
|
||||
// db.Reset()只会清除上下文参数干扰,不会更换链接,DB()则会更换链接
|
||||
var u2 []Users
|
||||
err = db.Table(&u2).Limit(10).Offset(1).Select()
|
||||
fmt.Println(u2)
|
||||
|
||||
// 统计数据
|
||||
var count int64
|
||||
// 这里reset清除上边查询的参数干扰, 可以统计所有数据, 如果不清除, 则条件为上边查询的条件
|
||||
// 同时, 可以新调用 DB(), 也不会产生干扰
|
||||
count,err = db.Reset().Count()
|
||||
// 或
|
||||
count, err = DB().Table(&u).Count()
|
||||
fmt.Println(count, err)
|
||||
}
|
||||
```
|
||||
|
||||
## 高级用法
|
||||
|
||||
- Chunk 数据分片 大量数据批量处理 (累积处理)
|
||||
|
||||
` 当需要操作大量数据的时候, 一次性取出再操作, 不太合理, 就可以使用chunk方法
|
||||
chunk的第一个参数是指定一次操作的数据量, 根据业务量, 取100条或者1000条都可以
|
||||
chunk的第二个参数是一个回调方法, 用于书写正常的数据处理逻辑
|
||||
目的是做到, 无感知处理大量数据
|
||||
实现原理是, 每一次操作, 自动记录当前的操作位置, 下一次重复取数据的时候, 从当前位置开始取
|
||||
`
|
||||
```go
|
||||
User := db.Table("users")
|
||||
User.Fields("id, name").Where("id",">",2).Chunk(2, func(data []gorose.Data) error {
|
||||
// for _,item := range data {
|
||||
// fmt.Println(item)
|
||||
// }
|
||||
fmt.Println(data)
|
||||
|
||||
// 这里不要忘记返回错误或nil
|
||||
return nil
|
||||
})
|
||||
|
||||
// 打印结果:
|
||||
// map[id:3 name:gorose]
|
||||
// map[id:4 name:fizzday]
|
||||
// map[id:5 name:fizz3]
|
||||
// map[id:6 name:gohouse]
|
||||
[map[id:3 name:gorose] map[name:fizzday id:4]]
|
||||
[map[id:5 name:fizz3] map[id:6 name:gohouse]]
|
||||
```
|
||||
|
||||
- Loop 数据分片 大量数据批量处理 (从头处理)
|
||||
|
||||
` 类似 chunk 方法, 实现原理是, 每一次操作, 都是从头开始取数据
|
||||
原因: 当我们更改数据时, 更改的结果可能作为where条件会影响我们取数据的结果,所以, 可以使用Loop`
|
||||
```go
|
||||
User := db.Table("users")
|
||||
User.Fields("id, name").Where("id",">",2).Loop(2, func(data []gorose.Data) error {
|
||||
// for _,item := range data {
|
||||
// fmt.Println(item)
|
||||
// }
|
||||
// 这里执行update / delete 等操作
|
||||
|
||||
// 这里不要忘记返回错误或nil
|
||||
return nil
|
||||
})
|
||||
```
|
||||
|
||||
- 嵌套where
|
||||
|
||||
```go
|
||||
// SELECT * FROM users
|
||||
// WHERE id > 1
|
||||
// and ( name = 'fizz'
|
||||
// or ( name = 'fizz2'
|
||||
// and ( name = 'fizz3' or website like 'fizzday%')
|
||||
// )
|
||||
// )
|
||||
// and job = 'it' LIMIT 1
|
||||
User := db.Table("users")
|
||||
User.Where("id", ">", 1).Where(func() {
|
||||
User.Where("name", "fizz").OrWhere(func() {
|
||||
User.Where("name", "fizz2").Where(func() {
|
||||
User.Where("name", "fizz3").OrWhere("website", "like", "fizzday%")
|
||||
})
|
||||
})
|
||||
}).Where("job", "it").First()
|
||||
```
|
||||
- 嵌入原生sql示例
|
||||
以下几种操作是等效的
|
||||
```go
|
||||
db.Table("users").WhereRegexp("name","\w+").BuildSql()
|
||||
db.Table("users").Where("name","regexp","\w+").BuildSql()
|
||||
db.Table("users").Where([]interface{}{"name","regexp","\w+"}).BuildSql()
|
||||
db.Table("users").Where(gorose.Data{"name regexp","\w+"}).BuildSql()
|
||||
```
|
||||
|
||||
## 升级日志
|
||||
- v2.1.5-master:
|
||||
* 增加`regexp`表达式在`where`中的使用
|
||||
|
||||
- v2.1.4:
|
||||
* logger修正
|
||||
* 事物改进
|
||||
* 依赖包改为 gohouse/golib(gohouse/t,gohouse/gocar)
|
||||
- v2.1.x:
|
||||
* join表自动加前缀,不需要再手动加前缀
|
||||
* 原生sql的`query()`方法,增加返回结果集`[]map[string]interface{}`
|
||||
- v2.0.0: 船新版本,船新架构
|
||||
|
||||
## 升级指南
|
||||
### 从2.0.x升级到2.1.x
|
||||
- `xxx.Join("pre_tablename")`更改为`xxx.Join("tablename")`,这里不需要手动指定表前缀
|
||||
- `err:=DB().Bind().Query()`,更改为多返回`res,err:=DB().Query()`,同时保留了`Bind()`用法
|
||||
### 从1.x升级到2.x, 全新安装
|
||||
|
||||
---
|
||||
## Jetbrains 开源支持
|
||||
`gorose` 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 free JetBrains Open Source license(s) 正版免费授权,在此表达我的谢意。
|
||||
[](https://www.jetbrains.com/?from=gorose)
|
||||
|
||||
-----
|
||||
## 赞助渠道
|
||||
微信|支付宝|[paypal: click](https://www.paypal.me/fizzday)
|
||||
---|---|---
|
||||
<img src="imgs/wechat.png" width="300">|<img src="imgs/alipay.png" width="300"> | <a href="https://www.paypal.me/fizzday"><img src="imgs/paypal.png" width="300"></a>
|
||||
|
||||
- 捐赠列表
|
||||
|
||||
total | avator
|
||||
---|---
|
||||
¥100 | [](https://github.com/sanjinhub)
|
||||
|
||||
447
README_en.md
447
README_en.md
@@ -1,447 +0,0 @@
|
||||
<base target="main">
|
||||
|
||||
# GoRose ORM
|
||||
[](https://godoc.org/github.com/gohouse/gorose/v2)
|
||||
[](https://goreportcard.com/report/github.com/gohouse/gorose/v2)
|
||||
[](https://github.com/gohouse/gorose/v2/releases/latest)
|
||||
[](https://gitter.im/gorose/wechat)
|
||||

|
||||

|
||||
<a target="_blank" href="https://jq.qq.com/?_wv=1027&k=5JJOG9E">
|
||||
<img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="gorose-orm" title="gorose-orm"></a>
|
||||
|
||||
```
|
||||
_______ ______ .______ ______ _______. _______
|
||||
/ _____| / __ \ | _ \ / __ \ / || ____|
|
||||
| | __ | | | | | |_) | | | | | | (----`| |__
|
||||
| | |_ | | | | | | / | | | | \ \ | __|
|
||||
| |__| | | `--' | | |\ \----.| `--' | .----) | | |____
|
||||
\______| \______/ | _| `._____| \______/ |_______/ |_______|
|
||||
```
|
||||
|
||||
## translations
|
||||
[English readme](README_en.md) |
|
||||
[中文 readme](README.md)
|
||||
|
||||
## document
|
||||
[2.x doc](https://www.kancloud.cn/fizz/gorose-2/1135835) |
|
||||
[1.x doc](https://www.kancloud.cn/fizz/gorose/769179) |
|
||||
[0.x doc](https://gohouse.github.io/gorose/dist/en/index.html)
|
||||
|
||||
## introduction
|
||||
gorose is a golang orm framework, which is Inspired by laravel's eloquent.
|
||||
Gorose 2.0 adopts modular architecture, communicates through the API of interface, and strictly relies on the lower layer. Each module can be disassembled, and even can be customized to its preferred appearance.
|
||||
The module diagram is as follows:
|
||||

|
||||
|
||||
## installation
|
||||
- go.mod
|
||||
```bash
|
||||
require github.com/gohouse/gorose/v2 v2.1.10
|
||||
```
|
||||
> you should use it like `import "github.com/gohouse/gorose/v2"`
|
||||
don't forget the `v2` in the end
|
||||
|
||||
- docker
|
||||
```bash
|
||||
docker run -it --rm ababy/gorose sh -c "go run main.go"
|
||||
```
|
||||
> docker image: [ababy/gorose](https://cloud.docker.com/u/ababy/repository/docker/ababy/gorose), The docker image contains the packages and runtime environment necessary for gorose, [view `Dockerfile`](https://github.com/docker-box/gorose/blob/master/golang-alpine/Dockerfile)
|
||||
|
||||
- go get
|
||||
```bash
|
||||
go get -u github.com/gohouse/gorose
|
||||
```
|
||||
|
||||
## supported drivers
|
||||
- mysql : https://github.com/go-sql-driver/mysql
|
||||
- sqlite3 : https://github.com/mattn/go-sqlite3
|
||||
- postgres : https://github.com/lib/pq
|
||||
- oracle : https://github.com/mattn/go-oci8
|
||||
- mssql : https://github.com/denisenkom/go-mssqldb
|
||||
- clickhouse : https://github.com/kshvakov/clickhouse
|
||||
|
||||
## api preview
|
||||
```go
|
||||
db.Table().Fields().Distinct().Where().GroupBy().Having().OrderBy().Limit().Offset().Select()
|
||||
db.Table().Data().Insert()
|
||||
db.Table().Data().Where().Update()
|
||||
db.Table().Where().Delete()
|
||||
```
|
||||
|
||||
## simple usage example
|
||||
```go
|
||||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
var err error
|
||||
var engin *gorose.Engin
|
||||
func init() {
|
||||
// Global initialization and reuse of databases
|
||||
// The engin here needs to be saved globally, using either global variables or singletons
|
||||
// Configuration & gorose. Config {} is a single database configuration
|
||||
// If you configure a read-write separation cluster, use & gorose. ConfigCluster {}
|
||||
engin, err = gorose.Open(&gorose.Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
|
||||
// mysql demo, remeber import mysql driver of github.com/go-sql-driver/mysql
|
||||
// engin, err = gorose.Open(&gorose.Config{Driver: "mysql", Dsn: "root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true"})
|
||||
}
|
||||
func DB() gorose.IOrm {
|
||||
return engin.NewOrm()
|
||||
}
|
||||
func main() {
|
||||
// Native SQL, return results directly
|
||||
res,err := DB().Query("select * from users where uid>? limit 2", 1)
|
||||
fmt.Println(res)
|
||||
affected_rows,err := DB().Execute("delete from users where uid=?", 1)
|
||||
fmt.Println(affected_rows, err)
|
||||
|
||||
// orm chan operation, fetch one row
|
||||
res, err := DB().Table("users").First()
|
||||
// res's type is map[string]interface{}
|
||||
fmt.Println(res)
|
||||
|
||||
// rm chan operation, fetch more rows
|
||||
res2, _ := DB().Table("users").Get()
|
||||
// res2's type is []map[string]interface{}
|
||||
fmt.Println(res2)
|
||||
}
|
||||
```
|
||||
|
||||
## usage advise
|
||||
Gorose provides data object binding (map, struct), while supporting string table names and map data return. It provides great flexibility.
|
||||
|
||||
It is suggested that data binding should be used as a priority to complete query operation, so that the type of data source can be controlled.
|
||||
Gorose provides default `gorose. Map'and `gorose. Data' types to facilitate initialization of bindings and data
|
||||
|
||||
## Configuration and link initialization
|
||||
Simple configuration
|
||||
```go
|
||||
var configSimple = &gorose.Config{
|
||||
Driver: "sqlite3",
|
||||
Dsn: "./db.sqlite",
|
||||
}
|
||||
```
|
||||
More configurations, you can configure the cluster, or even configure different databases in a cluster at the same time. The database will randomly select the cluster database to complete the corresponding reading and writing operations, in which master is the writing database, slave is the reading database, you need to do master-slave replication, here only responsible for reading and writing.
|
||||
```go
|
||||
var config = &gorose.ConfigCluster{
|
||||
Master: []&gorose.Config{}{configSimple}
|
||||
Slave: []&gorose.Config{}{configSimple}
|
||||
Prefix: "pre_",
|
||||
Driver: "sqlite3",
|
||||
}
|
||||
```
|
||||
Initial usage
|
||||
```go
|
||||
var engin *gorose.Engin
|
||||
engin, err := Open(config)
|
||||
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
```
|
||||
|
||||
## Native SQL operation (add, delete, check), session usage
|
||||
Create user tables of `users`
|
||||
```sql
|
||||
DROP TABLE IF EXISTS "users";
|
||||
CREATE TABLE "users" (
|
||||
"uid" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"age" integer NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "users" VALUES (1, 'gorose', 18);
|
||||
INSERT INTO "users" VALUES (2, 'goroom', 18);
|
||||
INSERT INTO "users" VALUES (3, 'fizzday', 18);
|
||||
```
|
||||
define table struct
|
||||
```go
|
||||
type Users struct {
|
||||
Uid int `gorose:"uid"`
|
||||
Name string `gorose:"name"`
|
||||
Age int `gorose:"age"`
|
||||
}
|
||||
// Set the table name. If not, use struct's name by default
|
||||
func (u *Users) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
```
|
||||
Native query operation
|
||||
```go
|
||||
// Here is the structure object to be bound
|
||||
// If you don't define a structure, you can use map, map example directly
|
||||
// var u = gorose.Data{}
|
||||
// var u = gorose.Map{} Both are possible.
|
||||
var u Users
|
||||
session := engin.NewSession()
|
||||
// Here Bind () is used to store results. If you use NewOrm () initialization, you can use NewOrm (). Table (). Query () directly.
|
||||
_,err := session.Bind(&u).Query("select * from users where uid=? limit 2", 1)
|
||||
fmt.Println(err)
|
||||
fmt.Println(u)
|
||||
fmt.Println(session.LastSql())
|
||||
```
|
||||
Native inesrt delete update
|
||||
```go
|
||||
session.Execute("insert into users(name,age) values(?,?)(?,?)", "gorose",18,"fizzday",19)
|
||||
session.Execute("update users set name=? where uid=?","gorose",1)
|
||||
session.Execute("delete from users where uid=?", 1)
|
||||
```
|
||||
## Object Relational Mapping, the Use of ORM
|
||||
|
||||
- 1. Basic Chain Usage
|
||||
|
||||
```go
|
||||
var u Users
|
||||
db := engin.NewOrm()
|
||||
err := db.Table(&u).Fields("name").AddFields("uid","age").Distinct().Where("uid",">",0).OrWhere("age",18).
|
||||
Group("age").Having("age>1").OrderBy("uid desc").Limit(10).Offset(1).Select()
|
||||
```
|
||||
|
||||
- 2. If you don't want to define struct and want to bind map results of a specified type, you can define map types, such as
|
||||
```go
|
||||
type user gorose.Map
|
||||
// Or the following type definitions can be parsed properly
|
||||
type user2 map[string]interface{}
|
||||
type users3 []user
|
||||
type users4 []map[string]string
|
||||
type users5 []gorose.Map
|
||||
type users6 []gorose.Data
|
||||
```
|
||||
Start using map binding
|
||||
```go
|
||||
db.Table(&user).Select()
|
||||
db.Table(&users4).Limit(5).Select()
|
||||
```
|
||||
> Note: If the slice data structure is not used, only one piece of data can be obtained.
|
||||
|
||||
---
|
||||
The gorose. Data used here is actually the `map [string] interface {}'type.
|
||||
|
||||
And `gorose. Map'is actually a `t. MapString' type. Here comes a `t'package, a golang basic data type conversion package. See http://github.com/gohouse/t for more details.
|
||||
|
||||
|
||||
- 3. laravel's `First()`,`Get()`, Used to return the result set
|
||||
That is to say, you can even pass in the table name directly without passing in various bound structs and maps, and return two parameters, one is the `[] gorose. Map `result set, and the second is `error', which is considered simple and rude.
|
||||
|
||||
Usage is to replace the `Select ()'method above with Get, First, but `Select ()' returns only one parameter.
|
||||
|
||||
|
||||
- 4. orm Select Update Insert Delete
|
||||
```go
|
||||
db.Table(&user2).Limit(10.Select()
|
||||
db.Table(&user2).Where("uid", 1).Data(gorose.Data{"name","gorose"}).Update()
|
||||
db.Table(&user2).Data(gorose.Data{"name","gorose33"}).Insert()
|
||||
db.Table(&user2).Data([]gorose.Data{{"name","gorose33"},"name","gorose44"}).Insert()
|
||||
db.Table(&user2).Where("uid", 1).Delete()
|
||||
```
|
||||
|
||||
## Final SQL constructor, builder constructs SQL of different databases
|
||||
Currently supports mysql, sqlite3, postgres, oracle, mssql, Clickhouse and other database drivers that conform to `database/sql` interface support
|
||||
In this part, users are basically insensitive, sorted out, mainly for developers can freely add and modify related drivers to achieve personalized needs.
|
||||
|
||||
## binder, Data Binding Objects
|
||||
This part is also user-insensitive, mainly for incoming binding object parsing and data binding, and also for personalized customization by developers.
|
||||
|
||||
## Modularization
|
||||
Gorose 2.0 is fully modular, each module encapsulates the interface api, calling between modules, through the interface, the upper layer depends on the lower layer
|
||||
|
||||
- Main module
|
||||
- engin
|
||||
gorose Initialize the configuration module, which can be saved and reused globally
|
||||
- session
|
||||
Really operate the database underlying module, all operations, will eventually come here to obtain or modify data.
|
||||
- orm
|
||||
Object relational mapping module, all ORM operations, are done here
|
||||
- builder
|
||||
Building the ultimate execution SQL module, you can build any database sql, but to comply with the `database / SQL ` package interface
|
||||
- sub module
|
||||
- driver
|
||||
The database driver module, which is dependent on engin and builder, does things according to the driver
|
||||
- binder
|
||||
Result Set Binding Module, where all returned result sets are located
|
||||
|
||||
The above main modules are relatively independent and can be customized and replaced individually, as long as the interface of the corresponding modules is realized.
|
||||
|
||||
## Best Practices
|
||||
sql
|
||||
```sql
|
||||
DROP TABLE IF EXISTS "users";
|
||||
CREATE TABLE "users" (
|
||||
"uid" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL,
|
||||
"age" integer NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "users" VALUES (1, 'gorose', 18);
|
||||
INSERT INTO "users" VALUES (2, 'goroom', 18);
|
||||
INSERT INTO "users" VALUES (3, 'fizzday', 18);
|
||||
```
|
||||
Actual Code
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type Users struct {
|
||||
Uid int64 `gorose:"uid"`
|
||||
Name string `gorose:"name"`
|
||||
Age int64 `gorose:"age"`
|
||||
Xxx interface{} `gorose:"-"` // This field is ignored in ORM
|
||||
}
|
||||
|
||||
func (u *Users) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
var err error
|
||||
var engin *gorose.Engin
|
||||
|
||||
func init() {
|
||||
// Global initialization and reuse of databases
|
||||
// The engin here needs to be saved globally, using either global variables or singletons
|
||||
// Configuration & gorose. Config {} is a single database configuration
|
||||
// If you configure a read-write separation cluster, use & gorose. ConfigCluster {}
|
||||
engin, err = gorose.Open(&gorose.Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
|
||||
}
|
||||
func DB() gorose.IOrm {
|
||||
return engin.NewOrm()
|
||||
}
|
||||
func main() {
|
||||
// A variable DB is defined here to reuse the DB object, and you can use db. LastSql () to get the SQL that was executed last.
|
||||
// If you don't reuse db, but use DB () directly, you create a new ORM object, which is brand new every time.
|
||||
// So reusing DB must be within the current session cycle
|
||||
db := DB()
|
||||
|
||||
// fetch a row
|
||||
var u Users
|
||||
// bind result to user{}
|
||||
err = db.Table(&u).Fields("uid,name,age").Where("age",">",0).OrderBy("uid desc").Select()
|
||||
if err!=nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
fmt.Println(u, u.Name)
|
||||
fmt.Println(db.LastSql())
|
||||
|
||||
// fetch multi rows
|
||||
// bind result to []Users, db and context condition parameters are reused here
|
||||
// If you don't want to reuse, you can use DB() to open a new session, or db.Reset()
|
||||
// db.Reset() only removes contextual parameter interference, does not change links, DB() will change links.
|
||||
var u2 []Users
|
||||
err = db.Table(&u2).Limit(10).Offset(1).Select()
|
||||
fmt.Println(u2)
|
||||
|
||||
// count
|
||||
var count int64
|
||||
// Here reset clears the parameter interference of the upper query and can count all the data. If it is not clear, the condition is the condition of the upper query.
|
||||
// At the same time, DB () can be called new, without interference.
|
||||
count,err = db.Reset().Count()
|
||||
// or
|
||||
count, err = DB().Table(&u).Count()
|
||||
fmt.Println(count, err)
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
- Chunk Data Fragmentation, Mass Data Batch Processing (Cumulative Processing)
|
||||
|
||||
` When a large amount of data needs to be manipulated, the chunk method can be used if it is unreasonable to take it out at one time and then operate it again.
|
||||
The first parameter of chunk is the amount of data specified for a single operation. According to the volume of business, 100 or 1000 can be selected.
|
||||
The second parameter of chunk is a callback method for writing normal data processing logic
|
||||
The goal is to process large amounts of data senselessly
|
||||
The principle of implementation is that each operation automatically records the current operation position, and the next time the data is retrieved again, the data is retrieved from the current position.
|
||||
`
|
||||
```go
|
||||
User := db.Table("users")
|
||||
User.Fields("id, name").Where("id",">",2).Chunk(2, func(data []gorose.Data) error {
|
||||
// for _,item := range data {
|
||||
// fmt.Println(item)
|
||||
// }
|
||||
fmt.Println(data)
|
||||
|
||||
// don't forget return error or nil
|
||||
return nil
|
||||
})
|
||||
|
||||
// print result:
|
||||
// map[id:3 name:gorose]
|
||||
// map[id:4 name:fizzday]
|
||||
// map[id:5 name:fizz3]
|
||||
// map[id:6 name:gohouse]
|
||||
[map[id:3 name:gorose] map[name:fizzday id:4]]
|
||||
[map[id:5 name:fizz3] map[id:6 name:gohouse]]
|
||||
```
|
||||
|
||||
- Loop Data fragmentation, mass data batch processing (from scratch)
|
||||
|
||||
` Similar to chunk method, the implementation principle is that every operation is to fetch data from the beginning.
|
||||
Reason: When we change data, the result of the change may affect the result of our data taking as where condition, so we can use Loop.`
|
||||
```go
|
||||
User := db.Table("users")
|
||||
User.Fields("id, name").Where("id",">",2).Loop(2, func(data []gorose.Data) error {
|
||||
// for _,item := range data {
|
||||
// fmt.Println(item)
|
||||
// }
|
||||
// here run update / delete
|
||||
|
||||
// don't forget return error or nil
|
||||
return nil
|
||||
})
|
||||
```
|
||||
|
||||
- where nested
|
||||
|
||||
```go
|
||||
// SELECT * FROM users
|
||||
// WHERE id > 1
|
||||
// and ( name = 'fizz'
|
||||
// or ( name = 'fizz2'
|
||||
// and ( name = 'fizz3' or website like 'fizzday%')
|
||||
// )
|
||||
// )
|
||||
// and job = 'it' LIMIT 1
|
||||
User := db.Table("users")
|
||||
User.Where("id", ">", 1).Where(func() {
|
||||
User.Where("name", "fizz").OrWhere(func() {
|
||||
User.Where("name", "fizz2").Where(func() {
|
||||
User.Where("name", "fizz3").OrWhere("website", "like", "fizzday%")
|
||||
})
|
||||
})
|
||||
}).Where("job", "it").First()
|
||||
```
|
||||
|
||||
## realease log
|
||||
- v2.1.x:
|
||||
* update join with auto table prefix
|
||||
* add query return with []map[string]interface{}
|
||||
- v2.0.0: new version, new structure
|
||||
|
||||
## Upgrade Guide
|
||||
### from 2.0.x to 2.1.x
|
||||
- change `xxx.Join("pre_tablename")` into `xxx.Join("tablename")`,the join table name auto prefix
|
||||
- change `err:=DB().Bind().Query()` into `res,err:=DB().Query()` with multi return,leave the `Bind()` method as well
|
||||
### from 1.x to 2.x
|
||||
install it for new
|
||||
|
||||
|
||||
## Jetbrains non-commercial sponsorship
|
||||
[](https://www.jetbrains.com/?from=gorose)
|
||||
-----
|
||||
## pay me a coffee
|
||||
wechat|alipay|[paypal: click](https://www.paypal.me/fizzday)
|
||||
---|---|---
|
||||
<img src="imgs/wechat.png" width="300">|<img src="imgs/alipay.png" width="300"> | <a href="https://www.paypal.me/fizzday"><img src="imgs/paypal.png" width="300"></a>
|
||||
|
||||
- pay list
|
||||
|
||||
total | avator
|
||||
---|---
|
||||
¥100 | [](https://github.com/sanjinhub)
|
||||
|
||||
311
binder.go
311
binder.go
@@ -1,311 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gohouse/t"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Map ...
|
||||
type Map t.MapStringT
|
||||
|
||||
// Data ...
|
||||
type Data map[string]interface{}
|
||||
|
||||
// BindType ...
|
||||
type BindType int
|
||||
|
||||
const (
|
||||
// OBJECT_STRUCT 结构体 一条数据 (struct)
|
||||
OBJECT_STRUCT BindType = iota
|
||||
// OBJECT_STRUCT_SLICE 结构体 多条数据 ([]struct)
|
||||
OBJECT_STRUCT_SLICE
|
||||
// OBJECT_MAP map 一条数据 (map[string]interface{})
|
||||
OBJECT_MAP
|
||||
// OBJECT_MAP_SLICE map 多条数据 ([]map[string]interface{})
|
||||
OBJECT_MAP_SLICE
|
||||
// OBJECT_STRING 非结构体 表名字符串 ("users")
|
||||
OBJECT_STRING
|
||||
// OBJECT_MAP_T map 一条数据 (map[string]t.Type)
|
||||
OBJECT_MAP_T
|
||||
// OBJECT_MAP_SLICE_T map 多条数据 ([]map[string]t.Type)
|
||||
OBJECT_MAP_SLICE_T
|
||||
// OBJECT_NIL 默认没有传入任何绑定对象,一般用于query直接返回
|
||||
OBJECT_NIL
|
||||
)
|
||||
|
||||
// BindString ...
|
||||
var BindString = map[BindType]string{
|
||||
OBJECT_STRUCT: "OBJECT_STRUCT",
|
||||
OBJECT_STRUCT_SLICE: "OBJECT_STRUCT_SLICE",
|
||||
OBJECT_MAP: "OBJECT_MAP",
|
||||
OBJECT_MAP_SLICE: "OBJECT_MAP_SLICE",
|
||||
OBJECT_STRING: "OBJECT_STRING",
|
||||
OBJECT_MAP_T: "OBJECT_MAP_T",
|
||||
OBJECT_MAP_SLICE_T: "OBJECT_MAP_SLICE_T",
|
||||
OBJECT_NIL: "OBJECT_NIL",
|
||||
}
|
||||
|
||||
// BindType.String ...
|
||||
func (b BindType) String() string {
|
||||
return BindString[b]
|
||||
}
|
||||
|
||||
// Binder ...
|
||||
type Binder struct {
|
||||
// Bind是指传入的对象 [slice]map,[slice]struct
|
||||
// 传入的原始对象
|
||||
BindOrigin interface{}
|
||||
//BindOriginTableName []string
|
||||
// 解析出来的对象名字, 或者指定的method(TableName)获取到的名字
|
||||
BindName string
|
||||
// 一条结果的反射对象
|
||||
BindResult interface{}
|
||||
// 多条
|
||||
BindResultSlice reflect.Value
|
||||
// 传入结构体解析出来的字段
|
||||
BindFields []string
|
||||
// 传入的对象类型判定
|
||||
BindType BindType
|
||||
// 出入传入得是非slice对象, 则只需要取一条, 取多了也是浪费
|
||||
BindLimit int
|
||||
BindPrefix string
|
||||
// 多条map结果,传入的是string table时
|
||||
BindAll []Data
|
||||
}
|
||||
|
||||
var _ IBinder = &Binder{}
|
||||
|
||||
// NewBinder ...
|
||||
func NewBinder(o ...interface{}) *Binder {
|
||||
var binder = new(Binder)
|
||||
if len(o) > 0 {
|
||||
binder.SetBindOrigin(o[0])
|
||||
} else {
|
||||
binder.BindType = OBJECT_NIL
|
||||
}
|
||||
return binder
|
||||
}
|
||||
|
||||
// BindParse ...
|
||||
func (o *Binder) BindParse(prefix string) error {
|
||||
if o.GetBindOrigin() == nil {
|
||||
return nil
|
||||
}
|
||||
var BindName string
|
||||
switch o.GetBindOrigin().(type) {
|
||||
case string: // 直接传入的是表名
|
||||
o.SetBindType(OBJECT_STRING)
|
||||
BindName = o.GetBindOrigin().(string)
|
||||
//o.SetBindAll([]Map{})
|
||||
|
||||
// 传入的是struct或切片
|
||||
default:
|
||||
// 清空字段值,避免手动传入字段污染struct字段
|
||||
o.SetBindFields([]string{})
|
||||
// make sure dst is an appropriate type
|
||||
dstVal := reflect.ValueOf(o.GetBindOrigin())
|
||||
sliceVal := reflect.Indirect(dstVal)
|
||||
|
||||
switch sliceVal.Kind() {
|
||||
case reflect.Struct: // struct
|
||||
o.SetBindType(OBJECT_STRUCT)
|
||||
BindName = sliceVal.Type().Name()
|
||||
o.SetBindResult(o.GetBindOrigin())
|
||||
//// 默认只查一条
|
||||
//o.SetBindLimit(1)
|
||||
// 解析出字段
|
||||
o.parseFields()
|
||||
// 是否设置了表名
|
||||
switch dstVal.Kind() {
|
||||
case reflect.Ptr, reflect.Struct:
|
||||
if tn := dstVal.MethodByName("TableName"); tn.IsValid() {
|
||||
BindName = tn.Call(nil)[0].String()
|
||||
}
|
||||
default:
|
||||
return errors.New("传入的对象有误,示例:var user User,传入 &user{}")
|
||||
}
|
||||
case reflect.Map: // map
|
||||
o.SetBindType(OBJECT_MAP)
|
||||
//// 默认只查一条
|
||||
//o.SetBindLimit(1)
|
||||
//
|
||||
o.SetBindResult(o.GetBindOrigin())
|
||||
//TODO 检查map的值类型, 是否是t.Type
|
||||
if sliceVal.Type().Elem() == reflect.ValueOf(map[string]t.Type{}).Type().Elem() {
|
||||
o.SetBindType(OBJECT_MAP_T)
|
||||
}
|
||||
// 是否设置了表名
|
||||
if dstVal.Kind() != reflect.Ptr {
|
||||
return errors.New("传入的不是map指针,如:var user gorose.Map,传入 &user{}")
|
||||
}
|
||||
if tn := dstVal.MethodByName("TableName"); tn.IsValid() {
|
||||
BindName = tn.Call(nil)[0].String()
|
||||
}
|
||||
|
||||
case reflect.Slice: // []struct,[]map
|
||||
eltType := sliceVal.Type().Elem()
|
||||
|
||||
switch eltType.Kind() {
|
||||
case reflect.Map:
|
||||
o.SetBindType(OBJECT_MAP_SLICE)
|
||||
o.SetBindResult(reflect.MakeMap(eltType).Interface())
|
||||
o.SetBindResultSlice(sliceVal)
|
||||
//o.SetBindResultSlice(reflect.MakeSlice(sliceVal.Type(),0,0))
|
||||
//TODO 检查map的值类型, 是否是t.Type
|
||||
if eltType.Elem() == reflect.ValueOf(map[string]t.Type{}).Type().Elem() {
|
||||
o.SetBindType(OBJECT_MAP_SLICE_T)
|
||||
}
|
||||
if dstVal.Kind() != reflect.Ptr {
|
||||
return errors.New("传入的不是map指针,如:var user gorose.Map,传入 &user{}")
|
||||
}
|
||||
// 检查设置表名
|
||||
r2val := reflect.New(eltType)
|
||||
if tn := r2val.MethodByName("TableName"); tn.IsValid() {
|
||||
BindName = tn.Call(nil)[0].String()
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
o.SetBindType(OBJECT_STRUCT_SLICE)
|
||||
BindName = eltType.Name()
|
||||
br := reflect.New(eltType)
|
||||
o.SetBindResult(br.Interface())
|
||||
o.SetBindResultSlice(sliceVal)
|
||||
// 解析出字段
|
||||
o.parseFields()
|
||||
|
||||
// 是否设置了表名
|
||||
switch dstVal.Kind() {
|
||||
case reflect.Ptr, reflect.Struct:
|
||||
if tn := br.MethodByName("TableName"); tn.IsValid() {
|
||||
BindName = tn.Call(nil)[0].String()
|
||||
}
|
||||
default:
|
||||
return errors.New("传入的对象有误,示例:var user User,传入 &user{}")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("table只接收 struct,[]struct,map[string]interface{},[]map[string]interface{}的对象和地址, 但是传入的是: %T", o.GetBindOrigin())
|
||||
}
|
||||
// 是否设置了表名
|
||||
if tn := dstVal.MethodByName("TableName"); tn.IsValid() {
|
||||
BindName = tn.Call(nil)[0].String()
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("table只接收 struct,[]struct,map[string]interface{},[]map[string]interface{}, 但是传入的是: %T", o.GetBindOrigin())
|
||||
}
|
||||
}
|
||||
|
||||
o.SetBindName(prefix + BindName)
|
||||
o.SetBindPrefix(prefix)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Binder) parseFields() {
|
||||
if len(o.GetBindFields()) == 0 {
|
||||
o.SetBindFields(getTagName(o.GetBindResult(), TAGNAME))
|
||||
}
|
||||
}
|
||||
|
||||
// ResetBindResultSlice ...
|
||||
func (o *Binder) ResetBindResultSlice() {
|
||||
if o.BindType == OBJECT_MAP_SLICE_T {
|
||||
o.BindResultSlice = reflect.New(o.BindResultSlice.Type())
|
||||
}
|
||||
}
|
||||
|
||||
// SetBindPrefix ...
|
||||
func (o *Binder) SetBindPrefix(arg string) {
|
||||
o.BindPrefix = arg
|
||||
}
|
||||
|
||||
// GetBindPrefix ...
|
||||
func (o *Binder) GetBindPrefix() string {
|
||||
return o.BindPrefix
|
||||
}
|
||||
|
||||
// SetBindOrigin ...
|
||||
func (o *Binder) SetBindOrigin(arg interface{}) {
|
||||
o.BindOrigin = arg
|
||||
}
|
||||
|
||||
// GetBindOrigin ...
|
||||
func (o *Binder) GetBindOrigin() interface{} {
|
||||
return o.BindOrigin
|
||||
}
|
||||
|
||||
// SetBindName ...
|
||||
func (o *Binder) SetBindName(arg string) {
|
||||
o.BindName = arg
|
||||
}
|
||||
|
||||
// GetBindName ...
|
||||
func (o *Binder) GetBindName() string {
|
||||
return o.BindName
|
||||
}
|
||||
|
||||
// SetBindResult ...
|
||||
func (o *Binder) SetBindResult(arg interface{}) {
|
||||
o.BindResult = arg
|
||||
}
|
||||
|
||||
// GetBindResult ...
|
||||
func (o *Binder) GetBindResult() interface{} {
|
||||
return o.BindResult
|
||||
}
|
||||
|
||||
// SetBindResultSlice ...
|
||||
func (o *Binder) SetBindResultSlice(arg reflect.Value) {
|
||||
o.BindResultSlice = arg
|
||||
}
|
||||
|
||||
// GetBindResultSlice ...
|
||||
func (o *Binder) GetBindResultSlice() reflect.Value {
|
||||
return o.BindResultSlice
|
||||
}
|
||||
|
||||
// SetBindFields ...
|
||||
func (o *Binder) SetBindFields(arg []string) {
|
||||
o.BindFields = arg
|
||||
}
|
||||
|
||||
// GetBindFields ...
|
||||
func (o *Binder) GetBindFields() []string {
|
||||
return o.BindFields
|
||||
}
|
||||
|
||||
// SetBindType ...
|
||||
func (o *Binder) SetBindType(arg BindType) {
|
||||
o.BindType = arg
|
||||
}
|
||||
|
||||
// GetBindType ...
|
||||
func (o *Binder) GetBindType() BindType {
|
||||
return o.BindType
|
||||
}
|
||||
|
||||
// SetBindAll ...
|
||||
func (o *Binder) SetBindAll(arg []Data) {
|
||||
o.BindAll = arg
|
||||
}
|
||||
|
||||
// GetBindAll ...
|
||||
func (o *Binder) GetBindAll() []Data {
|
||||
return o.BindAll
|
||||
}
|
||||
|
||||
// ResetBinder ...
|
||||
func (o *Binder) ResetBinder() {
|
||||
switch o.BindType {
|
||||
case OBJECT_STRUCT, OBJECT_MAP, OBJECT_MAP_T:
|
||||
// 清空结果
|
||||
o.SetBindOrigin(nil)
|
||||
case OBJECT_STRUCT_SLICE, OBJECT_MAP_SLICE, OBJECT_MAP_SLICE_T:
|
||||
//var rvResult = reflect.ValueOf(o.GetBindResult())
|
||||
var rvResult = o.GetBindResultSlice()
|
||||
// 清空结果
|
||||
rvResult.Set(rvResult.Slice(0, 0))
|
||||
default:
|
||||
o.SetBindAll([]Data{})
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import "reflect"
|
||||
|
||||
// IBinder 数据绑定对象接口
|
||||
type IBinder interface {
|
||||
SetBindOrigin(arg interface{})
|
||||
GetBindOrigin() interface{}
|
||||
SetBindName(arg string)
|
||||
GetBindName() string
|
||||
SetBindResult(arg interface{})
|
||||
GetBindResult() interface{}
|
||||
SetBindResultSlice(arg reflect.Value)
|
||||
GetBindResultSlice() reflect.Value
|
||||
SetBindFields(arg []string)
|
||||
GetBindFields() []string
|
||||
SetBindType(arg BindType)
|
||||
GetBindType() BindType
|
||||
//SetBindLimit(arg int)
|
||||
//GetBindLimit() int
|
||||
BindParse(prefix string) error
|
||||
SetBindPrefix(arg string)
|
||||
GetBindPrefix() string
|
||||
ResetBindResultSlice()
|
||||
SetBindAll(arg []Data)
|
||||
GetBindAll() []Data
|
||||
ResetBinder()
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// NewBuilder 获取builder
|
||||
func NewBuilder(driver string) IBuilder {
|
||||
return NewBuilderDriver().Getter(driver)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package gorose
|
||||
|
||||
const (
|
||||
// DriverClickhouse ...
|
||||
DriverClickhouse = "clickhouse"
|
||||
)
|
||||
|
||||
// BuilderClickhouse ...
|
||||
type BuilderClickhouse struct {
|
||||
FieldQuotesDefault
|
||||
//IOrm
|
||||
driver string
|
||||
}
|
||||
|
||||
// sqlstr := fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s%s",
|
||||
// distinct, fields, table, join, where, group, having, order, limit, offset)
|
||||
// select {distinct} {fields} from {table} {join} {where} {group} {having} {order} {limit} {offset}
|
||||
// {execute} {table} {data} {where}
|
||||
func init() {
|
||||
var builder = &BuilderClickhouse{}
|
||||
NewBuilderDriver().Register(DriverClickhouse, builder)
|
||||
}
|
||||
|
||||
// NewBuilderClickhouse ...
|
||||
func NewBuilderClickhouse() *BuilderClickhouse {
|
||||
return new(BuilderClickhouse)
|
||||
}
|
||||
|
||||
// Clone : a new obj
|
||||
func (b *BuilderClickhouse) Clone() IBuilder {
|
||||
return &BuilderClickhouse{}
|
||||
}
|
||||
|
||||
// BuildQuery : build query sql string
|
||||
func (b *BuilderClickhouse) BuildQuery(o IOrm) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderClickhouse()).SetDriver(DriverClickhouse).BuildQuery()
|
||||
}
|
||||
|
||||
// BuildExecut : build execute sql string
|
||||
func (b *BuilderClickhouse) BuildExecute(o IOrm, operType string) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderClickhouse()).SetDriver(DriverClickhouse).BuildExecute(operType)
|
||||
}
|
||||
@@ -1,606 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gohouse/golib/structEngin"
|
||||
"github.com/gohouse/t"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var operator = []string{"=", ">", "<", "!=", "<>", ">=", "<=", "like", "not like",
|
||||
"in", "not in", "between", "not between", "regexp", "not regexp"}
|
||||
|
||||
// BuilderDefault 默认构造器, 其他的可以继承这个, 重写方法即可
|
||||
type BuilderDefault struct {
|
||||
IOrm
|
||||
operator []string
|
||||
placeholder int
|
||||
driver string
|
||||
bindValues []interface{}
|
||||
current IBuilder
|
||||
}
|
||||
|
||||
// NewBuilderDefault 初始化
|
||||
func NewBuilderDefault(o IOrm,current IBuilder) *BuilderDefault {
|
||||
//onceBuilderDefault.Do(func() {
|
||||
// builderDefault = new(BuilderDefault)
|
||||
// builderDefault.operator = operator
|
||||
// builderDefault.driver = "mysql"
|
||||
//})
|
||||
|
||||
builderDefault := new(BuilderDefault)
|
||||
builderDefault.operator = operator
|
||||
builderDefault.driver = "mysql"
|
||||
|
||||
builderDefault.IOrm = o
|
||||
// 每次使用的时候, 重置为0, 方便pg的占位符使用
|
||||
builderDefault.placeholder = 0
|
||||
|
||||
builderDefault.current = current
|
||||
return builderDefault
|
||||
}
|
||||
|
||||
// SetDriver 设置驱动, 方便获取占位符使用
|
||||
func (b *BuilderDefault) SetDriver(dr string) *BuilderDefault {
|
||||
b.driver = dr
|
||||
return b
|
||||
}
|
||||
|
||||
// SetBindValues ...
|
||||
func (b *BuilderDefault) SetBindValues(bv interface{}) {
|
||||
b.bindValues = append(b.bindValues, bv)
|
||||
}
|
||||
|
||||
// GetBindValues ...
|
||||
func (b *BuilderDefault) GetBindValues() []interface{} {
|
||||
return b.bindValues
|
||||
}
|
||||
|
||||
// GetPlaceholder 获取占位符
|
||||
func (b *BuilderDefault) GetPlaceholder() (phstr string) {
|
||||
switch b.driver {
|
||||
case "postgres":
|
||||
withLockContext(func() {
|
||||
ph := b.placeholder + 1
|
||||
phstr = fmt.Sprintf("$%v", ph)
|
||||
b.placeholder = ph
|
||||
})
|
||||
default:
|
||||
phstr = "?"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// BuildQuery 构造query
|
||||
func (b *BuilderDefault) BuildQuery() (sqlStr string, args []interface{}, err error) {
|
||||
//b.IOrm = o
|
||||
join, err := b.BuildJoin()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
where, err := b.BuildWhere()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
sqlStr = fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s%s",
|
||||
b.BuildDistinct(), b.BuildFields(), b.BuildTable(), join, where,
|
||||
b.BuildGroup(), b.BuildHaving(), b.BuildOrder(), b.BuildLimit(), b.BuildOffset())
|
||||
|
||||
//args = b.bindParams
|
||||
args = b.GetBindValues()
|
||||
return
|
||||
}
|
||||
|
||||
// BuilderDefault.BuildExecut : build execute query string
|
||||
func (b *BuilderDefault) BuildExecute(operType string) (sqlStr string, args []interface{}, err error) {
|
||||
// insert : {"name":"fizz, "website":"fizzday.net"} or {{"name":"fizz2", "website":"www.fizzday.net"}, {"name":"fizz", "website":"fizzday.net"}}}
|
||||
// update : {"name":"fizz", "website":"fizzday.net"}
|
||||
// delete : ...
|
||||
//b.IOrm = o
|
||||
var update, insertkey, insertval string
|
||||
if operType != "delete" {
|
||||
if b.IOrm.GetData() == nil {
|
||||
err = errors.New("insert,update请传入数据操作")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
update, insertkey, insertval = b.BuildData(operType)
|
||||
if update == "" && insertkey == "" {
|
||||
err = errors.New("传入数据为空")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var where string
|
||||
switch operType {
|
||||
case "insert":
|
||||
sqlStr = fmt.Sprintf("INSERT INTO %s (%s) VALUES %s", b.BuildTable(), insertkey, insertval)
|
||||
case "update":
|
||||
where, err = b.BuildWhere()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
if where == "" && b.IOrm.GetForce() == false {
|
||||
err = errors.New("出于安全考虑, update时where条件不能为空, 如果真的不需要where条件, 请使用Force()(如: db.xxx.Force().Update())")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
sqlStr = fmt.Sprintf("UPDATE %s SET %s%s", b.BuildTable(), update, where)
|
||||
case "delete":
|
||||
|
||||
where, err = b.BuildWhere()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
if where == "" && b.IOrm.GetForce() == false {
|
||||
err = errors.New("出于安全考虑, delete时where条件不能为空, 如果真的不需要where条件, 请使用Force()(如: db.xxx.Force().Delete())")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
sqlStr = fmt.Sprintf("DELETE FROM %s%s", b.BuildTable(), where)
|
||||
}
|
||||
|
||||
args = b.GetBindValues()
|
||||
return
|
||||
}
|
||||
|
||||
// BuildData : build inert or update data
|
||||
func (b *BuilderDefault) BuildData(operType string) (string, string, string) {
|
||||
data := b.IOrm.GetData()
|
||||
ref := reflect.Indirect(reflect.ValueOf(data))
|
||||
|
||||
switch ref.Kind() {
|
||||
case reflect.Struct:
|
||||
return b.parseData(operType, structEngin.New().SetExtraCols(b.IOrm.GetExtraCols()).StructContent2Map(data))
|
||||
case reflect.Map:
|
||||
var tmp = []map[string]interface{}{t.New(data).MapStringInterface()}
|
||||
return b.parseData(operType, tmp)
|
||||
case reflect.Slice:
|
||||
switch ref.Type().Elem().Kind() {
|
||||
case reflect.Struct:
|
||||
return b.parseData(operType, structEngin.New().SetExtraCols(b.IOrm.GetExtraCols()).StructContent2Map(data))
|
||||
case reflect.Map:
|
||||
return b.parseData(operType, t.New(data).SliceMapStringInterface())
|
||||
}
|
||||
case reflect.String:
|
||||
return data.(string), "", ""
|
||||
}
|
||||
return "", "", ""
|
||||
}
|
||||
|
||||
// BuildData2 ...
|
||||
func (b *BuilderDefault) BuildData2(operType string) (string, string, string) {
|
||||
// insert
|
||||
var dataFields []string
|
||||
var dataValues []string
|
||||
// update or delete
|
||||
var dataObj []string
|
||||
|
||||
data := b.IOrm.GetData()
|
||||
|
||||
switch data.(type) {
|
||||
case string:
|
||||
dataObj = append(dataObj, data.(string))
|
||||
case []map[string]interface{}, []Data: // insert multi datas ([]map[string]interface{})
|
||||
sliceData := t.New(data).Slice()
|
||||
for key := range sliceData[0].MapStringT() {
|
||||
if inArray(key, dataFields) == false {
|
||||
dataFields = append(dataFields, key)
|
||||
}
|
||||
}
|
||||
for _, itemt := range sliceData {
|
||||
item := itemt.MapStringT()
|
||||
var dataValuesSub []string
|
||||
for _, key := range dataFields {
|
||||
if item[key] == nil {
|
||||
dataValuesSub = append(dataValuesSub, "null")
|
||||
} else {
|
||||
dataValuesSub = append(dataValuesSub, b.GetPlaceholder())
|
||||
b.SetBindValues(item[key])
|
||||
}
|
||||
}
|
||||
dataValues = append(dataValues, "("+strings.Join(dataValuesSub, ",")+")")
|
||||
}
|
||||
case map[string]interface{}, Data: // update or insert (map[string]interface{})
|
||||
var dataValuesSub []string
|
||||
for key, val := range t.New(data).MapStringT() {
|
||||
if operType == "insert" {
|
||||
// insert
|
||||
dataFields = append(dataFields, key)
|
||||
if val.Interface() == nil {
|
||||
dataValuesSub = append(dataValuesSub, "null")
|
||||
} else {
|
||||
dataValuesSub = append(dataValuesSub, b.GetPlaceholder())
|
||||
b.SetBindValues(val.Interface())
|
||||
}
|
||||
} else if operType == "update" {
|
||||
// update
|
||||
if val.Interface() == nil {
|
||||
dataObj = append(dataObj, key+"=null")
|
||||
} else {
|
||||
dataObj = append(dataObj, key+"="+b.GetPlaceholder())
|
||||
b.SetBindValues(val.Interface())
|
||||
}
|
||||
}
|
||||
}
|
||||
if operType == "insert" {
|
||||
// insert
|
||||
dataValues = append(dataValues, "("+strings.Join(dataValuesSub, ",")+")")
|
||||
}
|
||||
default:
|
||||
//ref := reflect.Indirect(reflect.ValueOf(data))
|
||||
//switch ref.Kind() {
|
||||
//case reflect.Struct:
|
||||
// structEngin.New().StructContent2Map(data)
|
||||
//case reflect.Map:
|
||||
//case reflect.Slice:
|
||||
// switch ref.Type().Elem().Kind() {
|
||||
// case reflect.Struct:
|
||||
// case reflect.Map:
|
||||
// }
|
||||
//}
|
||||
|
||||
return "", "", ""
|
||||
}
|
||||
|
||||
return strings.Join(dataObj, ","), strings.Join(dataFields, ","), strings.Join(dataValues, ",")
|
||||
}
|
||||
|
||||
func (b *BuilderDefault) parseData(operType string, data []map[string]interface{}) (string, string, string) {
|
||||
// insert
|
||||
var insertFields []string
|
||||
var insertValues []string
|
||||
// update or delete
|
||||
var dataObj []string
|
||||
|
||||
if len(data) == 0 {
|
||||
return "", "", ""
|
||||
}
|
||||
|
||||
for key := range data[0] {
|
||||
if inArray(key, insertFields) == false {
|
||||
insertFields = append(insertFields, key)
|
||||
}
|
||||
}
|
||||
for _, item := range data {
|
||||
// 定义插入1条数据的存储
|
||||
var insertValuesSub []string
|
||||
for _, key := range insertFields {
|
||||
placeholder := b.GetPlaceholder()
|
||||
if item[key] == nil {
|
||||
if operType == "insert" {
|
||||
// 放入占位符
|
||||
insertValuesSub = append(insertValuesSub, placeholder)
|
||||
}
|
||||
// 保存真正的值为null
|
||||
b.SetBindValues("null")
|
||||
} else {
|
||||
if operType == "insert" {
|
||||
// 放入占位符
|
||||
insertValuesSub = append(insertValuesSub, placeholder)
|
||||
}
|
||||
// 保存真正的值
|
||||
b.SetBindValues(item[key])
|
||||
}
|
||||
//insertValues = append(insertValues, "("+strings.Join(insertValuesSub, ",")+")")
|
||||
// update
|
||||
dataObj = append(dataObj, fmt.Sprintf("%s = %s", b.current.AddFieldQuotes(key), placeholder))
|
||||
}
|
||||
insertValues = append(insertValues, "("+strings.Join(insertValuesSub, ",")+")")
|
||||
}
|
||||
var tmpInsertFields = insertFields[:0]
|
||||
for _,v := range insertFields {
|
||||
tmpInsertFields = append(tmpInsertFields, b.current.AddFieldQuotes(v))
|
||||
}
|
||||
return strings.Join(dataObj, ","), strings.Join(tmpInsertFields, ","), strings.Join(insertValues, ",")
|
||||
}
|
||||
|
||||
// BuildJoin ...
|
||||
func (b *BuilderDefault) BuildJoin() (s string, err error) {
|
||||
// 用户传入的join参数+join类型
|
||||
var join []interface{}
|
||||
var returnJoinArr []string
|
||||
joinArr := b.GetJoin()
|
||||
|
||||
for _, join = range joinArr {
|
||||
var w string
|
||||
var ok bool
|
||||
// 用户传入 join 的where值, 即第二个参数
|
||||
var args []interface{}
|
||||
|
||||
if len(join) != 2 {
|
||||
err = errors.New("join conditions are wrong")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取真正的用户传入的join参数
|
||||
if args, ok = join[1].([]interface{}); !ok {
|
||||
err = errors.New("join conditions are wrong")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
argsLength := len(args)
|
||||
var prefix = b.IOrm.GetISession().GetIEngin().GetPrefix()
|
||||
// 如果表名是 struct,则需要解析出表名
|
||||
if argsLength > 1 {
|
||||
// 只有长度大于2,才有可能使用对象.不然,就是个字符串
|
||||
switch args[0].(type) {
|
||||
case string:
|
||||
break
|
||||
default:
|
||||
rl := reflect.ValueOf(args[0])
|
||||
if tn := rl.MethodByName("TableName"); tn.IsValid() {
|
||||
args[0] = tn.Call(nil)[0].String()
|
||||
}
|
||||
}
|
||||
}
|
||||
switch argsLength {
|
||||
case 1: // join字符串 raw
|
||||
w = fmt.Sprintf("%s%s", prefix, args[0])
|
||||
case 2: // join表 + 字符串
|
||||
w = fmt.Sprintf("%s%s ON %s", prefix, args[0], args[1])
|
||||
case 3: // join表 + (a字段=b字段)
|
||||
w = fmt.Sprintf("%s%s ON %s = %s", prefix, args[0], args[1], args[2])
|
||||
case 4: // join表 + (a字段+关系+b字段)
|
||||
w = fmt.Sprintf("%s%s ON %s %s %s", prefix, args[0], args[1], args[2], args[3])
|
||||
default:
|
||||
err = errors.New("join format error")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
returnJoinArr = append(returnJoinArr, " "+join[0].(string)+" JOIN "+w)
|
||||
}
|
||||
|
||||
return strings.Join(returnJoinArr, " "), nil
|
||||
}
|
||||
|
||||
// BuildWhere ...
|
||||
func (b *BuilderDefault) BuildWhere() (where string, err error) {
|
||||
var beforeParseWhere = b.IOrm.GetWhere()
|
||||
where, err = b.parseWhere(b.IOrm)
|
||||
b.IOrm.SetWhere(beforeParseWhere)
|
||||
return If(where == "", "", " WHERE "+where).(string), err
|
||||
}
|
||||
|
||||
// BuildDistinct ...
|
||||
func (b *BuilderDefault) BuildDistinct() (dis string) {
|
||||
return If(b.IOrm.GetDistinct(), "DISTINCT ", "").(string)
|
||||
}
|
||||
|
||||
// BuildFields ...
|
||||
func (b *BuilderDefault) BuildFields() string {
|
||||
if len(b.IOrm.GetFields()) == 0 {
|
||||
return "*"
|
||||
}
|
||||
return strings.Join(b.IOrm.GetFields(), ",")
|
||||
}
|
||||
|
||||
// BuildTable ...
|
||||
func (b *BuilderDefault) BuildTable() string {
|
||||
return b.current.AddFieldQuotes(b.IOrm.GetTable())
|
||||
}
|
||||
|
||||
// BuildGroup ...
|
||||
func (b *BuilderDefault) BuildGroup() string {
|
||||
return If(b.IOrm.GetGroup() == "", "", " GROUP BY "+b.IOrm.GetGroup()).(string)
|
||||
}
|
||||
|
||||
// BuildHaving ...
|
||||
func (b *BuilderDefault) BuildHaving() string {
|
||||
return If(b.IOrm.GetHaving() == "", "", " HAVING "+b.IOrm.GetHaving()).(string)
|
||||
}
|
||||
|
||||
// BuildOrder ...
|
||||
func (b *BuilderDefault) BuildOrder() string {
|
||||
return If(b.IOrm.GetOrder() == "", "", " ORDER BY "+b.IOrm.GetOrder()).(string)
|
||||
}
|
||||
|
||||
// BuildLimit ...
|
||||
func (b *BuilderDefault) BuildLimit() string {
|
||||
//if b.IOrm.GetUnion() != nil {
|
||||
// return ""
|
||||
//}
|
||||
return If(b.IOrm.GetLimit() == 0, "", " LIMIT "+strconv.Itoa(b.IOrm.GetLimit())).(string)
|
||||
}
|
||||
|
||||
// BuildOffset ...
|
||||
func (b *BuilderDefault) BuildOffset() string {
|
||||
//if b.BuildLimit() == "" {
|
||||
// return ""
|
||||
//}
|
||||
if b.IOrm.GetUnion() != nil {
|
||||
return ""
|
||||
}
|
||||
return If(b.IOrm.GetOffset() == 0, "", " OFFSET "+strconv.Itoa(b.IOrm.GetOffset())).(string)
|
||||
}
|
||||
|
||||
// parseWhere : parse where condition
|
||||
func (b *BuilderDefault) parseWhere(ormApi IOrm) (string, error) {
|
||||
// 取出所有where
|
||||
wheres := ormApi.GetWhere()
|
||||
// where解析后存放每一项的容器
|
||||
var where []string
|
||||
|
||||
for _, args := range wheres {
|
||||
// and或者or条件
|
||||
var condition = args[0].(string)
|
||||
// 统计当前数组中有多少个参数
|
||||
params := args[1].([]interface{})
|
||||
paramsLength := len(params)
|
||||
|
||||
switch paramsLength {
|
||||
case 3: // 常规3个参数: {"id",">",1}
|
||||
res, err := b.parseParams(params, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
where = append(where, condition+" "+res)
|
||||
|
||||
case 2: // 常规2个参数: {"id",1}
|
||||
res, err := b.parseParams(params, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
where = append(where, condition+" "+res)
|
||||
case 1: // 二维数组或字符串
|
||||
switch paramReal := params[0].(type) {
|
||||
case string:
|
||||
where = append(where, condition+" ("+paramReal+")")
|
||||
case map[string]interface{}: // map
|
||||
var whereArr []string
|
||||
for key, val := range paramReal {
|
||||
whereArr = append(whereArr, b.current.AddFieldQuotes(key)+"="+b.GetPlaceholder())
|
||||
b.SetBindValues(val)
|
||||
}
|
||||
if len(whereArr) != 0 {
|
||||
where = append(where, condition+" ("+strings.Join(whereArr, " and ")+")")
|
||||
}
|
||||
case Data: // map
|
||||
var whereArr []string
|
||||
for key, val := range paramReal {
|
||||
whereArr = append(whereArr, b.current.AddFieldQuotes(key)+"="+b.GetPlaceholder())
|
||||
b.SetBindValues(val)
|
||||
}
|
||||
if len(whereArr) != 0 {
|
||||
where = append(where, condition+" ("+strings.Join(whereArr, " and ")+")")
|
||||
}
|
||||
case []interface{}: // 一维数组
|
||||
var whereArr []string
|
||||
whereMoreLength := len(paramReal)
|
||||
switch whereMoreLength {
|
||||
case 3, 2, 1:
|
||||
res, err := b.parseParams(paramReal, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
whereArr = append(whereArr, res)
|
||||
default:
|
||||
return "", errors.New("where data format is wrong")
|
||||
}
|
||||
if len(whereArr) != 0 {
|
||||
where = append(where, condition+" ("+strings.Join(whereArr, " and ")+")")
|
||||
}
|
||||
case [][]interface{}: // 二维数组
|
||||
var whereMore []string
|
||||
for _, arr := range paramReal { // {{"a", 1}, {"id", ">", 1}}
|
||||
whereMoreLength := len(arr)
|
||||
switch whereMoreLength {
|
||||
case 3, 2, 1:
|
||||
res, err := b.parseParams(arr, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
whereMore = append(whereMore, res)
|
||||
default:
|
||||
return "", errors.New("where data format is wrong")
|
||||
}
|
||||
}
|
||||
if len(whereMore) != 0 {
|
||||
where = append(where, condition+" ("+strings.Join(whereMore, " and ")+")")
|
||||
}
|
||||
case func():
|
||||
// 清空where,给嵌套的where让路,复用这个节点
|
||||
ormApi.SetWhere([][]interface{}{})
|
||||
|
||||
// 执行嵌套where放入Database struct
|
||||
paramReal()
|
||||
// 再解析一遍后来嵌套进去的where
|
||||
wherenested, err := b.parseWhere(ormApi)
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return "", err
|
||||
}
|
||||
// 嵌套的where放入一个括号内
|
||||
where = append(where, condition+" ("+wherenested+")")
|
||||
default:
|
||||
return "", errors.New("where data format is wrong")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 合并where,去掉左侧的空格,and,or并返回
|
||||
return strings.TrimLeft(
|
||||
strings.TrimPrefix(
|
||||
strings.TrimPrefix(
|
||||
strings.Trim(
|
||||
strings.Join(where, " "),
|
||||
" "),
|
||||
"and"),
|
||||
"or"),
|
||||
" "), nil
|
||||
}
|
||||
|
||||
/**
|
||||
* 将where条件中的参数转换为where条件字符串
|
||||
* example: {"id",">",1}, {"age", 18}
|
||||
*/
|
||||
// parseParams : 将where条件中的参数转换为where条件字符串
|
||||
func (b *BuilderDefault) parseParams(args []interface{}, ormApi IOrm) (s string, err error) {
|
||||
paramsLength := len(args)
|
||||
argsReal := args
|
||||
|
||||
// 存储当前所有数据的数组
|
||||
var paramsToArr []string
|
||||
|
||||
switch paramsLength {
|
||||
case 3: // 常规3个参数: {"id",">",1}
|
||||
//if !inArray(argsReal[1], b.GetRegex()) {
|
||||
if !inArray(argsReal[1], b.GetOperator()) {
|
||||
err = errors.New("where parameter is wrong")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//paramsToArr = append(paramsToArr, argsReal[0].(string))
|
||||
paramsToArr = append(paramsToArr, b.current.AddFieldQuotes(argsReal[0].(string)))
|
||||
paramsToArr = append(paramsToArr, argsReal[1].(string))
|
||||
|
||||
switch strings.Trim(strings.ToLower(t.New(argsReal[1]).String()), " ") {
|
||||
//case "like", "not like":
|
||||
// paramsToArr = append(paramsToArr, b.GetPlaceholder())
|
||||
// b.SetBindValues(argsReal[2])
|
||||
case "in", "not in":
|
||||
var tmp []string
|
||||
var ar2 = t.New(argsReal[2]).Slice()
|
||||
for _, item := range ar2 {
|
||||
tmp = append(tmp, b.GetPlaceholder())
|
||||
b.SetBindValues(t.New(item).Interface())
|
||||
}
|
||||
paramsToArr = append(paramsToArr, "("+strings.Join(tmp, ",")+")")
|
||||
|
||||
case "between", "not between":
|
||||
var ar2 = t.New(argsReal[2]).Slice()
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder()+" and "+b.GetPlaceholder())
|
||||
b.SetBindValues(ar2[0].Interface())
|
||||
b.SetBindValues(ar2[1].Interface())
|
||||
|
||||
default:
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder())
|
||||
b.SetBindValues(argsReal[2])
|
||||
}
|
||||
case 2:
|
||||
paramsToArr = append(paramsToArr, b.current.AddFieldQuotes(argsReal[0].(string)))
|
||||
paramsToArr = append(paramsToArr, "=")
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder())
|
||||
b.SetBindValues(argsReal[1])
|
||||
case 1:
|
||||
paramsToArr = append(paramsToArr, argsReal[0].(string))
|
||||
}
|
||||
|
||||
return strings.Join(paramsToArr, " "), nil
|
||||
}
|
||||
|
||||
// GetOperator ...
|
||||
func (b *BuilderDefault) GetOperator() []string {
|
||||
return b.operator
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// BuilderDriver ...
|
||||
type BuilderDriver struct {
|
||||
builders map[string]IBuilder
|
||||
b *sync.Map
|
||||
}
|
||||
|
||||
var builderDriverOnce sync.Once
|
||||
var builderDriver *BuilderDriver
|
||||
|
||||
// NewBuilderDriver ...
|
||||
func NewBuilderDriver() *BuilderDriver {
|
||||
builderDriverOnce.Do(func() {
|
||||
//builderDriver = &BuilderDriver{builders:make(map[string]IBuilder)}
|
||||
builderDriver = &BuilderDriver{b: &sync.Map{}}
|
||||
})
|
||||
return builderDriver
|
||||
}
|
||||
|
||||
// Register ...
|
||||
func (b *BuilderDriver) Register(driver string, val IBuilder) {
|
||||
//withLockContext(func() {
|
||||
// b.builders[driver] = val
|
||||
//})
|
||||
b.b.Store(driver, val)
|
||||
}
|
||||
|
||||
// Getter ...
|
||||
func (b *BuilderDriver) Getter(driver string) IBuilder {
|
||||
|
||||
//return b.builders[driver]
|
||||
if v, ok := b.b.Load(driver); ok {
|
||||
return v.(IBuilder)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// IBuilder ...
|
||||
type IBuilder interface {
|
||||
IFieldQuotes
|
||||
BuildQuery(orm IOrm) (sqlStr string, args []interface{}, err error)
|
||||
BuildExecute(orm IOrm, operType string) (sqlStr string, args []interface{}, err error)
|
||||
Clone() IBuilder
|
||||
//GetIOrm() IOrm
|
||||
}
|
||||
|
||||
// IFieldQuotes 给系统关键词冲突的字段加引号,如: mysql是反引号, pg是双引号
|
||||
type IFieldQuotes interface {
|
||||
AddFieldQuotes(field string) string
|
||||
}
|
||||
|
||||
type FieldQuotesDefault struct {
|
||||
|
||||
}
|
||||
|
||||
func (FieldQuotesDefault) AddFieldQuotes(field string) string {
|
||||
reg := regexp.MustCompile(`^\w+$`)
|
||||
if reg.MatchString(field) {
|
||||
return fmt.Sprintf("`%s`", field)
|
||||
}
|
||||
return field
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package gorose
|
||||
|
||||
const (
|
||||
// DriverMsSql ...
|
||||
DriverMsSql = "mssql"
|
||||
)
|
||||
|
||||
// BuilderMsSql ...
|
||||
type BuilderMsSql struct {
|
||||
FieldQuotesDefault
|
||||
//IOrm
|
||||
driver string
|
||||
}
|
||||
|
||||
// sqlstr := fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s%s",
|
||||
// distinct, fields, table, join, where, group, having, order, limit, offset)
|
||||
// select {distinct} {fields} from {table} {join} {where} {group} {having} {order} {limit} {offset}
|
||||
// {execute} {table} {data} {where}
|
||||
func init() {
|
||||
var builder = &BuilderMsSql{driver: DriverMsSql}
|
||||
NewBuilderDriver().Register(DriverMsSql, builder)
|
||||
}
|
||||
|
||||
// NewBuilderMsSql ...
|
||||
func NewBuilderMsSql() *BuilderMsSql {
|
||||
return new(BuilderMsSql)
|
||||
}
|
||||
|
||||
// Clone : a new obj
|
||||
func (b *BuilderMsSql) Clone() IBuilder {
|
||||
return &BuilderMsSql{driver: DriverMsSql}
|
||||
}
|
||||
|
||||
// BuildQuery : build query sql string
|
||||
func (b *BuilderMsSql) BuildQuery(o IOrm) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderMsSql()).SetDriver(b.driver).BuildQuery()
|
||||
}
|
||||
|
||||
// BuildExecut : build execute sql string
|
||||
func (b *BuilderMsSql) BuildExecute(o IOrm, operType string) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderMsSql()).SetDriver(b.driver).BuildExecute(operType)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package gorose
|
||||
|
||||
const (
|
||||
// DriverMysql ...
|
||||
DriverMysql = "mysql"
|
||||
)
|
||||
|
||||
// BuilderMysql ...
|
||||
type BuilderMysql struct {
|
||||
FieldQuotesDefault
|
||||
//IOrm
|
||||
driver string
|
||||
}
|
||||
|
||||
var _ IBuilder = (*BuilderMysql)(nil)
|
||||
|
||||
// sqlstr := fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s%s",
|
||||
// distinct, fields, table, join, where, group, having, order, limit, offset)
|
||||
// select {distinct} {fields} from {table} {join} {where} {group} {having} {order} {limit} {offset}
|
||||
// {execute} {table} {data} {where}
|
||||
func init() {
|
||||
NewBuilderDriver().Register(DriverMysql, NewBuilderMysql())
|
||||
}
|
||||
|
||||
// NewBuilderMysql ...
|
||||
func NewBuilderMysql() *BuilderMysql {
|
||||
return new(BuilderMysql)
|
||||
}
|
||||
|
||||
// Clone : a new obj
|
||||
func (b *BuilderMysql) Clone() IBuilder {
|
||||
return NewBuilderMysql()
|
||||
}
|
||||
|
||||
// BuildQuery : build query sql string
|
||||
func (b *BuilderMysql) BuildQuery(o IOrm) (sqlStr string, args []interface{}, err error) {
|
||||
//fmt.Println(o.GetTable(),o.GetWhere())
|
||||
sqlStr, args, err = NewBuilderDefault(o, NewBuilderMysql()).SetDriver(DriverMysql).BuildQuery()
|
||||
return
|
||||
}
|
||||
|
||||
// BuildExecut : build execute sql string
|
||||
func (b *BuilderMysql) BuildExecute(o IOrm, operType string) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderMysql()).SetDriver(DriverMysql).BuildExecute(operType)
|
||||
}
|
||||
@@ -1,467 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gohouse/golib/structEngin"
|
||||
"github.com/gohouse/t"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// DriverOracle ...
|
||||
DriverOracle = "oci8"
|
||||
)
|
||||
|
||||
var (
|
||||
operatorOracle = []string{"=", ">", "<", "!=", "<>", ">=", "<=", "like", "not like",
|
||||
"intersect", "minus", "union", "||", "in", "not in", "between", "not between"}
|
||||
)
|
||||
|
||||
// BuilderOracle ...
|
||||
type BuilderOracle struct {
|
||||
FieldQuotesDefault
|
||||
BuilderDefault
|
||||
}
|
||||
|
||||
// NewBuilderOracle ...
|
||||
func NewBuilderOracle(o IOrm) *BuilderOracle {
|
||||
//onceBuilderDefault.Do(func() {
|
||||
// builderOracle = new(BuilderOracle)
|
||||
// builderOracle.operator = operatorOracle
|
||||
//})
|
||||
builderOracle := new(BuilderOracle)
|
||||
builderOracle.operator = operatorOracle
|
||||
builderOracle.IOrm = o
|
||||
// 每次使用的时候, 重置为0, 方便pg的占位符使用
|
||||
builderOracle.placeholder = 0
|
||||
|
||||
return builderOracle
|
||||
}
|
||||
|
||||
func init() {
|
||||
var builder = &BuilderOracle{}
|
||||
NewBuilderDriver().Register(DriverOracle, builder)
|
||||
}
|
||||
|
||||
// Clone : a new obj
|
||||
func (b *BuilderOracle) Clone() IBuilder {
|
||||
return &BuilderOracle{}
|
||||
}
|
||||
|
||||
// SetDriver 设置驱动, 方便获取占位符使用
|
||||
func (b *BuilderOracle) SetDriver(dr string) *BuilderOracle {
|
||||
b.driver = dr
|
||||
return b
|
||||
}
|
||||
|
||||
// GetPlaceholder 获取占位符
|
||||
func (b *BuilderOracle) GetPlaceholder() (phstr string) {
|
||||
withLockContext(func() {
|
||||
ph := b.placeholder + 1
|
||||
phstr = fmt.Sprintf(":%v", ph)
|
||||
b.placeholder = ph
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// BuildQueryOra ...
|
||||
func (b *BuilderOracle) BuildQueryOra() (sqlStr string, args []interface{}, err error) {
|
||||
//b.IOrm = o
|
||||
join, err := b.BuildJoin()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
where, err := b.BuildWhere()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if len(b.GetJoin()) > 0 {
|
||||
b.GetFields()
|
||||
}
|
||||
|
||||
// 默认情况
|
||||
fieldsStr := b.BuildFields()
|
||||
tableName := b.BuildTable()
|
||||
sqlStr = fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s", b.BuildDistinct(), fieldsStr,
|
||||
tableName, join, where, b.BuildLimit(), b.BuildGroup(), b.BuildHaving(), b.BuildOrder())
|
||||
|
||||
// 批量取数据需嵌套写法
|
||||
if b.GetLimit() > 0 {
|
||||
aliasNameA := "tabA"
|
||||
aliasNameB := "tabB"
|
||||
page := b.GetOffset()/b.GetLimit() + 1
|
||||
startRow := (page-1)*b.GetLimit() + 1
|
||||
endRow := page*b.GetLimit() + 1
|
||||
|
||||
if fieldsStr == "*" {
|
||||
fieldsStr = b.GetTable() + ".*, rownum r"
|
||||
} else {
|
||||
if b.GetGroup() == "" {
|
||||
fieldsStr = fieldsStr + ", rownum r"
|
||||
}
|
||||
}
|
||||
|
||||
// 没有group by需要1层嵌套, 有group by需要2层嵌套
|
||||
// 如果考虑orderby优化,还需要一层嵌套。目前未考虑
|
||||
if b.GetGroup() == "" {
|
||||
sqlStr = fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s", b.BuildDistinct(), fieldsStr,
|
||||
tableName, join, where, b.BuildLimit(), b.BuildOrder())
|
||||
|
||||
sqlStr = fmt.Sprintf("select * from (%s) %s where %s.r>=%s",
|
||||
sqlStr, aliasNameA, aliasNameA, strconv.Itoa(startRow))
|
||||
} else {
|
||||
sqlStr = fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s", b.BuildDistinct(), fieldsStr,
|
||||
tableName, join, where, b.BuildGroup(), b.BuildHaving(), b.BuildOrder())
|
||||
|
||||
sqlStr = fmt.Sprintf(
|
||||
"select * from (select %s, rownum r from (%s) %s where rownum<%s ) %s where %s.r>=%s",
|
||||
aliasNameA+".*", sqlStr, aliasNameA, strconv.Itoa(endRow), aliasNameB, aliasNameB,
|
||||
strconv.Itoa(startRow))
|
||||
}
|
||||
}
|
||||
|
||||
//args = b.bindParams
|
||||
args = b.IOrm.GetBindValues()
|
||||
return
|
||||
}
|
||||
|
||||
// BuildExecuteOra ...
|
||||
func (b *BuilderOracle) BuildExecuteOra(operType string) (sqlStr string, args []interface{}, err error) {
|
||||
// insert : {"name":"fizz, "website":"fizzday.net"} or {{"name":"fizz2", "website":"www.fizzday.net"}, {"name":"fizz", "website":"fizzday.net"}}}
|
||||
// update : {"name":"fizz", "website":"fizzday.net"}
|
||||
// delete : ...
|
||||
//b.IOrm = o
|
||||
var update, insertkey, insertval string
|
||||
if operType != "delete" {
|
||||
if b.IOrm.GetData() == nil {
|
||||
err = errors.New("insert,update请传入数据操作")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
update, insertkey, insertval = b.BuildData(operType)
|
||||
}
|
||||
|
||||
where, err := b.BuildWhere()
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
switch operType {
|
||||
case "insert":
|
||||
sqlStr = fmt.Sprintf("INSERT INTO %s (%s) VALUES %s", b.BuildTable(), insertkey, insertval)
|
||||
case "update":
|
||||
if where == "" && b.IOrm.GetForce() == false {
|
||||
err = errors.New("出于安全考虑, update时where条件不能为空, 如果真的不需要where条件, 请使用Force()(如: db.xxx.Force().Update())")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
sqlStr = fmt.Sprintf("UPDATE %s SET %s%s", b.BuildTable(), update, where)
|
||||
case "delete":
|
||||
if where == "" && b.IOrm.GetForce() == false {
|
||||
err = errors.New("出于安全考虑, delete时where条件不能为空, 如果真的不需要where条件, 请使用Force()(如: db.xxx.Force().Delete())")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
sqlStr = fmt.Sprintf("DELETE FROM %s%s", b.BuildTable(), where)
|
||||
}
|
||||
|
||||
args = b.IOrm.GetBindValues()
|
||||
return
|
||||
}
|
||||
|
||||
// BuildData ...
|
||||
func (b *BuilderOracle) BuildData(operType string) (string, string, string) {
|
||||
data := b.IOrm.GetData()
|
||||
ref := reflect.Indirect(reflect.ValueOf(data))
|
||||
|
||||
switch ref.Kind() {
|
||||
case reflect.Struct:
|
||||
return b.parseData(operType, structEngin.New().SetExtraCols(b.IOrm.GetExtraCols()).StructContent2Map(data))
|
||||
case reflect.Map:
|
||||
var tmp = []map[string]interface{}{t.New(data).MapStringInterface()}
|
||||
return b.parseData(operType, tmp)
|
||||
case reflect.Slice:
|
||||
switch ref.Type().Elem().Kind() {
|
||||
case reflect.Struct:
|
||||
return b.parseData(operType, structEngin.New().SetExtraCols(b.IOrm.GetExtraCols()).StructContent2Map(data))
|
||||
case reflect.Map:
|
||||
return b.parseData(operType, t.New(data).SliceMapStringInterface())
|
||||
}
|
||||
}
|
||||
return "", "", ""
|
||||
}
|
||||
|
||||
// BuildData2 ...
|
||||
func (b *BuilderOracle) BuildData2(operType string) (string, string, string) {
|
||||
return b.BuilderDefault.BuildData2(operType)
|
||||
}
|
||||
|
||||
func (b *BuilderOracle) parseData(operType string, data []map[string]interface{}) (string, string, string) {
|
||||
// insert
|
||||
var dataFields []string
|
||||
var dataValues []string
|
||||
// update or delete
|
||||
var dataObj []string
|
||||
|
||||
for key := range data[0] {
|
||||
if inArray(key, dataFields) == false {
|
||||
dataFields = append(dataFields, key)
|
||||
}
|
||||
}
|
||||
for _, item := range data {
|
||||
// 定义1条数据的存储
|
||||
var dataValuesSub []string
|
||||
for _, key := range dataFields {
|
||||
if item[key] == nil {
|
||||
// 放入占位符
|
||||
dataValuesSub = append(dataValuesSub, b.GetPlaceholder())
|
||||
// 保存真正的值为null
|
||||
b.IOrm.SetBindValues("null")
|
||||
} else {
|
||||
// 放入占位符
|
||||
dataValuesSub = append(dataValuesSub, b.GetPlaceholder())
|
||||
// 保存真正的值
|
||||
b.IOrm.SetBindValues(item[key])
|
||||
}
|
||||
// update
|
||||
dataObj = append(dataObj, fmt.Sprintf("%s=%s", key, b.GetPlaceholder()))
|
||||
}
|
||||
dataValues = append(dataValues, "("+strings.Join(dataValuesSub, ",")+")")
|
||||
}
|
||||
return strings.Join(dataObj, ","), strings.Join(dataFields, ","), strings.Join(dataValues, ",")
|
||||
}
|
||||
|
||||
// BuildJoin ...
|
||||
func (b *BuilderOracle) BuildJoin() (s string, err error) {
|
||||
return b.BuilderDefault.BuildJoin()
|
||||
}
|
||||
|
||||
// BuildWhere ...
|
||||
func (b *BuilderOracle) BuildWhere() (where string, err error) {
|
||||
var beforeParseWhere = b.IOrm.GetWhere()
|
||||
where, err = b.parseWhere(b.IOrm)
|
||||
b.IOrm.SetWhere(beforeParseWhere)
|
||||
return If(where == "", "", " WHERE "+where).(string), err
|
||||
}
|
||||
|
||||
// BuildDistinct ...
|
||||
func (b *BuilderOracle) BuildDistinct() (dis string) {
|
||||
return b.BuilderDefault.BuildDistinct()
|
||||
}
|
||||
|
||||
// BuildFields ...
|
||||
func (b *BuilderOracle) BuildFields() string {
|
||||
return b.BuilderDefault.BuildFields()
|
||||
}
|
||||
|
||||
// BuildTable ...
|
||||
func (b *BuilderOracle) BuildTable() string {
|
||||
return b.BuilderDefault.BuildTable()
|
||||
}
|
||||
|
||||
// BuildGroup ...
|
||||
func (b *BuilderOracle) BuildGroup() string {
|
||||
return b.BuilderDefault.BuildGroup()
|
||||
}
|
||||
|
||||
// BuildHaving ...
|
||||
func (b *BuilderOracle) BuildHaving() string {
|
||||
return b.BuilderDefault.BuildHaving()
|
||||
}
|
||||
|
||||
// BuildOrder ...
|
||||
func (b *BuilderOracle) BuildOrder() string {
|
||||
return b.BuilderDefault.BuildOrder()
|
||||
}
|
||||
|
||||
// BuildLimit ...
|
||||
func (b *BuilderOracle) BuildLimit() string {
|
||||
//if b.IOrm.GetUnion() != nil {
|
||||
// return ""
|
||||
//}
|
||||
|
||||
if b.GetLimit() == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
page := b.GetOffset()/b.GetLimit() + 1
|
||||
endRow := page*b.GetLimit() + 1
|
||||
|
||||
var limitStr string
|
||||
if len(b.IOrm.GetWhere()) > 0 {
|
||||
limitStr = fmt.Sprintf(" and rownum < %d", endRow)
|
||||
} else {
|
||||
limitStr = fmt.Sprintf(" where rownum < %d", endRow)
|
||||
}
|
||||
return If(b.IOrm.GetLimit() == 0, "", limitStr).(string)
|
||||
}
|
||||
|
||||
// BuildOffset ...
|
||||
func (b *BuilderOracle) BuildOffset() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *BuilderOracle) parseWhere(ormApi IOrm) (string, error) {
|
||||
// 取出所有where
|
||||
wheres := ormApi.GetWhere()
|
||||
// where解析后存放每一项的容器
|
||||
var where []string
|
||||
|
||||
for _, args := range wheres {
|
||||
// and或者or条件
|
||||
var condition = args[0].(string)
|
||||
// 统计当前数组中有多少个参数
|
||||
params := args[1].([]interface{})
|
||||
paramsLength := len(params)
|
||||
|
||||
switch paramsLength {
|
||||
case 3: // 常规3个参数: {"id",">",1}
|
||||
res, err := b.parseParams(params, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
where = append(where, condition+" "+res)
|
||||
|
||||
case 2: // 常规2个参数: {"id",1}
|
||||
res, err := b.parseParams(params, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
where = append(where, condition+" "+res)
|
||||
case 1: // 二维数组或字符串
|
||||
switch paramReal := params[0].(type) {
|
||||
case string:
|
||||
where = append(where, condition+" ("+paramReal+")")
|
||||
case map[string]interface{}: // 一维数组
|
||||
var whereArr []string
|
||||
for key, val := range paramReal {
|
||||
whereArr = append(whereArr, key+"="+b.GetPlaceholder())
|
||||
b.IOrm.SetBindValues(val)
|
||||
}
|
||||
where = append(where, condition+" ("+strings.Join(whereArr, " and ")+")")
|
||||
case [][]interface{}: // 二维数组
|
||||
var whereMore []string
|
||||
for _, arr := range paramReal { // {{"a", 1}, {"id", ">", 1}}
|
||||
whereMoreLength := len(arr)
|
||||
switch whereMoreLength {
|
||||
case 3:
|
||||
res, err := b.parseParams(arr, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
whereMore = append(whereMore, res)
|
||||
case 2:
|
||||
res, err := b.parseParams(arr, ormApi)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
whereMore = append(whereMore, res)
|
||||
default:
|
||||
return "", errors.New("where data format is wrong")
|
||||
}
|
||||
}
|
||||
where = append(where, condition+" ("+strings.Join(whereMore, " and ")+")")
|
||||
case func():
|
||||
// 清空where,给嵌套的where让路,复用这个节点
|
||||
ormApi.SetWhere([][]interface{}{})
|
||||
|
||||
// 执行嵌套where放入Database struct
|
||||
paramReal()
|
||||
// 再解析一遍后来嵌套进去的where
|
||||
wherenested, err := b.parseWhere(ormApi)
|
||||
if err != nil {
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return "", err
|
||||
}
|
||||
// 嵌套的where放入一个括号内
|
||||
where = append(where, condition+" ("+wherenested+")")
|
||||
default:
|
||||
return "", errors.New("where data format is wrong")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 合并where,去掉左侧的空格,and,or并返回
|
||||
return strings.TrimLeft(
|
||||
strings.TrimPrefix(
|
||||
strings.TrimPrefix(
|
||||
strings.Trim(
|
||||
strings.Join(where, " "),
|
||||
" "),
|
||||
"and"),
|
||||
"or"),
|
||||
" "), nil
|
||||
}
|
||||
|
||||
func (b *BuilderOracle) parseParams(args []interface{}, ormApi IOrm) (s string, err error) {
|
||||
paramsLength := len(args)
|
||||
argsReal := args
|
||||
|
||||
// 存储当前所有数据的数组
|
||||
var paramsToArr []string
|
||||
|
||||
switch paramsLength {
|
||||
case 3: // 常规3个参数: {"id",">",1}
|
||||
//if !inArray(argsReal[1], b.GetRegex()) {
|
||||
if !inArray(argsReal[1], b.GetOperator()) {
|
||||
err = errors.New("where parameter is wrong")
|
||||
b.IOrm.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paramsToArr = append(paramsToArr, argsReal[0].(string))
|
||||
paramsToArr = append(paramsToArr, argsReal[1].(string))
|
||||
|
||||
switch argsReal[1] {
|
||||
case "like", "not like":
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder())
|
||||
b.IOrm.SetBindValues(argsReal[2])
|
||||
case "in", "not in":
|
||||
var tmp []string
|
||||
var ar2 = t.New(argsReal[2]).Slice()
|
||||
for _, item := range ar2 {
|
||||
tmp = append(tmp, b.GetPlaceholder())
|
||||
b.IOrm.SetBindValues(t.New(item).Interface())
|
||||
}
|
||||
paramsToArr = append(paramsToArr, "("+strings.Join(tmp, ",")+")")
|
||||
case "between", "not between":
|
||||
var ar2 = t.New(argsReal[2]).Slice()
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder()+" and "+b.GetPlaceholder())
|
||||
b.IOrm.SetBindValues(ar2[0].Interface())
|
||||
b.IOrm.SetBindValues(ar2[1].Interface())
|
||||
default:
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder())
|
||||
b.IOrm.SetBindValues(argsReal[2])
|
||||
}
|
||||
case 2:
|
||||
paramsToArr = append(paramsToArr, argsReal[0].(string))
|
||||
paramsToArr = append(paramsToArr, "=")
|
||||
paramsToArr = append(paramsToArr, b.GetPlaceholder())
|
||||
b.IOrm.SetBindValues(argsReal[1])
|
||||
}
|
||||
|
||||
return strings.Join(paramsToArr, " "), nil
|
||||
}
|
||||
|
||||
// GetOperator ...
|
||||
func (b *BuilderOracle) GetOperator() []string {
|
||||
return b.BuilderDefault.GetOperator()
|
||||
}
|
||||
|
||||
// 实现接口
|
||||
// BuildQuery : build query sql string
|
||||
func (b *BuilderOracle) BuildQuery(o IOrm) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderOracle(o).SetDriver(DriverOracle).BuildQueryOra()
|
||||
}
|
||||
|
||||
// BuildExecut : build execute sql string
|
||||
func (b *BuilderOracle) BuildExecute(o IOrm, operType string) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderOracle(o).SetDriver(DriverOracle).BuildExecuteOra(operType)
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const (
|
||||
// DriverPostgres ...
|
||||
DriverPostgres = "postgres"
|
||||
)
|
||||
|
||||
// BuilderPostgres ...
|
||||
type BuilderPostgres struct {
|
||||
//IOrm
|
||||
driver string
|
||||
}
|
||||
|
||||
// sqlstr := fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s%s",
|
||||
// distinct, fields, table, join, where, group, having, order, limit, offset)
|
||||
// select {distinct} {fields} from {table} {join} {where} {group} {having} {order} {limit} {offset}
|
||||
// {execute} {table} {data} {where}
|
||||
func init() {
|
||||
var builder = &BuilderPostgres{driver: DriverPostgres}
|
||||
NewBuilderDriver().Register(DriverPostgres, builder)
|
||||
}
|
||||
|
||||
// NewBuilderPostgres ...
|
||||
func NewBuilderPostgres() *BuilderPostgres {
|
||||
return new(BuilderPostgres)
|
||||
}
|
||||
|
||||
// Clone : a new obj
|
||||
func (b *BuilderPostgres) Clone() IBuilder {
|
||||
return &BuilderPostgres{driver: DriverPostgres}
|
||||
}
|
||||
|
||||
// BuildQuery : build query sql string
|
||||
func (b *BuilderPostgres) BuildQuery(o IOrm) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderPostgres()).SetDriver(b.driver).BuildQuery()
|
||||
}
|
||||
|
||||
// BuildExecut : build execute sql string
|
||||
func (b *BuilderPostgres) BuildExecute(o IOrm, operType string) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderPostgres()).SetDriver(b.driver).BuildExecute(operType)
|
||||
}
|
||||
|
||||
func (*BuilderPostgres) AddFieldQuotes(field string) string {
|
||||
reg := regexp.MustCompile(`^\w+$`)
|
||||
if reg.MatchString(field) {
|
||||
return fmt.Sprintf(`"%s"`, field)
|
||||
}
|
||||
return field
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package gorose
|
||||
|
||||
const (
|
||||
// DriverSqlite3 ...
|
||||
DriverSqlite3 = "sqlite3"
|
||||
)
|
||||
|
||||
// BuilderSqlite3 ...
|
||||
type BuilderSqlite3 struct {
|
||||
FieldQuotesDefault
|
||||
//IOrm
|
||||
driver string
|
||||
}
|
||||
|
||||
// sqlstr := fmt.Sprintf("SELECT %s%s FROM %s%s%s%s%s%s%s%s",
|
||||
// distinct, fields, table, join, where, group, having, order, limit, offset)
|
||||
// select {distinct} {fields} from {table} {join} {where} {group} {having} {order} {limit} {offset}
|
||||
// {execute} {table} {data} {where}
|
||||
func init() {
|
||||
var builder = &BuilderSqlite3{}
|
||||
NewBuilderDriver().Register(DriverSqlite3, builder)
|
||||
}
|
||||
|
||||
// NewBuilderSqlite3 ...
|
||||
func NewBuilderSqlite3() *BuilderSqlite3 {
|
||||
return new(BuilderSqlite3)
|
||||
}
|
||||
|
||||
// Clone : a new obj
|
||||
func (b *BuilderSqlite3) Clone() IBuilder {
|
||||
return &BuilderSqlite3{driver: DriverSqlite3}
|
||||
}
|
||||
|
||||
// BuildQuery : build query sql string
|
||||
func (b *BuilderSqlite3) BuildQuery(o IOrm) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderSqlite3()).SetDriver(b.driver).BuildQuery()
|
||||
}
|
||||
|
||||
// BuildExecut : build execute sql string
|
||||
func (b *BuilderSqlite3) BuildExecute(o IOrm, operType string) (sqlStr string, args []interface{}, err error) {
|
||||
return NewBuilderDefault(o, NewBuilderSqlite3()).SetDriver(b.driver).BuildExecute(operType)
|
||||
}
|
||||
65
config.go
65
config.go
@@ -1,20 +1,59 @@
|
||||
package gorose
|
||||
|
||||
// Config ...
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Driver string `json:"driver"` // 驱动: mysql/sqlite3/oracle/mssql/postgres/clickhouse, 如果集群配置了驱动, 这里可以省略
|
||||
// mysql 示例:
|
||||
// root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true
|
||||
Dsn string `json:"dsn"` // 数据库链接
|
||||
SetMaxOpenConns int `json:"setMaxOpenConns"` // (连接池)最大打开的连接数,默认值为0表示不限制
|
||||
SetMaxIdleConns int `json:"setMaxIdleConns"` // (连接池)闲置的连接数, 默认0
|
||||
Prefix string `json:"prefix"` // 表前缀, 如果集群配置了前缀, 这里可以省略
|
||||
Driver string
|
||||
DSN string
|
||||
Prefix string
|
||||
Weight int8 // 权重越高,选中的概率越大
|
||||
MaxIdleConns int
|
||||
MaxOpenConns int
|
||||
ConnMaxLifetime time.Duration
|
||||
ConnMaxIdleTime time.Duration
|
||||
}
|
||||
|
||||
// ConfigCluster ...
|
||||
type ConfigCluster struct {
|
||||
Master []Config // 主
|
||||
Slave []Config // 从
|
||||
Driver string // 驱动
|
||||
Prefix string // 前缀
|
||||
WriteConf []Config
|
||||
ReadConf []Config
|
||||
}
|
||||
|
||||
//type ConfigMulti map[string]ConfigCluster
|
||||
|
||||
func (c ConfigCluster) init() (master []*sql.DB, slave []*sql.DB) {
|
||||
if len(c.WriteConf) > 0 {
|
||||
for _, v := range c.WriteConf {
|
||||
master = append(master, c.initDB(&v))
|
||||
}
|
||||
}
|
||||
if len(c.ReadConf) > 0 {
|
||||
for _, v := range c.ReadConf {
|
||||
slave = append(master, c.initDB(&v))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (c ConfigCluster) initDB(v *Config) *sql.DB {
|
||||
db, err := sql.Open(v.Driver, v.DSN)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
if v.MaxIdleConns > 0 {
|
||||
db.SetMaxIdleConns(v.MaxIdleConns)
|
||||
}
|
||||
if v.MaxOpenConns > 0 {
|
||||
db.SetMaxOpenConns(v.MaxOpenConns)
|
||||
}
|
||||
if v.ConnMaxLifetime > 0 {
|
||||
db.SetConnMaxLifetime(v.ConnMaxLifetime)
|
||||
}
|
||||
if v.ConnMaxIdleTime > 0 {
|
||||
db.SetConnMaxIdleTime(v.ConnMaxIdleTime)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
18
context.go
Normal file
18
context.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package gorose
|
||||
|
||||
type Context struct {
|
||||
TableClause *TableClause
|
||||
SelectClause *SelectClause
|
||||
JoinClause *JoinClause
|
||||
WhereClause *WhereClause
|
||||
GroupClause *GroupClause
|
||||
HavingClause *HavingClause
|
||||
OrderByClause *OrderByClause
|
||||
LimitOffsetClause *LimitOffsetClause
|
||||
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func NewContext(prefix string) *Context {
|
||||
return &Context{Prefix: prefix}
|
||||
}
|
||||
137
database.go
Normal file
137
database.go
Normal file
@@ -0,0 +1,137 @@
|
||||
package gorose
|
||||
|
||||
type Database struct {
|
||||
Driver *Driver
|
||||
Engin *Engin
|
||||
Context *Context
|
||||
}
|
||||
|
||||
func NewDatabase(g *GoRose) *Database {
|
||||
return &Database{
|
||||
Driver: NewDriver(GetDriver(g.driver)),
|
||||
Engin: NewEngin(g),
|
||||
Context: NewContext(g.prefix),
|
||||
}
|
||||
}
|
||||
func (db *Database) Table(table any, alias ...string) *Database {
|
||||
db.Context.TableClause.Table(table, alias...)
|
||||
return db
|
||||
}
|
||||
// Select specifies the columns to retrieve.
|
||||
// Select("a","b")
|
||||
// Select("a.id as aid","b.id bid")
|
||||
// Select("id,nickname name")
|
||||
func (db *Database) Select(columns ...string) *Database {
|
||||
db.Context.SelectClause.Select(columns...)
|
||||
return db
|
||||
}
|
||||
// AddSelect 添加选择列
|
||||
func (db *Database) AddSelect(columns ...string) *Database {
|
||||
db.Context.SelectClause.AddSelect(columns...)
|
||||
return db
|
||||
}
|
||||
// SelectRaw 允许直接在查询中插入原始SQL片段作为选择列。
|
||||
func (db *Database) SelectRaw(raw string, binds ...any) *Database {
|
||||
db.Context.SelectClause.SelectRaw(raw, binds...)
|
||||
return db
|
||||
}
|
||||
|
||||
// Join clause
|
||||
func (db *Database) Join(table any, argOrFn ...any) *Database {
|
||||
db.Context.JoinClause.Join(table, argOrFn...)
|
||||
return db
|
||||
}
|
||||
// LeftJoin clause
|
||||
func (db *Database) LeftJoin(table any, argOrFn ...any) *Database {
|
||||
db.Context.JoinClause.LeftJoin(table, argOrFn...)
|
||||
return db
|
||||
}
|
||||
// RightJoin clause
|
||||
func (db *Database) RightJoin(table any, argOrFn ...any) *Database {
|
||||
db.Context.JoinClause.RightJoin(table, argOrFn...)
|
||||
return db
|
||||
}
|
||||
// CrossJoin clause
|
||||
func (db *Database) CrossJoin(table any, argOrFn ...any) *Database {
|
||||
db.Context.JoinClause.CrossJoin(table, argOrFn...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) Where(column any, argsOrclosure ...any) *Database {
|
||||
db.Context.WhereClause.Where(column, argsOrclosure...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhere(column any, argsOrclosure ...any) *Database {
|
||||
db.Context.WhereClause.OrWhere(column, argsOrclosure...)
|
||||
return db
|
||||
}
|
||||
|
||||
// WhereRaw 在查询中添加一个原生SQL“where”条件。
|
||||
//
|
||||
// sql: 原生SQL条件字符串。
|
||||
// bindings: SQL绑定参数数组。
|
||||
func (db *Database) WhereRaw(raw string, bindings ...any) *Database {
|
||||
db.Context.WhereClause.WhereRaw(raw, bindings...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereRaw(raw string, bindings ...any) *Database {
|
||||
db.Context.WhereClause.OrWhereRaw(raw, bindings...)
|
||||
return db
|
||||
}
|
||||
|
||||
// GroupBy 添加 GROUP BY 子句
|
||||
func (db *Database) GroupBy(columns ...string) *Database {
|
||||
db.Context.GroupClause.GroupBy(columns...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) GroupByRaw(columns ...string) *Database {
|
||||
db.Context.GroupClause.GroupByRaw(columns...)
|
||||
return db
|
||||
}
|
||||
|
||||
// Having 添加 HAVING 子句, 同where
|
||||
func (db *Database) Having(column any, argsOrClosure ...any) *Database {
|
||||
db.Context.HavingClause.Where(column, argsOrClosure...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrHaving(column any, argsOrClosure ...any) *Database {
|
||||
db.Context.HavingClause.OrWhere(column, argsOrClosure...)
|
||||
return db
|
||||
}
|
||||
|
||||
// HavingRaw 添加 HAVING 子句, 同where
|
||||
func (db *Database) HavingRaw(raw string, argsOrClosure ...any) *Database {
|
||||
db.Context.HavingClause.WhereRaw(raw, argsOrClosure...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrHavingRaw(raw string, argsOrClosure ...any) *Database {
|
||||
db.Context.HavingClause.OrWhereRaw(raw, argsOrClosure...)
|
||||
return db
|
||||
}
|
||||
|
||||
// OrderBy adds an ORDER BY clause to the query.
|
||||
func (db *Database) OrderBy(column string, directions ...string) *Database {
|
||||
db.Context.OrderByClause.OrderBy(column, directions...)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrderByRaw(column string) *Database {
|
||||
db.Context.OrderByClause.OrderByRaw(column)
|
||||
return db
|
||||
}
|
||||
|
||||
// Limit 设置查询结果的限制数量。
|
||||
func (db *Database) Limit(limit int) *Database {
|
||||
db.Context.LimitOffsetClause.Limit = limit
|
||||
return db
|
||||
}
|
||||
|
||||
// Offset 设置查询结果的偏移量。
|
||||
func (db *Database) Offset(offset int) *Database {
|
||||
db.Context.LimitOffsetClause.Offset = offset
|
||||
return db
|
||||
}
|
||||
|
||||
// Page 页数,根据limit确定
|
||||
func (db *Database) Page(num int) *Database {
|
||||
db.Context.LimitOffsetClause.Page = num
|
||||
return db
|
||||
}
|
||||
56
driver.go
Normal file
56
driver.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Bindings struct {
|
||||
sql4prepare string
|
||||
err error
|
||||
bindings []any
|
||||
}
|
||||
type IDriver interface {
|
||||
ToSql(c *Context) (sql4prepare string, binds []any, err error)
|
||||
ToSqlIncDec(c *Context, symbol string, data map[string]any) (sql4prepare string, values []any, err error)
|
||||
ToSqlSelect(c *Context) (sql4prepare string, binds []any)
|
||||
ToSqlTable(c *Context) (sql4prepare string, values []any, err error)
|
||||
ToSqlJoin(c *Context) (sql4prepare string, binds []any, err error)
|
||||
ToSqlWhere(c *Context) (sql4prepare string, values []any, err error)
|
||||
ToSqlOrderBy(c *Context) (sql4prepare string)
|
||||
ToSqlLimitOffset(c *Context) (sqlSegment string, binds []any)
|
||||
ToSqlInsert(c *Context, obj any, ignore string, onDuplicateKeys []string, mustFields ...string) (sqlSegment string, binds []any, err error)
|
||||
ToSqlUpdate(c *Context, obj any, mustFields ...string) (sqlSegment string, binds []any, err error)
|
||||
ToSqlDelete(c *Context, obj any) (sqlSegment string, binds []any, err error)
|
||||
}
|
||||
|
||||
type Driver struct {
|
||||
IDriver
|
||||
}
|
||||
|
||||
func NewDriver(d IDriver) *Driver {
|
||||
return &Driver{IDriver: d}
|
||||
}
|
||||
|
||||
var driverMap = map[string]IDriver{}
|
||||
var driverLock sync.RWMutex
|
||||
|
||||
func Register(driver string, parser IDriver) {
|
||||
driverLock.Lock()
|
||||
defer driverLock.Unlock()
|
||||
driverMap[driver] = parser
|
||||
}
|
||||
|
||||
func GetDriver(driver string) IDriver {
|
||||
driverLock.RLock()
|
||||
defer driverLock.RUnlock()
|
||||
return driverMap[driver]
|
||||
}
|
||||
|
||||
func DriverList() (dr []string) {
|
||||
driverLock.RLock()
|
||||
defer driverLock.RUnlock()
|
||||
for d := range driverMap {
|
||||
dr = append(dr, d)
|
||||
}
|
||||
return
|
||||
}
|
||||
388
engin.go
388
engin.go
@@ -2,243 +2,231 @@ package gorose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"errors"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// TAGNAME ...
|
||||
var TAGNAME = "gorose"
|
||||
|
||||
// IGNORE ...
|
||||
var IGNORE = "-"
|
||||
|
||||
type cluster struct {
|
||||
master []*sql.DB
|
||||
masterSize int
|
||||
slave []*sql.DB
|
||||
slaveSize int
|
||||
}
|
||||
|
||||
// Engin ...
|
||||
type Engin struct {
|
||||
config *ConfigCluster
|
||||
driver string
|
||||
prefix string
|
||||
dbs *cluster
|
||||
logger ILogger
|
||||
*GoRose
|
||||
tx *sql.Tx
|
||||
autoSavePoint uint8
|
||||
}
|
||||
|
||||
var _ IEngin = (*Engin)(nil)
|
||||
func NewEngin(g *GoRose) *Engin {
|
||||
return &Engin{g, nil, 0}
|
||||
}
|
||||
|
||||
// NewEngin : init Engin struct pointer
|
||||
// NewEngin : 初始化 Engin 结构体对象指针
|
||||
func NewEngin(conf ...interface{}) (e *Engin, err error) {
|
||||
engin := new(Engin)
|
||||
if len(conf) == 0 {
|
||||
func (s *Engin) execute(query string, args ...any) (int64, error) {
|
||||
exec, err := s.Exec(query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return exec.RowsAffected()
|
||||
}
|
||||
func (s *Engin) Exec(query string, args ...any) (sql.Result, error) {
|
||||
if s.tx != nil {
|
||||
return s.tx.Exec(query, args...)
|
||||
}
|
||||
return s.MasterDB().Exec(query, args...)
|
||||
}
|
||||
func (s *Engin) Begin() (err error) {
|
||||
if s.tx != nil {
|
||||
s.autoSavePoint += 1
|
||||
return s.SavePoint(s.autoSavePoint)
|
||||
}
|
||||
s.tx, err = s.MasterDB().Begin()
|
||||
return
|
||||
}
|
||||
func (s *Engin) SavePoint(name any) (err error) {
|
||||
_, err = s.tx.Exec("SAVEPOINT ?", name)
|
||||
return
|
||||
}
|
||||
func (s *Engin) RollbackTo(name any) (err error) {
|
||||
_, err = s.tx.Exec("ROLLBACK TO ?", name)
|
||||
return
|
||||
}
|
||||
func (s *Engin) Rollback() (err error) {
|
||||
if s.autoSavePoint > 0 {
|
||||
// decrease in advance whether rollbackTo fail
|
||||
currentPoint := s.autoSavePoint
|
||||
s.autoSavePoint -= 1
|
||||
return s.RollbackTo(currentPoint)
|
||||
}
|
||||
err = s.tx.Rollback()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.tx = nil
|
||||
return
|
||||
}
|
||||
func (s *Engin) Commit() (err error) {
|
||||
if s.autoSavePoint > 0 {
|
||||
s.autoSavePoint -= 1
|
||||
return
|
||||
}
|
||||
err = s.tx.Commit()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
s.tx = nil
|
||||
return
|
||||
}
|
||||
func (s *Engin) Transaction(closure ...func(*Engin) error) (err error) {
|
||||
if err = s.Begin(); err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range closure {
|
||||
err = v(s)
|
||||
if err != nil {
|
||||
return s.Rollback()
|
||||
}
|
||||
}
|
||||
return s.Commit()
|
||||
}
|
||||
|
||||
// 使用默认的log, 如果自定义了logger, 则只需要调用 Use() 方法即可覆盖
|
||||
engin.Use(DefaultLogger())
|
||||
func (s *Engin) Query(query string, args ...any) (rows *sql.Rows, err error) {
|
||||
if s.tx != nil {
|
||||
return s.tx.Query(query, args...)
|
||||
} else {
|
||||
return s.SlaveDB().Query(query, args...)
|
||||
}
|
||||
}
|
||||
|
||||
switch conf[0].(type) {
|
||||
// 传入的是单个配置
|
||||
case *Config:
|
||||
err = engin.bootSingle(conf[0].(*Config))
|
||||
// 传入的是集群配置
|
||||
case *ConfigCluster:
|
||||
engin.config = conf[0].(*ConfigCluster)
|
||||
err = engin.bootCluster()
|
||||
func (s *Engin) QueryRow(query string, args ...any) *sql.Row {
|
||||
if s.tx != nil {
|
||||
return s.tx.QueryRow(query, args...)
|
||||
} else {
|
||||
return s.SlaveDB().QueryRow(query, args...)
|
||||
}
|
||||
}
|
||||
func (s *Engin) QueryTo(bind any, query string, args ...any) (err error) {
|
||||
var rows *sql.Rows
|
||||
if rows, err = s.Query(query, args...); err != nil {
|
||||
return
|
||||
}
|
||||
return s.rowsToBind(rows, bind)
|
||||
}
|
||||
func (s *Engin) rowsToBind(rows *sql.Rows, bind any) (err error) {
|
||||
rfv := reflect.Indirect(reflect.ValueOf(bind))
|
||||
switch rfv.Kind() {
|
||||
case reflect.Slice:
|
||||
switch rfv.Type().Elem().Kind() {
|
||||
case reflect.Map:
|
||||
return s.rowsToMap(rows, rfv)
|
||||
case reflect.Struct:
|
||||
return s.rowsToStruct(rows, rfv)
|
||||
default:
|
||||
return errors.New("only struct(slice) or map(slice) supported")
|
||||
}
|
||||
case reflect.Map:
|
||||
return s.rowsToMap(rows, rfv)
|
||||
case reflect.Struct:
|
||||
return s.rowsToStruct(rows, rfv)
|
||||
default:
|
||||
panic(fmt.Sprint("Open() need *gorose.Config or *gorose.ConfigCluster param, also can empty for build sql string only, but ",
|
||||
conf, " given"))
|
||||
}
|
||||
|
||||
return engin, err
|
||||
}
|
||||
|
||||
// Use ...
|
||||
func (c *Engin) Use(closers ...func(e *Engin)) {
|
||||
for _, closer := range closers {
|
||||
closer(c)
|
||||
return errors.New("only struct(slice) or map(slice) supported")
|
||||
}
|
||||
}
|
||||
|
||||
// Ping ...
|
||||
func (c *Engin) Ping() error {
|
||||
//for _,item := range c.dbs.master {
|
||||
//
|
||||
//}
|
||||
return c.GetQueryDB().Ping()
|
||||
}
|
||||
func (s *Engin) rowsToStruct(rows *sql.Rows, rfv reflect.Value) error {
|
||||
//FieldTag, FieldStruct, _ := structsParse(rfv)
|
||||
FieldTag, FieldStruct, _ := structsTypeParse(rfv.Type())
|
||||
|
||||
// TagName 自定义结构体对应的orm字段,默认gorose
|
||||
func (c *Engin) TagName(arg string) {
|
||||
//c.tagName = arg
|
||||
TAGNAME = arg
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// IgnoreName 自定义结构体对应的orm忽略字段名字,默认-
|
||||
func (c *Engin) IgnoreName(arg string) {
|
||||
//c.ignoreName = arg
|
||||
IGNORE = arg
|
||||
}
|
||||
|
||||
// SetPrefix 设置表前缀
|
||||
func (c *Engin) SetPrefix(pre string) {
|
||||
c.prefix = pre
|
||||
}
|
||||
|
||||
// GetPrefix 获取前缀
|
||||
func (c *Engin) GetPrefix() string {
|
||||
return c.prefix
|
||||
}
|
||||
|
||||
// GetDriver ...
|
||||
func (c *Engin) GetDriver() string {
|
||||
return c.driver
|
||||
}
|
||||
|
||||
// GetQueryDB : get a slave db for using query operation
|
||||
// GetQueryDB : 获取一个从库用来做查询操作
|
||||
func (c *Engin) GetQueryDB() *sql.DB {
|
||||
if c.dbs.slaveSize == 0 {
|
||||
return c.GetExecuteDB()
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var randint = getRandomInt(c.dbs.slaveSize)
|
||||
return c.dbs.slave[randint]
|
||||
}
|
||||
|
||||
// GetExecuteDB : get a master db for using execute operation
|
||||
// GetExecuteDB : 获取一个主库用来做查询之外的操作
|
||||
func (c *Engin) GetExecuteDB() *sql.DB {
|
||||
if c.dbs.masterSize == 0 {
|
||||
return nil
|
||||
}
|
||||
var randint = getRandomInt(c.dbs.masterSize)
|
||||
return c.dbs.master[randint]
|
||||
}
|
||||
// 列的个数
|
||||
count := len(columns)
|
||||
|
||||
// GetLogger ...
|
||||
func (c *Engin) GetLogger() ILogger {
|
||||
return c.logger
|
||||
}
|
||||
|
||||
// SetLogger ...
|
||||
func (c *Engin) SetLogger(lg ILogger) {
|
||||
c.logger = lg
|
||||
}
|
||||
|
||||
func (c *Engin) bootSingle(conf *Config) error {
|
||||
// 如果传入的是单一配置, 则转换成集群配置, 方便统一管理
|
||||
var cc = new(ConfigCluster)
|
||||
cc.Master = append(cc.Master, *conf)
|
||||
c.config = cc
|
||||
return c.bootCluster()
|
||||
}
|
||||
|
||||
func (c *Engin) bootCluster() error {
|
||||
//fmt.Println(len(c.config.Slave))
|
||||
if len(c.config.Slave) > 0 {
|
||||
for _, item := range c.config.Slave {
|
||||
if c.config.Driver != "" {
|
||||
item.Driver = c.config.Driver
|
||||
}
|
||||
if c.config.Prefix != "" {
|
||||
item.Prefix = c.config.Prefix
|
||||
}
|
||||
db, err := c.bootReal(item)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if c.dbs == nil {
|
||||
c.dbs = new(cluster)
|
||||
}
|
||||
c.dbs.slave = append(c.dbs.slave, db)
|
||||
c.dbs.slaveSize++
|
||||
c.driver = item.Driver
|
||||
for rows.Next() {
|
||||
// 要先扫描到map, 再做字段比对, 因为这里不确定具体字段数量
|
||||
// 主要针对 select * 或者直接sql语句
|
||||
//todo 如果是由struct转换而来, 可以新开一个方法, 不需要做转换比对过程
|
||||
entry, err := s.rowsToMapSingle(rows, columns, count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var pre, dr string
|
||||
if len(c.config.Master) > 0 {
|
||||
for _, item := range c.config.Master {
|
||||
if c.config.Driver != "" {
|
||||
item.Driver = c.config.Driver
|
||||
}
|
||||
if c.config.Prefix != "" {
|
||||
item.Prefix = c.config.Prefix
|
||||
}
|
||||
db, err := c.bootReal(item)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
if rfv.Kind() == reflect.Slice {
|
||||
rfvItem := reflect.Indirect(reflect.New(rfv.Type().Elem()))
|
||||
for i, key := range FieldTag {
|
||||
if v, ok := entry[key]; ok {
|
||||
rfvItem.FieldByName(FieldStruct[i]).Set(reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
if c.dbs == nil {
|
||||
c.dbs = new(cluster)
|
||||
}
|
||||
c.dbs.master = append(c.dbs.master, db)
|
||||
c.dbs.masterSize = c.dbs.masterSize + 1
|
||||
c.driver = item.Driver
|
||||
//fmt.Println(c.dbs.masterSize)
|
||||
if item.Prefix != "" {
|
||||
pre = item.Prefix
|
||||
}
|
||||
if item.Driver != "" {
|
||||
dr = item.Driver
|
||||
rfv.Set(reflect.Append(rfv, rfvItem))
|
||||
} else {
|
||||
for i, key := range FieldTag {
|
||||
if v, ok := entry[key]; ok {
|
||||
rfv.FieldByName(FieldStruct[i]).Set(reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
//rfv.Set(entry)
|
||||
}
|
||||
}
|
||||
// 如果config没有设置prefix,且configcluster设置了prefix,则使用cluster的prefix
|
||||
if pre != "" && c.prefix == "" {
|
||||
c.prefix = pre
|
||||
}
|
||||
// 如果config没有设置driver,且configcluster设置了driver,则使用cluster的driver
|
||||
if dr != "" && c.driver == "" {
|
||||
c.driver = dr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// boot sql driver
|
||||
func (c *Engin) bootReal(dbConf Config) (db *sql.DB, err error) {
|
||||
//db, err = sql.Open("mysql", "root:root@tcp(localhost:3306)/test?charset=utf8mb4")
|
||||
// 开始驱动
|
||||
db, err = sql.Open(dbConf.Driver, dbConf.Dsn)
|
||||
func (s *Engin) rowsToMapSingle(rows *sql.Rows, columns []string, count int) (entry map[string]any, err error) {
|
||||
// 一条数据的各列的值(需要指定长度为列的个数,以便获取地址)
|
||||
values := make([]any, count)
|
||||
// 一条数据的各列的值的地址
|
||||
valPointers := make([]any, count)
|
||||
// 获取各列的值的地址
|
||||
for i := 0; i < count; i++ {
|
||||
valPointers[i] = &values[i]
|
||||
}
|
||||
// 获取各列的值,放到对应的地址中
|
||||
err = rows.Scan(valPointers...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 一条数据的Map (列名和值的键值对)
|
||||
entry = make(map[string]any)
|
||||
|
||||
// 检查是否可以ping通
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
return
|
||||
// Map 赋值
|
||||
for i, col := range columns {
|
||||
var v any
|
||||
// 值复制给val(所以Scan时指定的地址可重复使用)
|
||||
val := values[i]
|
||||
b, ok := val.([]byte)
|
||||
if ok {
|
||||
// 字符切片转为字符串
|
||||
v = string(b)
|
||||
} else {
|
||||
v = val
|
||||
}
|
||||
entry[col] = v
|
||||
}
|
||||
|
||||
// 连接池设置
|
||||
if dbConf.SetMaxOpenConns > 0 {
|
||||
db.SetMaxOpenConns(dbConf.SetMaxOpenConns)
|
||||
}
|
||||
if dbConf.SetMaxIdleConns > 0 {
|
||||
db.SetMaxIdleConns(dbConf.SetMaxIdleConns)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// NewSession 获取session实例
|
||||
// 这是一个语法糖, 为了方便使用(engin.NewSession())添加的
|
||||
// 添加后会让engin和session耦合, 如果不想耦合, 就删掉此方法
|
||||
// 删掉这个方法后,可以使用 gorose.NewSession(gorose.IEngin)
|
||||
// 通过 gorose.IEngin 依赖注入的方式, 达到解耦的目的
|
||||
func (c *Engin) NewSession() ISession {
|
||||
return NewSession(c)
|
||||
}
|
||||
func (s *Engin) rowsToMap(rows *sql.Rows, rfv reflect.Value) error {
|
||||
defer rows.Close()
|
||||
|
||||
// NewOrm 获取orm实例
|
||||
// 这是一个语法糖, 为了方便使用(engin.NewOrm())添加的
|
||||
// 添加后会让engin和 orm 耦合, 如果不想耦合, 就删掉此方法
|
||||
// 删掉这个方法后,可以使用 gorose.NewOrm(gorose.NewSession(gorose.IEngin))
|
||||
// 通过 gorose.ISession 依赖注入的方式, 达到解耦的目的
|
||||
func (c *Engin) NewOrm() IOrm {
|
||||
return NewOrm(c)
|
||||
columns, err := rows.Columns()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 列的个数
|
||||
count := len(columns)
|
||||
|
||||
for rows.Next() {
|
||||
entry, err := s.rowsToMapSingle(rows, columns, count)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rfv.Kind() == reflect.Slice {
|
||||
rfv.Set(reflect.Append(rfv, reflect.ValueOf(entry)))
|
||||
} else {
|
||||
rfv.Set(reflect.ValueOf(entry))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// IEngin ...
|
||||
type IEngin interface {
|
||||
GetExecuteDB() *sql.DB
|
||||
GetQueryDB() *sql.DB
|
||||
//EnableSqlLog(e ...bool)
|
||||
//IfEnableSqlLog() (e bool)
|
||||
//SetPrefix(pre string)
|
||||
GetPrefix() (pre string)
|
||||
//NewSession() ISession
|
||||
//NewOrm() IOrm
|
||||
SetLogger(lg ILogger)
|
||||
GetLogger() ILogger
|
||||
GetDriver() string
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"github.com/gohouse/t"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type aaa t.MapStringT
|
||||
|
||||
func (u *aaa) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
//type bbb MapRows
|
||||
type bbb []t.MapStringT
|
||||
|
||||
func (u *bbb) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
type UsersMap Data
|
||||
|
||||
func (*UsersMap) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
// 定义map多返回绑定表名,一定要像下边这样,单独定义,否则无法获取对应的 TableName()
|
||||
type UsersMapSlice []Data
|
||||
|
||||
func (u *UsersMapSlice) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
type Users struct {
|
||||
Uid int64 `orm:"uid"`
|
||||
Name string `orm:"name"`
|
||||
Age int64 `orm:"age"`
|
||||
Fi string `orm:"ignore"`
|
||||
}
|
||||
|
||||
func (Users) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
|
||||
type Orders struct {
|
||||
Id int `orm:"id"`
|
||||
GoodsName string `orm:"goodsname"`
|
||||
Price float64 `orm:"price"`
|
||||
}
|
||||
|
||||
func TestEngin(t *testing.T) {
|
||||
e := initDB()
|
||||
e.SetPrefix("pre_")
|
||||
|
||||
t.Log(e.GetPrefix())
|
||||
|
||||
db := e.GetQueryDB()
|
||||
|
||||
err := db.Ping()
|
||||
|
||||
if err != nil {
|
||||
t.Error("gorose初始化失败")
|
||||
}
|
||||
t.Log("gorose初始化成功")
|
||||
t.Log(e.GetLogger())
|
||||
}
|
||||
103
err.go
103
err.go
@@ -1,103 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Error ...
|
||||
type Error uint
|
||||
|
||||
// Lang ...
|
||||
type Lang uint
|
||||
|
||||
const (
|
||||
// CHINESE ...
|
||||
CHINESE Lang = iota
|
||||
// ENGLISH ...
|
||||
ENGLISH
|
||||
// CHINESE_TRADITIONAL ...
|
||||
CHINESE_TRADITIONAL
|
||||
)
|
||||
|
||||
const (
|
||||
// ERR_PARAMS_COUNTS ...
|
||||
ERR_PARAMS_COUNTS Error = iota
|
||||
// ERR_PARAMS_MISSING ...
|
||||
ERR_PARAMS_MISSING
|
||||
// ERR_PARAMS_FORMAT ...
|
||||
ERR_PARAMS_FORMAT
|
||||
)
|
||||
|
||||
// Default ...
|
||||
func (e *Err) Default() map[Error]string {
|
||||
return map[Error]string{
|
||||
ERR_PARAMS_COUNTS: "参数数量有误",
|
||||
ERR_PARAMS_MISSING: "参数缺失",
|
||||
ERR_PARAMS_FORMAT: "参数格式错误",
|
||||
}
|
||||
}
|
||||
|
||||
var langString = map[Lang]string{
|
||||
CHINESE: "chinese",
|
||||
ENGLISH: "english",
|
||||
CHINESE_TRADITIONAL: "chinese_traditional",
|
||||
}
|
||||
|
||||
// String ...
|
||||
func (l Lang) String() string {
|
||||
return langString[l]
|
||||
}
|
||||
|
||||
// Err ...
|
||||
type Err struct {
|
||||
lang Lang
|
||||
err map[Lang]map[Error]string
|
||||
}
|
||||
|
||||
//var gOnce *sync.Once
|
||||
var gErr *Err
|
||||
|
||||
func init() {
|
||||
var tmpLang = make(map[Lang]map[Error]string)
|
||||
gErr = &Err{err: tmpLang}
|
||||
gErr.lang = CHINESE
|
||||
gErr.Register(gErr.Default())
|
||||
}
|
||||
|
||||
// NewErr ...
|
||||
func NewErr() *Err {
|
||||
return gErr
|
||||
}
|
||||
|
||||
// SetLang ...
|
||||
func (e *Err) SetLang(l Lang) {
|
||||
e.lang = l
|
||||
}
|
||||
|
||||
// GetLang ...
|
||||
func (e *Err) GetLang() Lang {
|
||||
return e.lang
|
||||
}
|
||||
|
||||
// Register ...
|
||||
func (e *Err) Register(err map[Error]string) {
|
||||
e.err[e.GetLang()] = err
|
||||
}
|
||||
|
||||
// Get ...
|
||||
func (e *Err) Get(err Error) string {
|
||||
return e.err[e.GetLang()][err]
|
||||
}
|
||||
|
||||
// GetErr ...
|
||||
func GetErr(err Error, args ...interface{}) error {
|
||||
var argreal string
|
||||
if len(args) > 0 {
|
||||
argreal = fmt.Sprint(":", args)
|
||||
}
|
||||
return errors.New(fmt.Sprint(
|
||||
NewErr().
|
||||
Get(err),
|
||||
argreal))
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 读写分离集群
|
||||
var config1 = gorose.Config{Dsn: "./db.sqlite"}
|
||||
var config2 = gorose.Config{Dsn: "./db2.sqlite"}
|
||||
var config3 = gorose.Config{Dsn: "./db3.sqlite"}
|
||||
var config4 = gorose.Config{Dsn: "./db4.sqlite"}
|
||||
var configCluster = &gorose.ConfigCluster{
|
||||
Master: []gorose.Config{config3, config4},
|
||||
Slave: []gorose.Config{config1, config2},
|
||||
Driver: "sqlite3",
|
||||
}
|
||||
engin, err := gorose.Open(configCluster)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
for i := 0; i < 5; i++ {
|
||||
db := engin.NewOrm()
|
||||
res, err := db.Table("users").First()
|
||||
fmt.Println(err)
|
||||
fmt.Println(res)
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package dbobj
|
||||
|
||||
import (
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
GetSqliteEngin()
|
||||
}
|
||||
|
||||
func GetMysqlEngin() *gorose.Engin {
|
||||
var err error
|
||||
var engin *gorose.Engin
|
||||
engin, err = gorose.Open(&gorose.Config{
|
||||
Driver: "mysql",
|
||||
Dsn: "root:123456@tcp(localhost:3306)/test?charset=utf8mb4",
|
||||
Prefix: "nv_",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return engin
|
||||
}
|
||||
|
||||
var engin *gorose.Engin
|
||||
func GetSqliteEngin() *gorose.Engin {
|
||||
var err error
|
||||
engin, err = gorose.Open(&gorose.Config{
|
||||
Driver: "sqlite3",
|
||||
Dsn: "./db.sqlite",
|
||||
Prefix: "",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return engin
|
||||
}
|
||||
|
||||
func Getdb() gorose.IOrm {
|
||||
return engin.NewOrm()
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
"github.com/gohouse/gorose/v2/examples/dbobj"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var res interface{}
|
||||
var err error
|
||||
db := dbobj.Getdb()
|
||||
res, err = db.Table("users").Insert(gorose.Data{"uid2": 222})
|
||||
if err != nil {
|
||||
fmt.Println("0 - err:", err.Error())
|
||||
}
|
||||
fmt.Println("0 - res:", res)
|
||||
res, err = db.Reset().Table("users").Where("uid2", ">", 1).First()
|
||||
if err != nil {
|
||||
fmt.Println("1 - err:", err.Error())
|
||||
}
|
||||
fmt.Println("1 - res:", res)
|
||||
|
||||
res, err = db.Reset().Table("users").Where("age", ">", 0).First()
|
||||
if err != nil {
|
||||
fmt.Println("2 - err:", err.Error())
|
||||
}
|
||||
fmt.Println("2 - res:", res)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
//_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
"time"
|
||||
)
|
||||
|
||||
var engin *gorose.Engin
|
||||
var err error
|
||||
|
||||
func init() {
|
||||
engin, err = gorose.Open(&gorose.Config{Prefix: "nv_", Driver: "mysql", Dsn: "root:123456@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true"})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
func db() gorose.IOrm {
|
||||
return engin.NewOrm()
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
Id int64 `gorose:"id" json:"id"`
|
||||
TagTitle string `gorose:"tag_title" json:"tag_title"`
|
||||
TagStatus int64 `gorose:"tag_status" json:"tag_status"` // 标签状态:默认0未启用,1启用
|
||||
CreatedAt time.Time `gorose:"created_at" json:"created_at"`
|
||||
}
|
||||
|
||||
func (Tag) TableName() string {
|
||||
return "tag"
|
||||
}
|
||||
func main() {
|
||||
//fmt.Println(strings.TrimPrefix("nv_novel_chapter", "nv_"))
|
||||
//return
|
||||
var t = Tag{
|
||||
TagTitle: "0",
|
||||
TagStatus: 1,
|
||||
}
|
||||
res, err := db().Insert(&t)
|
||||
fmt.Println(res, err)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func initDB() *gorose.Engin {
|
||||
e, err := gorose.Open(&gorose.Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
|
||||
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
// 这里可以设置日志相关配置
|
||||
//e.Use(func(eg *gorose.Engin) {
|
||||
// eg.SetLogger(gorose.NewLogger(&gorose.LogOption{
|
||||
// FilePath: "./log",
|
||||
// EnableSqlLog: true,
|
||||
// EnableSlowLog: 5,
|
||||
// EnableErrorLog: true,
|
||||
// }))
|
||||
//})
|
||||
|
||||
return e
|
||||
}
|
||||
func main() {
|
||||
db := initDB().NewOrm()
|
||||
res, err := db.Table("users").First()
|
||||
fmt.Println(err)
|
||||
fmt.Println(db.LastSql())
|
||||
fmt.Println(res)
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
//_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 测试时, 不要忘记打开import内的pg包注释
|
||||
pgtest()
|
||||
}
|
||||
func main2() {
|
||||
dsn := "user=postgres dbname=postgres password=123456 sslmode=disable"
|
||||
engin, err := gorose.Open(&gorose.Config{Driver: "postgres", Dsn: dsn})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
var orm = engin.NewOrm()
|
||||
res, err := orm.Query("select * from users where uid>$1", 1)
|
||||
fmt.Println(res, err)
|
||||
|
||||
fmt.Println(engin.NewOrm().Table("users").
|
||||
Data(map[string]interface{}{"uname": "fizz22"}).
|
||||
//Where("uid",4).BuildSql("insert"))
|
||||
Where("uid", 4).BuildSql("update"))
|
||||
}
|
||||
|
||||
func pgtest() {
|
||||
dsn := "user=postgres dbname=postgres password=123456 sslmode=disable"
|
||||
engin, err := gorose.Open(&gorose.Config{Driver: "postgres", Dsn: dsn})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
var orm = engin.NewOrm()
|
||||
res,p,err := orm.Table("users").Where("a",1).BuildSql()
|
||||
fmt.Println(res,p, err)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
//_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/gohouse/gorose/v2"
|
||||
)
|
||||
|
||||
func initEngin() *gorose.Engin {
|
||||
var err error
|
||||
var engin *gorose.Engin
|
||||
engin, err = gorose.Open(&gorose.Config{
|
||||
Driver: "mysql",
|
||||
Dsn: "root:123456@tcp(localhost:3306)/test?charset=utf8mb4",
|
||||
Prefix: "nv_",
|
||||
})
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return engin
|
||||
}
|
||||
func main() {
|
||||
err := trans()
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func trans() error {
|
||||
var aff int64
|
||||
var err error
|
||||
var engin = initEngin()
|
||||
db := engin.NewOrm()
|
||||
db.Begin()
|
||||
aff, err = db.Table("logs").Insert(gorose.Data{"username": "xx"})
|
||||
if err != nil || aff == 0 {
|
||||
db.Rollback()
|
||||
return err
|
||||
}
|
||||
aff, err = db.Table("logs").Where("id", 2).Update(gorose.Data{"username": "xx1232xx1232xx1232xx1232xx1232xx1232xx1232xx1232"})
|
||||
if err != nil || aff == 0 {
|
||||
db.Rollback()
|
||||
return err
|
||||
}
|
||||
aff, err = db.Table("logs").Insert(gorose.Data{"username": "xx", "pkid": 1})
|
||||
if err != nil || aff == 0 {
|
||||
db.Rollback()
|
||||
return err
|
||||
}
|
||||
return db.Commit()
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gohouse/gorose/v2/examples/dbobj"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var res interface{}
|
||||
var err error
|
||||
db := dbobj.Getdb()
|
||||
res, err = db.Reset().Table("users").Get()
|
||||
if err != nil {
|
||||
fmt.Println("1 - err:", err.Error())
|
||||
}
|
||||
fmt.Println("1 - res:", res)
|
||||
|
||||
res, err = db.Reset().Table("users").OrderBy("uid desc").Limit(1).Paginate(1)
|
||||
if err != nil {
|
||||
fmt.Println("2 - err:", err.Error())
|
||||
}
|
||||
fmt.Println("2 - res:", res)
|
||||
|
||||
res, err = db.Reset().Table("users").Where("age", ">", 0).Limit(1).Paginate(3)
|
||||
if err != nil {
|
||||
fmt.Println("2 - err:", err.Error())
|
||||
}
|
||||
fmt.Println("2 - res:", res)
|
||||
}
|
||||
10
go.mod
10
go.mod
@@ -1,9 +1,3 @@
|
||||
module github.com/gohouse/gorose/v2
|
||||
module github.com/gohouse/gorose
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/gohouse/golib v0.0.0-20210711163732-a5c22059eb75
|
||||
github.com/gohouse/t v0.0.0-20201007094014-630049a6bfe9
|
||||
github.com/mattn/go-sqlite3 v1.10.0
|
||||
)
|
||||
go 1.21.5
|
||||
|
||||
77
go.sum
77
go.sum
@@ -1,77 +0,0 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/gohouse/e v0.0.3-rc.0.20200724104652-25ebf8c9c305/go.mod h1:1rpOAa2dVfkpkhb9vgH5XHcvyCh/NJCx1bkudcX/TcM=
|
||||
github.com/gohouse/golib v0.0.0-20200312063614-067523159413/go.mod h1:80IWOo3CRajPn7UsEtJdFwYbJ0zs4Je5RNE4EfyR0BM=
|
||||
github.com/gohouse/golib v0.0.0-20200727025018-43fec7d17e79 h1:R5Zdy/CBTqppDoUCwH33RugxEe14TPJFr8JwqkNSJKQ=
|
||||
github.com/gohouse/golib v0.0.0-20200727025018-43fec7d17e79/go.mod h1:uvI6dMBJXJI1hR+0ZI/v7/NTVNpj7hSQhcCnGXjY/GI=
|
||||
github.com/gohouse/t v0.0.0-20200724104622-78e2ef6ec88c/go.mod h1:JZl4QiZrsKArORKRFgfwSiMpMR32C4pvTky0FlGs88U=
|
||||
github.com/gohouse/t v0.0.0-20201007094014-630049a6bfe9 h1:dRFRgLiddQthn4ZJqDk/R1IaXc626TRqAZZYC6L7HGo=
|
||||
github.com/gohouse/t v0.0.0-20201007094014-630049a6bfe9/go.mod h1:kUDPW94gfrQKjvhhqW1l0rGCJ/1BUnhD4AIzdEmnI6M=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200723000907-a7c6fd066f6d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200311144346-b662892dd51b/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
144
gorose.go
144
gorose.go
@@ -1,38 +1,120 @@
|
||||
package gorose
|
||||
|
||||
// GOROSE_IMG ...
|
||||
const GOROSE_IMG = `
|
||||
|
||||
,ad8888ba, 88888888ba
|
||||
d8"' '"8b 88 "8b
|
||||
d8' 88 ,8P
|
||||
88 ,adPPYba, 88aaaaaa8P' ,adPPYba, ,adPPYba, ,adPPYba,
|
||||
88 88888 a8" "8a 88""""88' a8" "8a I8[ "" a8P_____88
|
||||
Y8, 88 8b d8 88 '8b 8b d8 '"Y8ba, 8PP"""""""
|
||||
Y8a. .a88 "8a, ,a8" 88 '8b "8a, ,a8" aa ]8I "8b, ,aa
|
||||
'"Y88888P" '"YbbdP"' 88 '8b '"YbbdP"' '"YbbdP"' '"Ybbd8"'
|
||||
|
||||
`
|
||||
|
||||
const (
|
||||
// VERSION_TEXT ...
|
||||
VERSION_TEXT = "\ngolang orm of gorose's version : "
|
||||
// VERSION_NO ...
|
||||
VERSION_NO = "v2.1.10"
|
||||
// VERSION ...
|
||||
VERSION = VERSION_TEXT + VERSION_NO + GOROSE_IMG
|
||||
import (
|
||||
"database/sql"
|
||||
"math"
|
||||
)
|
||||
|
||||
// Open ...
|
||||
func Open(conf ...interface{}) (engin *Engin, err error) {
|
||||
// 驱动engin
|
||||
engin, err = NewEngin(conf...)
|
||||
if err != nil {
|
||||
if engin.GetLogger().EnableErrorLog() {
|
||||
engin.GetLogger().Error(err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type GoRose struct {
|
||||
Cluster *ConfigCluster
|
||||
master []*sql.DB
|
||||
slave []*sql.DB
|
||||
driver string
|
||||
prefix string
|
||||
handlers HandlersChain
|
||||
}
|
||||
|
||||
type HandlerFunc func(*Context)
|
||||
type HandlersChain []HandlerFunc
|
||||
|
||||
const abortIndex int8 = math.MaxInt8 >> 1
|
||||
|
||||
//type handlers struct {
|
||||
// handlers HandlersChain
|
||||
// index int8
|
||||
//}
|
||||
//
|
||||
//func (c *handlers) Next() {
|
||||
// c.index++
|
||||
// for c.index < int8(len(c.handlers)) {
|
||||
// c.handlers[c.index](c)
|
||||
// c.index++
|
||||
// }
|
||||
//}
|
||||
|
||||
func (g *GoRose) Use(h ...HandlerFunc) *GoRose {
|
||||
g.handlers = append(g.handlers, h...)
|
||||
return g
|
||||
}
|
||||
|
||||
//func (dg *GoRose) Use(h ...HandlerFunc) *GoRose {
|
||||
//}
|
||||
|
||||
// Open db
|
||||
// examples
|
||||
//
|
||||
// Open("mysql", "root:root@tcp(localhost:3306)/test?charset=utf8mb4&parseTime=true")
|
||||
// Open(&ConfigCluster{...})
|
||||
func Open(conf ...any) *GoRose {
|
||||
var g = GoRose{}
|
||||
switch len(conf) {
|
||||
case 1:
|
||||
if single, ok := conf[0].(*Config); ok {
|
||||
g.driver = single.Driver
|
||||
g.prefix = single.Prefix
|
||||
g.Cluster = &ConfigCluster{WriteConf: []Config{*single}}
|
||||
if single == nil { // build sql only
|
||||
//g.ConfigCluster = &ConfigCluster{Prefix: "test_"}
|
||||
return &g
|
||||
}
|
||||
g.master, g.slave = g.Cluster.init()
|
||||
} else if cluster, ok := conf[0].(*ConfigCluster); ok {
|
||||
g.driver = cluster.WriteConf[0].Driver
|
||||
g.prefix = cluster.WriteConf[0].Prefix
|
||||
g.Cluster = cluster
|
||||
if cluster == nil { // build sql only
|
||||
//g.ConfigCluster = &ConfigCluster{Prefix: "test_"}
|
||||
return &g
|
||||
}
|
||||
g.master, g.slave = g.Cluster.init()
|
||||
} else {
|
||||
g.driver = "mysql" // for toSql test
|
||||
}
|
||||
case 2:
|
||||
g.driver = conf[0].(string)
|
||||
db, err := sql.Open(g.driver, conf[1].(string))
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
g.master = append(g.master, db)
|
||||
default:
|
||||
panic("config must be *gorose.ConfigCluster or sql.Open() origin params")
|
||||
}
|
||||
return &g
|
||||
}
|
||||
|
||||
func (g *GoRose) Close() (err error) {
|
||||
if len(g.master) > 0 {
|
||||
for _, db := range g.master {
|
||||
err = db.Close()
|
||||
}
|
||||
}
|
||||
if len(g.slave) > 0 {
|
||||
for _, db := range g.slave {
|
||||
err = db.Close()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (g *GoRose) MasterDB() *sql.DB {
|
||||
if len(g.master) == 0 {
|
||||
return nil
|
||||
}
|
||||
return g.master[GetRandomInt(len(g.master))]
|
||||
}
|
||||
func (g *GoRose) SlaveDB() *sql.DB {
|
||||
if len(g.slave) == 0 {
|
||||
return g.MasterDB()
|
||||
}
|
||||
return g.slave[GetRandomInt(len(g.slave))]
|
||||
}
|
||||
|
||||
func (g *GoRose) NewDatabase() *Database {
|
||||
return NewDatabase(g)
|
||||
}
|
||||
|
||||
func (g *GoRose) NewSession() *Engin {
|
||||
return NewEngin(g)
|
||||
}
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
func initDB() *Engin {
|
||||
e, err := Open(&Config{Driver: "sqlite3", Dsn: "./db.sqlite"})
|
||||
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
e.TagName("orm")
|
||||
e.IgnoreName("ignore")
|
||||
|
||||
initTable(e)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func initTable(e *Engin) {
|
||||
var sql = `CREATE TABLE IF NOT EXISTS "users" (
|
||||
"uid" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"name" TEXT NOT NULL default "",
|
||||
"age" integer NOT NULL default 0
|
||||
)`
|
||||
var s = e.NewSession()
|
||||
var err error
|
||||
var aff int64
|
||||
|
||||
aff, err = s.Execute(sql)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if aff == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
aff, err = s.Execute("insert into users(name,age) VALUES(?,?),(?,?),(?,?)",
|
||||
"fizz", 18, "gorose", 19, "fizzday", 20)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
fmt.Println("初始化数据和表成功:", aff)
|
||||
}
|
||||
25
group.go
Normal file
25
group.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package gorose
|
||||
|
||||
type TypeGroupItem struct {
|
||||
Column string
|
||||
IsRaw bool
|
||||
}
|
||||
type GroupClause struct {
|
||||
Groups []TypeGroupItem
|
||||
}
|
||||
// GroupBy 添加 GROUP BY 子句
|
||||
func (db *GroupClause) GroupBy(columns ...string) {
|
||||
for _, col := range columns {
|
||||
db.Groups = append(db.Groups, TypeGroupItem{
|
||||
Column: col,
|
||||
})
|
||||
}
|
||||
}
|
||||
func (db *GroupClause) GroupByRaw(columns ...string) {
|
||||
for _, col := range columns {
|
||||
db.Groups = append(db.Groups, TypeGroupItem{
|
||||
Column: col,
|
||||
IsRaw: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
7
having.go
Normal file
7
having.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package gorose
|
||||
|
||||
|
||||
// HavingClause 类似于WhereClause,但应用于HAVING子句。
|
||||
type HavingClause struct {
|
||||
*WhereClause
|
||||
}
|
||||
BIN
imgs/alipay.png
BIN
imgs/alipay.png
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
BIN
imgs/er.jpg
BIN
imgs/er.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
BIN
imgs/paypal.png
BIN
imgs/paypal.png
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
BIN
imgs/wechat.png
BIN
imgs/wechat.png
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
112
join.go
Normal file
112
join.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Examples for join
|
||||
//
|
||||
// db.Table("users").Join("card", "users.id", "card.uid")
|
||||
// db.Table("users").Join("card", "users.id", "=", "card.uid")
|
||||
// db.Table("users").Join("card", "users.id", "=", "card.uid", "left")
|
||||
// db.Table("users").Join("card", "users.age", "=", db().Table("user_info").Max("age"))
|
||||
// db.Table("users").Join(db().Table("user_info").Where("uid", 2))
|
||||
// db.xxx.LeftJoin(xxx, <same as before>)
|
||||
// db.Table(gorose.As("users", "a")).Join(gorose.As("card", "b"), "a.id", "b.uid")
|
||||
|
||||
package gorose
|
||||
|
||||
import "errors"
|
||||
|
||||
type IJoinOn interface {
|
||||
On(column string, args ...string) IJoinOn
|
||||
OrOn(column string, args ...string) IJoinOn
|
||||
}
|
||||
|
||||
// JoinClause 描述JOIN操作
|
||||
type JoinClause struct {
|
||||
JoinItems []any
|
||||
Err error
|
||||
}
|
||||
|
||||
// TypeJoinSub 描述JOIN操作
|
||||
type TypeJoinSub struct {
|
||||
IDriver
|
||||
}
|
||||
type TypeJoinStandard struct {
|
||||
TableClause
|
||||
Type string // JOIN类型(INNER, LEFT, RIGHT等)
|
||||
Column1 string
|
||||
Operator string
|
||||
Column2 string
|
||||
}
|
||||
type TypeJoinOn struct {
|
||||
TableClause
|
||||
OnClause func(IJoinOn)
|
||||
Type string // JOIN类型(INNER, LEFT, RIGHT等)
|
||||
}
|
||||
type TypeJoinOnCondition struct {
|
||||
Conditions []TypeJoinOnConditionItem
|
||||
}
|
||||
type TypeJoinOnConditionItem struct {
|
||||
Relation string // and/or
|
||||
Column1 string
|
||||
Operator string
|
||||
Column2 string
|
||||
}
|
||||
|
||||
func (db *JoinClause) join(joinType string, table any, argOrFn ...any) *JoinClause {
|
||||
var tab TableClause
|
||||
switch table.(type) {
|
||||
case string:
|
||||
tab.Tables = table
|
||||
case TableClause:
|
||||
tab = table.(TableClause)
|
||||
case IDriver:
|
||||
db.JoinItems = append(db.JoinItems, TypeJoinSub{table.(IDriver)})
|
||||
return db
|
||||
}
|
||||
|
||||
switch len(argOrFn) {
|
||||
case 1:
|
||||
if v, ok := argOrFn[0].(func(on IJoinOn)); ok {
|
||||
db.JoinItems = append(db.JoinItems, TypeJoinOn{
|
||||
TableClause: tab,
|
||||
OnClause: v,
|
||||
Type: joinType,
|
||||
})
|
||||
}
|
||||
case 2:
|
||||
db.JoinItems = append(db.JoinItems, TypeJoinStandard{
|
||||
TableClause: tab,
|
||||
Column1: argOrFn[0].(string),
|
||||
Operator: "=",
|
||||
Column2: argOrFn[1].(string),
|
||||
Type: joinType,
|
||||
})
|
||||
case 3:
|
||||
db.JoinItems = append(db.JoinItems, TypeJoinStandard{
|
||||
TableClause: tab,
|
||||
Column1: argOrFn[0].(string),
|
||||
Operator: argOrFn[1].(string),
|
||||
Column2: argOrFn[2].(string),
|
||||
Type: joinType,
|
||||
})
|
||||
default:
|
||||
db.Err = errors.New("join args error")
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *JoinClause) Join(table any, argOrFn ...any) *JoinClause {
|
||||
return db.join("INNER JOIN", table, argOrFn...)
|
||||
}
|
||||
|
||||
// LeftJoin 描述LEFT JOIN操作
|
||||
func (db *JoinClause) LeftJoin(table any, argOrFn ...any) *JoinClause {
|
||||
return db.join("LEFT JOIN", table, argOrFn...)
|
||||
}
|
||||
|
||||
// RightJoin 描述RIGHT JOIN操作
|
||||
func (db *JoinClause) RightJoin(table any, argOrFn ...any) *JoinClause {
|
||||
return db.join("RIGHT JOIN", table, argOrFn...)
|
||||
}
|
||||
|
||||
// CrossJoin 描述CROSS JOIN操作
|
||||
func (db *JoinClause) CrossJoin(table any, argOrFn ...any) *JoinClause {
|
||||
return db.join("CROSS JOIN", table, argOrFn...)
|
||||
}
|
||||
9
limit_offset.go
Normal file
9
limit_offset.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package gorose
|
||||
|
||||
// LimitOffsetClause 存储LIMIT和OFFSET信息。
|
||||
type LimitOffsetClause struct {
|
||||
Limit int
|
||||
Offset int
|
||||
Page int
|
||||
}
|
||||
|
||||
124
logger.go
124
logger.go
@@ -1,124 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogLevel 日志级别
|
||||
type LogLevel uint
|
||||
|
||||
const (
|
||||
// LOG_SQL ...
|
||||
LOG_SQL LogLevel = iota
|
||||
// LOG_SLOW ...
|
||||
LOG_SLOW
|
||||
// LOG_ERROR ...
|
||||
LOG_ERROR
|
||||
)
|
||||
|
||||
// String ...
|
||||
func (l LogLevel) String() string {
|
||||
switch l {
|
||||
case LOG_SQL:
|
||||
return "SQL"
|
||||
case LOG_SLOW:
|
||||
return "SLOW"
|
||||
case LOG_ERROR:
|
||||
return "ERROR"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// LogOption ...
|
||||
type LogOption struct {
|
||||
FilePath string
|
||||
EnableSqlLog bool
|
||||
// 是否记录慢查询, 默认0s, 不记录, 设置记录的时间阀值, 比如 1, 则表示超过1s的都记录
|
||||
EnableSlowLog float64
|
||||
EnableErrorLog bool
|
||||
}
|
||||
|
||||
// Logger ...
|
||||
type Logger struct {
|
||||
filePath string
|
||||
sqlLog bool
|
||||
slowLog float64
|
||||
//infoLog bool
|
||||
errLog bool
|
||||
}
|
||||
|
||||
var _ ILogger = (*Logger)(nil)
|
||||
|
||||
//var onceLogger sync.Once
|
||||
var logger *Logger
|
||||
|
||||
// NewLogger ...
|
||||
func NewLogger(o *LogOption) *Logger {
|
||||
//onceLogger.Do(func() {
|
||||
logger = &Logger{filePath: "./"}
|
||||
if o.FilePath != "" {
|
||||
logger.filePath = o.FilePath
|
||||
}
|
||||
logger.sqlLog = o.EnableSqlLog
|
||||
logger.slowLog = o.EnableSlowLog
|
||||
logger.errLog = o.EnableErrorLog
|
||||
//})
|
||||
return logger
|
||||
}
|
||||
|
||||
// DefaultLogger ...
|
||||
func DefaultLogger() func(e *Engin) {
|
||||
return func(e *Engin) {
|
||||
e.logger = NewLogger(&LogOption{})
|
||||
}
|
||||
}
|
||||
|
||||
// EnableSqlLog ...
|
||||
func (l *Logger) EnableSqlLog() bool {
|
||||
return l.sqlLog
|
||||
}
|
||||
|
||||
// EnableErrorLog ...
|
||||
func (l *Logger) EnableErrorLog() bool {
|
||||
return l.errLog
|
||||
}
|
||||
|
||||
// EnableSlowLog ...
|
||||
func (l *Logger) EnableSlowLog() float64 {
|
||||
return l.slowLog
|
||||
}
|
||||
|
||||
// Slow ...
|
||||
func (l *Logger) Slow(sqlStr string, runtime time.Duration) {
|
||||
if l.EnableSlowLog() > 0 && runtime.Seconds() > l.EnableSlowLog() {
|
||||
logger.write(LOG_SLOW, "gorose_slow", sqlStr, runtime.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Sql ...
|
||||
func (l *Logger) Sql(sqlStr string, runtime time.Duration) {
|
||||
if l.EnableSqlLog() {
|
||||
logger.write(LOG_SQL, "gorose_sql", sqlStr, runtime.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Error ...
|
||||
func (l *Logger) Error(msg string) {
|
||||
if l.EnableErrorLog() {
|
||||
logger.write(LOG_ERROR, "gorose", msg, "0")
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) write(ll LogLevel, filename string, msg string, runtime string) {
|
||||
now := time.Now()
|
||||
date := now.Format("20060102")
|
||||
datetime := now.Format("2006-01-02 15:04:05")
|
||||
f := readFile(fmt.Sprintf("%s/%v_%v.log", l.filePath, date, filename))
|
||||
content := fmt.Sprintf("[%v] [%v] %v --- %v\n", ll.String(), datetime, runtime, msg)
|
||||
withLockContext(func() {
|
||||
defer f.Close()
|
||||
buf := []byte(content)
|
||||
f.Write(buf)
|
||||
})
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"time"
|
||||
"io"
|
||||
)
|
||||
|
||||
// ILogger ...
|
||||
type ILogger interface {
|
||||
Sql(sqlStr string, runtime time.Duration)
|
||||
Slow(sqlStr string, runtime time.Duration)
|
||||
Error(msg string)
|
||||
EnableSqlLog() bool
|
||||
EnableErrorLog() bool
|
||||
EnableSlowLog() float64
|
||||
}
|
||||
|
||||
|
||||
// 暂时规划
|
||||
type ilogger interface {
|
||||
// Persist 持久化
|
||||
Persist(w io.Writer)
|
||||
// Info 常规日志
|
||||
Info(args ...string)
|
||||
// Error 错误日志
|
||||
Error(args ...string)
|
||||
// Debug 调试日志
|
||||
Debug(args ...string)
|
||||
|
||||
Infof(format string, args ...string)
|
||||
Errorf(format string, args ...string)
|
||||
Debugf(format string, args ...string)
|
||||
InfoWithCtx(ctx interface{}, args ...string)
|
||||
ErrorWithCtx(ctx interface{}, args ...string)
|
||||
DebugWithCtx(ctx interface{}, args ...string)
|
||||
InfofWithCtxf(ctx interface{}, format string, args ...string)
|
||||
ErrorfWithCtxf(ctx interface{}, format string, args ...string)
|
||||
DebugfWithCtxf(ctx interface{}, format string, args ...string)
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDefaultLogger(t *testing.T) {
|
||||
l := NewLogger(&LogOption{FilePath: "/tmp/gorose.log", EnableErrorLog: true})
|
||||
var sqlstr = "select xxx from xxx where a='a' and b=\"33\""
|
||||
l.Sql(sqlstr, time.Duration(1<<4))
|
||||
t.Log("logger success")
|
||||
}
|
||||
31
order_by.go
Normal file
31
order_by.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package gorose
|
||||
|
||||
|
||||
type OrderByItem struct {
|
||||
Column string
|
||||
Direction string // "asc" 或 "desc"
|
||||
IsRaw bool
|
||||
}
|
||||
|
||||
// OrderByClause 存储排序信息。
|
||||
type OrderByClause struct {
|
||||
Columns []OrderByItem
|
||||
}
|
||||
|
||||
// OrderBy adds an ORDER BY clause to the query.
|
||||
func (db *OrderByClause) OrderBy(column string, directions ...string) {
|
||||
var direction string
|
||||
if len(directions) > 0 {
|
||||
direction = directions[0]
|
||||
}
|
||||
db.Columns = append(db.Columns, OrderByItem{
|
||||
Column: column,
|
||||
Direction: direction,
|
||||
})
|
||||
}
|
||||
func (db *OrderByClause) OrderByRaw(column string) {
|
||||
db.Columns = append(db.Columns, OrderByItem{
|
||||
Column: column,
|
||||
IsRaw: true,
|
||||
})
|
||||
}
|
||||
401
orm.go
401
orm.go
@@ -1,401 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"github.com/gohouse/t"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Orm ...
|
||||
type Orm struct {
|
||||
ISession
|
||||
//IBinder
|
||||
*OrmApi
|
||||
driver string
|
||||
bindValues []interface{}
|
||||
}
|
||||
|
||||
var _ IOrm = (*Orm)(nil)
|
||||
|
||||
// NewOrm ...
|
||||
func NewOrm(e IEngin) *Orm {
|
||||
var orm = new(Orm)
|
||||
orm.SetISession(NewSession(e))
|
||||
//orm.IBinder = b
|
||||
orm.OrmApi = new(OrmApi)
|
||||
return orm
|
||||
}
|
||||
|
||||
func NewOrmBuilder() *Orm {
|
||||
return new(Orm)
|
||||
}
|
||||
|
||||
// Close ...
|
||||
func (dba *Orm) Close() {
|
||||
dba.GetISession().Close()
|
||||
}
|
||||
|
||||
// ExtraCols 额外的字段
|
||||
func (dba *Orm) ExtraCols(args ...string) IOrm {
|
||||
dba.extraCols = append(dba.extraCols, args...)
|
||||
return dba
|
||||
}
|
||||
|
||||
// ResetExtraCols ...
|
||||
func (dba *Orm) ResetExtraCols() IOrm {
|
||||
dba.extraCols = []string{}
|
||||
return dba
|
||||
}
|
||||
|
||||
// SetBindValues ...
|
||||
func (dba *Orm) SetBindValues(v interface{}) {
|
||||
dba.bindValues = append(dba.bindValues, v)
|
||||
}
|
||||
|
||||
// ClearBindValues ...
|
||||
func (dba *Orm) ClearBindValues() {
|
||||
dba.bindValues = []interface{}{}
|
||||
}
|
||||
|
||||
// GetBindValues ...
|
||||
func (dba *Orm) GetBindValues() []interface{} {
|
||||
return dba.bindValues
|
||||
}
|
||||
|
||||
// GetDriver ...
|
||||
func (dba *Orm) GetDriver() string {
|
||||
return dba.driver
|
||||
}
|
||||
|
||||
// SetISession ...
|
||||
func (dba *Orm) SetISession(is ISession) {
|
||||
dba.ISession = is
|
||||
}
|
||||
|
||||
// GetISession ...
|
||||
func (dba *Orm) GetISession() ISession {
|
||||
return dba.ISession
|
||||
}
|
||||
|
||||
// GetOrmApi ...
|
||||
func (dba *Orm) GetOrmApi() *OrmApi {
|
||||
return dba.OrmApi
|
||||
}
|
||||
|
||||
// Fields : select fields
|
||||
func (dba *Orm) Table(tab interface{}) IOrm {
|
||||
dba.GetISession().Bind(tab)
|
||||
//dba.table = dba.GetISession().GetTableName()
|
||||
return dba
|
||||
}
|
||||
|
||||
// Fields : select fields
|
||||
func (dba *Orm) Fields(fields ...string) IOrm {
|
||||
dba.fields = fields
|
||||
return dba
|
||||
}
|
||||
|
||||
// AddFields : If you already have a query builder instance and you wish to add a column to its existing select clause, you may use the AddFields method:
|
||||
func (dba *Orm) AddFields(fields ...string) IOrm {
|
||||
dba.fields = append(dba.fields, fields...)
|
||||
return dba
|
||||
}
|
||||
|
||||
// Distinct : select distinct
|
||||
func (dba *Orm) Distinct() IOrm {
|
||||
dba.distinct = true
|
||||
|
||||
return dba
|
||||
}
|
||||
|
||||
// Data : insert or update data
|
||||
func (dba *Orm) Data(data interface{}) IOrm {
|
||||
dba.data = data
|
||||
return dba
|
||||
}
|
||||
|
||||
// Group : select group by
|
||||
func (dba *Orm) Group(group string) IOrm {
|
||||
dba.group = group
|
||||
return dba
|
||||
}
|
||||
|
||||
// GroupBy : equals Group()
|
||||
func (dba *Orm) GroupBy(group string) IOrm {
|
||||
return dba.Group(group)
|
||||
}
|
||||
|
||||
// Having : select having
|
||||
func (dba *Orm) Having(having string) IOrm {
|
||||
dba.having = having
|
||||
return dba
|
||||
}
|
||||
|
||||
// Order : select order by
|
||||
func (dba *Orm) Order(order string) IOrm {
|
||||
dba.order = order
|
||||
return dba
|
||||
}
|
||||
|
||||
// OrderBy : equal order
|
||||
func (dba *Orm) OrderBy(order string) IOrm {
|
||||
return dba.Order(order)
|
||||
}
|
||||
|
||||
// Limit : select limit
|
||||
func (dba *Orm) Limit(limit int) IOrm {
|
||||
dba.limit = limit
|
||||
return dba
|
||||
}
|
||||
|
||||
// Offset : select offset
|
||||
func (dba *Orm) Offset(offset int) IOrm {
|
||||
dba.offset = offset
|
||||
return dba
|
||||
}
|
||||
|
||||
// Page : select page
|
||||
func (dba *Orm) Page(page int) IOrm {
|
||||
dba.offset = (page - 1) * dba.GetLimit()
|
||||
return dba
|
||||
}
|
||||
|
||||
// Where : query or execute where condition, the relation is and
|
||||
func (dba *Orm) Where(args ...interface{}) IOrm {
|
||||
if len(args) == 0 ||
|
||||
t.New(args[0]).Bool() == false {
|
||||
return dba
|
||||
}
|
||||
// 如果只传入一个参数, 则可能是字符串、一维对象、二维数组
|
||||
// 重新组合为长度为3的数组, 第一项为关系(and/or), 第二项为具体传入的参数 []interface{}
|
||||
w := []interface{}{"and", args}
|
||||
|
||||
dba.where = append(dba.where, w)
|
||||
|
||||
return dba
|
||||
}
|
||||
|
||||
// Where : query or execute where condition, the relation is and
|
||||
func (dba *Orm) OrWhere(args ...interface{}) IOrm {
|
||||
// 如果只传入一个参数, 则可能是字符串、一维对象、二维数组
|
||||
|
||||
// 重新组合为长度为3的数组, 第一项为关系(and/or), 第二项为具体传入的参数 []interface{}
|
||||
w := []interface{}{"or", args}
|
||||
|
||||
dba.where = append(dba.where, w)
|
||||
|
||||
return dba
|
||||
}
|
||||
|
||||
// WhereNull ...
|
||||
func (dba *Orm) WhereNull(arg string) IOrm {
|
||||
return dba.Where(arg + " IS NULL")
|
||||
}
|
||||
|
||||
// OrWhereNull ...
|
||||
func (dba *Orm) OrWhereNull(arg string) IOrm {
|
||||
return dba.OrWhere(arg + " IS NULL")
|
||||
}
|
||||
|
||||
// WhereNotNull ...
|
||||
func (dba *Orm) WhereNotNull(arg string) IOrm {
|
||||
return dba.Where(arg + " IS NOT NULL")
|
||||
}
|
||||
|
||||
// OrWhereNotNull ...
|
||||
func (dba *Orm) OrWhereNotNull(arg string) IOrm {
|
||||
return dba.OrWhere(arg + " IS NOT NULL")
|
||||
}
|
||||
|
||||
// WhereRegexp ...
|
||||
func (dba *Orm) WhereRegexp(arg string, expstr string) IOrm {
|
||||
return dba.Where(arg, "REGEXP", expstr)
|
||||
}
|
||||
|
||||
// OrWhereRegexp ...
|
||||
func (dba *Orm) OrWhereRegexp(arg string, expstr string) IOrm {
|
||||
return dba.OrWhere(arg, "REGEXP", expstr)
|
||||
}
|
||||
|
||||
// WhereNotRegexp ...
|
||||
func (dba *Orm) WhereNotRegexp(arg string, expstr string) IOrm {
|
||||
return dba.Where(arg, "NOT REGEXP", expstr)
|
||||
}
|
||||
|
||||
// OrWhereNotRegexp ...
|
||||
func (dba *Orm) OrWhereNotRegexp(arg string, expstr string) IOrm {
|
||||
return dba.OrWhere(arg, "NOT REGEXP", expstr)
|
||||
}
|
||||
|
||||
// WhereIn ...
|
||||
func (dba *Orm) WhereIn(needle string, hystack []interface{}) IOrm {
|
||||
return dba.Where(needle, "IN", hystack)
|
||||
}
|
||||
|
||||
// OrWhereIn ...
|
||||
func (dba *Orm) OrWhereIn(needle string, hystack []interface{}) IOrm {
|
||||
return dba.OrWhere(needle, "IN", hystack)
|
||||
}
|
||||
|
||||
// WhereNotIn ...
|
||||
func (dba *Orm) WhereNotIn(needle string, hystack []interface{}) IOrm {
|
||||
return dba.Where(needle, "NOT IN", hystack)
|
||||
}
|
||||
|
||||
// OrWhereNotIn ...
|
||||
func (dba *Orm) OrWhereNotIn(needle string, hystack []interface{}) IOrm {
|
||||
return dba.OrWhere(needle, "NOT IN", hystack)
|
||||
}
|
||||
|
||||
// WhereBetween ...
|
||||
func (dba *Orm) WhereBetween(needle string, hystack []interface{}) IOrm {
|
||||
return dba.Where(needle, "BETWEEN", hystack)
|
||||
}
|
||||
|
||||
// OrWhereBetween ...
|
||||
func (dba *Orm) OrWhereBetween(needle string, hystack []interface{}) IOrm {
|
||||
return dba.OrWhere(needle, "BETWEEN", hystack)
|
||||
}
|
||||
|
||||
// WhereNotBetween ...
|
||||
func (dba *Orm) WhereNotBetween(needle string, hystack []interface{}) IOrm {
|
||||
return dba.Where(needle, "NOT BETWEEN", hystack)
|
||||
}
|
||||
|
||||
// OrWhereNotBetween ...
|
||||
func (dba *Orm) OrWhereNotBetween(needle string, hystack []interface{}) IOrm {
|
||||
return dba.OrWhere(needle, "NOT BETWEEN", hystack)
|
||||
}
|
||||
|
||||
// Join : select join query
|
||||
func (dba *Orm) Join(args ...interface{}) IOrm {
|
||||
dba._joinBuilder("INNER", args)
|
||||
return dba
|
||||
}
|
||||
|
||||
// LeftJoin ...
|
||||
func (dba *Orm) LeftJoin(args ...interface{}) IOrm {
|
||||
dba._joinBuilder("LEFT", args)
|
||||
return dba
|
||||
}
|
||||
|
||||
// RightJoin ...
|
||||
func (dba *Orm) RightJoin(args ...interface{}) IOrm {
|
||||
dba._joinBuilder("RIGHT", args)
|
||||
return dba
|
||||
}
|
||||
|
||||
// CrossJoin ...
|
||||
func (dba *Orm) CrossJoin(args ...interface{}) IOrm {
|
||||
dba._joinBuilder("CROSS", args)
|
||||
return dba
|
||||
}
|
||||
|
||||
// _joinBuilder
|
||||
func (dba *Orm) _joinBuilder(joinType string, args []interface{}) {
|
||||
dba.join = append(dba.join, []interface{}{joinType, args})
|
||||
}
|
||||
|
||||
// Reset orm api and bind values reset to init
|
||||
func (dba *Orm) Reset() IOrm {
|
||||
dba.OrmApi = new(OrmApi)
|
||||
dba.ClearBindValues()
|
||||
dba.ResetUnion()
|
||||
dba.ResetTable()
|
||||
dba.ResetWhere()
|
||||
dba.ResetExtraCols()
|
||||
return dba
|
||||
//return NewOrm(dba.GetIEngin())
|
||||
}
|
||||
|
||||
// ResetTable ...
|
||||
func (dba *Orm) ResetTable() IOrm {
|
||||
dba.GetISession().SetIBinder(NewBinder())
|
||||
return dba
|
||||
}
|
||||
|
||||
// ResetWhere ...
|
||||
func (dba *Orm) ResetWhere() IOrm {
|
||||
dba.where = [][]interface{}{}
|
||||
return dba
|
||||
}
|
||||
|
||||
// ResetUnion ...
|
||||
func (dba *Orm) ResetUnion() IOrm {
|
||||
dba.GetISession().SetUnion(nil)
|
||||
return dba
|
||||
}
|
||||
|
||||
// BuildSql
|
||||
// operType(select, insert, update, delete)
|
||||
func (dba *Orm) BuildSql(operType ...string) (a string, b []interface{}, err error) {
|
||||
// 解析table
|
||||
dba.table, err = dba.GetISession().GetTableName()
|
||||
if err != nil {
|
||||
dba.GetISession().GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
// 解析字段
|
||||
// 如果有union操作, 则不需要
|
||||
if inArray(dba.GetIBinder().GetBindType(), []interface{}{OBJECT_STRUCT, OBJECT_STRUCT_SLICE}) &&
|
||||
dba.GetUnion() == nil {
|
||||
dba.fields = getTagName(dba.GetIBinder().GetBindResult(), TAGNAME)
|
||||
}
|
||||
if len(operType) == 0 || (len(operType) > 0 && strings.ToLower(operType[0]) == "select") {
|
||||
//// 根据传入的struct, 设置limit, 有效的节约空间
|
||||
//if dba.union == "" {
|
||||
// var bindType = dba.GetIBinder().GetBindType()
|
||||
// if bindType == OBJECT_MAP || bindType == OBJECT_STRUCT {
|
||||
// dba.Limit(1)
|
||||
// }
|
||||
//}
|
||||
a, b, err = NewBuilder(dba.GetISession().GetIEngin().GetDriver()).BuildQuery(dba)
|
||||
if dba.GetISession().GetTransaction() {
|
||||
a = a + dba.GetPessimisticLock()
|
||||
}
|
||||
} else {
|
||||
a, b, err = NewBuilder(dba.GetISession().GetIEngin().GetDriver()).BuildExecute(dba, strings.ToLower(operType[0]))
|
||||
// 重置强制获取更新或插入的字段, 防止复用时感染
|
||||
dba.ResetExtraCols()
|
||||
}
|
||||
// 如果是事务, 因为需要复用单一对象, 故参数会产生感染
|
||||
// 所以, 在这里做一下数据绑定重置操作
|
||||
if dba.GetISession().GetTransaction() {
|
||||
dba.Reset()
|
||||
}
|
||||
// 这里统一清理一下绑定的数据吧, 万一要复用了, 造成绑定数据感染, 就尴尬了
|
||||
dba.ClearBindValues()
|
||||
return
|
||||
}
|
||||
|
||||
// Transaction ...
|
||||
func (dba *Orm) Transaction(closers ...func(db IOrm) error) (err error) {
|
||||
err = dba.ISession.Begin()
|
||||
if err != nil {
|
||||
dba.GetIEngin().GetLogger().Error(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
for _, closer := range closers {
|
||||
err = closer(dba)
|
||||
if err != nil {
|
||||
dba.GetIEngin().GetLogger().Error(err.Error())
|
||||
_ = dba.ISession.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
return dba.ISession.Commit()
|
||||
}
|
||||
|
||||
// SharedLock 共享锁
|
||||
// select * from xxx lock in share mode
|
||||
func (dba *Orm) SharedLock() *Orm {
|
||||
dba.pessimisticLock = " lock in share mode"
|
||||
return dba
|
||||
}
|
||||
|
||||
// LockForUpdate
|
||||
// select * from xxx for update
|
||||
func (dba *Orm) LockForUpdate() *Orm {
|
||||
dba.pessimisticLock = " for update"
|
||||
return dba
|
||||
}
|
||||
96
orm_api.go
96
orm_api.go
@@ -1,96 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// OrmApi ...
|
||||
type OrmApi struct {
|
||||
table string
|
||||
fields []string
|
||||
where [][]interface{}
|
||||
order string
|
||||
limit int
|
||||
offset int
|
||||
join [][]interface{}
|
||||
distinct bool
|
||||
//union string
|
||||
group string
|
||||
having string
|
||||
data interface{}
|
||||
force bool
|
||||
extraCols []string
|
||||
// 悲观锁
|
||||
pessimisticLock string
|
||||
}
|
||||
|
||||
// GetTable ...
|
||||
func (o *Orm) GetTable() string {
|
||||
return o.table
|
||||
}
|
||||
|
||||
// GetFields ...
|
||||
func (o *Orm) GetFields() []string {
|
||||
return o.fields
|
||||
}
|
||||
|
||||
// SetWhere ...
|
||||
func (o *Orm) SetWhere(arg [][]interface{}) {
|
||||
o.where = arg
|
||||
}
|
||||
|
||||
// GetWhere ...
|
||||
func (o *Orm) GetWhere() [][]interface{} {
|
||||
return o.where
|
||||
}
|
||||
|
||||
// GetOrder ...
|
||||
func (o *Orm) GetOrder() string {
|
||||
return o.order
|
||||
}
|
||||
|
||||
// GetLimit ...
|
||||
func (o *Orm) GetLimit() int {
|
||||
return o.limit
|
||||
}
|
||||
|
||||
// GetOffset ...
|
||||
func (o *Orm) GetOffset() int {
|
||||
return o.offset
|
||||
}
|
||||
|
||||
// GetJoin ...
|
||||
func (o *Orm) GetJoin() [][]interface{} {
|
||||
return o.join
|
||||
}
|
||||
|
||||
// GetDistinct ...
|
||||
func (o *Orm) GetDistinct() bool {
|
||||
return o.distinct
|
||||
}
|
||||
|
||||
// GetGroup ...
|
||||
func (o *Orm) GetGroup() string {
|
||||
return o.group
|
||||
}
|
||||
|
||||
// GetHaving ...
|
||||
func (o *Orm) GetHaving() string {
|
||||
return o.having
|
||||
}
|
||||
|
||||
// GetData ...
|
||||
func (o *Orm) GetData() interface{} {
|
||||
return o.data
|
||||
}
|
||||
|
||||
// GetForce ...
|
||||
func (o *Orm) GetForce() bool {
|
||||
return o.force
|
||||
}
|
||||
|
||||
// GetExtraCols ...
|
||||
func (o *Orm) GetExtraCols() []string {
|
||||
return o.extraCols
|
||||
}
|
||||
|
||||
// GetPessimisticLock ...
|
||||
func (o *Orm) GetPessimisticLock() string {
|
||||
return o.pessimisticLock
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// IOrmApi ...
|
||||
type IOrmApi interface {
|
||||
GetTable() string
|
||||
GetFields() []string
|
||||
SetWhere(arg [][]interface{})
|
||||
GetWhere() [][]interface{}
|
||||
GetOrder() string
|
||||
GetLimit() int
|
||||
GetOffset() int
|
||||
GetJoin() [][]interface{}
|
||||
GetDistinct() bool
|
||||
GetGroup() string
|
||||
GetHaving() string
|
||||
GetData() interface{}
|
||||
ExtraCols(args ...string) IOrm
|
||||
ResetExtraCols() IOrm
|
||||
GetExtraCols() []string
|
||||
GetPessimisticLock() string
|
||||
}
|
||||
142
orm_execute.go
142
orm_execute.go
@@ -1,142 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gohouse/t"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Insert : insert data and get affected rows
|
||||
func (dba *Orm) Insert(data ...interface{}) (int64, error) {
|
||||
return dba.exec("insert", data...)
|
||||
}
|
||||
|
||||
// insertGetId : insert data and get id
|
||||
func (dba *Orm) InsertGetId(data ...interface{}) (int64, error) {
|
||||
_, err := dba.Insert(data...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return dba.GetISession().LastInsertId(), nil
|
||||
}
|
||||
|
||||
// Update : update data
|
||||
func (dba *Orm) Update(data ...interface{}) (int64, error) {
|
||||
return dba.exec("update", data...)
|
||||
}
|
||||
|
||||
// Force 强制执行没有where的删除和修改
|
||||
func (dba *Orm) Force() IOrm {
|
||||
dba.force = true
|
||||
return dba
|
||||
}
|
||||
|
||||
// Delete : delete data
|
||||
func (dba *Orm) Delete() (int64, error) {
|
||||
return dba.exec("delete")
|
||||
}
|
||||
|
||||
// Delete : delete data
|
||||
func (dba *Orm) exec(operType string, data ...interface{}) (int64, error) {
|
||||
if operType == "insert" || operType == "update" {
|
||||
if dba.GetData() == nil {
|
||||
if len(data) > 0 {
|
||||
dba.Data(data[0])
|
||||
} else {
|
||||
return 0, GetErr(ERR_PARAMS_MISSING, "Data()")
|
||||
}
|
||||
}
|
||||
|
||||
//if dba.GetISession().GetIBinder() == nil {
|
||||
// 如果这里是默认值, 则需要对其进行table处理
|
||||
//if dba.GetISession().GetIBinder().GetBindType() == OBJECT_NIL {
|
||||
// if dba.GetData() != nil {
|
||||
// dba.Table(dba.GetData())
|
||||
// } else {
|
||||
// return 0, GetErr(ERR_PARAMS_MISSING, "Data() or Table()")
|
||||
// }
|
||||
//}
|
||||
rl := reflect.ValueOf(dba.GetData())
|
||||
rl2 := reflect.Indirect(rl)
|
||||
|
||||
switch rl2.Kind() {
|
||||
case reflect.Struct, reflect.Ptr:
|
||||
//return 0, errors.New("传入的结构体必须是对象的地址")
|
||||
if tn := rl2.MethodByName("TableName"); tn.IsValid() {
|
||||
dba.Table(dba.GetData())
|
||||
}
|
||||
case reflect.Map:
|
||||
if tn := rl2.MethodByName("TableName"); tn.IsValid() {
|
||||
dba.Table(dba.GetData())
|
||||
}
|
||||
if tn := rl.MethodByName("TableName"); tn.IsValid() {
|
||||
dba.Table(dba.GetData())
|
||||
}
|
||||
case reflect.Slice:
|
||||
r2 := rl2.Type().Elem()
|
||||
r2val := reflect.New(r2)
|
||||
switch r2val.Kind() {
|
||||
case reflect.Struct, reflect.Ptr:
|
||||
if tn := r2val.MethodByName("TableName"); tn.IsValid() {
|
||||
dba.Table(dba.GetData())
|
||||
}
|
||||
case reflect.Map:
|
||||
if tn := r2val.MethodByName("TableName"); tn.IsValid() {
|
||||
dba.Table(dba.GetData())
|
||||
}
|
||||
default:
|
||||
return 0, errors.New("表名有误")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// 构建sql
|
||||
sqlStr, args, err := dba.BuildSql(operType)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return dba.GetISession().Execute(sqlStr, args...)
|
||||
}
|
||||
|
||||
// Increment : auto Increment +1 default
|
||||
// we can define step (such as 2, 3, 6 ...) if give the second params
|
||||
// we can use this method as decrement with the third param as "-"
|
||||
// orm.Increment("top") , orm.Increment("top", 2, "-")=orm.Decrement("top",2)
|
||||
func (dba *Orm) Increment(args ...interface{}) (int64, error) {
|
||||
argLen := len(args)
|
||||
var field string
|
||||
var mode = "+"
|
||||
var value = "1"
|
||||
switch argLen {
|
||||
case 1:
|
||||
field = t.New(args[0]).String()
|
||||
case 2:
|
||||
field = t.New(args[0]).String()
|
||||
value = t.New(args[1]).String()
|
||||
case 3:
|
||||
field = t.New(args[0]).String()
|
||||
value = t.New(args[1]).String()
|
||||
mode = t.New(args[2]).String()
|
||||
default:
|
||||
return 0, errors.New("参数数量只允许1个,2个或3个")
|
||||
}
|
||||
dba.Data(field + "=" + field + mode + value)
|
||||
return dba.Update()
|
||||
}
|
||||
|
||||
// Decrement : auto Decrement -1 default
|
||||
// we can define step (such as 2, 3, 6 ...) if give the second params
|
||||
func (dba *Orm) Decrement(args ...interface{}) (int64, error) {
|
||||
arglen := len(args)
|
||||
switch arglen {
|
||||
case 1:
|
||||
args = append(args, 1)
|
||||
args = append(args, "-")
|
||||
case 2:
|
||||
args = append(args, "-")
|
||||
default:
|
||||
return 0, errors.New("Decrement参数个数有误")
|
||||
}
|
||||
return dba.Increment(args...)
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// IOrmExecute ...
|
||||
type IOrmExecute interface {
|
||||
GetForce() bool
|
||||
// insert,insertGetId
|
||||
Insert(data ...interface{}) (int64, error)
|
||||
InsertGetId(data ...interface{}) (int64, error)
|
||||
Update(data ...interface{}) (int64, error)
|
||||
// updateOrInsert
|
||||
|
||||
// increment,decrement
|
||||
// 在操作过程中你还可以指定额外的列进行更新:
|
||||
Increment(args ...interface{}) (int64, error)
|
||||
Decrement(args ...interface{}) (int64, error)
|
||||
// delete
|
||||
Delete() (int64, error)
|
||||
//LastInsertId() int64
|
||||
Force() IOrm
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOrm_Update(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
var u = []Users{{
|
||||
Name: "gorose2",
|
||||
Age: 19,
|
||||
}}
|
||||
|
||||
aff, err := db.Force().Update(&u)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(aff, db.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_Update2(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
//var u = []Users{{
|
||||
// Name: "gorose2",
|
||||
// Age: 11,
|
||||
//}}
|
||||
|
||||
aff, err := db.Table("users").Where("uid", 1).Update()
|
||||
if err != nil {
|
||||
//t.Error(err.Error())
|
||||
t.Log(err.Error())
|
||||
return
|
||||
}
|
||||
t.Log(aff, db.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_UpdateMap(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
//var u = []UsersMap{{"name": "gorose2", "age": 19}}
|
||||
var u = UsersMap{"name": "gorose2", "age": 19}
|
||||
|
||||
aff, err := db.Force().Update(&u)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(aff, db.LastSql())
|
||||
}
|
||||
|
||||
func TestTrans(t *testing.T) {
|
||||
var db = DB()
|
||||
var db2 = DB()
|
||||
var res Users
|
||||
db.Begin()
|
||||
db2.Table(&res).Select()
|
||||
t.Log(res)
|
||||
db.Commit()
|
||||
t.Log(res)
|
||||
}
|
||||
|
||||
func Test_Transaction(t *testing.T) {
|
||||
var db = DB()
|
||||
// 一键事务, 自动回滚和提交, 我们只需要关注业务即可
|
||||
err := db.Transaction(
|
||||
func(db IOrm) error {
|
||||
//db.Table("users").Limit(2).SharedLock().Get()
|
||||
//fmt.Println(db.LastSql())
|
||||
_, err := db.Table("users").Where("uid", 2).Update(Data{"name": "gorose2"})
|
||||
fmt.Println(db.LastSql())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Insert(&UsersMap{"name": "gorose2", "age": 0})
|
||||
fmt.Println(db.LastSql())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(db IOrm) error {
|
||||
_, err := db.Table("users").Where("uid", 3).Update(Data{"name": "gorose3"})
|
||||
fmt.Println(db.LastSql())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = db.Insert(&UsersMap{"name": "gorose2", "age": 0})
|
||||
fmt.Println(db.LastSql())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("事务测试通过")
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// IOrm ...
|
||||
type IOrm interface {
|
||||
IOrmApi
|
||||
IOrmQuery
|
||||
IOrmExecute
|
||||
IOrmSession
|
||||
//ISession
|
||||
Close()
|
||||
BuildSql(operType ...string) (string, []interface{}, error)
|
||||
Table(tab interface{}) IOrm
|
||||
// fields=select
|
||||
Fields(fields ...string) IOrm
|
||||
AddFields(fields ...string) IOrm
|
||||
// distinct 方法允许你强制查询返回不重复的结果集:
|
||||
Distinct() IOrm
|
||||
Data(data interface{}) IOrm
|
||||
// groupBy, orderBy, having
|
||||
Group(group string) IOrm
|
||||
GroupBy(group string) IOrm
|
||||
Having(having string) IOrm
|
||||
Order(order string) IOrm
|
||||
OrderBy(order string) IOrm
|
||||
Limit(limit int) IOrm
|
||||
Offset(offset int) IOrm
|
||||
Page(page int) IOrm
|
||||
// join(=innerJoin),leftJoin,rightJoin,crossJoin
|
||||
Join(args ...interface{}) IOrm
|
||||
LeftJoin(args ...interface{}) IOrm
|
||||
RightJoin(args ...interface{}) IOrm
|
||||
CrossJoin(args ...interface{}) IOrm
|
||||
// `Where`,`OrWhere`,`WhereNull / WhereNotNull`,`WhereIn / WhereNotIn / OrWhereIn / OrWhereNotIn`,`WhereBetween / WhereBetwee / OrWhereBetween / OrWhereNotBetween`
|
||||
Where(args ...interface{}) IOrm
|
||||
OrWhere(args ...interface{}) IOrm
|
||||
WhereNull(arg string) IOrm
|
||||
OrWhereNull(arg string) IOrm
|
||||
WhereNotNull(arg string) IOrm
|
||||
OrWhereNotNull(arg string) IOrm
|
||||
WhereRegexp(arg string, expstr string) IOrm
|
||||
OrWhereRegexp(arg string, expstr string) IOrm
|
||||
WhereNotRegexp(arg string, expstr string) IOrm
|
||||
OrWhereNotRegexp(arg string, expstr string) IOrm
|
||||
WhereIn(needle string, hystack []interface{}) IOrm
|
||||
OrWhereIn(needle string, hystack []interface{}) IOrm
|
||||
WhereNotIn(needle string, hystack []interface{}) IOrm
|
||||
OrWhereNotIn(needle string, hystack []interface{}) IOrm
|
||||
WhereBetween(needle string, hystack []interface{}) IOrm
|
||||
OrWhereBetween(needle string, hystack []interface{}) IOrm
|
||||
WhereNotBetween(needle string, hystack []interface{}) IOrm
|
||||
OrWhereNotBetween(needle string, hystack []interface{}) IOrm
|
||||
// truncate
|
||||
//Truncate()
|
||||
GetDriver() string
|
||||
//GetIBinder() IBinder
|
||||
SetBindValues(v interface{})
|
||||
GetBindValues() []interface{}
|
||||
ClearBindValues()
|
||||
Transaction(closers ...func(db IOrm) error) (err error)
|
||||
Reset() IOrm
|
||||
ResetTable() IOrm
|
||||
ResetWhere() IOrm
|
||||
GetISession() ISession
|
||||
GetOrmApi() *OrmApi
|
||||
// 悲观锁使用
|
||||
// sharedLock(lock in share mode) 不会阻塞其它事务读取被锁定行记录的值
|
||||
SharedLock() *Orm
|
||||
// 此外你还可以使用 lockForUpdate 方法。“for update”锁避免选择行被其它共享锁修改或删除:
|
||||
// 会阻塞其他锁定性读对锁定行的读取(非锁定性读仍然可以读取这些记录,lock in share mode 和 for update 都是锁定性读)
|
||||
LockForUpdate() *Orm
|
||||
//ResetUnion() IOrm
|
||||
}
|
||||
438
orm_query.go
438
orm_query.go
@@ -1,438 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"github.com/gohouse/t"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Select : select one or more rows , relation limit set
|
||||
func (dba *Orm) Select() error {
|
||||
switch dba.GetIBinder().GetBindType() {
|
||||
case OBJECT_STRUCT, OBJECT_MAP, OBJECT_MAP_T:
|
||||
dba.Limit(1)
|
||||
}
|
||||
// 构建sql
|
||||
sqlStr, args, err := dba.BuildSql()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 执行查询
|
||||
_, err = dba.GetISession().Query(sqlStr, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
// First : select one row , relation limit set
|
||||
func (dba *Orm) First() (result Data, err error) {
|
||||
dba.GetIBinder().SetBindType(OBJECT_STRING)
|
||||
err = dba.Limit(1).Select()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
res := dba.GetISession().GetBindAll()
|
||||
if len(res) > 0 {
|
||||
result = res[0]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get : select more rows , relation limit set
|
||||
func (dba *Orm) Get() (result []Data, err error) {
|
||||
dba.GetIBinder().SetBindType(OBJECT_STRING)
|
||||
tabname := dba.GetISession().GetIBinder().GetBindName()
|
||||
prefix := dba.GetISession().GetIBinder().GetBindPrefix()
|
||||
tabname2 := strings.TrimPrefix(tabname, prefix)
|
||||
dba.ResetTable()
|
||||
dba.Table(tabname2)
|
||||
err = dba.Select()
|
||||
result = dba.GetISession().GetBindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Count : select count rows
|
||||
func (dba *Orm) Count(args ...string) (int64, error) {
|
||||
fields := "*"
|
||||
if len(args) > 0 {
|
||||
fields = args[0]
|
||||
}
|
||||
count, err := dba._unionBuild("count", fields)
|
||||
if count == nil {
|
||||
return 0, err
|
||||
}
|
||||
return t.New(count).Int64(), err
|
||||
}
|
||||
|
||||
// Sum : select sum field
|
||||
func (dba *Orm) Sum(sum string) (interface{}, error) {
|
||||
return dba._unionBuild("sum", sum)
|
||||
}
|
||||
|
||||
// Avg : select avg field
|
||||
func (dba *Orm) Avg(avg string) (interface{}, error) {
|
||||
return dba._unionBuild("avg", avg)
|
||||
}
|
||||
|
||||
// Max : select max field
|
||||
func (dba *Orm) Max(max string) (interface{}, error) {
|
||||
return dba._unionBuild("max", max)
|
||||
}
|
||||
|
||||
// Min : select min field
|
||||
func (dba *Orm) Min(min string) (interface{}, error) {
|
||||
return dba._unionBuild("min", min)
|
||||
}
|
||||
|
||||
// _unionBuild : build union select real
|
||||
func (dba *Orm) _unionBuild(union, field string) (interface{}, error) {
|
||||
fields := union + "(" + field + ") as " + union
|
||||
dba.fields = []string{fields}
|
||||
|
||||
res, err := dba.First()
|
||||
if r, ok := res[union]; ok {
|
||||
return r, err
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
//func (dba *Orm) _unionBuild_bak(union, field string) (interface{}, error) {
|
||||
// var tmp interface{}
|
||||
//
|
||||
// dba.union = union + "(" + field + ") as " + union
|
||||
// // 缓存fields字段,暂时由union占用
|
||||
// fieldsTmp := dba.fields
|
||||
// dba.fields = []string{dba.union}
|
||||
// dba.GetISession().SetUnion(true)
|
||||
//
|
||||
// // 构建sql
|
||||
// sqls, args, err := dba.BuildSql()
|
||||
// if err != nil {
|
||||
// return tmp, err
|
||||
// }
|
||||
//
|
||||
// // 执行查询
|
||||
// _, err = dba.GetISession().Query(sqls, args...)
|
||||
// if err != nil {
|
||||
// return tmp, err
|
||||
// }
|
||||
//
|
||||
// // 重置union, 防止复用的时候感染
|
||||
// dba.union = ""
|
||||
// // 返还fields
|
||||
// dba.fields = fieldsTmp
|
||||
//
|
||||
// // 语法糖获取union值
|
||||
// if dba.GetISession().GetUnion() != nil {
|
||||
// tmp = dba.GetISession().GetUnion()
|
||||
// // 获取之后, 释放掉
|
||||
// dba.GetISession().SetUnion(nil)
|
||||
// }
|
||||
//
|
||||
// return tmp, nil
|
||||
//}
|
||||
|
||||
// Pluck 获取一列数据, 第二个字段可以指定另一个字段的值作为这一列数据的key
|
||||
func (dba *Orm) Pluck(field string, fieldKey ...string) (v interface{}, err error) {
|
||||
var resMap = make(map[interface{}]interface{}, 0)
|
||||
var resSlice = make([]interface{}, 0)
|
||||
|
||||
res, err := dba.Get()
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
for _, val := range res {
|
||||
if len(fieldKey) > 0 {
|
||||
resMap[val[fieldKey[0]]] = val[field]
|
||||
} else {
|
||||
resSlice = append(resSlice, val[field])
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(fieldKey) > 0 {
|
||||
v = resMap
|
||||
} else {
|
||||
v = resSlice
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pluck_bak ...
|
||||
func (dba *Orm) Pluck_bak(field string, fieldKey ...string) (v interface{}, err error) {
|
||||
var binder = dba.GetISession().GetIBinder()
|
||||
var resMap = make(map[interface{}]interface{}, 0)
|
||||
var resSlice = make([]interface{}, 0)
|
||||
|
||||
err = dba.Select()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch binder.GetBindType() {
|
||||
case OBJECT_MAP, OBJECT_MAP_T, OBJECT_STRUCT: // row
|
||||
var key, val interface{}
|
||||
if len(fieldKey) > 0 {
|
||||
key, err = dba.Value(fieldKey[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
val, err = dba.Value(field)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
resMap[key] = val
|
||||
} else {
|
||||
v, err = dba.Value(field)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
case OBJECT_MAP_SLICE, OBJECT_MAP_SLICE_T:
|
||||
for _, item := range t.New(binder.GetBindResultSlice().Interface()).Slice() {
|
||||
val := item.MapInterfaceT()
|
||||
if len(fieldKey) > 0 {
|
||||
resMap[val[fieldKey[0]].Interface()] = val[field].Interface()
|
||||
} else {
|
||||
resSlice = append(resSlice, val[field].Interface())
|
||||
}
|
||||
}
|
||||
case OBJECT_STRUCT_SLICE: // rows
|
||||
var brs = binder.GetBindResultSlice()
|
||||
for i := 0; i < brs.Len(); i++ {
|
||||
val := reflect.Indirect(brs.Index(i))
|
||||
if len(fieldKey) > 0 {
|
||||
mapkey := dba._valueFromStruct(val, fieldKey[0])
|
||||
mapVal := dba._valueFromStruct(val, field)
|
||||
resMap[mapkey] = mapVal
|
||||
} else {
|
||||
resSlice = append(resSlice, dba._valueFromStruct(val, field))
|
||||
}
|
||||
}
|
||||
case OBJECT_STRING:
|
||||
res := dba.GetISession().GetBindAll()
|
||||
if len(res) > 0 {
|
||||
for _, val := range res {
|
||||
if len(fieldKey) > 0 {
|
||||
resMap[val[fieldKey[0]]] = val[field]
|
||||
} else {
|
||||
resSlice = append(resSlice, val[field])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(fieldKey) > 0 {
|
||||
v = resMap
|
||||
} else {
|
||||
v = resSlice
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Type is get a row of a field value
|
||||
func (dba *Orm) Value(field string) (v interface{}, err error) {
|
||||
res, err := dba.First()
|
||||
if v, ok := res[field]; ok {
|
||||
return v, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Value_bak ...
|
||||
func (dba *Orm) Value_bak(field string) (v interface{}, err error) {
|
||||
dba.Limit(1)
|
||||
err = dba.Select()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var binder = dba.GetISession().GetIBinder()
|
||||
switch binder.GetBindType() {
|
||||
case OBJECT_MAP, OBJECT_MAP_SLICE, OBJECT_MAP_SLICE_T, OBJECT_MAP_T:
|
||||
v = reflect.ValueOf(binder.GetBindResult()).MapIndex(reflect.ValueOf(field)).Interface()
|
||||
case OBJECT_STRUCT, OBJECT_STRUCT_SLICE:
|
||||
bindResult := reflect.Indirect(reflect.ValueOf(binder.GetBindResult()))
|
||||
v = dba._valueFromStruct(bindResult, field)
|
||||
case OBJECT_STRING:
|
||||
res := dba.GetISession().GetBindAll()
|
||||
if len(res) > 0 {
|
||||
v = res[0][field]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (dba *Orm) _valueFromStruct(bindResult reflect.Value, field string) (v interface{}) {
|
||||
ostype := bindResult.Type()
|
||||
for i := 0; i < ostype.NumField(); i++ {
|
||||
tag := ostype.Field(i).Tag.Get(TAGNAME)
|
||||
if tag == field || ostype.Field(i).Name == field {
|
||||
v = bindResult.FieldByName(ostype.Field(i).Name).Interface()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Chunk : 分块处理数据,当要处理很多数据的时候, 我不需要知道具体是多少数据, 我只需要每次取limit条数据,
|
||||
// 然后不断的增加offset去取更多数据, 从而达到分块处理更多数据的目的
|
||||
//TODO 后续增加 gorotine 支持, 提高批量数据处理效率, 预计需要增加获取更多链接的支持
|
||||
func (dba *Orm) Chunk(limit int, callback func([]Data) error) (err error) {
|
||||
var page = 1
|
||||
var tabname = dba.GetISession().GetIBinder().GetBindName()
|
||||
prefix := dba.GetISession().GetIBinder().GetBindPrefix()
|
||||
tabname2 := strings.TrimPrefix(tabname, prefix)
|
||||
// 先执行一条看看是否报错, 同时设置指定的limit, offset
|
||||
result, err := dba.Table(tabname2).Limit(limit).Page(page).Get()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for len(result) > 0 {
|
||||
if err = callback(result); err != nil {
|
||||
break
|
||||
}
|
||||
page++
|
||||
// 清理绑定数据, 进行下一次操作, 因为绑定数据是每一次执行的时候都会解析并保存的
|
||||
// 而第二次以后执行的, 都会再次解析并保存, 数据结构是slice, 故会累积起来
|
||||
dba.ClearBindValues()
|
||||
result, _ = dba.Page(page).Get()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ChunkStruct : 同Chunk,只不过不用返回map, 而是绑定数据到传入的对象上
|
||||
// 这里一定要传入绑定struct
|
||||
func (dba *Orm) ChunkStruct(limit int, callback func() error) (err error) {
|
||||
var page = 0
|
||||
//var tableName = dba.GetISession().GetIBinder().GetBindName()
|
||||
// 先执行一条看看是否报错, 同时设置指定的limit, offset
|
||||
err = dba.Limit(limit).Offset(page * limit).Select()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch dba.GetIBinder().GetBindType() {
|
||||
case OBJECT_STRUCT, OBJECT_MAP, OBJECT_MAP_T:
|
||||
var ibinder = dba.GetIBinder()
|
||||
var result = ibinder.GetBindResult()
|
||||
for result != nil {
|
||||
if err = callback(); err != nil {
|
||||
break
|
||||
}
|
||||
page++
|
||||
// 清空结果
|
||||
//result = nil
|
||||
var rfRes = reflect.ValueOf(result)
|
||||
rfRes.Set(reflect.Zero(rfRes.Type()))
|
||||
// 清理绑定数据, 进行下一次操作, 因为绑定数据是每一次执行的时候都会解析并保存的
|
||||
// 而第二次以后执行的, 都会再次解析并保存, 数据结构是slice, 故会累积起来
|
||||
dba.ClearBindValues()
|
||||
_ = dba.Table(ibinder.GetBindOrigin()).Offset(page * limit).Select()
|
||||
result = dba.GetIBinder().GetBindResultSlice()
|
||||
}
|
||||
case OBJECT_STRUCT_SLICE, OBJECT_MAP_SLICE, OBJECT_MAP_SLICE_T:
|
||||
var ibinder = dba.GetIBinder()
|
||||
var result = ibinder.GetBindResultSlice()
|
||||
for result.Interface() != nil {
|
||||
if err = callback(); err != nil {
|
||||
break
|
||||
}
|
||||
page++
|
||||
// 清空结果
|
||||
result.Set(result.Slice(0, 0))
|
||||
// 清理绑定数据, 进行下一次操作, 因为绑定数据是每一次执行的时候都会解析并保存的
|
||||
// 而第二次以后执行的, 都会再次解析并保存, 数据结构是slice, 故会累积起来
|
||||
dba.ClearBindValues()
|
||||
_ = dba.Table(ibinder.GetBindOrigin()).Offset(page * limit).Select()
|
||||
result = dba.GetIBinder().GetBindResultSlice()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Loop : 同chunk, 不过, 这个是循环的取前limit条数据, 为什么是循环取这些数据呢
|
||||
// 因为, 我们考虑到一种情况, 那就是where条件如果刚好是要修改的值,
|
||||
// 那么最后的修改结果因为offset的原因, 只会修改一半, 比如:
|
||||
// DB().Where("age", 18) ===> DB().Data(gorose.Data{"age":19}).Where().Update()
|
||||
func (dba *Orm) Loop(limit int, callback func([]Data) error) (err error) {
|
||||
var page = 0
|
||||
var tabname = dba.GetISession().GetIBinder().GetBindName()
|
||||
prefix := dba.GetISession().GetIBinder().GetBindPrefix()
|
||||
tabname2 := strings.TrimPrefix(tabname, prefix)
|
||||
// 先执行一条看看是否报错, 同时设置指定的limit
|
||||
result, err := dba.Table(tabname2).Limit(limit).Get()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for len(result) > 0 {
|
||||
if err = callback(result); err != nil {
|
||||
break
|
||||
}
|
||||
page++
|
||||
// 同chunk
|
||||
dba.ClearBindValues()
|
||||
result, _ = dba.Get()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Paginate 自动分页
|
||||
// @param limit 每页展示数量
|
||||
// @param current_page 当前第几页, 从1开始
|
||||
// 以下是laravel的Paginate返回示例
|
||||
//{
|
||||
// "total": 50,
|
||||
// "per_page": 15,
|
||||
// "current_page": 1,
|
||||
// "lastPage": 4,
|
||||
// "first_page_url": "http://laravel.app?page=1",
|
||||
// "lastPage_url": "http://laravel.app?page=4",
|
||||
// "nextPage_url": "http://laravel.app?page=2",
|
||||
// "prevPage_url": null,
|
||||
// "path": "http://laravel.app",
|
||||
// "from": 1,
|
||||
// "to": 15,
|
||||
// "data":[
|
||||
// {
|
||||
// // Result Object
|
||||
// },
|
||||
// {
|
||||
// // Result Object
|
||||
// }
|
||||
// ]
|
||||
//}
|
||||
func (dba *Orm) Paginate(page ...int) (res Data, err error) {
|
||||
if len(page) > 0 {
|
||||
dba.Page(page[0])
|
||||
}
|
||||
var limit = dba.GetLimit()
|
||||
if limit == 0 {
|
||||
limit = 15
|
||||
}
|
||||
var offset = dba.GetOffset()
|
||||
var currentPage = int(math.Ceil(float64(offset+1) / float64(limit)))
|
||||
//dba.ResetUnion()
|
||||
// 获取结果
|
||||
resData, err := dba.Get()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// 统计总量
|
||||
dba.offset = 0
|
||||
count, err := dba.Count()
|
||||
var lastPage = int(math.Ceil(float64(count) / float64(limit)))
|
||||
var nextPage = currentPage + 1
|
||||
var prevPage = currentPage - 1
|
||||
res = Data{
|
||||
"total": count,
|
||||
"per_page": limit,
|
||||
"current_page": currentPage,
|
||||
"last_page": lastPage,
|
||||
"first_page_url": 1,
|
||||
"last_page_url": lastPage,
|
||||
"next_page_url": If(nextPage > lastPage, nil, nextPage),
|
||||
"prev_page_url": If(prevPage < 1, nil, prevPage),
|
||||
//"data": dba.GetIBinder().GetBindResultSlice().Interface(),
|
||||
"data": resData,
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// IOrmQuery ...
|
||||
type IOrmQuery interface {
|
||||
// 获取数据, 依据传入的绑定对象, 选择查询一条或多条数据并绑定到传入对象上
|
||||
// 当绑定对象传入的是string类型时, 返回多条结果集, 需要使用 Get() 来获取最终结果
|
||||
Select() error
|
||||
// 获取一条结果并返回, 只有当传入的table对象是字符串时生效
|
||||
First() (Data, error)
|
||||
// 获取多条结果并返回, 只有当传入的table对象是字符串时生效
|
||||
Get() ([]Data, error)
|
||||
// 如果你不需要完整的一行,可以使用 value 方法从结果中获取单个值,该方法会直接返回指定列的值:
|
||||
Value(field string) (v interface{}, err error)
|
||||
// 如果想要获取包含单个列值的数组,可以使用 pluck 方法
|
||||
// 还可以在返回数组中为列值指定自定义键(该自定义键必须是该表的其它字段列名,否则会报错)
|
||||
Pluck(field string, fieldKey ...string) (v interface{}, err error)
|
||||
// 查询构建器还提供了多个聚合方法,如count, max, min, avg 和 sum,你可以在构造查询之后调用这些方法:
|
||||
Count(args ...string) (int64, error)
|
||||
Sum(sum string) (interface{}, error)
|
||||
Avg(avg string) (interface{}, error)
|
||||
Max(max string) (interface{}, error)
|
||||
Min(min string) (interface{}, error)
|
||||
// 分页, 返回分页需要的基本数据
|
||||
Paginate(page ...int) (res Data, err error)
|
||||
// 组块结果集
|
||||
// 如果你需要处理成千上万或者更多条数据库记录,可以考虑使用 chunk 方法,该方法一次获取结果集的一小块,
|
||||
// 然后传递每一小块数据到闭包函数进行处理,该方法在编写处理大量数据库记录的 Artisan 命令的时候非常有用。
|
||||
// 例如,我们可以将处理全部 users 表数据分割成一次处理 100 条记录的小组块
|
||||
// 你可以通过从闭包函数中返回 err 来终止组块的运行
|
||||
Chunk(limit int, callback func([]Data) error) (err error)
|
||||
// 跟Chunk类似,只不过callback的是传入的结构体
|
||||
ChunkStruct(limit int, callback func() error) (err error)
|
||||
Loop(limit int, callback func([]Data) error) (err error)
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestOrm_BuildSql2(t *testing.T) {
|
||||
db := DB()
|
||||
var u = "age=age+1,num=num+1"
|
||||
var wheres interface{}
|
||||
wheres = [][]interface{}{{"a", ">", "b"}, {"a", "b"}, {"a is null"}}
|
||||
sqlstr, a, b := db.Force().Table("users").Data(u).Where(wheres).BuildSql("update")
|
||||
|
||||
t.Log(sqlstr, a, b)
|
||||
}
|
||||
|
||||
func TestOrm_BuildSql3(t *testing.T) {
|
||||
db := DB()
|
||||
var u = "age=age+1,num=num+1"
|
||||
var wheres interface{}
|
||||
wheres = [][]interface{}{{"a", ">", "b"}, {"a", "b"}}
|
||||
sqlstr, a, b := db.Force().Table(Users{}).Data(u).Where(wheres).BuildSql("update")
|
||||
|
||||
t.Log(sqlstr, a, b)
|
||||
}
|
||||
|
||||
func TestOrm_BuildSql4(t *testing.T) {
|
||||
db := DB()
|
||||
//var wheres interface{}
|
||||
//wheres = [][]interface{}{{"a", ">", "b"},{"lock",1}}
|
||||
|
||||
wheres := Data{"lock": 1,"`date`": 1}
|
||||
obj := db.Table(Users{}).Where(wheres).Where(func() {
|
||||
db.Where("c", 2).OrWhere("lock", ">", 4)
|
||||
}).Data(wheres)
|
||||
|
||||
sqlstr, a, b := obj.BuildSql()
|
||||
t.Log(sqlstr, a, b)
|
||||
|
||||
sqlstr, a, b = obj.BuildSql("update")
|
||||
t.Log(sqlstr, a, b)
|
||||
|
||||
sqlstr, a, b = obj.BuildSql("insert")
|
||||
t.Log(sqlstr, a, b)
|
||||
}
|
||||
|
||||
func TestOrm_BuildSql5(t *testing.T) {
|
||||
go func(t *testing.T) {
|
||||
for {
|
||||
//<-ticker.C
|
||||
db := DB()
|
||||
sqlstr, a, b := db.Table("users").Where("uid", ">", 1).BuildSql()
|
||||
//c,d := db.Table("users").Get()
|
||||
//t.Log(db.LastSql())
|
||||
count, d := db.First()
|
||||
|
||||
t.Log(sqlstr, a, b)
|
||||
t.Log(count, d)
|
||||
t.Log(db.LastSql())
|
||||
}
|
||||
}(t)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestOrm_BuildSql6(t *testing.T) {
|
||||
var db = DB()
|
||||
sqlstr, a, b := db.Table("users3").Limit(2).Offset(2).BuildSql()
|
||||
t.Log(sqlstr, a, b)
|
||||
|
||||
sqlstr, a, b = db.Table("users2").Limit(2).Offset(2).BuildSql()
|
||||
t.Log(sqlstr, a, b)
|
||||
|
||||
var u = Users{
|
||||
Uid: 1111,
|
||||
Name: "2",
|
||||
Age: 3,
|
||||
}
|
||||
res, err := db.Table("xxx").Where("xx", "xx").Update(&u)
|
||||
t.Log(db.LastSql(), res, err)
|
||||
}
|
||||
|
||||
func TestOrm_First(t *testing.T) {
|
||||
res, err := DB().Table(Users{}).Where("uid", 1).First()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res)
|
||||
}
|
||||
|
||||
func TestOrm_Select(t *testing.T) {
|
||||
db := DB()
|
||||
var err error
|
||||
|
||||
var u = []Users{}
|
||||
err = db.Table(&u).Select()
|
||||
t.Log(err, u, db.LastSql())
|
||||
|
||||
var u2 = Users{}
|
||||
err = db.Table(&u2).Select()
|
||||
t.Log(err, u2, db.LastSql())
|
||||
|
||||
var u3 Users
|
||||
err = db.Table(&u3).Select()
|
||||
t.Log(err, u3, db.LastSql())
|
||||
|
||||
var u4 []Users
|
||||
err = db.Table(&u4).Limit(2).Select()
|
||||
t.Log(err, u4, db.LastSql())
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(u, u2, u3, u4)
|
||||
}
|
||||
|
||||
func TestOrm_Select2(t *testing.T) {
|
||||
db := DB()
|
||||
var err error
|
||||
|
||||
var u = []UsersMap{}
|
||||
err = db.Table(&u).Limit(2).Select()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(u)
|
||||
|
||||
var u3 = UsersMap{}
|
||||
err = db.Table(&u3).Limit(1).Select()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(u)
|
||||
}
|
||||
|
||||
type Users2 struct {
|
||||
Name string `orm:"name"`
|
||||
Age int `orm:"age"`
|
||||
Uid int `orm:"uid"`
|
||||
Fi string `orm:"ignore"`
|
||||
}
|
||||
|
||||
func (u *Users2) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
func TestOrm_Get2(t *testing.T) {
|
||||
db := DB()
|
||||
var err error
|
||||
var u []Users2
|
||||
|
||||
res, err := db.Table("users").Where("uid", ">", 2).
|
||||
Where("1","=","1").
|
||||
//Where("1 = 1").
|
||||
Limit(2).Get()
|
||||
//res, err := db.Table(&u).Where("uid", ">", 0).Limit(2).Get()
|
||||
fmt.Println(db.LastSql())
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res, u)
|
||||
}
|
||||
|
||||
func TestOrm_Get(t *testing.T) {
|
||||
orm := DB()
|
||||
|
||||
var u = UsersMap{}
|
||||
ormObj := orm.Table(&u).Join("b", "a.id", "=", "b.id").
|
||||
RightJoin("userinfo d on a.id=d.id").
|
||||
Fields("a.uid,a.age").
|
||||
Order("uid desc").
|
||||
Where("a", 1).
|
||||
WhereNull("bb").
|
||||
WhereNotNull("cc").
|
||||
WhereIn("dd", []interface{}{1, 2}).
|
||||
OrWhereNotIn("ee", []interface{}{1, 2}).
|
||||
WhereBetween("ff", []interface{}{11, 21}).
|
||||
WhereNotBetween("ff", []interface{}{1, 2}).
|
||||
Where("a", "like", "%3%").
|
||||
OrWhere(func() {
|
||||
orm.Where("c", 3).OrWhere(func() {
|
||||
orm.Where("d", ">", 4)
|
||||
})
|
||||
}).Where("e", 5).Limit(5).Offset(2)
|
||||
s, a, err := ormObj.BuildSql()
|
||||
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(s, a, u)
|
||||
}
|
||||
|
||||
func TestOrm_Pluck(t *testing.T) {
|
||||
orm := DB()
|
||||
|
||||
//var u = UsersMapSlice{}
|
||||
//var u []Users
|
||||
ormObj := orm.Table("users")
|
||||
//res,err := ormObj.Pluck("name", "uid")
|
||||
res, err := ormObj.Limit(5).Pluck("name", "uid")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res, orm.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_Value(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
//var u = UsersMap{}
|
||||
//var u = UsersMapSlice{}
|
||||
//var u Users
|
||||
//var u []Users
|
||||
//ormObj := db.Table(&u)
|
||||
//ormObj := db.Table("users")
|
||||
ormObj := db.Table(Users{})
|
||||
res, err := ormObj.Limit(5).Value("uid")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res, db.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_Count(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
//var u = UsersMap{}
|
||||
//ormObj := db.Table(&u)
|
||||
ormObj := db.Table("users")
|
||||
|
||||
res, err := ormObj.Count()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res, db.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_Count2(t *testing.T) {
|
||||
var u Users
|
||||
var count int64
|
||||
count, err := DB().Table(&u).Count()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(count)
|
||||
}
|
||||
|
||||
func TestOrm_Chunk(t *testing.T) {
|
||||
orm := DB()
|
||||
|
||||
var u = []UsersMap{}
|
||||
err := orm.Table(&u).Chunk(1, func(data []Data) error {
|
||||
for _, item := range data {
|
||||
t.Log(item["name"])
|
||||
}
|
||||
return errors.New("故意停止,防止数据过多,浪费时间")
|
||||
//return nil
|
||||
})
|
||||
if err != nil && err.Error() != "故意停止,防止数据过多,浪费时间" {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("Chunk() success")
|
||||
}
|
||||
|
||||
func TestOrm_Chunk2(t *testing.T) {
|
||||
orm := DB()
|
||||
|
||||
var u []Users
|
||||
var i int
|
||||
err := orm.Table(&u).ChunkStruct(2, func() error {
|
||||
//for _, item := range u {
|
||||
t.Log(u)
|
||||
//}
|
||||
if i == 2 {
|
||||
return errors.New("故意停止,防止数据过多,浪费时间")
|
||||
}
|
||||
i++
|
||||
return nil
|
||||
})
|
||||
if err != nil && err.Error() != "故意停止,防止数据过多,浪费时间" {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("ChunkStruct() success")
|
||||
}
|
||||
|
||||
func TestOrm_Loop(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
var u = []UsersMap{}
|
||||
//aff,err := db.Table(&u).Force().Data(Data{"age": 18}).Update()
|
||||
//fmt.Println(aff,err)
|
||||
err := db.Table(&u).Where("age", 18).Loop(2, func(data []Data) error {
|
||||
for _, item := range data {
|
||||
_, err := DB().Table(&u).Data(Data{"age": 19}).Where("uid", item["uid"]).Update()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
return errors.New("故意停止,防止数据过多,浪费时间")
|
||||
//return nil
|
||||
})
|
||||
if err != nil && err.Error() != "故意停止,防止数据过多,浪费时间" {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("Loop() success")
|
||||
}
|
||||
|
||||
func TestOrm_Paginate(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
var u []Users
|
||||
res, err := db.Table(&u).Limit(2).Paginate()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res, u)
|
||||
t.Log(db.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_Paginate2(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
var u []Users
|
||||
res, err := db.Table(&u).Where("uid", ">", 1).Limit(2).Paginate(3)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res, u)
|
||||
t.Log(db.LastSql())
|
||||
}
|
||||
|
||||
func TestOrm_Sum(t *testing.T) {
|
||||
db := DB()
|
||||
|
||||
var u Users
|
||||
//res, err := db.Table(Users{}).First()
|
||||
res, err := db.Table(&u).Where(Data{"uid": 1}).Sum("age")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
//fmt.Printf("%#v\n",res)
|
||||
t.Log(res, u)
|
||||
t.Log(db.LastSql())
|
||||
}
|
||||
|
||||
func BenchmarkNewOrm(b *testing.B) {
|
||||
engin := initDB()
|
||||
for i := 0; i < b.N; i++ {
|
||||
engin.NewOrm().Table("users").First()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewOrm2(b *testing.B) {
|
||||
engin := initDB()
|
||||
for i := 0; i < b.N; i++ {
|
||||
engin.NewOrm().Table("users").First()
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// IOrmSession ...
|
||||
type IOrmSession interface {
|
||||
//Close()
|
||||
//Table(bind interface{}) IOrm
|
||||
//Bind(bind interface{}) ISession
|
||||
Begin() (err error)
|
||||
Rollback() (err error)
|
||||
Commit() (err error)
|
||||
//Transaction(closer ...func(session ISession) error) (err error)
|
||||
Query(sqlstring string, args ...interface{}) ([]Data, error)
|
||||
Execute(sqlstring string, args ...interface{}) (int64, error)
|
||||
//GetMasterDriver() string
|
||||
//GetSlaveDriver() string
|
||||
LastInsertId() int64
|
||||
LastSql() string
|
||||
//SetIBinder(b IBinder)
|
||||
//GetTableName() (string, error)
|
||||
GetIBinder() IBinder
|
||||
SetUnion(u interface{})
|
||||
GetUnion() interface{}
|
||||
}
|
||||
64
orm_test.go
64
orm_test.go
@@ -1,64 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func DB() IOrm {
|
||||
return initDB().NewOrm()
|
||||
}
|
||||
|
||||
func TestNewOrm(t *testing.T) {
|
||||
orm := DB()
|
||||
orm.Close()
|
||||
}
|
||||
|
||||
func TestOrm_AddFields(t *testing.T) {
|
||||
orm := DB()
|
||||
//var u = Users{}
|
||||
var fieldStmt = orm.Table("users").Fields("a").Where("m", 55)
|
||||
a, b, err := fieldStmt.AddFields("b").Where("d", 1).BuildSql()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(a, b)
|
||||
|
||||
fieldStmt.Reset()
|
||||
d, e, err := fieldStmt.Fields("a").AddFields("c").Where("d", 2).BuildSql()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(d, e)
|
||||
}
|
||||
|
||||
func TestOrm_BuildSql(t *testing.T) {
|
||||
var u = Users{
|
||||
Name: "gorose2",
|
||||
Age: 19,
|
||||
}
|
||||
|
||||
//aff, err := db.Force().Data(&u)
|
||||
a, b, err := DB().Table(&u).Where("age", ">", 1).Data(&u).BuildSql("update")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(a, b)
|
||||
}
|
||||
|
||||
func TestOrm_BuildSql_where(t *testing.T) {
|
||||
var u = Users{
|
||||
Name: "gorose2",
|
||||
Age: 19,
|
||||
}
|
||||
|
||||
var db = DB()
|
||||
a, b, err := db.Table(&u).Where("age", ">", 1).Where(func() {
|
||||
db.Where("name", "like", "%fizz%").OrWhere(func() {
|
||||
db.Where("age", ">", 10).Where("uid", ">", 2)
|
||||
})
|
||||
}).Limit(2).Offset(2).BuildSql()
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(a, b)
|
||||
}
|
||||
58
select.go
Normal file
58
select.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package gorose
|
||||
|
||||
import "strings"
|
||||
|
||||
// Column 表示SELECT语句中的列信息。
|
||||
type Column struct {
|
||||
Name string
|
||||
Alias string // 可选别名
|
||||
IsRaw bool // 是否是原生SQL片段
|
||||
Binds []any // 绑定数据
|
||||
}
|
||||
|
||||
// SelectClause 存储SELECT子句相关信息。
|
||||
type SelectClause struct {
|
||||
Columns []Column
|
||||
Distinct bool
|
||||
}
|
||||
|
||||
// Select specifies the columns to retrieve.
|
||||
// Select("a","b")
|
||||
// Select("a.id as aid","b.id bid")
|
||||
// Select("id,nickname name")
|
||||
func (db *SelectClause) Select(columns ...string) *SelectClause {
|
||||
for _, column := range columns {
|
||||
splits := strings.Split(column, ",")
|
||||
for _, split := range splits {
|
||||
parts := strings.Split(strings.TrimSpace(split), " ")
|
||||
switch len(parts) {
|
||||
case 3:
|
||||
db.Columns = append(db.Columns, Column{
|
||||
Name: strings.TrimSpace(parts[0]),
|
||||
Alias: strings.TrimSpace(parts[2]),
|
||||
})
|
||||
case 2:
|
||||
db.Columns = append(db.Columns, Column{
|
||||
Name: strings.TrimSpace(parts[0]),
|
||||
Alias: strings.TrimSpace(parts[1]),
|
||||
})
|
||||
case 1:
|
||||
db.Columns = append(db.Columns, Column{
|
||||
Name: strings.TrimSpace(parts[0]),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return db
|
||||
}
|
||||
func (db *SelectClause) AddSelect(columns ...string) *SelectClause { return db.Select(columns...) }
|
||||
|
||||
// SelectRaw 允许直接在查询中插入原始SQL片段作为选择列。
|
||||
func (db *SelectClause) SelectRaw(raw string, binds ...any) *SelectClause {
|
||||
db.Columns = append(db.Columns, Column{
|
||||
Name: raw,
|
||||
IsRaw: true,
|
||||
Binds: binds,
|
||||
})
|
||||
return db
|
||||
}
|
||||
485
session.go
485
session.go
@@ -1,485 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohouse/t"
|
||||
)
|
||||
|
||||
// Session ...
|
||||
type Session struct {
|
||||
IEngin
|
||||
IBinder
|
||||
master *sql.DB
|
||||
tx *sql.Tx
|
||||
slave *sql.DB
|
||||
lastInsertId int64
|
||||
sqlLogs []string
|
||||
lastSql string
|
||||
union interface{}
|
||||
transaction bool
|
||||
err error
|
||||
}
|
||||
|
||||
var _ ISession = (*Session)(nil)
|
||||
|
||||
// NewSession : 初始化 Session
|
||||
func NewSession(e IEngin) *Session {
|
||||
var s = new(Session)
|
||||
s.IEngin = e
|
||||
// 初始化 IBinder
|
||||
s.SetIBinder(NewBinder())
|
||||
|
||||
s.master = e.GetExecuteDB()
|
||||
s.slave = e.GetQueryDB()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Session) Close() {
|
||||
s.master.Close()
|
||||
s.slave.Close()
|
||||
}
|
||||
|
||||
// GetIEngin 获取engin
|
||||
func (s *Session) GetIEngin() IEngin {
|
||||
return s.IEngin
|
||||
}
|
||||
|
||||
// GetDriver 获取驱动
|
||||
func (s *Session) SetIEngin(ie IEngin) {
|
||||
s.IEngin = ie
|
||||
}
|
||||
|
||||
// Bind : 传入绑定结果的对象, 参数一为对象, 可以是 struct, gorose.MapRow 或对应的切片
|
||||
// 如果是做非query操作,第一个参数也可以仅仅指定为字符串表名
|
||||
func (s *Session) Bind(tab interface{}) ISession {
|
||||
//fmt.Println(tab, NewBinder(tab))
|
||||
//s.SetIBinder(NewBinder(tab))
|
||||
s.GetIBinder().SetBindOrigin(tab)
|
||||
s.err = s.IBinder.BindParse(s.GetIEngin().GetPrefix())
|
||||
return s
|
||||
}
|
||||
|
||||
// GetBinder 获取绑定对象
|
||||
func (s *Session) GetErr() error {
|
||||
return s.err
|
||||
}
|
||||
|
||||
// GetBinder 获取绑定对象
|
||||
func (s *Session) SetIBinder(ib IBinder) {
|
||||
s.IBinder = ib
|
||||
}
|
||||
|
||||
// GetBinder 获取绑定对象
|
||||
func (s *Session) GetIBinder() IBinder {
|
||||
return s.IBinder
|
||||
}
|
||||
|
||||
// GetBinder 获取绑定对象
|
||||
func (s *Session) ResetBinderResult() {
|
||||
_ = s.IBinder.BindParse(s.GetIEngin().GetPrefix())
|
||||
}
|
||||
|
||||
// GetTableName 获取解析后的名字, 提供给orm使用
|
||||
// 为什么要在这里重复添加该方法, 而不是直接继承 IBinder 的方法呢?
|
||||
// 是因为, 这里涉及到表前缀的问题, 只能通过session来传递, 所以IOrm就可以选择直接继承
|
||||
func (s *Session) GetTableName() (string, error) {
|
||||
//err := s.IBinder.BindParse(s.GetIEngin().GetPrefix())
|
||||
//fmt.Println(s.GetIBinder())
|
||||
return s.GetIBinder().GetBindName(), s.err
|
||||
}
|
||||
|
||||
// Begin ...
|
||||
func (s *Session) Begin() (err error) {
|
||||
s.tx, err = s.master.Begin()
|
||||
s.SetTransaction(true)
|
||||
return
|
||||
}
|
||||
|
||||
// Rollback ...
|
||||
func (s *Session) Rollback() (err error) {
|
||||
err = s.tx.Rollback()
|
||||
s.tx = nil
|
||||
s.SetTransaction(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Commit ...
|
||||
func (s *Session) Commit() (err error) {
|
||||
err = s.tx.Commit()
|
||||
s.tx = nil
|
||||
s.SetTransaction(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Transaction ...
|
||||
func (s *Session) Transaction(closers ...func(ses ISession) error) (err error) {
|
||||
err = s.Begin()
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
for _, closer := range closers {
|
||||
err = closer(s)
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
_ = s.Rollback()
|
||||
return
|
||||
}
|
||||
}
|
||||
return s.Commit()
|
||||
}
|
||||
|
||||
// Query ...
|
||||
func (s *Session) Query(sqlstring string, args ...interface{}) (result []Data, err error) {
|
||||
// 记录开始时间
|
||||
start := time.Now()
|
||||
//withRunTimeContext(func() {
|
||||
if s.err != nil {
|
||||
err = s.err
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
}
|
||||
// 记录sqlLog
|
||||
s.lastSql = fmt.Sprint(sqlstring, ", ", args)
|
||||
//if s.IfEnableSqlLog() {
|
||||
// s.sqlLogs = append(s.sqlLogs, s.lastSql)
|
||||
//}
|
||||
|
||||
var stmt *sql.Stmt
|
||||
// 如果是事务, 则从主库中读写
|
||||
if s.tx == nil {
|
||||
stmt, err = s.slave.Prepare(sqlstring)
|
||||
} else {
|
||||
stmt, err = s.tx.Prepare(sqlstring)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
defer stmt.Close()
|
||||
rows, err := stmt.Query(args...)
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// make sure we always close rows
|
||||
defer rows.Close()
|
||||
|
||||
err = s.scan(rows)
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
//}, func(duration time.Duration) {
|
||||
// //if duration.Seconds() > 1 {
|
||||
// // s.GetIEngin().GetLogger().Slow(s.LastSql(), duration)
|
||||
// //} else {
|
||||
// // s.GetIEngin().GetLogger().Sql(s.LastSql(), duration)
|
||||
// //}
|
||||
//})
|
||||
|
||||
timeduration := time.Since(start)
|
||||
//if timeduration.Seconds() > 1 {
|
||||
s.GetIEngin().GetLogger().Slow(s.LastSql(), timeduration)
|
||||
//} else {
|
||||
s.GetIEngin().GetLogger().Sql(s.LastSql(), timeduration)
|
||||
//}
|
||||
|
||||
result = s.GetIBinder().GetBindAll()
|
||||
return
|
||||
}
|
||||
|
||||
// Execute ...
|
||||
func (s *Session) Execute(sqlstring string, args ...interface{}) (rowsAffected int64, err error) {
|
||||
// 记录开始时间
|
||||
start := time.Now()
|
||||
//withRunTimeContext(func() {
|
||||
// err = s.GetIBinder().BindParse(s.GetIEngin().GetPrefix())
|
||||
if s.err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
s.lastSql = fmt.Sprint(sqlstring, ", ", args)
|
||||
//// 记录sqlLog
|
||||
//if s.IfEnableSqlLog() {
|
||||
// s.sqlLogs = append(s.sqlLogs, s.lastSql)
|
||||
//}
|
||||
|
||||
var operType = strings.ToLower(sqlstring[0:6])
|
||||
if operType == "select" {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
err = errors.New("Execute does not allow select operations, please use Query")
|
||||
return
|
||||
}
|
||||
|
||||
var stmt *sql.Stmt
|
||||
if s.tx == nil {
|
||||
stmt, err = s.master.Prepare(sqlstring)
|
||||
} else {
|
||||
stmt, err = s.tx.Prepare(sqlstring)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
//var err error
|
||||
defer stmt.Close()
|
||||
result, err := stmt.Exec(args...)
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if operType == "insert" {
|
||||
// get last insert id
|
||||
lastInsertId, err := result.LastInsertId()
|
||||
if err == nil {
|
||||
s.lastInsertId = lastInsertId
|
||||
} else {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
}
|
||||
}
|
||||
// get rows affected
|
||||
rowsAffected, err = result.RowsAffected()
|
||||
timeduration := time.Since(start)
|
||||
//}, func(duration time.Duration) {
|
||||
if timeduration.Seconds() > 1 {
|
||||
s.GetIEngin().GetLogger().Slow(s.LastSql(), timeduration)
|
||||
} else {
|
||||
s.GetIEngin().GetLogger().Sql(s.LastSql(), timeduration)
|
||||
}
|
||||
//})
|
||||
return
|
||||
}
|
||||
|
||||
// LastInsertId ...
|
||||
func (s *Session) LastInsertId() int64 {
|
||||
return s.lastInsertId
|
||||
}
|
||||
|
||||
// LastSql ...
|
||||
func (s *Session) LastSql() string {
|
||||
return s.lastSql
|
||||
}
|
||||
|
||||
func (s *Session) scan(rows *sql.Rows) (err error) {
|
||||
// 如果不需要绑定, 则需要初始化一下binder
|
||||
if s.GetIBinder() == nil {
|
||||
s.SetIBinder(NewBinder())
|
||||
}
|
||||
// 检查实多维数组还是一维数组
|
||||
switch s.GetBindType() {
|
||||
case OBJECT_STRING:
|
||||
err = s.scanAll(rows)
|
||||
case OBJECT_STRUCT, OBJECT_STRUCT_SLICE:
|
||||
err = s.scanStructAll(rows)
|
||||
//case OBJECT_MAP, OBJECT_MAP_T:
|
||||
// err = s.scanMap(rows, s.GetBindResult())
|
||||
case OBJECT_MAP, OBJECT_MAP_T, OBJECT_MAP_SLICE, OBJECT_MAP_SLICE_T:
|
||||
err = s.scanMapAll(rows)
|
||||
case OBJECT_NIL:
|
||||
err = s.scanAll(rows)
|
||||
default:
|
||||
err = errors.New("Bind value error")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//func (s *Session) scanMap(rows *sql.Rows, dst interface{}) (err error) {
|
||||
// return s.scanMapAll(rows, dst)
|
||||
//}
|
||||
|
||||
func (s *Session) scanMapAll(rows *sql.Rows) (err error) {
|
||||
var columns []string
|
||||
// 获取查询的所有字段
|
||||
if columns, err = rows.Columns(); err != nil {
|
||||
return
|
||||
}
|
||||
count := len(columns)
|
||||
|
||||
for rows.Next() {
|
||||
// 定义要绑定的结果集
|
||||
values := make([]interface{}, count)
|
||||
scanArgs := make([]interface{}, count)
|
||||
for i := 0; i < count; i++ {
|
||||
scanArgs[i] = &values[i]
|
||||
}
|
||||
// 获取结果
|
||||
_ = rows.Scan(scanArgs...)
|
||||
|
||||
// 定义预设的绑定对象
|
||||
//fmt.Println(reflect.TypeOf(s.GetBindResult()).Kind())
|
||||
var bindResultTmp = reflect.MakeMap(reflect.Indirect(reflect.ValueOf(s.GetBindResult())).Type())
|
||||
//// 定义union操作的map返回
|
||||
//var unionTmp = map[string]interface{}{}
|
||||
for i, col := range columns {
|
||||
var v interface{}
|
||||
val := values[i]
|
||||
if b, ok := val.([]byte); ok {
|
||||
v = string(b)
|
||||
} else {
|
||||
v = val
|
||||
}
|
||||
// 如果是union操作就不需要绑定数据直接返回, 否则就绑定数据
|
||||
//TODO 这里可能有点问题, 比如在group时, 返回的结果不止一条, 这里直接返回的就是第一条
|
||||
// 默认其实只是取了第一条, 满足常规的 union 操作(count,sum,max,min,avg)而已
|
||||
// 后边需要再行完善, 以便group时使用
|
||||
// 具体完善方法: 就是这里断点去掉, 不直接绑定union, 新增一个map,将结果放在map中,在方法最后统一返回
|
||||
if s.GetUnion() != nil {
|
||||
s.union = v
|
||||
return
|
||||
// 以下上通用解决方法
|
||||
//unionTmp[col] = v
|
||||
//s.union = unionTmp
|
||||
} else {
|
||||
br := reflect.Indirect(reflect.ValueOf(s.GetBindResult()))
|
||||
switch s.GetBindType() {
|
||||
case OBJECT_MAP_T, OBJECT_MAP_SLICE_T: // t.T类型
|
||||
// 绑定到一条数据结果对象上,方便其他地方的调用,永远存储最新一条
|
||||
br.SetMapIndex(reflect.ValueOf(col), reflect.ValueOf(t.New(v)))
|
||||
// 跟上一行干的事是一样的, 只不过防止上一行的数据被后续的数据改变, 而无法提供给下边多条数据报错的需要
|
||||
if s.GetBindType() == OBJECT_MAP_SLICE || s.GetBindType() == OBJECT_MAP_SLICE_T {
|
||||
bindResultTmp.SetMapIndex(reflect.ValueOf(col), reflect.ValueOf(t.New(v)))
|
||||
}
|
||||
default: // 普通类型map[string]interface{}, 具体代码注释参照 上一个 case
|
||||
br.SetMapIndex(reflect.ValueOf(col), reflect.ValueOf(v))
|
||||
if s.GetBindType() == OBJECT_MAP_SLICE || s.GetBindType() == OBJECT_MAP_SLICE_T {
|
||||
bindResultTmp.SetMapIndex(reflect.ValueOf(col), reflect.ValueOf(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果是union操作就不需要绑定数据直接返回, 否则就绑定数据
|
||||
if s.GetUnion() == nil {
|
||||
// 如果是多条数据集, 就插入到对应的结果集slice上
|
||||
if s.GetBindType() == OBJECT_MAP_SLICE || s.GetBindType() == OBJECT_MAP_SLICE_T {
|
||||
s.GetBindResultSlice().Set(reflect.Append(s.GetBindResultSlice(), bindResultTmp))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ScanAll scans all sql result rows into a slice of structs.
|
||||
// It reads all rows and closes rows when finished.
|
||||
// dst should be a pointer to a slice of the appropriate type.
|
||||
// The new results will be appended to any existing data in dst.
|
||||
func (s *Session) scanStructAll(rows *sql.Rows) error {
|
||||
// check if there is data waiting
|
||||
//if !rows.Next() {
|
||||
// if err := rows.Err(); err != nil {
|
||||
// s.GetIEngin().GetLogger().Error(err.Error())
|
||||
// return err
|
||||
// }
|
||||
// return sql.ErrNoRows
|
||||
//}
|
||||
var sfs = structForScan(s.GetBindResult())
|
||||
for rows.Next() {
|
||||
if s.GetUnion() != nil {
|
||||
var union interface{}
|
||||
err := rows.Scan(&union)
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return err
|
||||
}
|
||||
s.union = union
|
||||
return err
|
||||
}
|
||||
// scan it
|
||||
//fmt.Printf("%#v \n",structForScan(s.GetBindResult()))
|
||||
err := rows.Scan(sfs...)
|
||||
if err != nil {
|
||||
s.GetIEngin().GetLogger().Error(err.Error())
|
||||
return err
|
||||
}
|
||||
// 如果是union操作就不需要绑定数据直接返回, 否则就绑定数据
|
||||
if s.GetUnion() == nil {
|
||||
// 如果是多条数据集, 就插入到对应的结果集slice上
|
||||
if s.GetBindType() == OBJECT_STRUCT_SLICE {
|
||||
// add to the result slice
|
||||
s.GetBindResultSlice().Set(reflect.Append(s.GetBindResultSlice(),
|
||||
reflect.Indirect(reflect.ValueOf(s.GetBindResult()))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rows.Err()
|
||||
}
|
||||
|
||||
func (s *Session) scanAll(rows *sql.Rows) (err error) {
|
||||
var columns []string
|
||||
// 获取查询的所有字段
|
||||
if columns, err = rows.Columns(); err != nil {
|
||||
return
|
||||
}
|
||||
count := len(columns)
|
||||
|
||||
var result = []Data{}
|
||||
for rows.Next() {
|
||||
// 定义要绑定的结果集
|
||||
values := make([]interface{}, count)
|
||||
scanArgs := make([]interface{}, count)
|
||||
for i := 0; i < count; i++ {
|
||||
scanArgs[i] = &values[i]
|
||||
}
|
||||
// 获取结果
|
||||
_ = rows.Scan(scanArgs...)
|
||||
|
||||
// 定义预设的绑定对象
|
||||
var resultTmp = Data{}
|
||||
//// 定义union操作的map返回
|
||||
//var unionTmp = map[string]interface{}{}
|
||||
for i, col := range columns {
|
||||
var v interface{}
|
||||
val := values[i]
|
||||
if b, ok := val.([]byte); ok {
|
||||
v = string(b)
|
||||
} else {
|
||||
v = val
|
||||
}
|
||||
if s.GetUnion() != nil {
|
||||
s.union = v
|
||||
return
|
||||
// 以下上通用解决方法
|
||||
//unionTmp[col] = v
|
||||
//s.union = unionTmp
|
||||
}
|
||||
resultTmp[col] = v
|
||||
}
|
||||
result = append(result, resultTmp)
|
||||
}
|
||||
s.IBinder.SetBindAll(result)
|
||||
return
|
||||
}
|
||||
|
||||
// SetUnion ...
|
||||
func (s *Session) SetUnion(u interface{}) {
|
||||
s.union = u
|
||||
}
|
||||
|
||||
// GetUnion ...
|
||||
func (s *Session) GetUnion() interface{} {
|
||||
return s.union
|
||||
}
|
||||
|
||||
// SetTransaction ...
|
||||
func (s *Session) SetTransaction(b bool) {
|
||||
s.transaction = b
|
||||
}
|
||||
|
||||
// GetTransaction 提供给 orm 使用的, 方便reset操作
|
||||
func (s *Session) GetTransaction() bool {
|
||||
return s.transaction
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package gorose
|
||||
|
||||
// ISession ...
|
||||
type ISession interface {
|
||||
Close()
|
||||
//Table(bind interface{}) IOrm
|
||||
Bind(bind interface{}) ISession
|
||||
Begin() (err error)
|
||||
Rollback() (err error)
|
||||
Commit() (err error)
|
||||
Transaction(closer ...func(session ISession) error) (err error)
|
||||
Query(sqlstring string, args ...interface{}) ([]Data, error)
|
||||
Execute(sqlstring string, args ...interface{}) (int64, error)
|
||||
//GetDriver() string
|
||||
GetIEngin() IEngin
|
||||
LastInsertId() int64
|
||||
LastSql() string
|
||||
//SetIBinder(b IBinder)
|
||||
GetTableName() (string, error)
|
||||
SetIBinder(ib IBinder)
|
||||
GetIBinder() IBinder
|
||||
SetUnion(u interface{})
|
||||
GetUnion() interface{}
|
||||
SetTransaction(b bool)
|
||||
GetTransaction() bool
|
||||
//ResetBinder()
|
||||
GetBindAll() []Data
|
||||
GetErr() error
|
||||
}
|
||||
176
session_test.go
176
session_test.go
@@ -1,176 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func initSession() ISession {
|
||||
return initDB().NewSession()
|
||||
}
|
||||
|
||||
func TestSession_Query(t *testing.T) {
|
||||
var s = initSession()
|
||||
//var user []Users
|
||||
res, err := s.Query("select * from users where name=?", "gorose2")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
//t.Log(res)
|
||||
t.Log(res, s.LastSql())
|
||||
}
|
||||
|
||||
func TestSession_Query3(t *testing.T) {
|
||||
var s = initSession()
|
||||
var o []Users
|
||||
//var o []map[string]interface{}
|
||||
//var o []gorose.Data
|
||||
res, err := s.Bind(&o).Query("select * from users limit 2")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(res)
|
||||
t.Log(o, s.LastSql())
|
||||
}
|
||||
|
||||
func TestSession_Execute(t *testing.T) {
|
||||
var sql = `CREATE TABLE IF NOT EXISTS "orders" (
|
||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"goodsname" TEXT NOT NULL default "",
|
||||
"price" decimal default "0.00"
|
||||
)`
|
||||
var s = initSession()
|
||||
var err error
|
||||
var aff int64
|
||||
|
||||
aff, err = s.Execute(sql)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
if aff == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
aff, err = s.Execute("insert into orders(goodsname,price) VALUES(?,?),(?,?)",
|
||||
"goods1", 1.23, "goods2", 3.23)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log(aff)
|
||||
}
|
||||
|
||||
func TestSession_Query_struct(t *testing.T) {
|
||||
var s = initSession()
|
||||
var err error
|
||||
// defer s.Close()
|
||||
|
||||
var user []Users
|
||||
_, err = s.Bind(&user).Query("select * from users limit ?", 2)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("多条struct绑定:", user)
|
||||
|
||||
var user2 Users
|
||||
_, err = s.Bind(&user2).Query("select * from users limit ?", 2)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("一条struct绑定:", user2)
|
||||
}
|
||||
|
||||
//type UserMap map[string]interface{}
|
||||
|
||||
func TestSession_Query_map(t *testing.T) {
|
||||
var s = initSession()
|
||||
var err error
|
||||
|
||||
var user2 = aaa{}
|
||||
_, err = s.Bind(&user2).Query("select * from users limit ?", 2)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("一条map绑定:", user2)
|
||||
t.Log("一条map绑定的uid为:", user2["uid"])
|
||||
t.Log(s.LastSql())
|
||||
|
||||
var user = bbb{}
|
||||
_, err = s.Bind(&user).Query("select * from users limit ?", 2)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("多条map绑定:", user)
|
||||
t.Log("多条map绑定:", user[0]["age"].Int())
|
||||
t.Log(s.LastSql())
|
||||
}
|
||||
|
||||
func TestSession_Bind(t *testing.T) {
|
||||
var s = initSession()
|
||||
var err error
|
||||
|
||||
var user2 = aaa{}
|
||||
_, err = s.Bind(&user2).Query("select * from users limit ?", 2)
|
||||
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("session bind success")
|
||||
}
|
||||
|
||||
func TestSession_Transaction(t *testing.T) {
|
||||
var s = initSession()
|
||||
// 一键事务, 自动回滚和提交, 我们只需要关注业务即可
|
||||
err := s.Transaction(trans1, trans2)
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
t.Log("session transaction success")
|
||||
}
|
||||
|
||||
func trans1(s ISession) error {
|
||||
var err error
|
||||
var aff int64
|
||||
aff, err = s.Execute("update users set name=?,age=? where uid=?",
|
||||
"gorose3", 21, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aff == 0 {
|
||||
return errors.New("fail")
|
||||
}
|
||||
|
||||
aff, err = s.Execute("update users set name=?,age=? where uid=?",
|
||||
"gorose2", 20, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aff == 0 {
|
||||
return errors.New("fail")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func trans2(s ISession) error {
|
||||
var err error
|
||||
var aff int64
|
||||
aff, err = s.Execute("update users set name=?,age=? where uid=?",
|
||||
"gorose3", 21, 3)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aff == 0 {
|
||||
return errors.New("fail")
|
||||
}
|
||||
|
||||
aff, err = s.Execute("update users set name=?,age=? where uid=?",
|
||||
"gorose2", 20, 2)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if aff == 0 {
|
||||
return errors.New("fail")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
188
structsParser.go
Normal file
188
structsParser.go
Normal file
@@ -0,0 +1,188 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func StructsToTableName(rft reflect.Type) (tab string) {
|
||||
if field, ok := rft.FieldByName("TableName"); ok {
|
||||
if field.Tag.Get("db") != "" {
|
||||
tab = field.Tag.Get("db")
|
||||
}
|
||||
}
|
||||
if tab == "" {
|
||||
if tn := reflect.New(rft).Elem().MethodByName("TableName"); tn.IsValid() {
|
||||
tab = tn.Call(nil)[0].String()
|
||||
}
|
||||
}
|
||||
if tab == "" {
|
||||
tab = rft.Name()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StructsParse(obj any) (FieldTag []string, FieldStruct []string, pkField string) {
|
||||
rfv := reflect.Indirect(reflect.ValueOf(obj))
|
||||
switch rfv.Kind() {
|
||||
case reflect.Struct:
|
||||
return structsTypeParse(rfv.Type())
|
||||
case reflect.Slice:
|
||||
return structsTypeParse(rfv.Type())
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func structsTypeParse(rft reflect.Type) (fieldTag []string, fieldStruct []string, pkField string) {
|
||||
//rfv := reflect.Indirect(reflect.ValueOf(obj))
|
||||
if rft.Kind() == reflect.Slice {
|
||||
rft2 := rft.Elem()
|
||||
if rft2.Kind() == reflect.Struct {
|
||||
return structsTypeParse(rft2)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < rft.NumField(); i++ {
|
||||
field := rft.Field(i)
|
||||
if field.Anonymous {
|
||||
continue
|
||||
}
|
||||
tag := field.Tag.Get("db")
|
||||
if tag == "-" || field.Name == "TableName" {
|
||||
continue
|
||||
}
|
||||
if tag == "" {
|
||||
//field.Tag = reflect.StructTag("db:" + field.Name)
|
||||
fieldStruct = append(fieldStruct, field.Name)
|
||||
tag = field.Name
|
||||
} else {
|
||||
if strings.Contains(tag, ",") {
|
||||
tags := strings.Split(tag, ",")
|
||||
if slices.Contains(tags, "pk") {
|
||||
pkField = field.Name
|
||||
tag = tags[0]
|
||||
}
|
||||
}
|
||||
fieldStruct = append(fieldStruct, field.Name)
|
||||
}
|
||||
//else {
|
||||
// fieldStruct = append(fieldStruct, field.Tag.Get("db"))
|
||||
//}
|
||||
//if field.Tag.Get("pkField") == "true" {
|
||||
// pkField = field.Name
|
||||
// pkValue = rfv.FieldByName(field.Name)
|
||||
//}
|
||||
fieldTag = append(fieldTag, tag)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//func StructsToSelects(obj any) []string {
|
||||
// tag, fieldStruct, _ := StructsParse(obj)
|
||||
// if len(tag) > 0 {
|
||||
// return tag
|
||||
// } else {
|
||||
// return fieldStruct
|
||||
// }
|
||||
//}
|
||||
|
||||
func structDataToMap(rfv reflect.Value, tags, fieldStruct []string, mustFields ...string) (data map[string]any, err error) {
|
||||
data = make(map[string]any)
|
||||
for i, fieldName := range fieldStruct {
|
||||
field := rfv.FieldByName(fieldName)
|
||||
if (field.Kind() == reflect.Ptr && field.IsNil()) || (field.IsZero() && !slices.Contains(mustFields, tags[i])) {
|
||||
continue
|
||||
}
|
||||
var rfvVal = field.Interface()
|
||||
if v, ok := rfvVal.(driver.Valuer); ok {
|
||||
var value driver.Value
|
||||
value, err = v.Value()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data[tags[i]] = value
|
||||
} else {
|
||||
data[tags[i]] = rfvVal
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func structUpdateDataToMap(rfv reflect.Value, tags, fieldStruct []string, pkField string, mustFields ...string) (data map[string]any, err error) {
|
||||
data = make(map[string]any)
|
||||
for i, fieldName := range fieldStruct {
|
||||
field := rfv.FieldByName(fieldName)
|
||||
if (field.Kind() == reflect.Ptr && field.IsNil()) || (field.IsZero() && !slices.Contains(mustFields, tags[i])) || fieldName == pkField {
|
||||
continue
|
||||
}
|
||||
var rfvVal = field.Interface()
|
||||
if v, ok := rfvVal.(driver.Valuer); ok {
|
||||
var value driver.Value
|
||||
value, err = v.Value()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data[tags[i]] = value
|
||||
} else {
|
||||
data[tags[i]] = rfvVal
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StructToDelete(obj any) (data map[string]any, err error) {
|
||||
rfv := reflect.Indirect(reflect.ValueOf(obj))
|
||||
if rfv.Kind() == reflect.Struct {
|
||||
tag, fieldStruct, _ := structsTypeParse(rfv.Type())
|
||||
data, err = structDataToMap(rfv, tag, fieldStruct)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StructsToInsert(obj any, mustFields ...string) (datas []map[string]any, err error) {
|
||||
rfv := reflect.Indirect(reflect.ValueOf(obj))
|
||||
switch rfv.Kind() {
|
||||
case reflect.Struct:
|
||||
fieldTag, fieldStruct, _ := structsTypeParse(rfv.Type())
|
||||
var data = make(map[string]any)
|
||||
data, err = structDataToMap(rfv, fieldTag, fieldStruct, mustFields...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
datas = append(datas, data)
|
||||
case reflect.Slice:
|
||||
tag, fieldStruct, _ := structsTypeParse(rfv.Type())
|
||||
for i := 0; i < rfv.Len(); i++ {
|
||||
var data = make(map[string]any)
|
||||
data, err = structDataToMap(rfv.Index(i), tag, fieldStruct, mustFields...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
datas = append(datas, data)
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func StructToUpdate(obj any, mustFields ...string) (data map[string]any, pkTag string, pkValue any, err error) {
|
||||
tag, fieldStruct, pkField := StructsParse(obj)
|
||||
if len(tag) > 0 {
|
||||
data = make(map[string]any)
|
||||
rfv := reflect.Indirect(reflect.ValueOf(obj))
|
||||
data, err = structUpdateDataToMap(rfv, tag, fieldStruct, pkField, mustFields...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if pkField != "" {
|
||||
pkTag = tag[slices.Index(fieldStruct, pkField)]
|
||||
pkValue = rfv.FieldByName(pkField).Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
130
sugar.go
Normal file
130
sugar.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package gorose
|
||||
|
||||
//func (db *Database) MustFirst(columns ...string) (res map[string]any) {
|
||||
// res, db.Context.Err = db.First(columns...)
|
||||
// return
|
||||
//}
|
||||
//func (db *Database) MustGet(columns ...string) (res []map[string]any) {
|
||||
// res, db.Context.Err = db.Get(columns...)
|
||||
// return
|
||||
//}
|
||||
|
||||
func (db *Database) WhereIn(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereIn("AND", column, value)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereNotIn(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereIn("AND", column, value, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereIn(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereIn("OR", column, value)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereNotIn(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereIn("OR", column, value, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereNull(column string) *Database {
|
||||
db.Context.WhereClause.whereNull("AND", column)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereNotNull(column string) *Database {
|
||||
db.Context.WhereClause.whereNull("AND", column, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereNull(column string) *Database {
|
||||
db.Context.WhereClause.whereNull("OR", column)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereNotNull(column string) *Database {
|
||||
db.Context.WhereClause.whereNull("OR", column, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereBetween(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereBetween("AND", column, value)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereNotBetween(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereBetween("AND", column, value, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereBetween(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereBetween("OR", column, value)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereNotBetween(column string, value any) *Database {
|
||||
db.Context.WhereClause.whereBetween("OR", column, value, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereExists(clause IDriver) {
|
||||
db.Context.WhereClause.WhereExists(clause)
|
||||
}
|
||||
func (db *Database) WhereNotExists(clause IDriver) {
|
||||
db.Context.WhereClause.WhereNotExists(clause)
|
||||
}
|
||||
func (db *Database) WhereLike(column, value string) *Database {
|
||||
db.Context.WhereClause.whereLike("AND", column, value)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereNotLike(column, value string) *Database {
|
||||
db.Context.WhereClause.whereLike("AND", column, value, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereLike(column, value string) *Database {
|
||||
db.Context.WhereClause.whereLike("OR", column, value)
|
||||
return db
|
||||
}
|
||||
func (db *Database) OrWhereNotLike(column, value string) *Database {
|
||||
db.Context.WhereClause.whereLike("OR", column, value, true)
|
||||
return db
|
||||
}
|
||||
func (db *Database) WhereNot(column any, args ...any) *Database {
|
||||
db.Context.WhereClause.WhereNot(column, args...)
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *Database) OrderByAsc(column string) *Database {
|
||||
return db.OrderBy(column, "ASC")
|
||||
}
|
||||
|
||||
func (db *Database) OrderByDesc(column string) *Database {
|
||||
return db.OrderBy(column, "DESC")
|
||||
}
|
||||
|
||||
//func (db *Database) Paginate(obj ...any) (result Paginator, err error) {
|
||||
// if len(obj) > 0 {
|
||||
// db.Table(obj[0])
|
||||
// }
|
||||
// var count int64
|
||||
// count, err = db.Count()
|
||||
// if err != nil || count == 0 {
|
||||
// return
|
||||
// }
|
||||
// if db.Context.LimitOffsetClause.Limit == 0 {
|
||||
// db.Limit(15)
|
||||
// }
|
||||
// if db.Context.LimitOffsetClause.Page == 0 {
|
||||
// db.Page(1)
|
||||
// }
|
||||
//
|
||||
// res, err := db.Get()
|
||||
// if err != nil {
|
||||
// return result, err
|
||||
// }
|
||||
//
|
||||
// result.Total = count
|
||||
// result.Data = res
|
||||
// result.Limit = db.Context.LimitOffsetClause.Limit
|
||||
// result.Pages = int(math.Ceil(float64(count) / float64(db.Context.LimitOffsetClause.Limit)))
|
||||
// result.CurrentPage = db.Context.LimitOffsetClause.Page
|
||||
// result.PrevPage = db.Context.LimitOffsetClause.Page - 1
|
||||
// result.NextPage = db.Context.LimitOffsetClause.Page + 1
|
||||
// if db.Context.LimitOffsetClause.Page == 1 {
|
||||
// result.PrevPage = 1
|
||||
// }
|
||||
// if db.Context.LimitOffsetClause.Page == result.Pages {
|
||||
// result.NextPage = result.Pages
|
||||
// }
|
||||
// return
|
||||
//}
|
||||
24
table.go
Normal file
24
table.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package gorose
|
||||
|
||||
// TableClause table clause
|
||||
type TableClause struct {
|
||||
Tables any // table name or struct(slice) or subQuery
|
||||
Alias string
|
||||
}
|
||||
|
||||
func As(table any, alias string) *TableClause {
|
||||
return &TableClause{
|
||||
Tables: table,
|
||||
Alias: alias,
|
||||
}
|
||||
}
|
||||
|
||||
// Table sets the table name for the query.
|
||||
func (db *TableClause) Table(table any, alias ...string) {
|
||||
var as string
|
||||
if len(alias) > 0 {
|
||||
as = alias[0]
|
||||
}
|
||||
db.Tables = table
|
||||
db.Alias = as
|
||||
}
|
||||
273
util.go
273
util.go
@@ -1,136 +1,203 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gohouse/t"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
func getRandomInt(num int) int {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
func init() {
|
||||
rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
}
|
||||
func GetRandomInt(num int) int {
|
||||
return rand.Intn(num)
|
||||
}
|
||||
func GetRandomWeightedIndex(weights []int) int {
|
||||
if len(weights) == 0 {
|
||||
return 0
|
||||
}
|
||||
if len(weights) == 1 {
|
||||
return 0
|
||||
}
|
||||
totalWeight := 0
|
||||
for _, w := range weights {
|
||||
totalWeight += w
|
||||
}
|
||||
if totalWeight == 0 {
|
||||
return rand.Intn(len(weights))
|
||||
}
|
||||
|
||||
func structForScan(u interface{}) []interface{} {
|
||||
val := reflect.Indirect(reflect.ValueOf(u))
|
||||
v := make([]interface{}, 0)
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
valueField := val.Field(i)
|
||||
if val.Type().Field(i).Tag.Get(TAGNAME) != IGNORE {
|
||||
if valueField.CanAddr() {
|
||||
v = append(v, valueField.Addr().Interface())
|
||||
} else {
|
||||
//v[i] = valueField
|
||||
v = append(v, valueField)
|
||||
}
|
||||
rnd := rand.Intn(totalWeight)
|
||||
|
||||
currentWeight := 0
|
||||
for i, w := range weights {
|
||||
currentWeight += w
|
||||
if rnd < currentWeight {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return v
|
||||
return -1 // 如果权重都为 0,或者总权重为 0,则返回 -1
|
||||
}
|
||||
|
||||
// StructToMap ...
|
||||
func StructToMap(obj interface{}) map[string]interface{} {
|
||||
ty := reflect.TypeOf(obj)
|
||||
v := reflect.ValueOf(obj)
|
||||
//////////// struct field ptr 4 orm helpers ////////////
|
||||
|
||||
var data = make(map[string]interface{})
|
||||
for i := 0; i < ty.NumField(); i++ {
|
||||
data[ty.Field(i).Name] = v.Field(i).Interface()
|
||||
}
|
||||
return data
|
||||
// PtrBool helper
|
||||
func PtrBool(arg bool) *bool {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// getTagName 获取结构体中Tag的值,如果没有tag则返回字段值
|
||||
func getTagName(structName interface{}, tagstr string) []string {
|
||||
// 获取type
|
||||
tag := reflect.TypeOf(structName)
|
||||
// 如果是反射Ptr类型, 就获取他的 element type
|
||||
if tag.Kind() == reflect.Ptr {
|
||||
tag = tag.Elem()
|
||||
}
|
||||
// PtrString helper
|
||||
func PtrString(arg string) *string {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// 判断是否是struct
|
||||
if tag.Kind() != reflect.Struct {
|
||||
log.Println("Check type error not Struct")
|
||||
return nil
|
||||
}
|
||||
// 获取字段数量
|
||||
fieldNum := tag.NumField()
|
||||
result := make([]string, 0, fieldNum)
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
// tag 名字
|
||||
tagName := tag.Field(i).Tag.Get(tagstr)
|
||||
if tagName != IGNORE {
|
||||
// tag为-时, 不解析
|
||||
if tagName == "-" || tagName == "" {
|
||||
// 字段名字
|
||||
tagName = tag.Field(i).Name
|
||||
}
|
||||
result = append(result, tagName)
|
||||
// PtrInt helper
|
||||
func PtrInt(arg int) *int {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// PtrInt8 helper
|
||||
func PtrInt8(arg int8) *int8 {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// PtrInt16 helper
|
||||
func PtrInt16(arg int16) *int16 {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// PtrInt64 helper
|
||||
func PtrInt64(arg int64) *int64 {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// PtrFloat64 helper
|
||||
func PtrFloat64(arg float64) *float64 {
|
||||
return &arg
|
||||
}
|
||||
|
||||
// PtrTime helper
|
||||
func PtrTime(arg time.Time) *time.Time {
|
||||
return &arg
|
||||
}
|
||||
|
||||
//////////// sql.Null* type helpers ////////////
|
||||
|
||||
// NullInt64From helper
|
||||
func NullInt64From(arg int64) sql.NullInt64 { return sql.NullInt64{Int64: arg, Valid: true} }
|
||||
|
||||
// NullInt32From helper
|
||||
func NullInt32From(arg int32) sql.NullInt32 { return sql.NullInt32{Int32: arg, Valid: true} }
|
||||
|
||||
// NullInt16From helper
|
||||
func NullInt16From(arg int16) sql.NullInt16 { return sql.NullInt16{Int16: arg, Valid: true} }
|
||||
|
||||
// NullByteFrom helper
|
||||
func NullByteFrom(arg byte) sql.NullByte { return sql.NullByte{Byte: arg, Valid: true} }
|
||||
|
||||
// NullFloat64From helper
|
||||
func NullFloat64From(arg float64) sql.NullFloat64 { return sql.NullFloat64{Float64: arg, Valid: true} }
|
||||
|
||||
// NullBoolFrom helper
|
||||
func NullBoolFrom(arg bool) sql.NullBool { return sql.NullBool{Bool: arg, Valid: true} }
|
||||
|
||||
// NullTimeFrom helper
|
||||
func NullTimeFrom(arg time.Time) sql.NullTime { return sql.NullTime{Time: arg, Valid: true} }
|
||||
|
||||
func ToSlice(arg any) []any {
|
||||
ref := reflect.Indirect(reflect.ValueOf(arg))
|
||||
var res []any
|
||||
switch ref.Kind() {
|
||||
case reflect.Slice:
|
||||
l := ref.Len()
|
||||
v := ref.Slice(0, l)
|
||||
for i := 0; i < l; i++ {
|
||||
res = append(res, v.Index(i).Interface())
|
||||
}
|
||||
default:
|
||||
res = append(res, ref.Interface())
|
||||
}
|
||||
return result
|
||||
return res
|
||||
}
|
||||
|
||||
// If : ternary operator (三元运算)
|
||||
// condition:比较运算
|
||||
// trueVal:运算结果为真时的值
|
||||
// falseVal:运算结果为假时的值
|
||||
// return: 由于不知道传入值的类型, 所有, 必须在接收结果时, 指定对应的值类型
|
||||
func If(condition bool, trueVal, falseVal interface{}) interface{} {
|
||||
if condition {
|
||||
return trueVal
|
||||
func ToSliceAddressable(arg any) []any {
|
||||
ref := reflect.Indirect(reflect.ValueOf(arg))
|
||||
var res []any
|
||||
switch ref.Kind() {
|
||||
case reflect.Slice:
|
||||
l := ref.Len()
|
||||
v := ref.Slice(0, l)
|
||||
for i := 0; i < l; i++ {
|
||||
res = append(res, v.Index(i).Addr().Interface())
|
||||
}
|
||||
default:
|
||||
res = append(res, ref.Addr().Interface())
|
||||
}
|
||||
return falseVal
|
||||
return res
|
||||
}
|
||||
|
||||
func addQuotes(data interface{}, sep string) string {
|
||||
ret := t.New(data).String()
|
||||
ret = strings.Replace(ret, `\`, `\\`, -1)
|
||||
ret = strings.Replace(ret, `"`, `\"`, -1)
|
||||
ret = strings.Replace(ret, `'`, `\'`, -1)
|
||||
return fmt.Sprintf("%s%s%s", sep, ret, sep)
|
||||
}
|
||||
|
||||
// InArray :给定元素值 是否在 指定的数组中
|
||||
func inArray(needle, hystack interface{}) bool {
|
||||
nt := t.New(needle)
|
||||
for _, item := range t.New(hystack).Slice() {
|
||||
if strings.ToLower(nt.String()) == strings.ToLower(item.String()) {
|
||||
func SliceContains(haystack []string, needle string) bool {
|
||||
for _, v := range haystack {
|
||||
if v == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func withLockContext(fn func()) {
|
||||
var mu sync.Mutex
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
fn()
|
||||
}
|
||||
|
||||
func withRunTimeContext(closer func(), callback func(time.Duration)) {
|
||||
// 记录开始时间
|
||||
start := time.Now()
|
||||
closer()
|
||||
timeduration := time.Since(start)
|
||||
//log.Println("执行完毕,用时:", timeduration.Seconds(),timeduration.Seconds()>1.1)
|
||||
callback(timeduration)
|
||||
}
|
||||
|
||||
func readFile(filepath string) *os.File {
|
||||
file, err := os.OpenFile(filepath, os.O_WRONLY|os.O_APPEND, 0666)
|
||||
if err != nil && os.IsNotExist(err) {
|
||||
_ = os.MkdirAll(path.Dir(filepath), os.ModePerm)
|
||||
file, _ = os.Create(filepath)
|
||||
func Map[Data any, Datas ~[]Data, Result any](datas Datas, mapper func(Data) Result) []Result {
|
||||
results := make([]Result, 0, len(datas))
|
||||
for _, data := range datas {
|
||||
results = append(results, mapper(data))
|
||||
}
|
||||
return file
|
||||
return results
|
||||
}
|
||||
|
||||
func NamedSprintf(format string, a ...any) string {
|
||||
return strings.TrimSpace(regexp.MustCompile(`\s{2,}`).ReplaceAllString(fmt.Sprintf(regexp.MustCompile(`:\w+`).ReplaceAllString(format, "%s"), a...), " "))
|
||||
}
|
||||
|
||||
func BackQuotes(arg any) string {
|
||||
var tmp []string
|
||||
if v, ok := arg.(string); ok {
|
||||
split := strings.Split(v, " ")
|
||||
split2 := strings.Split(split[0], ".")
|
||||
if len(split2) > 1 {
|
||||
if split2[1] == "*" {
|
||||
tmp = append(tmp, fmt.Sprintf("`%s`.%s", split2[0], split2[1]))
|
||||
} else {
|
||||
tmp = append(tmp, fmt.Sprintf("`%s`.`%s`", split2[0], split2[1]))
|
||||
}
|
||||
} else {
|
||||
tmp = append(tmp, fmt.Sprintf("`%s`", split2[len(split2)-1]))
|
||||
}
|
||||
tmp = append(tmp, split[1:]...)
|
||||
}
|
||||
return strings.Join(tmp, " ")
|
||||
}
|
||||
|
||||
func SortedMapKeys(data any) (cols []string) {
|
||||
// 从 map 中获取所有的键,并转换为切片
|
||||
keys := reflect.ValueOf(data).MapKeys()
|
||||
|
||||
// 对切片进行排序
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].String() < keys[j].String()
|
||||
})
|
||||
|
||||
// 输出排序后的结果
|
||||
for _, key := range keys {
|
||||
cols = append(cols, key.String())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func IsExpression(obj any) (b bool) {
|
||||
rfv := reflect.Indirect(reflect.ValueOf(obj))
|
||||
if rfv.Kind() == reflect.String && strings.Contains(rfv.String(), "?") {
|
||||
b = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
40
util_test.go
40
util_test.go
@@ -1,40 +0,0 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStructToMap(t *testing.T) {
|
||||
user := Users{Uid: 1, Name: "gorose"}
|
||||
data := StructToMap(user)
|
||||
t.Log(data)
|
||||
}
|
||||
|
||||
func TestIf(t *testing.T) {
|
||||
closer := func() {
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
withRunTimeContext(closer, func(td time.Duration) {
|
||||
t.Log("用时:", td, td.Seconds() > 1)
|
||||
})
|
||||
}
|
||||
|
||||
//func TestStructToMap2(t *testing.T) {
|
||||
// var u Users
|
||||
// //res := structForScan(&u)
|
||||
// res := structForScan(reflect.ValueOf(&u).Interface())
|
||||
// for _, item := range res {
|
||||
// err := varBindValue.BindVal(item, 1234)
|
||||
// if err != nil {
|
||||
// t.Error(err.Error())
|
||||
// }
|
||||
// }
|
||||
// t.Log(res, u)
|
||||
//}
|
||||
func Test_getRandomInt(t *testing.T) {
|
||||
t.Log(getRandomInt(2))
|
||||
t.Log(getRandomInt(3))
|
||||
t.Log(getRandomInt(2))
|
||||
t.Log(getRandomInt(3))
|
||||
}
|
||||
382
where.go
Normal file
382
where.go
Normal file
@@ -0,0 +1,382 @@
|
||||
package gorose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IWhere interface {
|
||||
Where(column any, args ...any) IWhere
|
||||
OrWhere(column any, args ...any) IWhere
|
||||
WhereRaw(raw string, bindingsAndBoolean ...any) IWhere
|
||||
OrWhereRaw(sqlSeg string, bindingsAndBoolean ...any) IWhere
|
||||
|
||||
WhereBetween(column string, values any) IWhere
|
||||
OrWhereBetween(column string, values any) IWhere
|
||||
WhereNotBetween(column string, values any) IWhere
|
||||
OrWhereNotBetween(column string, values any) IWhere
|
||||
|
||||
WhereIn(column string, values any) IWhere
|
||||
OrWhereIn(column string, values any) IWhere
|
||||
WhereNotIn(column string, values any) IWhere
|
||||
OrWhereNotIn(column string, values any) IWhere
|
||||
|
||||
WhereNull(column string) IWhere
|
||||
OrWhereNull(column string) IWhere
|
||||
WhereNotNull(column string) IWhere
|
||||
OrWhereNotNull(column string) IWhere
|
||||
|
||||
WhereLike(column string, value string) IWhere
|
||||
OrWhereLike(column string, value string) IWhere
|
||||
WhereNotLike(column string, value string) IWhere
|
||||
OrWhereNotLike(column string, value string) IWhere
|
||||
|
||||
WhereExists(clause IDriver) IWhere
|
||||
WhereNotExists(clause IDriver) IWhere
|
||||
}
|
||||
|
||||
// WhereClause 存储所有WHERE条件 ///////////////////start
|
||||
type WhereClause struct {
|
||||
Conditions []any
|
||||
Not bool
|
||||
Err error
|
||||
}
|
||||
type TypeWhereRaw struct {
|
||||
LogicalOp string
|
||||
Column string
|
||||
Bindings []any
|
||||
}
|
||||
type TypeWhereNested struct {
|
||||
LogicalOp string
|
||||
Column func(where IWhere)
|
||||
}
|
||||
type TypeWhereSubQuery struct {
|
||||
LogicalOp string
|
||||
Column string
|
||||
Operator string
|
||||
SubQuery IDriver
|
||||
}
|
||||
type TypeWhereStandard struct {
|
||||
LogicalOp string
|
||||
Column string
|
||||
Operator string
|
||||
Value any
|
||||
}
|
||||
type TypeWhereIn struct {
|
||||
LogicalOp string
|
||||
Column string
|
||||
Operator string
|
||||
Value any
|
||||
}
|
||||
type TypeWhereBetween struct {
|
||||
LogicalOp string
|
||||
Column string
|
||||
Operator string
|
||||
Value any
|
||||
}
|
||||
type TypeWhereExists struct {
|
||||
IDriver
|
||||
Not bool
|
||||
}
|
||||
|
||||
// WhereRaw Add a raw where clause to the query.
|
||||
//
|
||||
// whereRaw($sql, $bindings = [], $boolean = 'and')
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// string $sql
|
||||
// mixed $bindings
|
||||
// string $boolean
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// SubQuery
|
||||
//
|
||||
// Laravel api
|
||||
func (w *WhereClause) WhereRaw(raw string, bindingsAndBoolean ...any) IWhere {
|
||||
return w.whereRaw("AND", raw, bindingsAndBoolean...)
|
||||
}
|
||||
|
||||
// OrWhereRaw clause
|
||||
func (w *WhereClause) OrWhereRaw(sqlSeg string, bindingsAndBoolean ...any) IWhere {
|
||||
return w.whereRaw("OR", sqlSeg, bindingsAndBoolean...)
|
||||
}
|
||||
|
||||
func (w *WhereClause) whereRaw(boolean string, sqlSeg string, bindingsAndBoolean ...any) IWhere {
|
||||
if sqlSeg == "" {
|
||||
return w
|
||||
}
|
||||
if len(bindingsAndBoolean) == 0 {
|
||||
return w.WhereRaw(sqlSeg, []any{}, boolean)
|
||||
} else if len(bindingsAndBoolean) == 1 {
|
||||
return w.WhereRaw(sqlSeg, bindingsAndBoolean[0], boolean)
|
||||
} else if len(bindingsAndBoolean) == 2 {
|
||||
rfv := reflect.ValueOf(bindingsAndBoolean[0])
|
||||
var bindTmp []any
|
||||
if rfv.Kind() == reflect.Slice {
|
||||
for i := 0; i < rfv.Len(); i++ {
|
||||
bindTmp = append(bindTmp, rfv.Index(i).Interface())
|
||||
}
|
||||
} else {
|
||||
bindTmp = append(bindTmp, rfv.Interface())
|
||||
}
|
||||
rfv1 := reflect.ValueOf(bindingsAndBoolean[1])
|
||||
if rfv1.Kind() == reflect.String {
|
||||
w.addTypeWhereRaw(rfv1.String(), sqlSeg, bindTmp)
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Where Add a basic where clause to the query.
|
||||
//
|
||||
// where($column, $operator = null, $value = null, $boolean = 'and')
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// array|Closure|Expression|string $column
|
||||
// mixed $operator
|
||||
// mixed $value
|
||||
// string $boolean
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// iface.WhereClause
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// Where("id=1")
|
||||
// Where("id=?",1)
|
||||
// Where("id",1)
|
||||
// Where("id","=",1)
|
||||
// Where("id","=",1,"AND")
|
||||
// Where("id","=",(select id from table limit 1))
|
||||
// Where("id","in",(select id from table), "AND")
|
||||
// Where(func(wh iface.WhereClause){wh.Where().OrWhere().WhereRaw()...})
|
||||
// Where(["id=1"])
|
||||
// Where(["id","=",1])
|
||||
// Where(["id",1])
|
||||
// Where([ ["id",1],["name","=","John"],["age",">",3] ])
|
||||
func (w *WhereClause) Where(column any, args ...any) IWhere {
|
||||
return w.where("AND", column, args...)
|
||||
}
|
||||
|
||||
// OrWhere clause
|
||||
func (w *WhereClause) OrWhere(column any, args ...any) IWhere {
|
||||
return w.where("OR", column, args...)
|
||||
}
|
||||
|
||||
func (w *WhereClause) where(boolean string, column any, args ...any) IWhere {
|
||||
if column == nil {
|
||||
return w
|
||||
}
|
||||
switch len(args) {
|
||||
case 0:
|
||||
rfv := reflect.Indirect(reflect.ValueOf(column))
|
||||
switch rfv.Kind() {
|
||||
case reflect.Map:
|
||||
keys := rfv.MapKeys()
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return keys[i].String() < keys[j].String()
|
||||
})
|
||||
for _, k := range keys {
|
||||
w.where("AND", k.Interface(), "=", rfv.MapIndex(k).Interface())
|
||||
}
|
||||
case reflect.Func:
|
||||
if fn, ok := column.(func(where IWhere)); ok {
|
||||
w.addTypeWhereNested(boolean, fn)
|
||||
} else {
|
||||
w.Err = errors.New("not supported where params")
|
||||
}
|
||||
case reflect.String:
|
||||
return w.whereRaw(boolean, rfv.String())
|
||||
case reflect.Slice:
|
||||
if rfv.Len() > 1 {
|
||||
rfvItem := rfv.Index(0)
|
||||
if rfvItem.Kind() == reflect.Slice {
|
||||
return w.where(boolean, rfvItem.Interface())
|
||||
} else {
|
||||
var tmp []any
|
||||
for i := 0; i < rfv.Len(); i++ {
|
||||
tmp = append(tmp, rfv.Index(i).Interface())
|
||||
}
|
||||
return w.where(boolean, tmp[0], tmp[1:]...)
|
||||
}
|
||||
} else if rfv.Len() > 0 {
|
||||
return w.whereRaw(boolean, rfv.Index(0).String())
|
||||
}
|
||||
w.Err = errors.New("not supported where params")
|
||||
default:
|
||||
w.Err = errors.New("not supported where params")
|
||||
}
|
||||
case 1:
|
||||
if IsExpression(column) {
|
||||
return w.whereRaw(boolean, column.(string), args...)
|
||||
}
|
||||
return w.where(boolean, column, "=", args[0], boolean)
|
||||
case 2:
|
||||
return w.where(boolean, column, args[0], args[1], boolean)
|
||||
case 3:
|
||||
rfv := reflect.Indirect(reflect.ValueOf(args[1]))
|
||||
if rfv.Kind() == reflect.Slice { // in/between
|
||||
var operators = []string{"in", "not in"}
|
||||
if slices.Contains(operators, strings.ToLower(args[0].(string))) {
|
||||
val := ToSlice(args[1])
|
||||
if len(val) > 0 {
|
||||
w.addTypeWhereIn(args[2].(string), column.(string), args[0].(string), ToSlice(args[1]))
|
||||
}
|
||||
}
|
||||
operators = []string{"between", "not between"}
|
||||
if slices.Contains(operators, strings.ToLower(args[0].(string))) {
|
||||
val := ToSlice(args[1])
|
||||
if len(val) > 0 {
|
||||
w.addTypeWhereBetween(args[2].(string), column.(string), args[0].(string), ToSlice(args[1]))
|
||||
}
|
||||
}
|
||||
} else if builder, ok := args[1].(IDriver); ok {
|
||||
w.addTypeWhereSubQuery(args[2].(string), column.(string), args[0].(string), builder)
|
||||
} else {
|
||||
w.addTypeWhereStandard(args[2].(string), column.(string), args[0].(string), args[1])
|
||||
}
|
||||
default:
|
||||
w.Err = errors.New("not supported where params")
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// WhereBetween 在指定列的值位于给定范围内时添加一个"where"条件。
|
||||
//
|
||||
// relation: and/or
|
||||
// column: 列名。
|
||||
// values: 区间范围数组。
|
||||
// not: 是否取反,默认为 false。
|
||||
func (w *WhereClause) WhereBetween(column string, values any) IWhere {
|
||||
return w.whereBetween("AND", column, values, false)
|
||||
}
|
||||
func (w *WhereClause) OrWhereBetween(column string, values any) IWhere {
|
||||
return w.whereBetween("OR", column, values, false)
|
||||
}
|
||||
func (w *WhereClause) WhereNotBetween(column string, values any) IWhere {
|
||||
return w.whereBetween("AND", column, values, true)
|
||||
}
|
||||
func (w *WhereClause) OrWhereNotBetween(column string, values any) IWhere {
|
||||
return w.whereBetween("OR", column, values, true)
|
||||
}
|
||||
func (w *WhereClause) whereBetween(relation string, column string, values any, not ...bool) IWhere {
|
||||
if len(not) > 0 && not[0] {
|
||||
return w.addTypeWhereBetween(relation, column, "NOT BETWEEN", values)
|
||||
}
|
||||
return w.addTypeWhereBetween(relation, column, "BETWEEN", values)
|
||||
}
|
||||
|
||||
// WhereIn 在指定列的值存在于给定的集合内时添加一个"where"条件。
|
||||
//
|
||||
// relation: and/or
|
||||
// column: 要检查的列名。
|
||||
// values: 集合值。
|
||||
// not: 是否取反,默认为 false。
|
||||
func (w *WhereClause) WhereIn(column string, values any) IWhere {
|
||||
return w.whereIn("AND", column, values, false)
|
||||
}
|
||||
func (w *WhereClause) OrWhereIn(column string, values any) IWhere {
|
||||
return w.whereIn("Or", column, values, false)
|
||||
}
|
||||
func (w *WhereClause) WhereNotIn(column string, values any) IWhere {
|
||||
return w.whereIn("AND", column, values, true)
|
||||
}
|
||||
func (w *WhereClause) OrWhereNotIn(column string, values any) IWhere {
|
||||
return w.whereIn("Or", column, values, true)
|
||||
}
|
||||
func (w *WhereClause) whereIn(relation string, column string, values any, not ...bool) IWhere {
|
||||
if len(not) > 0 && not[0] {
|
||||
return w.addTypeWhereIn(relation, column, "NOT IN", values)
|
||||
}
|
||||
return w.addTypeWhereIn(relation, column, "IN", values)
|
||||
}
|
||||
|
||||
// WhereNull 指定列的值为 NULL 时添加一个"where"条件。
|
||||
//
|
||||
// relation: and/or
|
||||
// column: 列名。
|
||||
func (w *WhereClause) WhereNull(column string) IWhere { return w.whereNull("AND", column, false) }
|
||||
func (w *WhereClause) OrWhereNull(column string) IWhere { return w.whereNull("OR", column, false) }
|
||||
func (w *WhereClause) WhereNotNull(column string) IWhere { return w.whereNull("AND", column, true) }
|
||||
func (w *WhereClause) OrWhereNotNull(column string) IWhere { return w.whereNull("OR", column, true) }
|
||||
func (w *WhereClause) whereNull(relation string, column string, not ...bool) IWhere {
|
||||
if len(not) > 0 && not[0] {
|
||||
return w.addTypeWhereStandard(relation, column, "IS NOT", "NULL")
|
||||
}
|
||||
return w.addTypeWhereStandard(relation, column, "IS", "NULL")
|
||||
}
|
||||
|
||||
// WhereLike 在指定列进行模糊匹配时添加一个"where"条件。
|
||||
//
|
||||
// relation: and/or
|
||||
// column: 要进行模糊匹配的列名。
|
||||
// value: 包含通配符(%)的匹配字符串。
|
||||
func (w *WhereClause) WhereLike(column string, value string) IWhere {
|
||||
return w.whereLike("NAD", column, value, false)
|
||||
}
|
||||
func (w *WhereClause) OrWhereLike(column string, value string) IWhere {
|
||||
return w.whereLike("OR", column, value, false)
|
||||
}
|
||||
func (w *WhereClause) WhereNotLike(column string, value string) IWhere {
|
||||
return w.whereLike("NAD", column, value, true)
|
||||
}
|
||||
func (w *WhereClause) OrWhereNotLike(column string, value string) IWhere {
|
||||
return w.whereLike("OR", column, value, true)
|
||||
}
|
||||
func (w *WhereClause) whereLike(relation string, column string, value string, not ...bool) IWhere {
|
||||
if len(not) > 0 && not[0] {
|
||||
return w.addTypeWhereStandard(relation, column, "NOT LIKE", value)
|
||||
}
|
||||
return w.addTypeWhereStandard(relation, column, "LIKE", value)
|
||||
}
|
||||
|
||||
// WhereExists 使用WHERE EXISTS子查询条件。
|
||||
//
|
||||
// clause: Database 语句,或者实现了 IDriver.ToSql() 接口的对象
|
||||
func (w *WhereClause) WhereExists(clause IDriver) IWhere { return w.whereExists(clause, false) }
|
||||
func (w *WhereClause) WhereNotExists(clause IDriver) IWhere { return w.whereExists(clause, true) }
|
||||
func (w *WhereClause) whereExists(clause IDriver, not ...bool) IWhere {
|
||||
var b bool
|
||||
if len(not) > 0 {
|
||||
b = not[0]
|
||||
}
|
||||
w.Conditions = append(w.Conditions, TypeWhereExists{clause, b})
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *WhereClause) WhereNot(column any, args ...any) IWhere {
|
||||
w.Not = true
|
||||
return w.Where(column, args...)
|
||||
}
|
||||
|
||||
func (w *WhereClause) addTypeWhereRaw(boolean string, value string, bindings []any) *WhereClause {
|
||||
w.Conditions = append(w.Conditions, TypeWhereRaw{LogicalOp: boolean, Column: value, Bindings: bindings})
|
||||
return w
|
||||
}
|
||||
func (w *WhereClause) addTypeWhereNested(boolean string, value func(where IWhere)) *WhereClause {
|
||||
w.Conditions = append(w.Conditions, TypeWhereNested{LogicalOp: boolean, Column: value})
|
||||
return w
|
||||
}
|
||||
func (w *WhereClause) addTypeWhereSubQuery(boolean string, column string, operator string, value IDriver) *WhereClause {
|
||||
w.Conditions = append(w.Conditions, TypeWhereSubQuery{LogicalOp: boolean, Column: column, Operator: operator, SubQuery: value})
|
||||
return w
|
||||
}
|
||||
func (w *WhereClause) addTypeWhereIn(boolean string, column string, operator string, value any) *WhereClause {
|
||||
w.Conditions = append(w.Conditions, TypeWhereIn{LogicalOp: boolean, Column: column, Operator: operator, Value: value})
|
||||
return w
|
||||
}
|
||||
func (w *WhereClause) addTypeWhereBetween(boolean string, column string, operator string, value any) *WhereClause {
|
||||
w.Conditions = append(w.Conditions, TypeWhereBetween{LogicalOp: boolean, Column: column, Operator: operator, Value: value})
|
||||
return w
|
||||
}
|
||||
func (w *WhereClause) addTypeWhereStandard(boolean string, column string, operator string, value any) *WhereClause {
|
||||
w.Conditions = append(w.Conditions, TypeWhereStandard{LogicalOp: boolean, Column: column, Operator: operator, Value: value})
|
||||
return w
|
||||
}
|
||||
Reference in New Issue
Block a user