本文档详细介绍 daog 提供的增删改查操作。
daog 提供两种调用方式:
- 函数方式: 调用
daog.GetById(tc, id, meta)等函数,需要传递TableMeta - QuickDao 方式: 调用
dal.UserInfoDao.GetById(tc, id),无需传递TableMeta
两种方式功能完全相同,本文档使用 QuickDao 方式演示。
import (
"github.com/rolandhe/daog"
txrequest "github.com/rolandhe/daog/tx"
"github.com/rolandhe/daog/ttypes"
"your/project/dal"
)
func createUser() (*dal.UserInfo, error) {
return daog.AutoTransWithResult(func() (*daog.TransContext, error) {
return daog.NewTransContext(datasource, txrequest.RequestWrite, "trace-id")
}, func(tc *daog.TransContext) (*dal.UserInfo, error) {
user := &dal.UserInfo{
Name: "张三",
CreateAt: ttypes.NormalDatetime(time.Now()),
}
// 插入并返回影响行数
affectedRows, err := dal.UserInfoDao.Insert(tc, user)
if err != nil {
return nil, err
}
// 自增 ID 已自动回填到 user.Id
fmt.Printf("插入成功,ID: %d, 影响行数: %d\n", user.Id, affectedRows)
return user, nil
})
}关键点:
- 自增 ID 列在插入时会被自动排除
- 插入成功后,自增 ID 会自动回填到结构体的对应字段
如果表的 TableMeta.StampColumns 配置了时间戳列,daog 会在插入时自动填充当前时间:
// 在 -ext.go 文件的 init 函数中配置
func init() {
// 1 表示插入时填充,2 表示更新时填充,3 表示插入和更新都填充
UserInfoMeta.StampColumns = map[string]int{
"create_at": 1, // 仅插入时填充
"modify_at": 3, // 插入和更新都填充
}
}// 查询所有字段
user, err := dal.UserInfoDao.GetById(tc, 100)
// 查询指定字段(视图)
user, err := dal.UserInfoDao.GetById(tc, 100,
dal.UserInfoFields.Id,
dal.UserInfoFields.Name)ids := []int64{1, 2, 3, 4, 5}
users, err := dal.UserInfoDao.GetByIds(tc, ids)
// 使用视图
users, err := dal.UserInfoDao.GetByIds(tc, ids,
dal.UserInfoFields.Id,
dal.UserInfoFields.Name)// 查询所有记录
users, err := dal.UserInfoDao.GetAll(tc)
// 查询指定字段
users, err := dal.UserInfoDao.GetAll(tc,
dal.UserInfoFields.Id,
dal.UserInfoFields.Name)matcher := daog.NewMatcher().
Eq(dal.UserInfoFields.Name, "张三")
user, err := dal.UserInfoDao.QueryOneMatcher(tc, matcher)
if user == nil {
// 未找到记录
}matcher := daog.NewMatcher().
Eq(dal.UserInfoFields.Status, 1).
Gt(dal.UserInfoFields.Age, 18)
// 基本查询
users, err := dal.UserInfoDao.QueryListMatcher(tc, matcher)
// 带排序
users, err := dal.UserInfoDao.QueryListMatcher(tc, matcher,
daog.NewDescOrder(dal.UserInfoFields.CreateAt))
// 指定查询字段
users, err := dal.UserInfoDao.QueryListMatcherWithViewColumns(tc, matcher,
[]string{dal.UserInfoFields.Id, dal.UserInfoFields.Name})matcher := daog.NewMatcher().Eq(dal.UserInfoFields.Status, 1)
// 创建分页对象:每页 10 条,查询第 1 页
pager := daog.NewPager(10, 1)
// 排序条件
order := daog.NewDescOrder(dal.UserInfoFields.CreateAt)
users, err := dal.UserInfoDao.QueryPageListMatcher(tc, matcher, pager, order)除了使用字符串数组指定查询列,还可以使用 View 对象:
// 包含指定字段
view := daog.NewView([]string{
dal.UserInfoFields.Id,
dal.UserInfoFields.Name,
})
users, err := dal.UserInfoDao.GetAllWithViewObj(tc, view)
// 排除指定字段
excludeView := daog.NewExcludeView([]string{
dal.UserInfoFields.Content, // 排除大字段
dal.UserInfoFields.BinData,
})
users, err := dal.UserInfoDao.GetAllWithViewObj(tc, excludeView)用于悲观锁场景,查询时锁定行:
// 标准 FOR UPDATE
user, err := dal.UserInfoDao.GetByIdForUpdate(tc, 100, false)
// FOR UPDATE SKIP LOCKED(跳过已锁定的行)
user, err := dal.UserInfoDao.GetByIdForUpdate(tc, 100, true)
// 条件查询 + FOR UPDATE
matcher := daog.NewMatcher().Eq(dal.UserInfoFields.Status, 1)
users, err := dal.UserInfoDao.QueryListMatcherForUpdate(tc, matcher, false)注意: FOR UPDATE 必须在写事务 (txrequest.RequestWrite) 中使用。
当需要处理大量数据时,使用批量处理可以避免内存溢出:
matcher := daog.NewMatcher().Eq(dal.UserInfoFields.Status, 1)
// 批量处理:每次处理 100 条,总共处理 10000 条
err := dal.UserInfoDao.QueryListMatcherByBatchHandle(tc, matcher,
10000, // totalLimit: 总数上限,0 表示无限制
100, // batchSize: 每批大小
func(batch []*dal.UserInfo) error {
// 处理当前批次的数据
for _, user := range batch {
// 业务处理...
}
return nil
},
daog.NewOrder(dal.UserInfoFields.Id), // 排序
)matcher := daog.NewMatcher().
Eq(dal.UserInfoFields.Status, 1).
Gt(dal.UserInfoFields.Age, 18)
count, err := dal.UserInfoDao.Count(tc, matcher)
fmt.Printf("符合条件的记录数: %d\n", count)更新整个对象,根据主键匹配:
func updateUser(id int64) error {
return daog.AutoTrans(func() (*daog.TransContext, error) {
return daog.NewTransContext(datasource, txrequest.RequestWrite, "trace-id")
}, func(tc *daog.TransContext) error {
// 先查询
user, err := dal.UserInfoDao.GetById(tc, id)
if err != nil {
return err
}
if user == nil {
return errors.New("user not found")
}
// 修改字段
user.Name = "李四"
user.ModifyAt = *ttypes.FromDatetime(time.Now())
// 更新(根据主键 Id 匹配)
affectedRows, err := dal.UserInfoDao.Update(tc, user)
return err
})
}users := []*dal.UserInfo{user1, user2, user3}
affectedRows, err := dal.UserInfoDao.UpdateList(tc, users)注意: 在非事务模式 (txrequest.RequestNone) 下,如果某条更新失败,之前的更新不会回滚。
使用 Modifier 只更新指定字段:
modifier := daog.NewModifier().
Add(dal.UserInfoFields.Name, "王五").
Add(dal.UserInfoFields.ModifyAt, ttypes.NormalDatetime(time.Now()))
affectedRows, err := dal.UserInfoDao.UpdateById(tc, modifier, 100)modifier := daog.NewModifier().
Add(dal.UserInfoFields.Status, 0)
ids := []int64{1, 2, 3, 4, 5}
affectedRows, err := dal.UserInfoDao.UpdateByIds(tc, modifier, ids)modifier := daog.NewModifier().
Add(dal.UserInfoFields.Status, 0)
matcher := daog.NewMatcher().
Eq(dal.UserInfoFields.Status, 1).
Lt(dal.UserInfoFields.CreateAt, expiredTime)
affectedRows, err := dal.UserInfoDao.UpdateByModifier(tc, modifier, matcher)modifier := daog.NewModifier().
// 普通赋值: SET name = ?
Add(dal.UserInfoFields.Name, "新名字").
// 自增: SET count = count + ?
SelfAdd(dal.UserInfoFields.ViewCount, 1).
// 自减: SET stock = stock - ?
SelfMinus(dal.UserInfoFields.Stock, 1)affectedRows, err := dal.UserInfoDao.DeleteById(tc, 100)ids := []int64{1, 2, 3, 4, 5}
affectedRows, err := dal.UserInfoDao.DeleteByIds(tc, ids)matcher := daog.NewMatcher().
Eq(dal.UserInfoFields.Status, 0).
Lt(dal.UserInfoFields.CreateAt, expiredTime)
affectedRows, err := dal.UserInfoDao.DeleteByMatcher(tc, matcher)安全限制: DeleteByMatcher 必须指定有效条件,不允许删除全表数据。如果 Matcher 为空或条件为空,操作会被拒绝。
sql := "UPDATE user_info SET status = ? WHERE id = ? AND status = ?"
affectedRows, err := dal.UserInfoDao.ExecRawSQL(tc, sql, 1, 100, 0)sql := "INSERT INTO user_info(name, create_at) VALUES(?, ?)"
lastInsertId, err := dal.UserInfoDao.ExecInsertWithAutoId(tc, sql,
"张三", time.Now())type UserSummary struct {
Id int64
Name string
Count int64
}
sql := `SELECT u.id, u.name, COUNT(o.id) as count
FROM user_info u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.status = ?
GROUP BY u.id`
// 提供字段映射函数
extract := func(ins *UserSummary) []any {
return []any{&ins.Id, &ins.Name, &ins.Count}
}
results, err := daog.QueryRawSQL(tc, extract, sql, 1)sql := "SELECT id, name, email FROM user_info WHERE status = ?"
extract := func(ins *dal.UserInfo) []any {
return []any{&ins.Id, &ins.Name, &ins.Email}
}
err := daog.QueryRawSQLByBatchHandle(tc, 100, func(batch []*dal.UserInfo) error {
for _, user := range batch {
// 处理数据...
}
return nil
}, extract, sql, 1)| 操作 | 函数方式 | QuickDao 方式 |
|---|---|---|
| 插入 | daog.Insert(tc, user, dal.UserInfoMeta) |
dal.UserInfoDao.Insert(tc, user) |
| 主键查询 | daog.GetById(tc, id, dal.UserInfoMeta) |
dal.UserInfoDao.GetById(tc, id) |
| 条件查询 | daog.QueryListMatcher(tc, m, dal.UserInfoMeta) |
dal.UserInfoDao.QueryListMatcher(tc, m) |
| 整体更新 | daog.Update(tc, user, dal.UserInfoMeta) |
dal.UserInfoDao.Update(tc, user) |
| 条件更新 | daog.UpdateByModifier(tc, mod, m, dal.UserInfoMeta) |
dal.UserInfoDao.UpdateByModifier(tc, mod, m) |
| 删除 | daog.DeleteById(tc, id, dal.UserInfoMeta) |
dal.UserInfoDao.DeleteById(tc, id) |
| 统计 | daog.Count(tc, m, dal.UserInfoMeta) |
dal.UserInfoDao.Count(tc, m) |