{"id":34193583,"url":"https://github.com/yinshuwei/osm","last_synced_at":"2026-03-10T20:04:13.918Z","repository":{"id":17277290,"uuid":"20047239","full_name":"yinshuwei/osm","owner":"yinshuwei","description":"The lightweight Go language ORM tool uses only the Go standard library to implement SQL templates, data introspection, and supports MySQL and PostgreSQL.","archived":false,"fork":false,"pushed_at":"2026-02-26T08:00:17.000Z","size":224,"stargazers_count":52,"open_issues_count":0,"forks_count":10,"subscribers_count":6,"default_branch":"main","last_synced_at":"2026-02-26T13:09:42.898Z","etag":null,"topics":["database","db","go","orm","sql-mapping","sqlmap"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yinshuwei.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2014-05-22T04:01:25.000Z","updated_at":"2026-02-26T08:00:20.000Z","dependencies_parsed_at":"2026-02-26T10:04:07.899Z","dependency_job_id":null,"html_url":"https://github.com/yinshuwei/osm","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/yinshuwei/osm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yinshuwei%2Fosm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yinshuwei%2Fosm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yinshuwei%2Fosm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yinshuwei%2Fosm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yinshuwei","download_url":"https://codeload.github.com/yinshuwei/osm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yinshuwei%2Fosm/sbom","scorecard":{"id":1239063,"data":{"date":"2025-10-13","repo":{"name":"github.com/yinshuwei/osm","commit":"acbe54626a6fd4d64bb4bf2fffadbe52f87f698a"},"scorecard":{"version":"v5.3.1-0.20251012233417-af419a5f4ac8","commit":"af419a5f4ac865788a844e5a2e726681cf4ff42b"},"score":3,"checks":[{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 1/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'","Warn: branch protection not enabled for branch 'zap'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 3 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/af419a5f4ac865788a844e5a2e726681cf4ff42b/docs/checks.md#sast"}}]},"last_synced_at":"2025-10-22T04:27:54.461Z","repository_id":17277290,"created_at":"2025-10-22T04:27:54.461Z","updated_at":"2025-10-22T04:27:54.461Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30351833,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:55:29.454Z","status":"ssl_error","status_checked_at":"2026-03-10T15:54:58.440Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","db","go","orm","sql-mapping","sqlmap"],"created_at":"2025-12-15T16:41:10.449Z","updated_at":"2026-03-10T20:04:13.908Z","avatar_url":"https://github.com/yinshuwei.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OSM - Object SQL Mapping\n\n[English](./README_EN.md) | 简体中文\n\nosm (Object SQL Mapping) 是用 Go 编写的轻量级 SQL 工具库，已在生产环境中广泛使用。\n\n**支持的数据库:** MySQL、PostgreSQL、SQL Server、SQLite、Oracle、TiDB、CockroachDB、ClickHouse\n\n## ✨ 核心理念\n\n提供极简且优雅的 SQL 操作接口，让数据库操作更加简单直观：\n\n```go\n// 链式调用风格\nusers, err := o.Select(\"SELECT * FROM users WHERE age \u003e #{Age}\", 18).Structs(\u0026users)\n\n// 传统风格\ncount, err := o.SelectStructs(\"SELECT * FROM users WHERE age \u003e #{Age}\", 18)(\u0026users)\n```\n\n## 🚀 核心特性\n\n### 零依赖\n- 仅依赖 Go 标准库，无第三方依赖\n- 轻量级设计，易于集成和维护\n\n### 灵活的参数绑定\n\n使用 `#{ParamName}` 语法进行参数绑定，支持多种参数类型：\n\n- **顺序参数**: 按参数顺序自动匹配\n- **Map 参数**: 支持 `map[string]interface{}`\n- **Struct 参数**: 直接使用结构体作为参数\n- **IN 查询**: 原生支持 SQL IN 语句\n\n### 原生 SQL 占位符支持\n\n除了 Named 参数绑定（`#{ParamName}`）之外，osm 还支持原生 SQL 占位符，以获得更好的性能：\n\n- **MySQL 原生占位符**: 使用 `?` 占位符\n- **PostgreSQL 原生占位符**: 使用 `$1`, `$2` 等占位符\n- **自动检测**: 根据 SQL 内容自动判断使用哪种模式\n- **高性能**: 原生占位符比 Named 参数快 10-12 倍\n\n**MySQL 原生占位符示例:**\n\n```go\n// MySQL 原生占位符\nvar users []User\n_, err := o.Select(\"SELECT * FROM users WHERE id = ? AND status = ?\", 1, \"active\").Structs(\u0026users)\n\n// IN 查询使用原生占位符\nvar users []User\n_, err := o.Select(\"SELECT * FROM users WHERE id IN (?,?,?)\", 1, 2, 3).Structs(\u0026users)\n```\n\n**PostgreSQL 原生占位符示例:**\n\n```go\n// PostgreSQL 原生占位符\nvar users []User\n_, err := o.Select(\"SELECT * FROM users WHERE id = $1 AND status = $2\", 1, \"active\").Structs(\u0026users)\n\n// IN 查询使用原生占位符\nvar users []User\n_, err := o.Select(\"SELECT * FROM users WHERE id IN ($1, $2, $3)\", 1, 2, 3).Structs(\u0026users)\n```\n\n**检测机制:**\n\n- 如果 SQL 中包含 `#{`，则使用 **Named 参数模式**（向后兼容）\n- 否则使用 **原生占位符模式**（直接将参数传递给数据库驱动）\n- 无需配置，完全自动\n\n**性能对比:**\n\n| 模式 | 性能 | 内存使用 | 分配次数 |\n|------|------------|--------------|--------|\n| 原生占位符 | ~100 ns/op | 160 B/op | 4 allocs/op |\n| Named 参数 | ~1100 ns/op | 2346 B/op | 33 allocs/op |\n\n**注意:** 原生占位符绕过参数解析，直接将参数传递给数据库驱动，因此性能显著提升。在不需要 Named 参数灵活性的场景下，推荐使用原生占位符。\n\n### 数据库占位符格式\n\n不同的数据库使用不同的原生占位符格式，osm 会自动根据数据库类型生成正确的占位符：\n\n| 数据库 | 占位符格式 | 示例 | 驱动名称 |\n|--------|-----------|------|---------|\n| MySQL | `?` | `SELECT * FROM users WHERE id = ?` | `mysql` |\n| SQLite | `?` | `SELECT * FROM users WHERE id = ?` | `sqlite3` |\n| PostgreSQL | `$1`, `$2` | `SELECT * FROM users WHERE id = $1` | `postgres` |\n| SQL Server | `$1`, `$2` | `SELECT * FROM users WHERE id = $1` | `mssql` |\n| Oracle | `:1`, `:2` | `SELECT * FROM users WHERE id = :1` | `oracle`, `godror` |\n| TiDB | `?` | `SELECT * FROM users WHERE id = ?` | `mysql` (兼容) |\n| CockroachDB | `$1`, `$2` | `SELECT * FROM users WHERE id = $1` | `postgres` (兼容) |\n| ClickHouse | `$1`, `$2` | `SELECT * FROM users WHERE id = $1` | `clickhouse` |\n\n**使用 Named 参数时无需关心占位符格式**：\n\n当使用 `#{ParamName}` 语法时，osm 会自动根据数据库类型转换为正确的占位符格式，你无需关心底层数据库的差异。\n\n```go\n// 这段代码在所有数据库上都能正常工作\nvar users []User\n_, err := o.Select(\"SELECT * FROM users WHERE id = #{Id}\", 1).Structs(\u0026users)\n\n// osm 会自动转换为各数据库的占位符格式：\n// MySQL/SQLite/TiDB:   SELECT * FROM users WHERE id = ?\n// PostgreSQL/CockroachDB/ClickHouse/MSSQL: SELECT * FROM users WHERE id = $1\n// Oracle:              SELECT * FROM users WHERE id = :1\n```\n\n### 丰富的结果处理\n\n支持多种数据接收方式，满足不同场景需求：\n\n| 方法类型 | 说明 | 使用场景 |\n|---------|------|---------|\n| `Value` / `Values` | 单行/多行多列值 | 查询多列不同类型的值 |\n| `Struct` / `Structs` | 单行/多行结构体 | 对象映射 |\n| `String` / `Strings` | 单个/多个字符串 | 简单字段查询 |\n| `Int` / `Ints` | 单个/多个整数 | 统计查询 |\n| `Float64` / `Float64s` | 单个/多个浮点数 | 数值计算 |\n| `Bool` / `Bools` | 单个/多个布尔值 | 状态标识 |\n| `Kvs` | 键值对映射 | 双列数据 → Map |\n| `ColumnsAndData` | 列名 + 数据行 | 数据交换/导出 |\n\n### 智能的 Struct 映射\n\n- 优先读取 `db` 标签\n- 智能的字段名转换（支持常见缩写词，如 ID、URL、HTTP 等）\n- 支持嵌套结构体\n- 支持指针类型（可表示 NULL）\n- [查看完整的字段映射规则](#field_column_mapping)\n\n### SQL 占位符替换\n\n支持在 SQL 语句中使用占位符，在运行时自动替换为配置的值。这对于以下场景特别有用：\n\n- **表前缀替换**: 在表名前添加统一前缀\n- **数据库 Schema 切换**: 根据环境动态切换数据库 schema\n- **环境标识**: 在 SQL 中插入环境相关的标识\n\n**配置示例:**\n\n```go\no, err := osm.New(\"mysql\", \"root:123456@/test?charset=utf8\", osm.Options{\n    SQLReplacements: map[string]string{\n        \"[TablePrefix]\": \"data_\",   // 表前缀\n        \"[Schema]\":      \"prod\",     // 数据库schema\n        \"[Env]\":         \"prod\",     // 环境标识\n    },\n})\n\n// SQL 中的占位符会被自动替换\n// SELECT * FROM [TablePrefix]users\n// 实际执行: SELECT * FROM data_users\n```\n\n**使用示例:**\n\n```go\n// 单表查询\no.Select(\"SELECT * FROM [TablePrefix]users WHERE id = #{Id}\", 1)\n\n// 多表 JOIN\no.Select(\"SELECT * FROM [Schema].[TablePrefix]users u JOIN [TablePrefix]orders o ON u.id = o.user_id\")\n\n// 环境相关的条件\no.Select(\"SELECT * FROM [TablePrefix]config WHERE env = '[Env]'\")\n```\n\n**特性:**\n- ✅ 性能高效：仅增加约 174ns 的开销\n- ✅ 零配置零开销：未配置时完全不影响性能\n- ✅ 支持多占位符：可同时替换多个不同的占位符\n- ✅ 支持重复占位符：同一个 SQL 中可以多次使用同一个占位符\n- ✅ 执行前替换：替换发生在参数解析之前，不影响 `#{...}` 参数绑定\n\n## 📦 安装\n\n```bash\ngo get github.com/yinshuwei/osm/v2\n```\n\n**go.mod:**\n```go\nrequire (\n    github.com/yinshuwei/osm/v2 v2.0.8\n)\n```\n\n## 📖 API 文档\n\n完整文档请访问: https://pkg.go.dev/github.com/yinshuwei/osm/v2\n\n## 🔗 链式调用 API\n\nosm 支持优雅的链式调用，通过 `Select()` 方法返回 `SelectResult` 对象，可灵活选择结果处理方式。\n\n### 快速开始\n\n```go\n// 查询结构体列表\nvar users []User\n_, err := o.Select(\"SELECT * FROM users WHERE id \u003e #{Id}\", 1).Structs(\u0026users)\n\n// 查询单个值\ncount, err := o.Select(\"SELECT COUNT(*) FROM users\").Int()\n\n// 查询字符串\nemail, err := o.Select(\"SELECT email FROM users WHERE id = #{Id}\", 1).String()\n\n// 查询多列不同类型的值\nvar id int64\nvar username string\n_, err := o.Select(\"SELECT id, username FROM users WHERE id = #{Id}\", 1).Value(\u0026id, \u0026username)\n```\n\n### 完整方法列表\n\n#### 1. Struct 和 Structs - 结构体查询\n\n**Struct** - 查询单行数据并存入struct\n\n```go\nvar user User\n_, err := o.Select(`SELECT * FROM users WHERE id = #{Id}`, 1).Struct(\u0026user)\n```\n\n**Structs** - 查询多行数据并存入struct切片\n\n```go\nvar users []User\n_, err := o.Select(`SELECT * FROM users`).Structs(\u0026users)\n```\n\n#### 2. Value 和 Values - 多列值查询\n\n**Value** - 查询单行多列的值\n\n```go\nvar id int64\nvar email string\n_, err := o.Select(`SELECT id, email FROM users WHERE id = #{Id}`, 1).Value(\u0026id, \u0026email)\n```\n\n**Values** - 查询多行多列的值\n\n```go\nvar ids []int64\nvar emails []string\n_, err := o.Select(`SELECT id, email FROM users`).Values(\u0026ids, \u0026emails)\n```\n\n#### 3. Kvs - 键值对查询\n\n查询多行两列数据并存入map，第一列作为key，第二列作为value\n\n```go\nvar idEmailMap = map[int64]string{}\n_, err := o.Select(`SELECT id, email FROM users`).Kvs(\u0026idEmailMap)\n```\n\n#### 4. ColumnsAndData - 列名和数据查询\n\n查询多行数据，返回列名和数据（常用于数据交换）\n\n```go\ncolumns, datas, err := o.Select(`SELECT id, email FROM users`).ColumnsAndData()\n// columns 为 []string\n// datas 为 [][]string\n```\n\n#### 5. String 和 Strings - 字符串查询\n\n**String** - 查询单个字符串值\n\n```go\nemail, err := o.Select(`SELECT email FROM users WHERE id = #{Id}`, 1).String()\n```\n\n**Strings** - 查询多个字符串值\n\n```go\nemails, err := o.Select(`SELECT email FROM users`).Strings()\n```\n\n#### 6. Int 和 Ints - 整数查询\n\n**Int** - 查询单个int值\n\n```go\ncount, err := o.Select(`SELECT COUNT(*) FROM users`).Int()\n```\n\n**Ints** - 查询多个int值\n\n```go\nages, err := o.Select(`SELECT age FROM users`).Ints()\n```\n\n#### 7. Int32 和 Int32s - 32位整数查询\n\n**Int32** - 查询单个int32值\n\n```go\ncount, err := o.Select(`SELECT count FROM table WHERE id = #{Id}`, 1).Int32()\n```\n\n**Int32s** - 查询多个int32值\n\n```go\ncounts, err := o.Select(`SELECT count FROM table`).Int32s()\n```\n\n#### 8. Int64 和 Int64s - 64位整数查询\n\n**Int64** - 查询单个int64值\n\n```go\nid, err := o.Select(`SELECT id FROM users WHERE email = #{Email}`, \"test@example.com\").Int64()\n```\n\n**Int64s** - 查询多个int64值\n\n```go\nids, err := o.Select(`SELECT id FROM users`).Int64s()\n```\n\n#### 9. Uint 和 Uints - 无符号整数查询\n\n**Uint** - 查询单个uint值\n\n```go\ncount, err := o.Select(`SELECT COUNT(*) FROM users`).Uint()\n```\n\n**Uints** - 查询多个uint值\n\n```go\ncounts, err := o.Select(`SELECT count FROM table`).Uints()\n```\n\n#### 10. Uint64 和 Uint64s - 64位无符号整数查询\n\n**Uint64** - 查询单个uint64值\n\n```go\nid, err := o.Select(`SELECT id FROM users WHERE email = #{Email}`, \"test@example.com\").Uint64()\n```\n\n**Uint64s** - 查询多个uint64值\n\n```go\nids, err := o.Select(`SELECT id FROM users`).Uint64s()\n```\n\n#### 11. Float32 和 Float32s - 32位浮点数查询\n\n**Float32** - 查询单个float32值\n\n```go\nprice, err := o.Select(`SELECT price FROM products WHERE id = #{Id}`, 1).Float32()\n```\n\n**Float32s** - 查询多个float32值\n\n```go\nprices, err := o.Select(`SELECT price FROM products`).Float32s()\n```\n\n#### 12. Float64 和 Float64s - 64位浮点数查询\n\n**Float64** - 查询单个float64值\n\n```go\navg, err := o.Select(`SELECT AVG(score) FROM users`).Float64()\n```\n\n**Float64s** - 查询多个float64值\n\n```go\nscores, err := o.Select(`SELECT score FROM users`).Float64s()\n```\n\n#### 13. Bool 和 Bools - 布尔值查询\n\n**Bool** - 查询单个布尔值\n\n```go\nisActive, err := o.Select(`SELECT is_active FROM users WHERE id = #{Id}`, 1).Bool()\n```\n\n**Bools** - 查询多个布尔值\n\n```go\nstatuses, err := o.Select(`SELECT is_active FROM users`).Bools()\n```\n\n### 📊 方法分类总结\n\n| 数据类型 | 单值方法 | 多值方法 | 典型用途 |\n|---------|---------|---------|---------|\n| **通用多列** | `Value()` | `Values()` | 查询多列不同类型的值 |\n| 字符串 | `String()` | `Strings()` | 名称、邮箱等文本字段 |\n| 整数 | `Int()` | `Ints()` | 计数、年龄等整数 |\n| 32位整数 | `Int32()` | `Int32s()` | 小范围整数 |\n| 64位整数 | `Int64()` | `Int64s()` | ID、大整数 |\n| 无符号整数 | `Uint()` | `Uints()` | 正整数 |\n| 64位无符号 | `Uint64()` | `Uint64s()` | 大范围正整数 |\n| 32位浮点 | `Float32()` | `Float32s()` | 价格、比率等小精度 |\n| 64位浮点 | `Float64()` | `Float64s()` | 科学计算、高精度数值 |\n| 布尔值 | `Bool()` | `Bools()` | 状态标识、开关 |\n| 结构体 | `Struct()` | `Structs()` | 完整对象映射 |\n| 键值对 | - | `Kvs()` | 双列数据 → Map |\n| 通用数据 | - | `ColumnsAndData()` | 数据导出、交换 |\n\n### ⚠️ 重要说明\n\n- **多列查询**: `Value()` 和 `Values()` 方法支持查询多列不同类型的值，适用于查询不同数据类型的多个字段\n- **零值处理**: 单值方法在无结果时返回类型零值（`0`, `\"\"`, `false`）\n- **空切片**: 多值方法在无结果时返回空切片 `[]`\n- **数据交换**: `ColumnsAndData()` 返回的数据全部为字符串类型，适合跨语言数据交换\n- **键值对**: `Kvs()` 要求查询结果必须是两列（第一列为key，第二列为value）\n\n## 🔄 事务支持\n\nosm 提供了完整的事务支持，包括传统方式和闭包方式两种使用模式。\n\n### 传统方式\n\n传统方式需要手动开启事务、提交或回滚：\n\n```go\n// 开启事务\ntx, err := o.Begin()\nif err != nil {\n    return err\n}\n\n// 执行插入操作\nuser := User{\n    EmailStruct: EmailStruct{Email: \"test@foxmail.com\"},\n    Nickname:   \"haha\",\n    CreateTime: time.Now(),\n}\ninsertID, count, err := tx.Insert(\"INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});\", user)\nif err != nil {\n    tx.Rollback()  // 发生错误时回滚\n    return err\n}\n\n// 执行更新操作\ncount, err = tx.Update(\"UPDATE user SET nickname=#{Nickname} WHERE id=#{ID}\", \"hello\", insertID)\nif err != nil {\n    tx.Rollback()  // 发生错误时回滚\n    return err\n}\n\n// 提交事务\nerr = tx.Commit()\nif err != nil {\n    return err\n}\n```\n\n### 闭包方式（推荐）\n\n闭包方式更加简洁，自动处理 commit 和 rollback：\n\n```go\nerr := o.Transaction(func(tx *Tx) error {\n    // 执行插入操作\n    user := User{\n        EmailStruct: EmailStruct{Email: \"test@foxmail.com\"},\n        Nickname:   \"haha\",\n        CreateTime: time.Now(),\n    }\n    insertID, _, err := tx.Insert(\"INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});\", user)\n    if err != nil {\n        return err  // 返回错误会自动 rollback\n    }\n\n    // 执行更新操作\n    _, err = tx.Update(\"UPDATE user SET nickname=#{Nickname} WHERE id=#{ID}\", \"hello\", insertID)\n    if err != nil {\n        return err  // 返回错误会自动 rollback\n    }\n\n    // 查询操作\n    var result User\n    _, err = tx.Select(\"SELECT * FROM user WHERE id = #{ID}\", insertID).Struct(\u0026result)\n    if err != nil {\n        return err  // 返回错误会自动 rollback\n    }\n\n    return nil  // 返回 nil 会自动 commit\n})\nif err != nil {\n    logger.Error(\"transaction error\", zap.Error(err))\n}\n```\n\n**闭包方式的优势：**\n\n- ✅ **自动提交**: 闭包函数返回 `nil` 时自动执行 `Commit()`\n- ✅ **自动回滚**: 闭包函数返回 `error` 时自动执行 `Rollback()`\n- ✅ **异常安全**: 即使闭包中发生 `panic`，也会先执行 `Rollback()` 再抛出 panic\n- ✅ **简化代码**: 无需在每次操作后检查错误并手动回滚\n- ✅ **保持一致性**: 闭包中直接使用 `*Tx` 对象，API 与传统方式完全一致\n\n**事务中支持的操作：**\n\n在事务中（无论是传统方式还是闭包方式），都可以使用所有数据库操作方法：\n\n- **查询操作**: `Select()`, `SelectStruct()`, `SelectStructs()`, `SelectValue()`, `SelectValues()`, `SelectKVS()`, `SelectStrings()` 等\n- **写操作**: `Insert()`, `Update()`, `UpdateMulti()`, `Delete()`\n- **链式调用**: 所有 `Select()` 返回的 `SelectResult` 方法（`.Struct()`, `.Int()`, `.String()` 等）\n\n## 💡 完整示例\n\n### 数据库准备\n\n```sql\nCREATE DATABASE test;\nUSE test;\n\nCREATE TABLE `user` (\n  `id` int(11) NOT NULL AUTO_INCREMENT,\n  `email` varchar(255) DEFAULT NULL,\n  `nickname` varchar(45) DEFAULT NULL,\n  `create_time` varchar(255) DEFAULT NULL,\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';\n```\n\n### 示例代码\n\n**基础示例 (osm_demo.go)**\n\n```go\npackage main\n\nimport (\n    \"encoding/json\"\n    \"fmt\"\n    \"time\"\n\n    _ \"github.com/go-sql-driver/mysql\"\n    \"github.com/yinshuwei/osm/v2\"\n    \"go.uber.org/zap\"\n)\n\n// InfoLogger 适配zap logger\ntype InfoLogger struct {\n\tzapLogger *zap.Logger\n}\n\n// WarnLoggor 适配zap logger\ntype WarnLoggor struct {\n\tzapLogger *zap.Logger\n}\n\n// ErrorLogger 适配zap logger\ntype ErrorLogger struct {\n\tzapLogger *zap.Logger\n}\n\nfunc loggerFields(data map[string]string) []zap.Field {\n\tvar fields []zap.Field\n\tfor key, val := range data {\n\t\tfields = append(fields, zap.String(key, val))\n\t}\n\treturn fields\n}\n\nfunc (l *ErrorLogger) Log(msg string, data map[string]string) {\n\tif l == nil || l.zapLogger == nil {\n\t\treturn\n\t}\n\tl.zapLogger.Error(msg, loggerFields(data)...)\n}\n\nfunc (l *InfoLogger) Log(msg string, data map[string]string) {\n\tif l == nil || l.zapLogger == nil {\n\t\treturn\n\t}\n\tl.zapLogger.Info(msg, loggerFields(data)...)\n}\n\nfunc (l *WarnLoggor) Log(msg string, data map[string]string) {\n\tif l == nil || l.zapLogger == nil {\n\t\treturn\n\t}\n\tl.zapLogger.Warn(msg, loggerFields(data)...)\n}\n\n// User 用户Model\ntype User struct {\n\tID         int64\n\tNickname   string `db:\"name\"`\n\tCreateTime time.Time\n\tEmailStruct // 匿名属性\n}\n\ntype EmailStruct struct {\n\tEmail string `db:\"email\"`\n}\n\nfunc main() {\n\tlogger, _ := zap.NewDevelopment()\n\to, err := osm.New(\"mysql\", \"root:123456@/test?charset=utf8\", osm.Options{\n\t\tMaxIdleConns:    0,                    // int\n\t\tMaxOpenConns:    0,                    // int\n\t\tConnMaxLifetime: 0,                    // time.Duration\n\t\tConnMaxIdleTime: 0,                    // time.Duration\n\t\tWarnLogger:      \u0026WarnLoggor{logger},  // Logger\n\t\tErrorLogger:     \u0026ErrorLogger{logger}, // Logger\n\t\tInfoLogger:      \u0026InfoLogger{logger},  // Logger\n\t\tShowSQL:         true,                 // bool\n\t\tSlowLogDuration: 0,                    // time.Duration\n\t\tSQLReplacements: map[string]string{    // SQL替换映射（可选）\n\t\t\t\"[TablePrefix]\": \"data_\",         // 表前缀\n\t\t\t\"[Schema]\":      \"prod\",          // 数据库schema\n\t\t},\n\t})\n\tif err != nil {\n\t\tfmt.Println(err.Error())\n\t}\n\n\t// 插入数据\n\tuser := User{\n\t\tEmailStruct: EmailStruct{\n\t\t\tEmail: \"test@foxmail.com\",\n\t\t},\n\t\tNickname:   \"haha\",\n\t\tCreateTime: time.Now(),\n\t}\n\tid, count, err := o.Insert(\"INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});\", user)\n\tif err != nil {\n\t\tlogger.Error(\"insert error\", zap.Error(err))\n\t}\n\tlogger.Info(\"test insert\", zap.Int64(\"id\", id), zap.Int64(\"count\", count))\n\n\t// 更新数据\n\tuser = User{\n\t\tEmailStruct: EmailStruct{\n\t\t\tEmail: \"test@foxmail.com\",\n\t\t},\n\t\tNickname: \"hello\",\n\t}\n\tcount, err = o.Update(\"UPDATE user SET nickname=#{name} WHERE email=#{Email}\", user)\n\tif err != nil {\n\t\tlogger.Error(\"update error\", zap.Error(err))\n\t}\n\tlogger.Info(\"test update\", zap.Int64(\"count\", count))\n\n\t// 查询数据\n\tuser = User{\n\t\tEmailStruct: EmailStruct{\n\t\t\tEmail: \"test@foxmail.com\",\n\t\t},\n\t}\n\tvar results []User\n\tcount, err = o.SelectStructs(\"SELECT id,email,nickname,create_time FROM user WHERE email=#{Email} or email=#{email};\", user)(\u0026results)\n\tif err != nil {\n\t\tlogger.Error(\"test select\", zap.Error(err))\n\t}\n\tresultBytes, _ := json.Marshal(results)\n\tlogger.Info(\"test select\", zap.Int64(\"count\", count), zap.ByteString(\"result\", resultBytes))\n\n\t// 删除数据\n\tcount, err = o.Delete(\"DELETE FROM user WHERE email=#{Email}\", user)\n\tif err != nil {\n\t\tlogger.Error(\"test delete\", zap.Error(err))\n\t}\n\tlogger.Info(\"test delete\", zap.Int64(\"count\", count))\n\n\t// 关闭连接\n\terr = o.Close()\n\tif err != nil {\n\t\tlogger.Error(\"close\", zap.Error(err))\n\t}\n}\n```\n\n**运行结果:**\n\n```log\n2025-01-13T16:16:41.301+0800    INFO    osmtt/main.go:47        main.go:95, readSQLParamsBySQL showSql  {\"dbParams\": \"[\\\"test@foxmail.com\\\",\\\"haha\\\",\\\"2025-01-13 16:16:41\\\"]\", \"sql\": \"INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});\", \"params\": \"{\\\"ID\\\":0,\\\"Nickname\\\":\\\"haha\\\",\\\"CreateTime\\\":\\\"2025-01-13T16:16:41.301032455+08:00\\\",\\\"Email\\\":\\\"test@foxmail.com\\\"}\", \"dbSql\": \"INSERT INTO user (email,nickname,create_time) VALUES (?,?,?);\"}\n2025-01-13T16:16:41.305+0800    INFO    osmtt/main.go:99        test insert     {\"id\": 11, \"count\": 1}\n2025-01-13T16:16:41.305+0800    INFO    osmtt/main.go:47        main.go:108, readSQLParamsBySQL showSql {\"dbParams\": \"[\\\"hello\\\",\\\"test@foxmail.com\\\"]\", \"sql\": \"UPDATE user SET nickname=#{name} WHERE email=#{Email}\", \"params\": \"{\\\"ID\\\":0,\\\"Nickname\\\":\\\"hello\\\",\\\"CreateTime\\\":\\\"0001-01-01T00:00:00Z\\\",\\\"Email\\\":\\\"test@foxmail.com\\\"}\", \"dbSql\": \"UPDATE user SET nickname=? WHERE email=?\"}\n2025-01-13T16:16:41.310+0800    INFO    osmtt/main.go:112       test update     {\"count\": 1}\n2025-01-13T16:16:41.310+0800    INFO    osmtt/main.go:47        main.go:121, readSQLParamsBySQL showSql {\"params\": \"{\\\"ID\\\":0,\\\"Nickname\\\":\\\"\\\",\\\"CreateTime\\\":\\\"0001-01-01T00:00:00Z\\\",\\\"Email\\\":\\\"test@foxmail.com\\\"}\", \"dbSql\": \"SELECT id,email,nickname,create_time FROM user WHERE email=? or email=?;\", \"dbParams\": \"[\\\"test@foxmail.com\\\",\\\"test@foxmail.com\\\"]\", \"sql\": \"SELECT id,email,nickname,create_time FROM user WHERE email=#{Email} or email=#{email};\"}\n2025-01-13T16:16:41.310+0800    INFO    osmtt/main.go:126       test select     {\"count\": 1, \"result\": \"[{\\\"ID\\\":11,\\\"Nickname\\\":\\\"hello\\\",\\\"CreateTime\\\":\\\"2025-01-13T16:16:41+08:00\\\",\\\"Email\\\":\\\"test@foxmail.com\\\"}]\"}\n2025-01-13T16:16:41.310+0800    INFO    osmtt/main.go:47        main.go:129readSQLParamsBySQL showSql   {\"dbParams\": \"[\\\"test@foxmail.com\\\"]\", \"sql\": \"DELETE FROM user WHERE email=#{Email}\", \"params\": \"{\\\"ID\\\":0,\\\"Nickname\\\":\\\"\\\",\\\"CreateTime\\\":\\\"0001-01-01T00:00:00Z\\\",\\\"Email\\\":\\\"test@foxmail.com\\\"}\", \"dbSql\": \"DELETE FROM user WHERE email=?\"}\n2025-01-13T16:16:41.313+0800    INFO    osmtt/main.go:133       test delete     {\"count\": 1}\n```\n\n### 指针类型示例\n\n**指针类型支持 NULL (osm_demo2.go)**\n\n```go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"time\"\n\n\t_ \"github.com/go-sql-driver/mysql\"\n\t\"github.com/yinshuwei/osm/v2\"\n\t\"go.uber.org/zap\"\n)\n\n// InfoLogger 适配zap logger\ntype InfoLogger struct {\n\tzapLogger *zap.Logger\n}\n\n// WarnLoggor 适配zap logger\ntype WarnLoggor struct {\n\tzapLogger *zap.Logger\n}\n\n// ErrorLogger 适配zap logger\ntype ErrorLogger struct {\n\tzapLogger *zap.Logger\n}\n\nfunc loggerFields(data map[string]string) []zap.Field {\n\tvar fields []zap.Field\n\tfor key, val := range data {\n\t\tfields = append(fields, zap.String(key, val))\n\t}\n\treturn fields\n}\n\nfunc (l *ErrorLogger) Log(msg string, data map[string]string) {\n\tif l == nil || l.zapLogger == nil {\n\t\treturn\n\t}\n\tl.zapLogger.Error(msg, loggerFields(data)...)\n}\n\nfunc (l *InfoLogger) Log(msg string, data map[string]string) {\n\tif l == nil || l.zapLogger == nil {\n\t\treturn\n\t}\n\tl.zapLogger.Info(msg, loggerFields(data)...)\n}\n\nfunc (l *WarnLoggor) Log(msg string, data map[string]string) {\n\tif l == nil || l.zapLogger == nil {\n\t\treturn\n\t}\n\tl.zapLogger.Warn(msg, loggerFields(data)...)\n}\n\n// User 用户Model\ntype User struct {\n\tID         *int64\n\tEmail      *string\n\tNickname   *string\n\tCreateTime *time.Time\n}\n\nfunc stringPoint(t string) *string {\n\treturn \u0026t\n}\n\nfunc timePoint(t time.Time) *time.Time {\n\treturn \u0026t\n}\n\nfunc main() {\n\tlogger, _ := zap.NewDevelopment()\n\to, err := osm.New(\"mysql\", \"root:123456@/test?charset=utf8\", osm.Options{\n\t\tMaxIdleConns:    0,                    // int\n\t\tMaxOpenConns:    0,                    // int\n\t\tConnMaxLifetime: 0,                    // time.Duration\n\t\tConnMaxIdleTime: 0,                    // time.Duration\n\t\tWarnLogger:      \u0026WarnLoggor{logger},  // Logger\n\t\tErrorLogger:     \u0026ErrorLogger{logger}, // Logger\n\t\tInfoLogger:      \u0026InfoLogger{logger},  // Logger\n\t\tShowSQL:         true,                 // bool\n\t\tSlowLogDuration: 0,                    // time.Duration\n\t\tSQLReplacements: map[string]string{    // SQL替换映射（可选）\n\t\t\t\"[TablePrefix]\": \"data_\",\n\t\t\t\"[Schema]\":      \"prod\",\n\t\t},\n\t})\n\tif err != nil {\n\t\tfmt.Println(err.Error())\n\t}\n\n\t// 插入数据（Nickname 为 nil，表示 NULL）\n\t{\n\t\tuser := User{\n\t\t\tEmail:      stringPoint(\"test@foxmail.com\"),\n\t\t\tNickname:   nil, // NULL 值\n\t\t\tCreateTime: timePoint(time.Now()),\n\t\t}\n\t\tid, count, err := o.Insert(\"INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});\", user)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"insert error\", zap.Error(err))\n\t\t}\n\t\tlogger.Info(\"test insert\", zap.Int64(\"id\", id), zap.Int64(\"count\", count))\n\t}\n\n\t// 查询数据\n\t{\n\t\tuser := User{\n\t\t\tEmail: stringPoint(\"test@foxmail.com\"),\n\t\t}\n\t\tvar results []User\n\t\tcount, err := o.SelectStructs(\"SELECT id,email,nickname,create_time FROM user WHERE email=#{Email};\", user)(\u0026results)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"test select\", zap.Error(err))\n\t\t}\n\t\tresultBytes, _ := json.Marshal(results)\n\t\tlogger.Info(\"test select\", zap.Int64(\"count\", count), zap.ByteString(\"result\", resultBytes))\n\t}\n\n\t// 更新数据\n\t{\n\t\tuser := User{\n\t\t\tEmail:    stringPoint(\"test@foxmail.com\"),\n\t\t\tNickname: stringPoint(\"hello\"),\n\t\t}\n\t\tcount, err := o.Update(\"UPDATE user SET nickname=#{Nickname} WHERE email=#{Email}\", user)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"update error\", zap.Error(err))\n\t\t}\n\t\tlogger.Info(\"test update\", zap.Int64(\"count\", count))\n\t}\n\n\t// 再次查询验证\n\t{\n\t\tuser := User{\n\t\t\tEmail: stringPoint(\"test@foxmail.com\"),\n\t\t}\n\t\tvar results []User\n\t\tcount, err := o.SelectStructs(\"SELECT id,email,nickname,create_time FROM user WHERE email=#{Email};\", user)(\u0026results)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"test select\", zap.Error(err))\n\t\t}\n\t\tresultBytes, _ := json.Marshal(results)\n\t\tlogger.Info(\"test select\", zap.Int64(\"count\", count), zap.ByteString(\"result\", resultBytes))\n\t}\n\n\t// 删除数据\n\t{\n\t\tuser := User{\n\t\t\tEmail: stringPoint(\"test@foxmail.com\"),\n\t\t}\n\t\tcount, err := o.Delete(\"DELETE FROM user WHERE email=#{Email}\", user)\n\t\tif err != nil {\n\t\t\tlogger.Error(\"test delete\", zap.Error(err))\n\t\t}\n\t\tlogger.Info(\"test delete\", zap.Int64(\"count\", count))\n\t}\n\n\t// 关闭连接\n\t{\n\t\terr = o.Close()\n\t\tif err != nil {\n\t\t\tlogger.Error(\"close\", zap.Error(err))\n\t\t}\n\t}\n}\n\n```\n\n**运行结果:**\n\n```log\n2022-02-21T11:42:44.591+0800    INFO    v2@v2.0.2/sql.go:311    readSQLParamsBySQL showSql, sql: INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});, params: {\"ID\":null,\"Email\":\"test@foxmail.com\",\"Nickname\":null,\"CreateTime\":\"2022-02-21T11:42:44.591619385+08:00\"}, dbSql: INSERT INTO user (email,nickname,create_time) VALUES (?,?,?);, dbParams: [\"test@foxmail.com\",null,\"2022-02-21T11:42:44.591619385+08:00\"]\n2022-02-21T11:42:44.596+0800    INFO    osm_demo/main.go:48     test insert     {\"id\": 10, \"count\": 1}\n2022-02-21T11:42:44.596+0800    INFO    v2@v2.0.2/sql.go:311    readSQLParamsBySQL showSql, sql: SELECT id,email,nickname,create_time FROM user WHERE email=#{Email};, params: {\"ID\":null,\"Email\":\"test@foxmail.com\",\"Nickname\":null,\"CreateTime\":null}, dbSql: SELECT id,email,nickname,create_time FROM user WHERE email=?;, dbParams: [\"test@foxmail.com\"]\n2022-02-21T11:42:44.597+0800    INFO    osm_demo/main.go:61     test select     {\"count\": 1, \"result\": \"[{\\\"ID\\\":10,\\\"Email\\\":\\\"test@foxmail.com\\\",\\\"Nickname\\\":\\\"\\\",\\\"CreateTime\\\":\\\"2022-02-21T03:42:44+08:00\\\"}]\"}\n2022-02-21T11:42:44.597+0800    INFO    v2@v2.0.2/sql.go:311    readSQLParamsBySQL showSql, sql: UPDATE user SET nickname=#{Nickname} WHERE email=#{Email}, params: {\"ID\":null,\"Email\":\"test@foxmail.com\",\"Nickname\":\"hello\",\"CreateTime\":null}, dbSql: UPDATE user SET nickname=? WHERE email=?, dbParams: [\"hello\",\"test@foxmail.com\"]\n2022-02-21T11:42:44.598+0800    INFO    osm_demo/main.go:73     test update     {\"count\": 1}\n2022-02-21T11:42:44.598+0800    INFO    v2@v2.0.2/sql.go:311    readSQLParamsBySQL showSql, sql: SELECT id,email,nickname,create_time FROM user WHERE email=#{Email};, params: {\"ID\":null,\"Email\":\"test@foxmail.com\",\"Nickname\":null,\"CreateTime\":null}, dbSql: SELECT id,email,nickname,create_time FROM user WHERE email=?;, dbParams: [\"test@foxmail.com\"]\n2022-02-21T11:42:44.599+0800    INFO    osm_demo/main.go:86     test select     {\"count\": 1, \"result\": \"[{\\\"ID\\\":10,\\\"Email\\\":\\\"test@foxmail.com\\\",\\\"Nickname\\\":\\\"hello\\\",\\\"CreateTime\\\":\\\"2022-02-21T03:42:44+08:00\\\"}]\"}\n2022-02-21T11:42:44.600+0800    INFO    v2@v2.0.2/sql.go:311    readSQLParamsBySQL showSql, sql: DELETE FROM user WHERE email=#{Email}, params: {\"ID\":null,\"Email\":\"test@foxmail.com\",\"Nickname\":null,\"CreateTime\":null}, dbSql: DELETE FROM user WHERE email=?, dbParams: [\"test@foxmail.com\"]\n2022-02-21T11:42:44.603+0800    INFO    osm_demo/main.go:97     test delete     {\"count\": 1}\n```\n\n## \u003ca id=\"field_column_mapping\"\u003e\u003c/a\u003e🔤 Struct 字段映射规则\n\n### 自动转换规则\n\nSQL 列名会自动转换为 Go 结构体字段名，转换过程如下：\n\n1. **分隔**: 用 `_` 分隔列名 \n   - 例: `user_email` → `user`, `email`\n\n2. **首字母大写**: 每个部分转为首字母大写，其余小写\n   - 例: `user`, `email` → `User`, `Email`\n\n3. **拼接**: 拼接所有部分\n   - 例: `User`, `Email` → `UserEmail`\n\n**示例:**\n```\nuser_name     → UserName\ncreate_time   → CreateTime\nuser_id       → UserId 或 UserID\n```\n\n### 常见缩写词支持\n\n以下缩写词支持两种形式（大小写不敏感），可在结构体中任选一种：\n\n**示例:** `user_id` 列可映射到 `UserId` 或 `UserID` 字段\n\n\u003e ⚠️ **注意**: 同一结构体中不能同时包含两种形式（如同时有 `UserId` 和 `UserID`），否则只有一个会被赋值。\n\n**支持的缩写词列表:**\n```\n  Acl  或   ACL\n  Api  或   API\n  Ascii  或 ASCII\n  Cpu  或   CPU\n  Css  或   CSS\n  Dns  或   DNS\n  Eof  或   EOF\n  Guid  或  GUID\n  Html  或  HTML\n  Http  或  HTTP\n  Https  或 HTTPS\n  Id  或    ID\n  Ip  或    IP\n  Json  或  JSON\n  Lhs  或   LHS\n  Qps  或   QPS\n  Ram  或   RAM\n  Rhs  或   RHS\n  Rpc  或   RPC\n  Sla  或   SLA\n  Smtp  或  SMTP\n  Sql  或   SQL\n  Ssh  或   SSH\n  Tcp  或   TCP\n  Tls  或   TLS\n  Ttl  或   TTL\n  Udp  或   UDP\n  Ui  或    UI\n  Uid  或   UID\n  Uuid  或  UUID\n  Uri  或   URI\n  Url  或   URL\n  Utf8  或  UTF8\n  Vm  或    VM\n  Xml  或   XML\n  Xmpp  或  XMPP\n  Xsrf  或  XSRF\n  Xss  或   XSS\n```\n\n### 使用 db 标签\n\n可以使用 `db` 标签显式指定字段与列的映射关系，标签优先级最高：\n\n```go\ntype User struct {\n    ID       int64  `db:\"user_id\"`      // 显式映射到 user_id 列\n    Name     string `db:\"user_name\"`    // 显式映射到 user_name 列\n    Email    string                      // 自动映射到 email 列\n    IsActive bool   `db:\"is_active\"`    // 显式映射到 is_active 列\n}\n```\n\n## 🤝 贡献\n\n欢迎提交 Issue 和 Pull Request！\n\n## 📄 许可证\n\n本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件\n\n---\n\n**如果这个项目对你有帮助，请给个 ⭐️ Star 支持一下！**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyinshuwei%2Fosm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyinshuwei%2Fosm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyinshuwei%2Fosm/lists"}