SkillAgentSearch skills...

Osm

The lightweight Go language ORM tool uses only the Go standard library to implement SQL templates, data introspection, and supports MySQL and PostgreSQL.

Install / Use

/learn @yinshuwei/Osm
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

OSM - Object SQL Mapping

English | 简体中文

osm (Object SQL Mapping) 是用 Go 编写的轻量级 SQL 工具库,已在生产环境中广泛使用。

支持的数据库: MySQL、PostgreSQL、SQL Server、SQLite、Oracle、TiDB、CockroachDB、ClickHouse

✨ 核心理念

提供极简且优雅的 SQL 操作接口,让数据库操作更加简单直观:

// 链式调用风格
users, err := o.Select("SELECT * FROM users WHERE age > #{Age}", 18).Structs(&users)

// 传统风格
count, err := o.SelectStructs("SELECT * FROM users WHERE age > #{Age}", 18)(&users)

🚀 核心特性

零依赖

  • 仅依赖 Go 标准库,无第三方依赖
  • 轻量级设计,易于集成和维护

灵活的参数绑定

使用 #{ParamName} 语法进行参数绑定,支持多种参数类型:

  • 顺序参数: 按参数顺序自动匹配
  • Map 参数: 支持 map[string]interface{}
  • Struct 参数: 直接使用结构体作为参数
  • IN 查询: 原生支持 SQL IN 语句

原生 SQL 占位符支持

