refactor(db): 重命名批量插入方法并更新文档

- 将 BatchInsert 方法重命名为 Inserts,以更好地反映其功能
- 更新示例代码和文档,确保使用新方法名
- 删除过时的文档文件,整合 HoTimeDB 使用说明和 API 参考
- 优化 README.md,增强框架特性和安装说明的清晰度
This commit is contained in:
2026-01-22 20:32:29 +08:00
parent 5235ef133d
commit e704701752
9 changed files with 366 additions and 452 deletions
-513
View File
@@ -1,513 +0,0 @@
# HoTimeDB API 快速参考
## 条件查询语法规则
**新版本改进:**
- 多条件自动用 AND 连接,无需手动包装
- 关键字支持大小写(如 `LIMIT``limit` 都有效)
- 新增 `HAVING` 和独立 `OFFSET` 支持
```go
// ✅ 推荐:简化语法(多条件自动 AND)
Map{"status": 1, "age[>]": 18}
// 生成: WHERE `status`=? AND `age`>?
// ✅ 仍然支持:显式 AND 包装(向后兼容)
Map{
"AND": Map{
"status": 1,
"age[>]": 18,
},
}
// ✅ 混合条件和特殊关键字
Map{
"status": 1,
"age[>]": 18,
"ORDER": "id DESC", // 或 "order": "id DESC"
"LIMIT": 10, // 或 "limit": 10
}
```
## 基本方法
### 数据库连接
```go
database.SetConnect(func() (master, slave *sql.DB) { ... })
database.InitDb()
```
### 链式查询构建器
```go
// 创建查询构建器
builder := database.Table("tablename")
// 设置条件
builder.Where(key, value)
builder.And(key, value) builder.And(map)
builder.Or(key, value) builder.Or(map)
// JOIN操作
builder.LeftJoin(table, condition)
builder.RightJoin(table, condition)
builder.InnerJoin(table, condition)
builder.FullJoin(table, condition)
builder.Join(map) // 通用JOIN
// 排序和分组
builder.Order(fields...)
builder.Group(fields...)
builder.Limit(args...)
builder.Having(map) // 新增
// 分页
builder.Page(page, pageSize)
builder.Offset(offset) // 新增
// 执行查询
builder.Select(fields...) // 返回 []Map
builder.Get(fields...) // 返回 Map
builder.Count() // 返回 int
builder.Update(data) // 返回 int64
builder.Delete() // 返回 int64
```
## CRUD 操作
### 查询 (Select)
```go
// 基本查询
data := database.Select("table")
data := database.Select("table", "field1,field2")
data := database.Select("table", []string{"field1", "field2"})
data := database.Select("table", "*", whereMap)
// 带JOIN查询
data := database.Select("table", joinSlice, "fields", whereMap)
```
### 获取单条 (Get)
```go
// 自动添加 LIMIT 1
row := database.Get("table", "fields", whereMap)
```
### 插入 (Insert)
```go
id := database.Insert("table", dataMap)
// 返回新插入记录的ID
```
### 批量插入 (BatchInsert) - 新增
```go
// 使用 []Map 格式,更直观简洁
affected := database.BatchInsert("table", []Map{
{"col1": "val1", "col2": "val2", "col3": "val3"},
{"col1": "val4", "col2": "val5", "col3": "val6"},
})
// 返回受影响的行数
// 支持 [#] 标记直接 SQL
affected := database.BatchInsert("log", []Map{
{"user_id": 1, "created_time[#]": "NOW()"},
{"user_id": 2, "created_time[#]": "NOW()"},
})
```
### 更新 (Update)
```go
affected := database.Update("table", dataMap, whereMap)
// 返回受影响的行数
```
### Upsert - 新增
```go
// 使用 Slice 格式
affected := database.Upsert("table",
dataMap, // 插入数据
Slice{"unique_key"}, // 唯一键
Slice{"col1", "col2"}, // 冲突时更新的字段
)
// 也支持可变参数
affected := database.Upsert("table", dataMap, Slice{"id"}, "col1", "col2")
// 返回受影响的行数
```
### 删除 (Delete)
```go
affected := database.Delete("table", whereMap)
// 返回删除的行数
```
## 聚合函数
### 计数
```go
count := database.Count("table")
count := database.Count("table", whereMap)
count := database.Count("table", joinSlice, whereMap)
```
### 求和
```go
sum := database.Sum("table", "column")
sum := database.Sum("table", "column", whereMap)
```
### 平均值 - 新增
```go
avg := database.Avg("table", "column")
avg := database.Avg("table", "column", whereMap)
```
### 最大值 - 新增
```go
max := database.Max("table", "column")
max := database.Max("table", "column", whereMap)
```
### 最小值 - 新增
```go
min := database.Min("table", "column")
min := database.Min("table", "column", whereMap)
```
## 分页查询
```go
// 设置分页
database.Page(page, pageSize)
// 分页查询
data := database.Page(page, pageSize).PageSelect("table", "fields", whereMap)
```
## 条件语法参考
### 比较操作符
| 写法 | SQL | 说明 |
|------|-----|------|
| `"field": value` | `field = ?` | 等于 |
| `"field[!]": value` | `field != ?` | 不等于 |
| `"field[>]": value` | `field > ?` | 大于 |
| `"field[>=]": value` | `field >= ?` | 大于等于 |
| `"field[<]": value` | `field < ?` | 小于 |
| `"field[<=]": value` | `field <= ?` | 小于等于 |
### 模糊查询
| 写法 | SQL | 说明 |
|------|-----|------|
| `"field[~]": "keyword"` | `field LIKE '%keyword%'` | 包含 |
| `"field[~!]": "keyword"` | `field LIKE 'keyword%'` | 以...开头 |
| `"field[!~]": "keyword"` | `field LIKE '%keyword'` | 以...结尾 |
| `"field[~~]": "%keyword%"` | `field LIKE '%keyword%'` | 手动LIKE |
### 范围查询
| 写法 | SQL | 说明 |
|------|-----|------|
| `"field[<>]": [min, max]` | `field BETWEEN ? AND ?` | 区间内 |
| `"field[><]": [min, max]` | `field NOT BETWEEN ? AND ?` | 区间外 |
### 集合查询
| 写法 | SQL | 说明 |
|------|-----|------|
| `"field": [v1, v2, v3]` | `field IN (?, ?, ?)` | 在集合中 |
| `"field[!]": [v1, v2, v3]` | `field NOT IN (?, ?, ?)` | 不在集合中 |
### NULL查询
| 写法 | SQL | 说明 |
|------|-----|------|
| `"field": nil` | `field IS NULL` | 为空 |
| `"field[!]": nil` | `field IS NOT NULL` | 不为空 |
### 直接SQL
| 写法 | SQL | 说明 |
|------|-----|------|
| `"field[#]": "NOW()"` | `field = NOW()` | 直接SQL函数 |
| `"[##]": "a > b"` | `a > b` | 直接SQL片段 |
| `"field[#!]": "1"` | `field != 1` | 不等于(不参数化) |
## 逻辑连接符
### AND 条件
```go
// 简化语法(推荐)
whereMap := Map{
"status": 1,
"age[>]": 18,
}
// 生成: WHERE `status`=? AND `age`>?
// 显式 AND(向后兼容)
whereMap := Map{
"AND": Map{
"status": 1,
"age[>]": 18,
},
}
```
### OR 条件
```go
whereMap := Map{
"OR": Map{
"status": 1,
"type": 2,
},
}
```
### 嵌套条件
```go
whereMap := Map{
"AND": Map{
"status": 1,
"OR": Map{
"age[<]": 30,
"level[>]": 5,
},
},
}
```
## JOIN 语法
### 传统语法
```go
joinSlice := Slice{
Map{"[>]profile": "user.id = profile.user_id"}, // LEFT JOIN
Map{"[<]department": "user.dept_id = department.id"}, // RIGHT JOIN
Map{"[><]role": "user.role_id = role.id"}, // INNER JOIN
Map{"[<>]group": "user.group_id = group.id"}, // FULL JOIN
}
```
### 链式语法
```go
builder.LeftJoin("profile", "user.id = profile.user_id")
builder.RightJoin("department", "user.dept_id = department.id")
builder.InnerJoin("role", "user.role_id = role.id")
builder.FullJoin("group", "user.group_id = group.id")
```
## 特殊字段语法
### ORDER BY
```go
Map{
"ORDER": []string{"created_time DESC", "id ASC"},
}
// 或
Map{
"order": "created_time DESC", // 支持小写
}
```
### GROUP BY
```go
Map{
"GROUP": []string{"department", "level"},
}
// 或
Map{
"group": "department", // 支持小写
}
```
### HAVING - 新增
```go
Map{
"GROUP": "dept_id",
"HAVING": Map{
"COUNT(*).[>]": 5,
},
}
```
### LIMIT
```go
Map{
"LIMIT": []int{10, 20}, // offset 10, limit 20
}
// 或
Map{
"limit": 20, // limit 20,支持小写
}
```
### OFFSET - 新增
```go
Map{
"LIMIT": 10,
"OFFSET": 20, // 独立的 OFFSET
}
```
## 事务处理
```go
success := database.Action(func(tx HoTimeDB) bool {
// 在这里执行数据库操作
// 返回 true 提交事务
// 返回 false 回滚事务
id := tx.Insert("table", data)
if id == 0 {
return false // 回滚
}
affected := tx.Update("table2", data2, where2)
if affected == 0 {
return false // 回滚
}
return true // 提交
})
```
## 原生SQL执行
### 查询
```go
results := database.Query("SELECT * FROM user WHERE age > ?", 18)
```
### 执行
```go
result, err := database.Exec("UPDATE user SET status = ? WHERE id = ?", 1, 100)
affected, _ := result.RowsAffected()
```
## PostgreSQL 支持 - 新增
```go
// 配置 PostgreSQL
database := &db.HoTimeDB{
Type: "postgres", // 设置类型
}
// 框架自动处理差异:
// - 占位符: ? -> $1, $2, $3...
// - 引号: `name` -> "name"
// - Upsert: ON DUPLICATE KEY -> ON CONFLICT
```
## 错误处理
```go
// 检查最后的错误
if database.LastErr.GetError() != nil {
fmt.Println("错误:", database.LastErr.GetError())
}
// 查看最后执行的SQL
fmt.Println("SQL:", database.LastQuery)
fmt.Println("参数:", database.LastData)
```
## 工具方法
### 数据库信息
```go
prefix := database.GetPrefix() // 获取表前缀
dbType := database.GetType() // 获取数据库类型
dialect := database.GetDialect() // 获取方言适配器
```
### 设置模式
```go
database.Mode = 0 // 生产模式
database.Mode = 1 // 测试模式
database.Mode = 2 // 开发模式(输出SQL日志)
```
## 常用查询模式
### 分页列表查询
```go
// 获取总数
total := database.Count("user", Map{"status": 1})
// 分页数据
users := database.Table("user").
Where("status", 1).
Order("created_time DESC").
Page(page, pageSize).
Select("id,name,email,created_time")
// 计算分页信息
totalPages := (total + pageSize - 1) / pageSize
```
### 关联查询
```go
orders := database.Table("order").
LeftJoin("user", "order.user_id = user.id").
LeftJoin("product", "order.product_id = product.id").
Where("order.status", "paid").
Select(`
order.*,
user.name as user_name,
product.title as product_title
`)
```
### 统计查询
```go
stats := database.Select("order",
"user_id, COUNT(*) as order_count, SUM(amount) as total_amount",
Map{
"status": "paid",
"created_time[>]": "2023-01-01",
"GROUP": "user_id",
"ORDER": "total_amount DESC",
})
```
### 批量操作
```go
// 批量插入(使用 []Map 格式)
affected := database.BatchInsert("user", []Map{
{"name": "用户1", "email": "user1@example.com", "status": 1},
{"name": "用户2", "email": "user2@example.com", "status": 1},
{"name": "用户3", "email": "user3@example.com", "status": 1},
})
// Upsert(插入或更新,使用 Slice 格式)
affected := database.Upsert("user",
Map{"id": 1, "name": "新名称", "email": "new@example.com"},
Slice{"id"},
Slice{"name", "email"},
)
```
## 链式调用完整示例
```go
// 复杂查询链式调用
result := database.Table("order").
LeftJoin("user", "order.user_id = user.id").
LeftJoin("product", "order.product_id = product.id").
Where("order.status", "paid").
And("order.created_time[>]", "2023-01-01").
And(Map{
"OR": Map{
"user.level": "vip",
"order.amount[>]": 1000,
},
}).
Group("user.id").
Having(Map{"total_amount[>]": 500}).
Order("total_amount DESC").
Page(1, 20).
Select(`
user.id,
user.name,
user.email,
COUNT(order.id) as order_count,
SUM(order.amount) as total_amount
`)
```
---
*快速参考版本: 2.0*
*更新日期: 2026年1月*
-830
View File
@@ -1,830 +0,0 @@
# HoTimeDB ORM 使用说明书
## 概述
HoTimeDB是一个基于Golang实现的轻量级ORM框架,参考PHP Medoo设计,提供简洁的数据库操作接口。支持MySQL、SQLite、PostgreSQL等数据库,并集成了缓存、事务、链式查询等功能。
## 目录
- [快速开始](#快速开始)
- [数据库配置](#数据库配置)
- [基本操作](#基本操作)
- [查询(Select)](#查询select)
- [获取单条记录(Get)](#获取单条记录get)
- [插入(Insert)](#插入insert)
- [批量插入(BatchInsert)](#批量插入batchinsert)
- [更新(Update)](#更新update)
- [Upsert操作](#upsert操作)
- [删除(Delete)](#删除delete)
- [链式查询构建器](#链式查询构建器)
- [条件查询语法](#条件查询语法)
- [JOIN操作](#join操作)
- [分页查询](#分页查询)
- [聚合函数](#聚合函数)
- [事务处理](#事务处理)
- [缓存机制](#缓存机制)
- [PostgreSQL支持](#postgresql支持)
- [高级特性](#高级特性)
## 快速开始
### 初始化数据库连接
```go
import (
"code.hoteas.com/golang/hotime/db"
"code.hoteas.com/golang/hotime/common"
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
// 创建连接函数
func createConnection() (master, slave *sql.DB) {
master, _ = sql.Open("mysql", "user:password@tcp(localhost:3306)/database")
// slave是可选的,用于读写分离
slave = master // 或者连接到从数据库
return
}
// 初始化HoTimeDB
database := &db.HoTimeDB{
Type: "mysql", // 可选:mysql, sqlite3, postgres
}
database.SetConnect(createConnection)
```
## 数据库配置
### 基本配置
```go
type HoTimeDB struct {
*sql.DB
ContextBase
DBName string
*cache.HoTimeCache
Log *logrus.Logger
Type string // 数据库类型:mysql, sqlite3, postgres
Prefix string // 表前缀
LastQuery string // 最后执行的SQL
LastData []interface{} // 最后的参数
ConnectFunc func(err ...*Error) (*sql.DB, *sql.DB)
LastErr *Error
limit Slice
*sql.Tx // 事务对象
SlaveDB *sql.DB // 从数据库
Mode int // 0生产模式,1测试模式,2开发模式
Dialect Dialect // 数据库方言适配器
}
```
### 设置表前缀
```go
database.Prefix = "app_"
```
### 设置运行模式
```go
database.Mode = 2 // 开发模式,会输出SQL日志
```
## 基本操作
### 查询(Select)
#### 基本查询
```go
// 查询所有字段
users := database.Select("user")
// 查询指定字段
users := database.Select("user", "id,name,email")
// 查询指定字段(数组形式)
users := database.Select("user", []string{"id", "name", "email"})
// 单条件查询
users := database.Select("user", "*", common.Map{
"status": 1,
})
// 多条件查询(自动用 AND 连接)
users := database.Select("user", "*", common.Map{
"status": 1,
"age[>]": 18,
})
// 生成: WHERE `status`=? AND `age`>?
```
#### 复杂条件查询
```go
// 简化语法:多条件自动用 AND 连接
users := database.Select("user", "*", common.Map{
"status": 1,
"age[>]": 18,
"name[~]": "张",
})
// 生成: WHERE `status`=? AND `age`>? AND `name` LIKE ?
// 显式 AND 条件(与上面等效)
users := database.Select("user", "*", common.Map{
"AND": common.Map{
"status": 1,
"age[>]": 18,
"name[~]": "张",
},
})
// OR 条件
users := database.Select("user", "*", common.Map{
"OR": common.Map{
"status": 1,
"type": 2,
},
})
// 混合条件(嵌套 AND/OR
users := database.Select("user", "*", common.Map{
"AND": common.Map{
"status": 1,
"OR": common.Map{
"age[<]": 30,
"level[>]": 5,
},
},
})
// 带 ORDER BY、LIMIT 等特殊条件(关键字支持大小写)
users := database.Select("user", "*", common.Map{
"status": 1,
"age[>]": 18,
"ORDER": "id DESC", // 或 "order": "id DESC"
"LIMIT": 10, // 或 "limit": 10
})
// 带多个特殊条件
users := database.Select("user", "*", common.Map{
"OR": common.Map{
"level": "vip",
"balance[>]": 1000,
},
"ORDER": []string{"created_time DESC", "id ASC"},
"GROUP": "department",
"LIMIT": []int{0, 20}, // offset 0, limit 20
})
// 使用 HAVING 过滤分组结果
users := database.Select("user", "dept_id, COUNT(*) as cnt", common.Map{
"GROUP": "dept_id",
"HAVING": common.Map{
"cnt[>]": 5,
},
})
// 使用独立 OFFSET
users := database.Select("user", "*", common.Map{
"status": 1,
"LIMIT": 10,
"OFFSET": 20,
})
```
### 获取单条记录(Get)
```go
// 获取单个用户
user := database.Get("user", "*", common.Map{
"id": 1,
})
// 获取指定字段
user := database.Get("user", "id,name,email", common.Map{
"status": 1,
})
```
### 插入(Insert)
```go
// 基本插入
id := database.Insert("user", common.Map{
"name": "张三",
"email": "zhangsan@example.com",
"age": 25,
"status": 1,
"created_time[#]": "NOW()", // [#]表示直接插入SQL函数
})
// 返回插入的ID
fmt.Println("插入的用户ID:", id)
```
### 批量插入(BatchInsert)
```go
// 批量插入多条记录(使用 []Map 格式,更直观)
affected := database.BatchInsert("user", []common.Map{
{"name": "张三", "email": "zhang@example.com", "age": 25},
{"name": "李四", "email": "li@example.com", "age": 30},
{"name": "王五", "email": "wang@example.com", "age": 28},
})
// 生成: INSERT INTO `user` (`age`, `email`, `name`) VALUES (?, ?, ?), (?, ?, ?), (?, ?, ?)
fmt.Printf("批量插入 %d 条记录\n", affected)
// 支持 [#] 标记直接插入 SQL 表达式
affected := database.BatchInsert("log", []common.Map{
{"user_id": 1, "action": "login", "created_time[#]": "NOW()"},
{"user_id": 2, "action": "logout", "created_time[#]": "NOW()"},
})
```
### 更新(Update)
```go
// 基本更新
affected := database.Update("user", common.Map{
"name": "李四",
"email": "lisi@example.com",
"updated_time[#]": "NOW()",
}, common.Map{
"id": 1,
})
// 条件更新(多条件自动 AND 连接)
affected := database.Update("user", common.Map{
"status": 0,
}, common.Map{
"age[<]": 18,
"status": 1,
})
fmt.Println("更新的记录数:", affected)
```
### Upsert操作
Upsert(插入或更新):如果记录存在则更新,不存在则插入。
```go
// Upsert 操作(使用 Slice 格式)
affected := database.Upsert("user",
common.Map{
"id": 1,
"name": "张三",
"email": "zhang@example.com",
"login_count": 1,
},
common.Slice{"id"}, // 唯一键(用于冲突检测)
common.Slice{"name", "email", "login_count"}, // 冲突时更新的字段
)
// MySQL 生成:
// INSERT INTO user (id,name,email,login_count) VALUES (?,?,?,?)
// ON DUPLICATE KEY UPDATE name=VALUES(name), email=VALUES(email), login_count=VALUES(login_count)
// PostgreSQL 生成:
// INSERT INTO "user" (id,name,email,login_count) VALUES ($1,$2,$3,$4)
// ON CONFLICT (id) DO UPDATE SET name=EXCLUDED.name, email=EXCLUDED.email, login_count=EXCLUDED.login_count
// 使用 [#] 标记直接 SQL 更新
affected := database.Upsert("user",
common.Map{
"id": 1,
"name": "张三",
"login_count[#]": "login_count + 1", // 直接 SQL 表达式
},
common.Slice{"id"},
common.Slice{"name", "login_count"},
)
// 也支持可变参数形式
affected := database.Upsert("user",
common.Map{"id": 1, "name": "张三"},
common.Slice{"id"},
"name", "email", // 可变参数
)
```
### 删除(Delete)
```go
// 根据ID删除
affected := database.Delete("user", common.Map{
"id": 1,
})
// 条件删除(多条件自动 AND 连接)
affected := database.Delete("user", common.Map{
"status": 0,
"created_time[<]": "2023-01-01",
})
fmt.Println("删除的记录数:", affected)
```
## 链式查询构建器
HoTimeDB提供了链式查询构建器,让查询更加直观:
```go
// 基本链式查询
users := database.Table("user").
Where("status", 1).
And("age[>]", 18).
Order("created_time DESC").
Limit(10, 20). // offset, limit
Select()
// 链式获取单条记录
user := database.Table("user").
Where("id", 1).
Get()
// 链式更新
affected := database.Table("user").
Where("id", 1).
Update(common.Map{
"name": "新名称",
"updated_time[#]": "NOW()",
})
// 链式删除
affected := database.Table("user").
Where("status", 0).
Delete()
// 链式统计
count := database.Table("user").
Where("status", 1).
Count()
```
### 链式条件组合
```go
// 复杂条件组合
users := database.Table("user").
Where("status", 1).
And("age[>=]", 18).
Or(common.Map{
"level[>]": 5,
"vip": 1,
}).
Order("created_time DESC", "id ASC").
Group("department").
Having(common.Map{"COUNT(*).[>]": 5}). // 新增 HAVING 支持
Limit(0, 20).
Offset(10). // 新增独立 OFFSET 支持
Select("id,name,email,age")
```
## 条件查询语法
HoTimeDB支持丰富的条件查询语法,类似于Medoo:
### 基本比较
```go
// 等于
"id": 1
// 不等于
"id[!]": 1
// 大于
"age[>]": 18
// 大于等于
"age[>=]": 18
// 小于
"age[<]": 60
// 小于等于
"age[<=]": 60
```
### 模糊查询
```go
// LIKE %keyword%
"name[~]": "张"
// LIKE keyword% (右边任意)
"name[~!]": "张"
// LIKE %keyword (左边任意)
"name[!~]": "san"
// 手动LIKE(需要手动添加%
"name[~~]": "%张%"
```
### 区间查询
```go
// BETWEEN
"age[<>]": []int{18, 60}
// NOT BETWEEN
"age[><]": []int{18, 25}
```
### IN查询
```go
// IN
"id": []int{1, 2, 3, 4, 5}
// NOT IN
"id[!]": []int{1, 2, 3}
```
### NULL查询
```go
// IS NULL
"deleted_at": nil
// IS NOT NULL
"deleted_at[!]": nil
```
### 直接SQL
```go
// 直接插入SQL表达式(注意防注入)
"created_time[#]": "> DATE_SUB(NOW(), INTERVAL 1 DAY)"
// 字段直接赋值(不使用参数化查询)
"update_time[#]": "NOW()"
// 直接SQL片段
"[##]": "user.status = 1 AND user.level > 0"
```
## JOIN操作
### 链式JOIN
```go
// LEFT JOIN
users := database.Table("user").
LeftJoin("profile", "user.id = profile.user_id").
LeftJoin("department", "user.dept_id = department.id").
Where("user.status", 1).
Select("user.*, profile.avatar, department.name AS dept_name")
// RIGHT JOIN
users := database.Table("user").
RightJoin("order", "user.id = order.user_id").
Select()
// INNER JOIN
users := database.Table("user").
InnerJoin("profile", "user.id = profile.user_id").
Select()
// FULL JOIN
users := database.Table("user").
FullJoin("profile", "user.id = profile.user_id").
Select()
```
### 传统JOIN语法
```go
users := database.Select("user",
common.Slice{
common.Map{"[>]profile": "user.id = profile.user_id"},
common.Map{"[>]department": "user.dept_id = department.id"},
},
"user.*, profile.avatar, department.name AS dept_name",
common.Map{
"user.status": 1,
},
)
```
### JOIN类型说明
- `[>]`: LEFT JOIN
- `[<]`: RIGHT JOIN
- `[><]`: INNER JOIN
- `[<>]`: FULL JOIN
## 分页查询
### 基本分页
```go
// 设置分页:页码3,每页20条
users := database.Page(3, 20).PageSelect("user", "*", common.Map{
"status": 1,
})
// 链式分页
users := database.Table("user").
Where("status", 1).
Page(2, 15). // 第2页,每页15条
Select()
```
### 分页信息获取
```go
// 获取总数
total := database.Count("user", common.Map{
"status": 1,
})
// 计算分页信息
page := 2
pageSize := 20
offset := (page - 1) * pageSize
totalPages := (total + pageSize - 1) / pageSize
fmt.Printf("总记录数: %d, 总页数: %d, 当前页: %d\n", total, totalPages, page)
```
## 聚合函数
### 计数
```go
// 总数统计
total := database.Count("user")
// 条件统计
activeUsers := database.Count("user", common.Map{
"status": 1,
})
// JOIN统计
count := database.Count("user",
common.Slice{
common.Map{"[>]profile": "user.id = profile.user_id"},
},
common.Map{
"user.status": 1,
"profile.verified": 1,
},
)
```
### 求和
```go
// 基本求和
totalAmount := database.Sum("order", "amount")
// 条件求和
paidAmount := database.Sum("order", "amount", common.Map{
"status": "paid",
"created_time[>]": "2023-01-01",
})
```
### 平均值
```go
// 基本平均值
avgAge := database.Avg("user", "age")
// 条件平均值
avgAge := database.Avg("user", "age", common.Map{
"status": 1,
})
```
### 最大值/最小值
```go
// 最大值
maxAge := database.Max("user", "age")
// 最小值
minAge := database.Min("user", "age")
// 条件最大值
maxBalance := database.Max("user", "balance", common.Map{
"status": 1,
})
```
## 事务处理
```go
// 事务操作
success := database.Action(func(tx db.HoTimeDB) bool {
// 在事务中执行多个操作
// 扣减用户余额
affected1 := tx.Update("user", common.Map{
"balance[#]": "balance - 100",
}, common.Map{
"id": 1,
})
if affected1 == 0 {
return false // 回滚
}
// 创建订单
orderId := tx.Insert("order", common.Map{
"user_id": 1,
"amount": 100,
"status": "paid",
"created_time[#]": "NOW()",
})
if orderId == 0 {
return false // 回滚
}
return true // 提交
})
if success {
fmt.Println("事务执行成功")
} else {
fmt.Println("事务回滚")
fmt.Println("错误:", database.LastErr.GetError())
}
```
## 缓存机制
HoTimeDB集成了缓存功能,可以自动缓存查询结果:
### 缓存配置
```go
import "code.hoteas.com/golang/hotime/cache"
// 设置缓存
database.HoTimeCache = &cache.HoTimeCache{
// 缓存配置
}
```
### 缓存行为
- 查询操作会自动检查缓存
- 增删改操作会自动清除相关缓存
- 缓存键格式:`表名:查询MD5`
- `cached`表不会被缓存
### 缓存清理
```go
// 手动清除表缓存
database.HoTimeCache.Db("user*", nil) // 清除user表所有缓存
```
## PostgreSQL支持
HoTimeDB 支持 PostgreSQL 数据库,自动处理语法差异。
### PostgreSQL 配置
```go
import (
"code.hoteas.com/golang/hotime/db"
_ "github.com/lib/pq" // PostgreSQL 驱动
)
database := &db.HoTimeDB{
Type: "postgres", // 设置数据库类型为 postgres
Prefix: "app_",
}
database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) {
dsn := "host=localhost port=5432 user=postgres password=secret dbname=mydb sslmode=disable"
master, _ = sql.Open("postgres", dsn)
return master, master
})
```
### 主要差异
| 特性 | MySQL | PostgreSQL |
|------|-------|------------|
| 标识符引号 | \`name\` | "name" |
| 占位符 | ? | $1, $2, $3... |
| Upsert | ON DUPLICATE KEY UPDATE | ON CONFLICT DO UPDATE |
所有这些差异由框架自动处理,无需手动调整代码。
## 高级特性
### 调试模式
```go
// 设置调试模式
database.Mode = 2
// 查看最后执行的SQL
fmt.Println("最后的SQL:", database.LastQuery)
fmt.Println("参数:", database.LastData)
fmt.Println("错误:", database.LastErr.GetError())
```
### 主从分离
```go
func createConnection() (master, slave *sql.DB) {
// 主库连接
master, _ = sql.Open("mysql", "user:password@tcp(master:3306)/database")
// 从库连接
slave, _ = sql.Open("mysql", "user:password@tcp(slave:3306)/database")
return master, slave
}
database.SetConnect(createConnection)
// 查询会自动使用从库,增删改使用主库
```
### 原生SQL执行
```go
// 执行查询SQL
results := database.Query("SELECT * FROM user WHERE age > ? AND status = ?", 18, 1)
// 执行更新SQL
result, err := database.Exec("UPDATE user SET last_login = NOW() WHERE id = ?", 1)
if err.GetError() == nil {
affected, _ := result.RowsAffected()
fmt.Println("影响行数:", affected)
}
```
## 特殊语法详解
### 条件标记符说明
| 标记符 | 功能 | 示例 | 生成SQL |
|--------|------|------|---------|
| `[>]` | 大于 | `"age[>]": 18` | `age > 18` |
| `[<]` | 小于 | `"age[<]": 60` | `age < 60` |
| `[>=]` | 大于等于 | `"age[>=]": 18` | `age >= 18` |
| `[<=]` | 小于等于 | `"age[<=]": 60` | `age <= 60` |
| `[!]` | 不等于/NOT IN | `"id[!]": 1` | `id != 1` |
| `[~]` | LIKE模糊查询 | `"name[~]": "张"` | `name LIKE '%张%'` |
| `[!~]` | 左模糊 | `"name[!~]": "张"` | `name LIKE '%张'` |
| `[~!]` | 右模糊 | `"name[~!]": "张"` | `name LIKE '张%'` |
| `[~~]` | 手动LIKE | `"name[~~]": "%张%"` | `name LIKE '%张%'` |
| `[<>]` | BETWEEN | `"age[<>]": [18,60]` | `age BETWEEN 18 AND 60` |
| `[><]` | NOT BETWEEN | `"age[><]": [18,25]` | `age NOT BETWEEN 18 AND 25` |
| `[#]` | 直接SQL | `"time[#]": "NOW()"` | `time = NOW()` |
| `[##]` | SQL片段 | `"[##]": "a > b"` | `a > b` |
| `[#!]` | 不等于直接SQL | `"status[#!]": "1"` | `status != 1` |
| `[!#]` | 不等于直接SQL | `"status[!#]": "1"` | `status != 1` |
### 特殊关键字(支持大小写)
| 关键字 | 功能 | 示例 |
|--------|------|------|
| `ORDER` / `order` | 排序 | `"ORDER": "id DESC"` |
| `GROUP` / `group` | 分组 | `"GROUP": "dept_id"` |
| `LIMIT` / `limit` | 限制 | `"LIMIT": 10` |
| `OFFSET` / `offset` | 偏移 | `"OFFSET": 20` |
| `HAVING` / `having` | 分组过滤 | `"HAVING": Map{"cnt[>]": 5}` |
| `DISTINCT` / `distinct` | 去重 | 在 SELECT 中使用 |
## 常见问题
### Q1: 多条件查询需要用 AND 包装吗?
A1: 不再需要!现在多条件会自动用 AND 连接。当然,使用 `AND` 包装仍然有效(向后兼容)。
### Q2: 如何处理事务中的错误?
A2: 在 `Action` 函数中返回 `false` 即可触发回滚,所有操作都会被撤销。
### Q3: 缓存何时会被清除?
A3: 执行 `Insert``Update``Delete``Upsert``BatchInsert` 操作时会自动清除对应表的缓存。
### Q4: 如何执行复杂的原生SQL?
A4: 使用 `Query` 方法执行查询,使用 `Exec` 方法执行更新操作。
### Q5: 主从分离如何工作?
A5: 查询操作自动使用从库(如果配置了),增删改操作使用主库。
### Q6: 如何处理NULL值?
A6: 使用 `nil` 作为值,查询时使用 `"field": nil` 表示 `IS NULL`
### Q7: PostgreSQL 和 MySQL 语法有区别吗?
A7: 框架会自动处理差异(占位符、引号等),代码无需修改。
---
*文档版本: 2.0*
*最后更新: 2026年1月*
> 本文档基于HoTimeDB源码分析生成,如有疑问请参考源码实现。该ORM框架参考了PHP Medoo的设计理念,但根据Golang语言特性进行了适配和优化。
-252
View File
@@ -1,252 +0,0 @@
# HoTimeDB ORM 文档集合
这是HoTimeDB ORM框架的完整文档集合,包含使用说明、API参考、示例代码和测试数据。
## ⚠️ 重要更新说明
**语法修正通知**:经过对源码的深入分析,发现HoTimeDB的条件查询语法有特定规则:
-**单个条件**:可以直接写在Map中
- ⚠️ **多个条件**:必须使用`AND``OR`包装
- 📝 所有文档和示例代码已按正确语法更新
## 📚 文档列表
### 1. [HoTimeDB_使用说明.md](./HoTimeDB_使用说明.md)
**完整使用说明书** - 详细的功能介绍和使用指南
- 🚀 快速开始
- ⚙️ 数据库配置
- 🔧 基本操作 (CRUD)
- 🔗 链式查询构建器
- 🔍 条件查询语法
- 🔄 JOIN操作
- 📄 分页查询
- 📊 聚合函数
- 🔐 事务处理
- 💾 缓存机制
- ⚡ 高级特性
### 2. [HoTimeDB_API参考.md](./HoTimeDB_API参考.md)
**快速API参考手册** - 开发时的速查手册
- 📖 基本方法
- 🔧 CRUD操作
- 📊 聚合函数
- 📄 分页查询
- 🔍 条件语法参考
- 🔗 JOIN语法
- 🔐 事务处理
- 🛠️ 工具方法
### 3. [示例代码文件](../examples/hotimedb_examples.go)
**完整示例代码集合** - 可运行的实际应用示例(语法已修正)
- 🏗️ 基本初始化和配置
- 📝 基本CRUD操作
- 🔗 链式查询操作
- 🤝 JOIN查询操作
- 🔍 条件查询语法
- 📄 分页查询
- 📊 聚合函数查询
- 🔐 事务处理
- 💾 缓存机制
- 🔧 原生SQL执行
- 🚨 错误处理和调试
- ⚡ 性能优化技巧
- 🎯 完整应用示例
### 4. [test_tables.sql](./test_tables.sql)
**测试数据库结构** - 快速搭建测试环境
- 🏗️ 完整的表结构定义
- 📊 测试数据插入
- 🔍 索引优化
- 👁️ 视图示例
- 🔧 存储过程示例
## 🎯 核心特性
### 🌟 主要优势
- **类Medoo语法**: 参考PHP Medoo设计,语法简洁易懂
- **链式查询**: 支持流畅的链式查询构建器
- **条件丰富**: 支持丰富的条件查询语法
- **事务支持**: 完整的事务处理机制
- **缓存集成**: 内置查询结果缓存
- **读写分离**: 支持主从数据库配置
- **类型安全**: 基于Golang的强类型系统
### 🔧 支持的数据库
- ✅ MySQL
- ✅ SQLite
- ✅ 其他标准SQL数据库
## 🚀 快速开始
### 1. 安装依赖
```bash
go mod init your-project
go get github.com/go-sql-driver/mysql
go get github.com/sirupsen/logrus
```
### 2. 创建测试数据库
```bash
mysql -u root -p < test_tables.sql
```
### 3. 基本使用
```go
import (
"code.hoteas.com/golang/hotime/db"
"code.hoteas.com/golang/hotime/common"
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
// 初始化数据库
database := &db.HoTimeDB{
Prefix: "app_",
Mode: 2, // 开发模式
}
database.SetConnect(func() (master, slave *sql.DB) {
master, _ = sql.Open("mysql", "user:pass@tcp(localhost:3306)/dbname")
return master, master
})
// 链式查询(链式语法支持单独Where然后用And添加条件)
users := database.Table("user").
Where("status", 1). // 链式中可以单独Where
And("age[>]", 18). // 用And添加更多条件
Order("created_time DESC").
Limit(0, 10).
Select("id,name,email")
// 或者使用传统语法(多个条件必须用AND包装)
users2 := database.Select("user", "id,name,email", common.Map{
"AND": common.Map{
"status": 1,
"age[>]": 18,
},
"ORDER": "created_time DESC",
"LIMIT": []int{0, 10},
})
```
## ⚠️ 重要语法规则
**条件查询语法规则:**
-**单个条件**:可以直接写在Map中
-**多个条件**:必须使用`AND``OR`包装
-**特殊参数**`ORDER``GROUP``LIMIT`与条件同级
```go
// ✅ 正确:单个条件
Map{"status": 1}
// ✅ 正确:多个条件用AND包装
Map{
"AND": Map{
"status": 1,
"age[>]": 18,
},
"ORDER": "id DESC",
}
// ❌ 错误:多个条件不用AND包装
Map{
"status": 1,
"age[>]": 18, // 不支持!
}
```
## 📝 条件查询语法速查
| 语法 | SQL | 说明 |
|------|-----|------|
| `"field": value` | `field = ?` | 等于 |
| `"field[!]": value` | `field != ?` | 不等于 |
| `"field[>]": value` | `field > ?` | 大于 |
| `"field[>=]": value` | `field >= ?` | 大于等于 |
| `"field[<]": value` | `field < ?` | 小于 |
| `"field[<=]": value` | `field <= ?` | 小于等于 |
| `"field[~]": "keyword"` | `field LIKE '%keyword%'` | 包含 |
| `"field[<>]": [min, max]` | `field BETWEEN ? AND ?` | 区间内 |
| `"field": [v1, v2, v3]` | `field IN (?, ?, ?)` | 在集合中 |
| `"field": nil` | `field IS NULL` | 为空 |
| `"field[#]": "NOW()"` | `field = NOW()` | 直接SQL |
## 🔗 JOIN语法速查
| 语法 | SQL | 说明 |
|------|-----|------|
| `"[>]table"` | `LEFT JOIN` | 左连接 |
| `"[<]table"` | `RIGHT JOIN` | 右连接 |
| `"[><]table"` | `INNER JOIN` | 内连接 |
| `"[<>]table"` | `FULL JOIN` | 全连接 |
## 🛠️ 链式方法速查
```go
db.Table("table") // 指定表名
.Where(key, value) // WHERE条件
.And(key, value) // AND条件
.Or(map) // OR条件
.LeftJoin(table, on) // LEFT JOIN
.Order(fields...) // ORDER BY
.Group(fields...) // GROUP BY
.Limit(offset, limit) // LIMIT
.Page(page, pageSize) // 分页
.Select(fields...) // 查询
.Get(fields...) // 获取单条
.Count() // 计数
.Update(data) // 更新
.Delete() // 删除
```
## ⚡ 性能优化建议
### 🔍 查询优化
- 使用合适的索引字段作为查询条件
- IN查询会自动优化为BETWEEN(连续数字)
- 避免SELECT *,指定需要的字段
- 合理使用LIMIT限制结果集大小
### 💾 缓存使用
- 查询结果会自动缓存
- 增删改操作会自动清除缓存
- `cached`表不参与缓存
### 🔐 事务处理
- 批量操作使用事务提高性能
- 事务中避免长时间操作
- 合理设置事务隔离级别
## 🚨 注意事项
### 🔒 安全相关
- 使用参数化查询防止SQL注入
- `[#]`语法需要注意防止注入
- 敏感数据加密存储
### 🎯 最佳实践
- 开发时设置`Mode = 2`便于调试
- 生产环境设置`Mode = 0`
- 合理设置表前缀
- 定期检查慢查询日志
## 🤝 与PHP Medoo的差异
1. **类型系统**: 使用`common.Map``common.Slice`
2. **错误处理**: Golang风格的错误处理
3. **链式调用**: 提供更丰富的链式API
4. **缓存集成**: 内置缓存功能
5. **并发安全**: 需要注意并发使用
## 📞 技术支持
- 📧 查看源码:`hotimedb.go`
- 📖 参考文档:本目录下的各个文档文件
- 🔧 示例代码:运行`HoTimeDB_示例代码.go`中的示例
---
**HoTimeDB ORM框架 - 让数据库操作更简单!** 🎉
> 本文档基于HoTimeDB源码分析生成,参考了PHP Medoo的设计理念,并根据Golang语言特性进行了优化。
+3 -3
View File
@@ -307,19 +307,19 @@ func (that *HoTimeDB) Insert(table string, data map[string]interface{}) int64 {
return id
}
// BatchInsert 批量插入数据
// Inserts 批量插入数据
// table: 表名
// dataList: 数据列表,每个元素是一个 Map
// 返回受影响的行数
//
// 示例:
//
// affected := db.BatchInsert("user", []Map{
// affected := db.Inserts("user", []Map{
// {"name": "张三", "age": 25, "email": "zhang@example.com"},
// {"name": "李四", "age": 30, "email": "li@example.com"},
// {"name": "王五", "age": 28, "email": "wang@example.com"},
// })
func (that *HoTimeDB) BatchInsert(table string, dataList []Map) int64 {
func (that *HoTimeDB) Inserts(table string, dataList []Map) int64 {
if len(dataList) == 0 {
return 0
}