# HoTimeDB ORM 使用说明书 ## 概述 HoTimeDB是一个基于Golang实现的轻量级ORM框架,参考PHP Medoo设计,提供简洁的数据库操作接口。支持MySQL、SQLite等数据库,并集成了缓存、事务、链式查询等功能。 ## 目录 - [快速开始](#快速开始) - [数据库配置](#数据库配置) - [基本操作](#基本操作) - [查询(Select)](#查询select) - [获取单条记录(Get)](#获取单条记录get) - [插入(Insert)](#插入insert) - [更新(Update)](#更新update) - [删除(Delete)](#删除delete) - [链式查询构建器](#链式查询构建器) - [条件查询语法](#条件查询语法) - [JOIN操作](#join操作) - [分页查询](#分页查询) - [聚合函数](#聚合函数) - [事务处理](#事务处理) - [缓存机制](#缓存机制) - [高级特性](#高级特性) ## 快速开始 ### 初始化数据库连接 ```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 db := &db.HoTimeDB{} db.SetConnect(createConnection) ``` ## 数据库配置 ### 基本配置 ```go type HoTimeDB struct { *sql.DB ContextBase DBName string *cache.HoTimeCache Log *logrus.Logger Type string // 数据库类型 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开发模式 } ``` ### 设置表前缀 ```go db.Prefix = "app_" ``` ### 设置运行模式 ```go db.Mode = 2 // 开发模式,会输出SQL日志 ``` ## 基本操作 ### 查询(Select) #### 基本查询 ```go // 查询所有字段 users := db.Select("user") // 查询指定字段 users := db.Select("user", "id,name,email") // 查询指定字段(数组形式) users := db.Select("user", []string{"id", "name", "email"}) // 单条件查询 users := db.Select("user", "*", common.Map{ "status": 1, }) // 多条件查询(必须使用AND包装) users = db.Select("user", "*", common.Map{ "AND": common.Map{ "status": 1, "age[>]": 18, }, }) ``` #### 复杂条件查询 **重要说明:多个条件必须使用AND或OR包装,不能直接在根Map中写多个字段条件** ```go // AND条件(多个条件必须用AND包装) users := db.Select("user", "*", common.Map{ "AND": common.Map{ "status": 1, "age[>]": 18, "name[~]": "张", }, }) // OR条件 users := db.Select("user", "*", common.Map{ "OR": common.Map{ "status": 1, "type": 2, }, }) // 混合条件(嵌套AND/OR) users := db.Select("user", "*", common.Map{ "AND": common.Map{ "status": 1, "OR": common.Map{ "age[<]": 30, "level[>]": 5, }, }, }) // 带ORDER BY、LIMIT等特殊条件 users := db.Select("user", "*", common.Map{ "AND": common.Map{ "status": 1, "age[>]": 18, }, "ORDER": "id DESC", "LIMIT": 10, }) // 带多个特殊条件 users := db.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 }) ``` ### 获取单条记录(Get) ```go // 获取单个用户 user := db.Get("user", "*", common.Map{ "id": 1, }) // 获取指定字段 user := db.Get("user", "id,name,email", common.Map{ "status": 1, }) ``` ### 插入(Insert) ```go // 基本插入 id := db.Insert("user", common.Map{ "name": "张三", "email": "zhangsan@example.com", "age": 25, "status": 1, "created_time[#]": "NOW()", // [#]表示直接插入SQL函数 }) // 返回插入的ID fmt.Println("插入的用户ID:", id) ``` ### 更新(Update) ```go // 基本更新 affected := db.Update("user", common.Map{ "name": "李四", "email": "lisi@example.com", "updated_time[#]": "NOW()", }, common.Map{ "id": 1, }) // 条件更新 affected := db.Update("user", common.Map{ "status": 0, }, common.Map{ "age[<]": 18, "status": 1, }) fmt.Println("更新的记录数:", affected) ``` ### 删除(Delete) ```go // 根据ID删除 affected := db.Delete("user", common.Map{ "id": 1, }) // 条件删除 affected := db.Delete("user", common.Map{ "status": 0, "created_time[<]": "2023-01-01", }) fmt.Println("删除的记录数:", affected) ``` ## 链式查询构建器 HoTimeDB提供了链式查询构建器,让查询更加直观: ```go // 基本链式查询 users := db.Table("user"). Where("status", 1). And("age[>]", 18). Order("created_time DESC"). Limit(10, 20). // offset, limit Select() // 链式获取单条记录 user := db.Table("user"). Where("id", 1). Get() // 链式更新 affected := db.Table("user"). Where("id", 1). Update(common.Map{ "name": "新名称", "updated_time[#]": "NOW()", }) // 链式删除 affected := db.Table("user"). Where("status", 0). Delete() // 链式统计 count := db.Table("user"). Where("status", 1). Count() ``` ### 链式条件组合 ```go // 复杂条件组合 users := db.Table("user"). Where("status", 1). And("age[>=]", 18). Or(common.Map{ "level[>]": 5, "vip": 1, }). Order("created_time DESC", "id ASC"). Group("department"). Limit(0, 20). 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 := db.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 := db.Table("user"). RightJoin("order", "user.id = order.user_id"). Select() // INNER JOIN users := db.Table("user"). InnerJoin("profile", "user.id = profile.user_id"). Select() // FULL JOIN users := db.Table("user"). FullJoin("profile", "user.id = profile.user_id"). Select() ``` ### 传统JOIN语法 ```go users := db.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 := db.Page(3, 20).PageSelect("user", "*", common.Map{ "status": 1, }) // 链式分页 users := db.Table("user"). Where("status", 1). Page(2, 15). // 第2页,每页15条 Select() ``` ### 分页信息获取 ```go // 获取总数 total := db.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 := db.Count("user") // 条件统计 activeUsers := db.Count("user", common.Map{ "status": 1, }) // JOIN统计 count := db.Count("user", common.Slice{ common.Map{"[>]profile": "user.id = profile.user_id"}, }, common.Map{ "user.status": 1, "profile.verified": 1, }, ) ``` ### 求和 ```go // 基本求和 totalAmount := db.Sum("order", "amount") // 条件求和 paidAmount := db.Sum("order", "amount", common.Map{ "status": "paid", "created_time[>]": "2023-01-01", }) // JOIN求和 sum := db.Sum("order", "amount", common.Slice{ common.Map{"[>]user": "order.user_id = user.id"}, }, common.Map{ "user.level": "vip", "order.status": "paid", }, ) ``` ## 事务处理 ```go // 事务操作 success := db.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 // 回滚 } // 添加订单详情 detailId := tx.Insert("order_detail", common.Map{ "order_id": orderId, "product_id": 1001, "quantity": 1, "price": 100, }) if detailId == 0 { return false // 回滚 } return true // 提交 }) if success { fmt.Println("事务执行成功") } else { fmt.Println("事务回滚") fmt.Println("错误:", db.LastErr.GetError()) } ``` ## 缓存机制 HoTimeDB集成了缓存功能,可以自动缓存查询结果: ### 缓存配置 ```go import "code.hoteas.com/golang/hotime/cache" // 设置缓存 db.HoTimeCache = &cache.HoTimeCache{ // 缓存配置 } ``` ### 缓存行为 - 查询操作会自动检查缓存 - 增删改操作会自动清除相关缓存 - 缓存键格式:`表名:查询MD5` - `cached`表不会被缓存 ### 缓存清理 ```go // 手动清除表缓存 db.HoTimeCache.Db("user*", nil) // 清除user表所有缓存 ``` ## 高级特性 ### 调试模式 ```go // 设置调试模式 db.Mode = 2 // 查看最后执行的SQL fmt.Println("最后的SQL:", db.LastQuery) fmt.Println("参数:", db.LastData) fmt.Println("错误:", db.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 } db.SetConnect(createConnection) // 查询会自动使用从库,增删改使用主库 ``` ### 原生SQL执行 ```go // 执行查询SQL results := db.Query("SELECT * FROM user WHERE age > ? AND status = ?", 18, 1) // 执行更新SQL result, err := db.Exec("UPDATE user SET last_login = NOW() WHERE id = ?", 1) if err.GetError() == nil { affected, _ := result.RowsAffected() fmt.Println("影响行数:", affected) } ``` ### 数据类型处理 ```go // 时间戳插入 db.Insert("log", common.Map{ "user_id": 1, "action": "login", "created_time[#]": "UNIX_TIMESTAMP()", }) // JSON数据 db.Insert("user", common.Map{ "name": "张三", "preferences": `{"theme": "dark", "language": "zh-CN"}`, }) ``` ### 数据行处理 HoTimeDB内部的`Row`方法会自动处理不同数据类型: ```go // 自动转换[]uint8为字符串 // 保持其他类型的原始值 // 处理NULL值 ``` ## 错误处理 ```go // 检查错误 users := db.Select("user", "*", common.Map{"status": 1}) if db.LastErr.GetError() != nil { fmt.Println("查询错误:", db.LastErr.GetError()) return } // 插入时检查错误 id := db.Insert("user", common.Map{ "name": "test", "email": "test@example.com", }) if id == 0 && db.LastErr.GetError() != nil { fmt.Println("插入失败:", db.LastErr.GetError()) } ``` ## 性能优化 ### IN查询优化 HoTimeDB会自动优化IN查询,将连续的数字转换为BETWEEN查询以提高性能: ```go // 这个查询会被自动优化(单个IN条件) users := db.Select("user", "*", common.Map{ "id": []int{1, 2, 3, 4, 5, 10, 11, 12}, // 自动转为 (id BETWEEN 1 AND 5) OR (id BETWEEN 10 AND 12) }) ``` ### 批量操作 ```go // 使用事务进行批量插入 success := db.Action(func(tx db.HoTimeDB) bool { for i := 0; i < 1000; i++ { id := tx.Insert("user", common.Map{ "name": fmt.Sprintf("User%d", i), "email": fmt.Sprintf("user%d@example.com", i), "created_time[#]": "NOW()", }) if id == 0 { return false } } return true }) ``` ## 特殊语法详解 ### 条件标记符说明 | 标记符 | 功能 | 示例 | 生成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` | ## 与PHP Medoo的差异 1. **类型系统**: Golang的强类型要求使用`common.Map`和`common.Slice` 2. **语法差异**: 某些条件语法可能略有不同 3. **错误处理**: 使用Golang的错误处理模式 4. **并发安全**: 需要注意并发使用时的安全性 5. **缓存集成**: 内置了缓存功能 6. **链式调用**: 提供了更丰富的链式API ## 完整示例 ```go package main import ( "fmt" "database/sql" "code.hoteas.com/golang/hotime/db" "code.hoteas.com/golang/hotime/common" _ "github.com/go-sql-driver/mysql" ) func main() { // 初始化数据库 database := &db.HoTimeDB{ Prefix: "app_", Mode: 2, // 开发模式 } database.SetConnect(func(err ...*common.Error) (master, slave *sql.DB) { master, _ = sql.Open("mysql", "root:password@tcp(localhost:3306)/testdb") return master, master }) // 查询用户列表 users := database.Table("user"). Where("status", 1). And("age[>=]", 18). Order("created_time DESC"). Limit(0, 10). Select("id,name,email,age") for _, user := range users { fmt.Printf("用户: %s, 邮箱: %s, 年龄: %v\n", user.GetString("name"), user.GetString("email"), user.Get("age")) } // 创建新用户 userId := database.Insert("user", common.Map{ "name": "新用户", "email": "new@example.com", "age": 25, "status": 1, "created_time[#]": "NOW()", }) fmt.Printf("创建用户ID: %d\n", userId) // 更新用户 affected := database.Table("user"). Where("id", userId). Update(common.Map{ "last_login[#]": "NOW()", "login_count[#]": "login_count + 1", }) fmt.Printf("更新记录数: %d\n", affected) // 复杂查询示例 orders := 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(common.Map{ "OR": common.Map{ "user.level": "vip", "order.amount[>]": 1000, }, }). Order("order.created_time DESC"). Group("user.id"). Select("user.name, user.email, SUM(order.amount) as total_amount, COUNT(*) as order_count") // 事务示例 success := database.Action(func(tx db.HoTimeDB) bool { // 在事务中执行操作 orderId := tx.Insert("order", common.Map{ "user_id": userId, "total": 100.50, "status": "pending", "created_time[#]": "NOW()", }) if orderId == 0 { return false } detailId := tx.Insert("order_detail", common.Map{ "order_id": orderId, "product_id": 1, "quantity": 2, "price": 50.25, }) return detailId > 0 }) if success { fmt.Println("订单创建成功") } else { fmt.Println("订单创建失败:", database.LastErr.GetError()) } // 统计示例 totalUsers := database.Count("user", common.Map{"status": 1}) totalAmount := database.Sum("order", "amount", common.Map{"status": "paid"}) fmt.Printf("活跃用户数: %d, 总交易额: %.2f\n", totalUsers, totalAmount) } ``` ## 常见问题 ### Q1: 如何处理事务中的错误? A1: 在`Action`函数中返回`false`即可触发回滚,所有操作都会被撤销。 ### Q2: 缓存何时会被清除? A2: 执行`Insert`、`Update`、`Delete`操作时会自动清除对应表的缓存。 ### Q3: 如何执行复杂的原生SQL? A3: 使用`Query`方法执行查询,使用`Exec`方法执行更新操作。 ### Q4: 主从分离如何工作? A4: 查询操作自动使用从库(如果配置了),增删改操作使用主库。 ### Q5: 如何处理NULL值? A5: 使用`nil`作为值,查询时使用`"field": nil`表示`IS NULL`。 --- *文档版本: 1.0* *最后更新: 2024年* > 本文档基于HoTimeDB源码分析生成,如有疑问请参考源码实现。该ORM框架参考了PHP Medoo的设计理念,但根据Golang语言特性进行了适配和优化。