除了 Named 参数绑定(#{ParamName})之外,osm 还支持原生 SQL 占位符,以获得更好的性能:

  • MySQL 原生占位符: 使用 ? 占位符
  • PostgreSQL 原生占位符: 使用 $1, $2 等占位符
  • 自动检测: 根据 SQL 内容自动判断使用哪种模式
  • 高性能: 原生占位符比 Named 参数快 10-12 倍

MySQL 原生占位符示例:

// MySQL 原生占位符
var users []User
_, err := o.Select("SELECT * FROM users WHERE id = ? AND status = ?", 1, "active").Structs(&users)

// IN 查询使用原生占位符
var users []User
_, err := o.Select("SELECT * FROM users WHERE id IN (?,?,?)", 1, 2, 3).Structs(&users)

PostgreSQL 原生占位符示例:

// PostgreSQL 原生占位符
var users []User
_, err := o.Select("SELECT * FROM users WHERE id = $1 AND status = $2", 1, "active").Structs(&users)

// IN 查询使用原生占位符
var users []User
_, err := o.Select("SELECT * FROM users WHERE id IN ($1, $2, $3)", 1, 2, 3).Structs(&users)

检测机制:

  • 如果 SQL 中包含 #{,则使用 Named 参数模式(向后兼容)
  • 否则使用 原生占位符模式(直接将参数传递给数据库驱动)
  • 无需配置,完全自动

性能对比:

| 模式 | 性能 | 内存使用 | 分配次数 | |------|------------|--------------|--------| | 原生占位符 | ~100 ns/op | 160 B/op | 4 allocs/op | | Named 参数 | ~1100 ns/op | 2346 B/op | 33 allocs/op |

注意: 原生占位符绕过参数解析,直接将参数传递给数据库驱动,因此性能显著提升。在不需要 Named 参数灵活性的场景下,推荐使用原生占位符。

数据库占位符格式

不同的数据库使用不同的原生占位符格式,osm 会自动根据数据库类型生成正确的占位符:

| 数据库 | 占位符格式 | 示例 | 驱动名称 | |--------|-----------|------|---------| | MySQL | ? | SELECT * FROM users WHERE id = ? | mysql | | SQLite | ? | SELECT * FROM users WHERE id = ? | sqlite3 | | PostgreSQL | $1, $2 | SELECT * FROM users WHERE id = $1 | postgres | | SQL Server | $1, $2 | SELECT * FROM users WHERE id = $1 | mssql | | Oracle | :1, :2 | SELECT * FROM users WHERE id = :1 | oracle, godror | | TiDB | ? | SELECT * FROM users WHERE id = ? | mysql (兼容) | | CockroachDB | $1, $2 | SELECT * FROM users WHERE id = $1 | postgres (兼容) | | ClickHouse | $1, $2 | SELECT * FROM users WHERE id = $1 | clickhouse |

使用 Named 参数时无需关心占位符格式

当使用 #{ParamName} 语法时,osm 会自动根据数据库类型转换为正确的占位符格式,你无需关心底层数据库的差异。

// 这段代码在所有数据库上都能正常工作
var users []User
_, err := o.Select("SELECT * FROM users WHERE id = #{Id}", 1).Structs(&users)

// osm 会自动转换为各数据库的占位符格式:
// MySQL/SQLite/TiDB:   SELECT * FROM users WHERE id = ?
// PostgreSQL/CockroachDB/ClickHouse/MSSQL: SELECT * FROM users WHERE id = $1
// Oracle:              SELECT * FROM users WHERE id = :1

丰富的结果处理

支持多种数据接收方式,满足不同场景需求:

| 方法类型 | 说明 | 使用场景 | |---------|------|---------| | Value / Values | 单行/多行多列值 | 查询多列不同类型的值 | | Struct / Structs | 单行/多行结构体 | 对象映射 | | String / Strings | 单个/多个字符串 | 简单字段查询 | | Int / Ints | 单个/多个整数 | 统计查询 | | Float64 / Float64s | 单个/多个浮点数 | 数值计算 | | Bool / Bools | 单个/多个布尔值 | 状态标识 | | Kvs | 键值对映射 | 双列数据 → Map | | ColumnsAndData | 列名 + 数据行 | 数据交换/导出 |

智能的 Struct 映射

  • 优先读取 db 标签
  • 智能的字段名转换(支持常见缩写词,如 ID、URL、HTTP 等)
  • 支持嵌套结构体
  • 支持指针类型(可表示 NULL)
  • 查看完整的字段映射规则

SQL 占位符替换

支持在 SQL 语句中使用占位符,在运行时自动替换为配置的值。这对于以下场景特别有用:

  • 表前缀替换: 在表名前添加统一前缀
  • 数据库 Schema 切换: 根据环境动态切换数据库 schema
  • 环境标识: 在 SQL 中插入环境相关的标识

配置示例:

o, err := osm.New("mysql", "root:123456@/test?charset=utf8", osm.Options{
    SQLReplacements: map[string]string{
        "[TablePrefix]": "data_",   // 表前缀
        "[Schema]":      "prod",     // 数据库schema
        "[Env]":         "prod",     // 环境标识
    },
})

// SQL 中的占位符会被自动替换
// SELECT * FROM [TablePrefix]users
// 实际执行: SELECT * FROM data_users

使用示例:

// 单表查询
o.Select("SELECT * FROM [TablePrefix]users WHERE id = #{Id}", 1)

// 多表 JOIN
o.Select("SELECT * FROM [Schema].[TablePrefix]users u JOIN [TablePrefix]orders o ON u.id = o.user_id")

// 环境相关的条件
o.Select("SELECT * FROM [TablePrefix]config WHERE env = '[Env]'")

特性:

  • ✅ 性能高效:仅增加约 174ns 的开销
  • ✅ 零配置零开销:未配置时完全不影响性能
  • ✅ 支持多占位符:可同时替换多个不同的占位符
  • ✅ 支持重复占位符:同一个 SQL 中可以多次使用同一个占位符
  • ✅ 执行前替换:替换发生在参数解析之前,不影响 #{...} 参数绑定

📦 安装

go get github.com/yinshuwei/osm/v2

go.mod:

require (
    github.com/yinshuwei/osm/v2 v2.0.8
)

📖 API 文档

完整文档请访问: https://pkg.go.dev/github.com/yinshuwei/osm/v2

🔗 链式调用 API

osm 支持优雅的链式调用,通过 Select() 方法返回 SelectResult 对象,可灵活选择结果处理方式。

快速开始

// 查询结构体列表
var users []User
_, err := o.Select("SELECT * FROM users WHERE id > #{Id}", 1).Structs(&users)

// 查询单个值
count, err := o.Select("SELECT COUNT(*) FROM users").Int()

// 查询字符串
email, err := o.Select("SELECT email FROM users WHERE id = #{Id}", 1).String()

// 查询多列不同类型的值
var id int64
var username string
_, err := o.Select("SELECT id, username FROM users WHERE id = #{Id}", 1).Value(&id, &username)

完整方法列表

1. Struct 和 Structs - 结构体查询

Struct - 查询单行数据并存入struct

var user User
_, err := o.Select(`SELECT * FROM users WHERE id = #{Id}`, 1).Struct(&user)

Structs - 查询多行数据并存入struct切片

var users []User
_, err := o.Select(`SELECT * FROM users`).Structs(&users)

2. Value 和 Values - 多列值查询

Value - 查询单行多列的值

var id int64
var email string
_, err := o.Select(`SELECT id, email FROM users WHERE id = #{Id}`, 1).Value(&id, &email)

Values - 查询多行多列的值

var ids []int64
var emails []string
_, err := o.Select(`SELECT id, email FROM users`).Values(&ids, &emails)

3. Kvs - 键值对查询

查询多行两列数据并存入map,第一列作为key,第二列作为value

var idEmailMap = map[int64]string{}
_, err := o.Select(`SELECT id, email FROM users`).Kvs(&idEmailMap)

4. ColumnsAndData - 列名和数据查询

查询多行数据,返回列名和数据(常用于数据交换)

columns, datas, err := o.Select(`SELECT id, email FROM users`).ColumnsAndData()
// columns 为 []string
// datas 为 [][]string

5. String 和 Strings - 字符串查询

String - 查询单个字符串值

email, err := o.Select(`SELECT email FROM users WHERE id = #{Id}`, 1).String()

Strings - 查询多个字符串值

emails, err := o.Select(`SELECT email FROM users`).Strings()

6. Int 和 Ints - 整数查询

Int - 查询单个int值

count, err := o.Select(`SELECT COUNT(*) FROM users`).Int()

Ints - 查询多个int值

ages, err := o.Select(`SELECT age FROM users`).Ints()

7. Int32 和 Int32s - 32位整数查询

Int32 - 查询单个int32值

count, err := o.Select(`SELECT count FROM table WHERE id = #{Id}`, 1).Int32()

Int32s - 查询多个int32值

counts, err := o.Select(`SELECT count FROM table`).Int32s()

8. Int64 和 Int64s - 64位整数查询

Int64 - 查询单个int64值

id, err := o.Select(`SELECT id FROM users WHERE email = #{Email}`, "test@example.com").Int64()

Int64s - 查询多个int64值

ids, err := o.Select(`SELECT id FROM users`).Int64s()

9. Uint 和 Uints - 无符号整数查询

Uint - 查询单个uint值

count, err := o.Select(`SELECT COUNT(*) FROM users`).Uint()

Uints - 查询多个uint值

counts, err := o.Select(`SELECT count FROM table`).Uints()

10. Uint64 和 Uint64s - 64位无符号整数查询

Uint64 - 查询单个uint64值

id, err := o.Select(`SELECT id FROM users WHERE email = #{Email}`, "test@example.com").Uint64()

Uint64s - 查询多个uint64值

ids, err := o.Select(`SELECT id FROM users`).Uint64s()

11. Float32 和 Float32s - 32位浮点数查询

Float32 - 查询单个float32值

price, err := o.Select(`SELECT price FROM products WHERE id = #{Id}`, 1).Float32()

Float32s - 查询多个float32值

prices, err := o.Select(`SELECT price FROM products`).Float32s()

12. Float64 和 Float64s - 64位浮点数查询

Float64 - 查询单个float64值

avg, err := o.Select(`SELECT AVG(score) FROM users`).Float64()

Float64s - 查询多个float64值

scores, err := o.Select(`SELECT score FROM users`).Float64s()

13. Bool 和 Bools - 布尔值查询

Bool - 查询单个布尔值

isActive, err := o.Select(`SELECT is_active FROM users WHERE id = #{Id}`, 1).Bool()

Bools - 查询多个布尔值

statuses, err := o.Select(`SELECT is_active FROM users`).Bools()

📊 方法分类总结

| 数据类型 | 单值方法 | 多值方法 | 典型用途 | |---------|---------|---------|---------| | 通用多列 | Value() | Values() | 查询多列不同类型的值 | | 字符串 | String() | Strings() | 名称、邮箱等文本字段 | | 整数 | Int() | Ints() | 计数、年龄等整数 | | 32位整数 | Int32() | Int32s() | 小范围整数 | | 64位整数 | Int64() | Int64s() | ID、大整数 | | 无符号整数 | Uint() | Uints() | 正整数 | | 64位无符号 | Uint64() | Uint64s() | 大范围正整数 | | 32位浮点 | Float32() | Float32s() | 价格、比率等小精度 | | 64位浮点 | Float64() | Float64s() | 科学计算、高精度数值 | | 布尔值 | Bool() | Bools() | 状态标识、开关 | | 结构体 | Struct() | Structs() | 完整对象映射 | | 键值对 | - | Kvs() | 双列数据 → Map | | 通用数据 | - | ColumnsAndData() | 数据导出、交换 |

⚠️ 重要说明

  • 多列查询: Value()Values() 方法支持查询多列不同类型的值,适用于查询不同数据类型的多个字段
  • 零值处理: 单值方法在无结果时返回类型零值(0, "", false
  • 空切片: 多值方法在无结果时返回空切片 []
  • 数据交换: ColumnsAndData() 返回的数据全部为字符串类型,适合跨语言数据交换
  • 键值对: Kvs() 要求查询结果必须是两列(第一列为key,第二列为value)

🔄 事务支持

osm 提供了完整的事务支持,包括传统方式和闭包方式两种使用模式。

传统方式

传统方式需要手动开启事务、提交或回滚:

// 开启事务
tx, err := o.Begin()
if err != nil {
    return err
}

// 执行插入操作
user := User{
    EmailStruct: EmailStruct{Email: "test@foxmail.com"},
    Nickname:   "haha",
    CreateTime: time.Now(),
}
insertID, count, err := tx.Insert("INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});", user)
if err != nil {
    tx.Rollback()  // 发生错误时回滚
    return err
}

// 执行更新操作
count, err = tx.Update("UPDATE user SET nickname=#{Nickname} WHERE id=#{ID}", "hello", insertID)
if err != nil {
    tx.Rollback()  // 发生错误时回滚
    return err
}

// 
View on GitHub
GitHub Stars52
CategoryData
Updated1mo ago
Forks10

Languages

Go

Security Score

100/100

Audited on Mar 5, 2026

No findings