raw query & execute

This commit is contained in:
kk
2024-03-02 14:59:16 +08:00
parent cf081c996d
commit a73c658e0e
69 changed files with 1703 additions and 6265 deletions

1
.gitignore vendored
View File

@@ -20,3 +20,4 @@ glide.lock
*.sqlite
*.log
filter.sh
tmp/

467
README.md
View File

@@ -1,467 +0,0 @@
# GoRose ORM
[![GoDoc](https://godoc.org/github.com/gohouse/gorose/v2?status.svg)](https://godoc.org/github.com/gohouse/gorose/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/gohouse/gorose/v2)](https://goreportcard.com/report/github.com/gohouse/gorose/v2)
[![GitHub release](https://img.shields.io/github/release/gohouse/gorose.svg)](https://github.com/gohouse/gorose/v2/releases/latest)
[![Gitter](https://badges.gitter.im/gohouse/gorose.svg)](https://gitter.im/gorose/wechat)
![GitHub](https://img.shields.io/github/license/gohouse/gorose?color=blue)
![GitHub All Releases](https://img.shields.io/github/downloads/gohouse/gorose/total?color=blue)
<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通信,严格的上层依赖下层.每一个模块都可以拆卸, 甚至可以自定义为自己喜欢的样子.
模块关系图如下: ![gorose-2.0-design](https://i.loli.net/2019/06/19/5d0a1273f12ef86624.jpg)
## 安装
- 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/shop/static/images/jetbrains-logo-inv.svg)](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://avatars1.githubusercontent.com/u/53846155?s=96&v=4)](https://github.com/sanjinhub)

View File

@@ -1,447 +0,0 @@
<base target="main">
# GoRose ORM
[![GoDoc](https://godoc.org/github.com/gohouse/gorose/v2?status.svg)](https://godoc.org/github.com/gohouse/gorose/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/gohouse/gorose/v2)](https://goreportcard.com/report/github.com/gohouse/gorose/v2)
[![GitHub release](https://img.shields.io/github/release/gohouse/gorose.svg)](https://github.com/gohouse/gorose/v2/releases/latest)
[![Gitter](https://badges.gitter.im/gohouse/gorose.svg)](https://gitter.im/gorose/wechat)
![GitHub](https://img.shields.io/github/license/gohouse/gorose?color=blue)
![GitHub All Releases](https://img.shields.io/github/downloads/gohouse/gorose/total?color=blue)
<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:
![gorose.2.0.jpg](https://i.loli.net/2019/08/06/7R2GlbwUiFKOrNP.jpg)
## 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/shop/static/images/jetbrains-logo-inv.svg)](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://avatars1.githubusercontent.com/u/53846155?s=96&v=4)](https://github.com/sanjinhub)

311
binder.go
View File

@@ -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{})
}
}

View File

@@ -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()
}

View File

@@ -1,6 +0,0 @@
package gorose
// NewBuilder 获取builder
func NewBuilder(driver string) IBuilder {
return NewBuilderDriver().Getter(driver)
}

View File

@@ -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)
}

View File

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

View File

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

View File

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

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

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

View File

@@ -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)
}

View File

@@ -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
View 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
View 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
View 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
View File

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

View File

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

View File

@@ -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
View File

@@ -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))
}

View File

@@ -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)
}
}

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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()
}

View File

@@ -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
View File

@@ -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
View File

@@ -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
View File

@@ -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)
}

View File

@@ -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
View 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
View File

@@ -0,0 +1,7 @@
package gorose
// HavingClause 类似于WhereClause但应用于HAVING子句。
type HavingClause struct {
*WhereClause
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

112
join.go Normal file
View 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
View File

@@ -0,0 +1,9 @@
package gorose
// LimitOffsetClause 存储LIMIT和OFFSET信息。
type LimitOffsetClause struct {
Limit int
Offset int
Page int
}

124
logger.go
View File

@@ -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)
})
}

View File

@@ -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)
}

View File

@@ -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
View 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
View File

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

View File

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

View File

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

View File

@@ -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...)
}

View File

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

View File

@@ -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("事务测试通过")
}

View File

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

View File

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

View File

@@ -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)
}

View File

@@ -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()
}
}

View File

@@ -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{}
}

View File

@@ -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
View 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
}

View File

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

View File

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

View File

@@ -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
View 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
View 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
View 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
View File

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

View File

@@ -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
View 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
}