English | 简体中文
osm (Object SQL Mapping) 是用 Go 编写的轻量级 SQL 工具库,已在生产环境中广泛使用。
支持的数据库: MySQL、PostgreSQL、SQL Server
提供极简且优雅的 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 语句
支持多种数据接收方式,满足不同场景需求:
| 方法类型 | 说明 | 使用场景 |
|---|---|---|
Value / Values |
单行/多行多列值 | 查询多列不同类型的值 |
Struct / Structs |
单行/多行结构体 | 对象映射 |
String / Strings |
单个/多个字符串 | 简单字段查询 |
Int / Ints |
单个/多个整数 | 统计查询 |
Float64 / Float64s |
单个/多个浮点数 | 数值计算 |
Bool / Bools |
单个/多个布尔值 | 状态标识 |
Kvs |
键值对映射 | 双列数据 → Map |
ColumnsAndData |
列名 + 数据行 | 数据交换/导出 |
- 优先读取
db标签 - 智能的字段名转换(支持常见缩写词,如 ID、URL、HTTP 等)
- 支持嵌套结构体
- 支持指针类型(可表示 NULL)
- 查看完整的字段映射规则
支持在 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/v2go.mod:
require (
github.com/yinshuwei/osm/v2 v2.0.8
)完整文档请访问: https://pkg.go.dev/github.com/yinshuwei/osm/v2
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)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)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)查询多行两列数据并存入map,第一列作为key,第二列作为value
var idEmailMap = map[int64]string{}
_, err := o.Select(`SELECT id, email FROM users`).Kvs(&idEmailMap)查询多行数据,返回列名和数据(常用于数据交换)
columns, datas, err := o.Select(`SELECT id, email FROM users`).ColumnsAndData()
// columns 为 []string
// datas 为 [][]stringString - 查询单个字符串值
email, err := o.Select(`SELECT email FROM users WHERE id = #{Id}`, 1).String()Strings - 查询多个字符串值
emails, err := o.Select(`SELECT email FROM users`).Strings()Int - 查询单个int值
count, err := o.Select(`SELECT COUNT(*) FROM users`).Int()Ints - 查询多个int值
ages, err := o.Select(`SELECT age FROM users`).Ints()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()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()Uint - 查询单个uint值
count, err := o.Select(`SELECT COUNT(*) FROM users`).Uint()Uints - 查询多个uint值
counts, err := o.Select(`SELECT count FROM table`).Uints()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()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()Float64 - 查询单个float64值
avg, err := o.Select(`SELECT AVG(score) FROM users`).Float64()Float64s - 查询多个float64值
scores, err := o.Select(`SELECT score FROM users`).Float64s()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)
CREATE DATABASE test;
USE test;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`email` varchar(255) DEFAULT NULL,
`nickname` varchar(45) DEFAULT NULL,
`create_time` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';基础示例 (osm_demo.go)
package main
import (
"encoding/json"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/yinshuwei/osm/v2"
"go.uber.org/zap"
)
// InfoLogger 适配zap logger
type InfoLogger struct {
zapLogger *zap.Logger
}
// WarnLoggor 适配zap logger
type WarnLoggor struct {
zapLogger *zap.Logger
}
// ErrorLogger 适配zap logger
type ErrorLogger struct {
zapLogger *zap.Logger
}
func loggerFields(data map[string]string) []zap.Field {
var fields []zap.Field
for key, val := range data {
fields = append(fields, zap.String(key, val))
}
return fields
}
func (l *ErrorLogger) Log(msg string, data map[string]string) {
if l == nil || l.zapLogger == nil {
return
}
l.zapLogger.Error(msg, loggerFields(data)...)
}
func (l *InfoLogger) Log(msg string, data map[string]string) {
if l == nil || l.zapLogger == nil {
return
}
l.zapLogger.Info(msg, loggerFields(data)...)
}
func (l *WarnLoggor) Log(msg string, data map[string]string) {
if l == nil || l.zapLogger == nil {
return
}
l.zapLogger.Warn(msg, loggerFields(data)...)
}
// User 用户Model
type User struct {
ID int64
Nickname string `db:"name"`
CreateTime time.Time
EmailStruct // 匿名属性
}
type EmailStruct struct {
Email string `db:"email"`
}
func main() {
logger, _ := zap.NewDevelopment()
o, err := osm.New("mysql", "root:123456@/test?charset=utf8", osm.Options{
MaxIdleConns: 0, // int
MaxOpenConns: 0, // int
ConnMaxLifetime: 0, // time.Duration
ConnMaxIdleTime: 0, // time.Duration
WarnLogger: &WarnLoggor{logger}, // Logger
ErrorLogger: &ErrorLogger{logger}, // Logger
InfoLogger: &InfoLogger{logger}, // Logger
ShowSQL: true, // bool
SlowLogDuration: 0, // time.Duration
SQLReplacements: map[string]string{ // SQL替换映射(可选)
"[TablePrefix]": "data_", // 表前缀
"[Schema]": "prod", // 数据库schema
},
})
if err != nil {
fmt.Println(err.Error())
}
// 插入数据
user := User{
EmailStruct: EmailStruct{
Email: "test@foxmail.com",
},
Nickname: "haha",
CreateTime: time.Now(),
}
id, count, err := o.Insert("INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});", user)
if err != nil {
logger.Error("insert error", zap.Error(err))
}
logger.Info("test insert", zap.Int64("id", id), zap.Int64("count", count))
// 更新数据
user = User{
EmailStruct: EmailStruct{
Email: "test@foxmail.com",
},
Nickname: "hello",
}
count, err = o.Update("UPDATE user SET nickname=#{name} WHERE email=#{Email}", user)
if err != nil {
logger.Error("update error", zap.Error(err))
}
logger.Info("test update", zap.Int64("count", count))
// 查询数据
user = User{
EmailStruct: EmailStruct{
Email: "test@foxmail.com",
},
}
var results []User
count, err = o.SelectStructs("SELECT id,email,nickname,create_time FROM user WHERE email=#{Email} or email=#{email};", user)(&results)
if err != nil {
logger.Error("test select", zap.Error(err))
}
resultBytes, _ := json.Marshal(results)
logger.Info("test select", zap.Int64("count", count), zap.ByteString("result", resultBytes))
// 删除数据
count, err = o.Delete("DELETE FROM user WHERE email=#{Email}", user)
if err != nil {
logger.Error("test delete", zap.Error(err))
}
logger.Info("test delete", zap.Int64("count", count))
// 关闭连接
err = o.Close()
if err != nil {
logger.Error("close", zap.Error(err))
}
}运行结果:
2025-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 (?,?,?);"}
2025-01-13T16:16:41.305+0800 INFO osmtt/main.go:99 test insert {"id": 11, "count": 1}
2025-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=?"}
2025-01-13T16:16:41.310+0800 INFO osmtt/main.go:112 test update {"count": 1}
2025-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};"}
2025-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\"}]"}
2025-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=?"}
2025-01-13T16:16:41.313+0800 INFO osmtt/main.go:133 test delete {"count": 1}
指针类型支持 NULL (osm_demo2.go)
package main
import (
"encoding/json"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/yinshuwei/osm/v2"
"go.uber.org/zap"
)
// InfoLogger 适配zap logger
type InfoLogger struct {
zapLogger *zap.Logger
}
// WarnLoggor 适配zap logger
type WarnLoggor struct {
zapLogger *zap.Logger
}
// ErrorLogger 适配zap logger
type ErrorLogger struct {
zapLogger *zap.Logger
}
func loggerFields(data map[string]string) []zap.Field {
var fields []zap.Field
for key, val := range data {
fields = append(fields, zap.String(key, val))
}
return fields
}
func (l *ErrorLogger) Log(msg string, data map[string]string) {
if l == nil || l.zapLogger == nil {
return
}
l.zapLogger.Error(msg, loggerFields(data)...)
}
func (l *InfoLogger) Log(msg string, data map[string]string) {
if l == nil || l.zapLogger == nil {
return
}
l.zapLogger.Info(msg, loggerFields(data)...)
}
func (l *WarnLoggor) Log(msg string, data map[string]string) {
if l == nil || l.zapLogger == nil {
return
}
l.zapLogger.Warn(msg, loggerFields(data)...)
}
// User 用户Model
type User struct {
ID *int64
Email *string
Nickname *string
CreateTime *time.Time
}
func stringPoint(t string) *string {
return &t
}
func timePoint(t time.Time) *time.Time {
return &t
}
func main() {
logger, _ := zap.NewDevelopment()
o, err := osm.New("mysql", "root:123456@/test?charset=utf8", osm.Options{
MaxIdleConns: 0, // int
MaxOpenConns: 0, // int
ConnMaxLifetime: 0, // time.Duration
ConnMaxIdleTime: 0, // time.Duration
WarnLogger: &WarnLoggor{logger}, // Logger
ErrorLogger: &ErrorLogger{logger}, // Logger
InfoLogger: &InfoLogger{logger}, // Logger
ShowSQL: true, // bool
SlowLogDuration: 0, // time.Duration
SQLReplacements: map[string]string{ // SQL替换映射(可选)
"[TablePrefix]": "data_",
"[Schema]": "prod",
},
})
if err != nil {
fmt.Println(err.Error())
}
// 插入数据(Nickname 为 nil,表示 NULL)
{
user := User{
Email: stringPoint("test@foxmail.com"),
Nickname: nil, // NULL 值
CreateTime: timePoint(time.Now()),
}
id, count, err := o.Insert("INSERT INTO user (email,nickname,create_time) VALUES (#{Email},#{Nickname},#{CreateTime});", user)
if err != nil {
logger.Error("insert error", zap.Error(err))
}
logger.Info("test insert", zap.Int64("id", id), zap.Int64("count", count))
}
// 查询数据
{
user := User{
Email: stringPoint("test@foxmail.com"),
}
var results []User
count, err := o.SelectStructs("SELECT id,email,nickname,create_time FROM user WHERE email=#{Email};", user)(&results)
if err != nil {
logger.Error("test select", zap.Error(err))
}
resultBytes, _ := json.Marshal(results)
logger.Info("test select", zap.Int64("count", count), zap.ByteString("result", resultBytes))
}
// 更新数据
{
user := User{
Email: stringPoint("test@foxmail.com"),
Nickname: stringPoint("hello"),
}
count, err := o.Update("UPDATE user SET nickname=#{Nickname} WHERE email=#{Email}", user)
if err != nil {
logger.Error("update error", zap.Error(err))
}
logger.Info("test update", zap.Int64("count", count))
}
// 再次查询验证
{
user := User{
Email: stringPoint("test@foxmail.com"),
}
var results []User
count, err := o.SelectStructs("SELECT id,email,nickname,create_time FROM user WHERE email=#{Email};", user)(&results)
if err != nil {
logger.Error("test select", zap.Error(err))
}
resultBytes, _ := json.Marshal(results)
logger.Info("test select", zap.Int64("count", count), zap.ByteString("result", resultBytes))
}
// 删除数据
{
user := User{
Email: stringPoint("test@foxmail.com"),
}
count, err := o.Delete("DELETE FROM user WHERE email=#{Email}", user)
if err != nil {
logger.Error("test delete", zap.Error(err))
}
logger.Info("test delete", zap.Int64("count", count))
}
// 关闭连接
{
err = o.Close()
if err != nil {
logger.Error("close", zap.Error(err))
}
}
}运行结果:
2022-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"]
2022-02-21T11:42:44.596+0800 INFO osm_demo/main.go:48 test insert {"id": 10, "count": 1}
2022-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"]
2022-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\"}]"}
2022-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"]
2022-02-21T11:42:44.598+0800 INFO osm_demo/main.go:73 test update {"count": 1}
2022-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"]
2022-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\"}]"}
2022-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"]
2022-02-21T11:42:44.603+0800 INFO osm_demo/main.go:97 test delete {"count": 1}
SQL 列名会自动转换为 Go 结构体字段名,转换过程如下:
-
分隔: 用
_分隔列名- 例:
user_email→user,email
- 例:
-
首字母大写: 每个部分转为首字母大写,其余小写
- 例:
user,email→User,Email
- 例:
-
拼接: 拼接所有部分
- 例:
User,Email→UserEmail
- 例:
示例:
user_name → UserName
create_time → CreateTime
user_id → UserId 或 UserID
以下缩写词支持两种形式(大小写不敏感),可在结构体中任选一种:
示例: user_id 列可映射到 UserId 或 UserID 字段
⚠️ 注意: 同一结构体中不能同时包含两种形式(如同时有UserId和UserID),否则只有一个会被赋值。
支持的缩写词列表:
Acl 或 ACL
Api 或 API
Ascii 或 ASCII
Cpu 或 CPU
Css 或 CSS
Dns 或 DNS
Eof 或 EOF
Guid 或 GUID
Html 或 HTML
Http 或 HTTP
Https 或 HTTPS
Id 或 ID
Ip 或 IP
Json 或 JSON
Lhs 或 LHS
Qps 或 QPS
Ram 或 RAM
Rhs 或 RHS
Rpc 或 RPC
Sla 或 SLA
Smtp 或 SMTP
Sql 或 SQL
Ssh 或 SSH
Tcp 或 TCP
Tls 或 TLS
Ttl 或 TTL
Udp 或 UDP
Ui 或 UI
Uid 或 UID
Uuid 或 UUID
Uri 或 URI
Url 或 URL
Utf8 或 UTF8
Vm 或 VM
Xml 或 XML
Xmpp 或 XMPP
Xsrf 或 XSRF
Xss 或 XSS
可以使用 db 标签显式指定字段与列的映射关系,标签优先级最高:
type User struct {
ID int64 `db:"user_id"` // 显式映射到 user_id 列
Name string `db:"user_name"` // 显式映射到 user_name 列
Email string // 自动映射到 email 列
IsActive bool `db:"is_active"` // 显式映射到 is_active 列
}欢迎提交 Issue 和 Pull Request!
本项目采用 MIT 许可证 - 详见 LICENSE 文件
如果这个项目对你有帮助,请给个 ⭐️ Star 支持一下!