Documentation ¶
Overview ¶
Package orm 一个简单小巧的 orm 实现方案
目前内置了对以下数据库的支持:
- sqlite3: github.com/mattn/go-sqlite3
- mysql/mariadb: github.com/go-sql-driver/mysql
- postgres: github.com/lib/pq
- sqlite modernc.org/sqlite 纯 Go 的 sqlite3,无须 CGO。
其它数据库,用户可以通过实现 Dialect 接口,来实现相应的支持。
初始化:
默认情况下,orm 包并不会加载任何数据库的实例。所以想要用哪个数据库,需要手动初始化:
import ( _ github.com/mattn/go-sqlite3 // 加载数据库驱动 _ github.com/issue9/orm/v6/dialect // sqlite3 的 dialect 声明在此处 ) // 初始化一个 DB db1 := orm.NewDB("./db1", dialect.Sqlite3("sqlite3")) // 另一个 DB 实例 db2 := orm.NewDB("./db2", dialect.Sqlite3("sqlite3"))
占位符
SQL 中可以使用以下占位符:
- {} 包含一个关键字,使其它成为普通列名;
- # 表名前缀,为采用 core.Engine.TablePrefix 进行替换;
如:
select * from user where {#group}=1
在实际执行时,相关的占位符就会被替换成与当前环境想容的实例, 如在数据库为 mysql 时,会被替换成以下语句,然后再执行:
select * from p_user where `group`=1
[DB.Query]、[DB.Exec]、[DB.Prepare]、DB.Where 及 Tx 与之对应的函数都可以使用占位符。
Model 不能指定占位符,它们默认总会使用占位符,且无法取消。
Model:
orm 包通过 struct tag 来描述 model 在数据库中的结构。大概格式如下:
type User struct { Id int64 `orm:"name(id);ai;"` FirstName string `orm:"name(first_name);index(index_name)"` LastName string `orm:"name(first_name);index(index_name)"` // 此处group会自动加上引号,无须担心是否为关键字 Group string `orm:"name(group)"` } // 通过 [ApplyModeler] 接口,指定表的额外数据。若不需要,可不用实现该接口 func(u *User) ApplyModel(m *core.Model) error { m.Name = "user" m.Options["engine"] = "innodb" m.Options["charset"] = "utf8" return nil }
目前支持以下的 struct tag:
name(fieldName): 指定当前字段在数据表中的名称,如果未指定, 则和字段名相同。只有可导出的字段才有效果。 len(l1, l2): 指定字段的长度。比如 mysql 中的int(5),varchar(255),double(1,2), 不支持该特性的数据,将会忽略该标签的内容,比如 sqlite3。 NOTE:字符串类型必须指定长度,若长度过大或是将长度设置了 -1, 想使用类似于 TEXT 等不定长的形式表达。 如果是日期类型,则第一个可选参数表示日期精度。 nullable(true|false): 相当于定义表结构时的 NULL。 pk: 主键,支持联合主键,给多个字段加上 pk 的 struct tag 即可。 ai: 自增,若指定了自增列,则将自动取消其它的 pk 设置。无法指定起始值和步长。 可手动设置一个非零值来更改某条数据的 AI 行为。 unique(index_name): 唯一索引,支持联合索引,index_name 为约束名, 会将 index_name 为一样的字段定义为一个联合索引。 index(index_name): 普通的关键字索引,同 unique 一样会将名称相同的索引定义为一个联合索引。 occ(true|false) 当前列作为乐观锁字段。 作为乐观锁的字段,其值表示的是线上数据的值,在更新时,会自动给线上的值加 1。 default(value): 指定默认值。相当于定义表结构时的 DEFAULT。 当一个字段如果是个零值(reflect.Zero())时,将会使用它的默认值, 但是系统无法判断该零值是人为指定,还是未指定被默认初始化零值的, 所以在需要用到零值的字段,最好不要用 default 的 struct tag。 fk(fk_name,refTable,refColName,updateRule,deleteRule): 定义物理外键,最少需要指定 fk_name,refTable,refColName 三个值。 分别对应约束名,引用的表和引用的字段,updateRule,deleteRule, 在不指定的情况下,使用数据库的默认值。
ApplyModeler:
用于将一个对象转换成 Model 对象时执行的函数,给予用户修改 Model 的机会, 在 ApplyModeler 中可以修改任意模型的内容,所以也可以由 ApplyModeler 代替 struct tag 的操作。
约束名:
index、unique、check 和 fk 都是可以指定约束名的,在表中,约束名必须是唯一的, 即便是不同类型的约束,比如已经有一个 unique 的约束名叫作 name,那么其它类 型的约束,就不能再取这个名称了。 部分数据库(比如 postgres)可能要求约束名是整个数据库唯一的, 为了统一,在 ORM 中所有数据都强制约束名全局(数据库)唯一。
如何使用:
Create: 可以通过 DB.Create() 或是 Tx.Create() 创建一张表。
// 创建表 db.Create(&User{})
Update:
// 将 id 为 1 的记录的 FirstName 更改为 abc;对象中的零值不会被提交。 db.Update(&User{Id:1,FirstName:"abc"}) sqlbuilder.Update(db).Table("table").Where("id=?",1).Set("FirstName", "abc").Exec()
Delete:
// 删除 id 为 1 的记录 e.Delete(&User{Id:1}) sqlbuilder.Delete(e).Table("table").Where("id=?",1).Exec()
Insert:
// 插入一条数据 db.Insert(&User{Id:1,FirstName:"abc"}) // 一次性插入多条数据 tx.InsertMany(&User{Id:1,FirstName:"abc"},&User{Id:1,FirstName:"abc"})
Select:
// 导出 id=1 的数据 _,err := sqlbuilder.Select(e, e.Dialect()).Select("*").From("{table}").Where("id=1").QueryObj(obj) // 导出 id 为 1 的数据,并回填到 user 实例中 user := &User{Id:1} err := e.Select(u)
Query/Exec:
// Query 返回参数与 sql.Query 是相同的 sql := "select * from tbl_name where id=?" rows, err := e.Query(sql, []interface{}{5}) // Exec 返回参数与 sql.Exec 是相同的 sql = "update tbl_name set name=? where id=?" r, err := e.Exec(sql, []interface{}{"name1", 5})
事务:
默认的 DB 是不支持事务的,若需要事务支持,则需要调用 DB.Begin 返回事务对象 Tx,当然并不是所有的数据库都支持事务操作的。 Tx 拥有一组与 DB 相同的接口。
Index ¶
- Variables
- func NowNullTime() sql.NullTime
- func TableName(t TableNamer) string
- type AfterFetcher
- type ApplyModeler
- type BeforeInserter
- type BeforeUpdater
- type Column
- type DB
- func (db *DB) Begin() (*Tx, error)
- func (db *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
- func (db *DB) Close() error
- func (db *DB) Create(v ...TableNamer) error
- func (db *DB) CreateContext(ctx context.Context, v ...TableNamer) error
- func (db *DB) DB() *sql.DB
- func (db *DB) Delete(v TableNamer) (sql.Result, error)
- func (db *DB) DeleteContext(ctx context.Context, v TableNamer) (sql.Result, error)
- func (db *DB) DoTransaction(f func(tx *Tx) error) error
- func (db *DB) DoTransactionTx(ctx context.Context, opt *sql.TxOptions, f func(tx *Tx) error) error
- func (db *DB) Drop(v ...TableNamer) error
- func (db *DB) DropContext(ctx context.Context, v ...TableNamer) error
- func (db *DB) Insert(v TableNamer) (sql.Result, error)
- func (db *DB) InsertContext(ctx context.Context, v TableNamer) (sql.Result, error)
- func (db *DB) InsertMany(max int, v ...TableNamer) error
- func (db *DB) InsertManyContext(ctx context.Context, max int, v ...TableNamer) error
- func (db *DB) LastInsertID(v TableNamer) (int64, error)
- func (db *DB) LastInsertIDContext(ctx context.Context, v TableNamer) (int64, error)
- func (db *DB) New(tablePrefix string) *DB
- func (db *DB) Ping() error
- func (db *DB) PingContext(ctx context.Context) error
- func (db *DB) SQLBuilder() *sqlbuilder.SQLBuilder
- func (db *DB) Select(v TableNamer) (bool, error)
- func (db *DB) SelectContext(ctx context.Context, v TableNamer) (bool, error)
- func (db *DB) Stats() sql.DBStats
- func (db *DB) TablePrefix() string
- func (db *DB) Truncate(v ...TableNamer) error
- func (db *DB) TruncateContext(ctx context.Context, v ...TableNamer) error
- func (db *DB) Update(v TableNamer, cols ...string) (sql.Result, error)
- func (db *DB) UpdateContext(ctx context.Context, v TableNamer, cols ...string) (sql.Result, error)
- func (db *DB) Upgrade(v TableNamer) (*Upgrader, error)
- func (db *DB) Version() string
- func (db *DB) Where(cond string, args ...any) *WhereStmt
- type Decimal
- type Dialect
- type Engine
- type Rat
- type TableNamer
- type Tx
- func (tx *Tx) Commit() error
- func (tx *Tx) Create(v ...TableNamer) error
- func (tx *Tx) CreateContext(ctx context.Context, v ...TableNamer) error
- func (tx *Tx) Delete(v TableNamer) (sql.Result, error)
- func (tx *Tx) DeleteContext(ctx context.Context, v TableNamer) (sql.Result, error)
- func (tx *Tx) Drop(v ...TableNamer) error
- func (tx *Tx) DropContext(ctx context.Context, v ...TableNamer) error
- func (tx *Tx) ForUpdate(v TableNamer) error
- func (tx *Tx) ForUpdateContext(ctx context.Context, v TableNamer) error
- func (tx *Tx) Insert(v TableNamer) (sql.Result, error)
- func (tx *Tx) InsertContext(ctx context.Context, v TableNamer) (sql.Result, error)
- func (tx *Tx) InsertMany(max int, v ...TableNamer) error
- func (tx *Tx) InsertManyContext(ctx context.Context, max int, v ...TableNamer) error
- func (tx *Tx) LastInsertID(v TableNamer) (int64, error)
- func (tx *Tx) LastInsertIDContext(ctx context.Context, v TableNamer) (int64, error)
- func (tx *Tx) NewEngine(tablePrefix string) Engine
- func (tx *Tx) Rollback() error
- func (tx *Tx) SQLBuilder() *sqlbuilder.SQLBuilder
- func (tx *Tx) Select(v TableNamer) (bool, error)
- func (tx *Tx) SelectContext(ctx context.Context, v TableNamer) (bool, error)
- func (tx *Tx) Truncate(v ...TableNamer) error
- func (tx *Tx) TruncateContext(ctx context.Context, v ...TableNamer) error
- func (tx *Tx) Tx() *sql.Tx
- func (tx *Tx) Update(v TableNamer, cols ...string) (sql.Result, error)
- func (tx *Tx) UpdateContext(ctx context.Context, v TableNamer, cols ...string) (sql.Result, error)
- func (tx *Tx) Where(cond string, args ...any) *WhereStmt
- type Unix
- type Upgrader
- func (u *Upgrader) AddColumn(name ...string) *Upgrader
- func (u *Upgrader) AddConstraint(name ...string) *Upgrader
- func (u *Upgrader) AddIndex(name ...string) *Upgrader
- func (u *Upgrader) Do() error
- func (u *Upgrader) DropColumn(name ...string) *Upgrader
- func (u *Upgrader) DropConstraint(conts ...string) *Upgrader
- func (u *Upgrader) DropIndex(name ...string) *Upgrader
- func (u *Upgrader) DropPK(name string) *Upgrader
- func (u *Upgrader) Engine() Engine
- func (u *Upgrader) Err() error
- type WhereStmt
Constants ¶
This section is empty.
Variables ¶
var ErrNeedAutoIncrementColumn = errors.New("必须存在自增列")
ErrNeedAutoIncrementColumn 缺少必要的自增列
当以 LastInsertID 的方式插入一条没有 AI 列的对象时,会返回此错误。
Functions ¶
func TableName ¶
func TableName(t TableNamer) string
Types ¶
type ApplyModeler ¶
type ApplyModeler = core.ApplyModeler
type BeforeInserter ¶
type BeforeInserter interface {
BeforeInsert() error
}
BeforeInserter 在插入之前调用的函数
type DB ¶
DB 数据库操作实例
func NewDB ¶
NewDB 声明一个新的 DB 实例
NOTE: 不同驱动对时间的处理不尽相同,如果有在不同数据库之间移植的需求, 那么建议将保存时的时区都统一设置为 UTC:
- postgres 已经固定为 UTC;
- sqlite3 可以在 dsn 中通过 _loc=UTC 指定;
- mysql 默认是 UTC,也可以在 DSN 中通过 loc=UTC 指定;
func NewDBWithStdDB ¶
NewDBWithStdDB 从 sql.DB 构建 DB 实例
NOTE: 请确保用于打开 db 的 driverName 参数与 dialect.DriverName() 是相同的, 否则后续操作的结果是未知的。
NOTE: db 的资源由返回的 DB 接管,外部不应该直接关闭 db 连接。
func (*DB) Create ¶
func (db *DB) Create(v ...TableNamer) error
func (*DB) CreateContext ¶
func (db *DB) CreateContext(ctx context.Context, v ...TableNamer) error
func (*DB) DeleteContext ¶
func (*DB) DoTransaction ¶
DoTransaction 将 f 中的内容以事务的方式执行
func (*DB) Drop ¶
func (db *DB) Drop(v ...TableNamer) error
func (*DB) DropContext ¶
func (db *DB) DropContext(ctx context.Context, v ...TableNamer) error
func (*DB) Insert ¶
func (db *DB) Insert(v TableNamer) (sql.Result, error)
Insert 插入数据
NOTE: 若需一次性插入多条数据,请使用 Tx.InsertMany。
func (*DB) InsertContext ¶
func (*DB) InsertMany ¶
func (db *DB) InsertMany(max int, v ...TableNamer) error
InsertMany 一次插入多条数据
会自动转换成事务进行处理。
func (*DB) InsertManyContext ¶
func (*DB) LastInsertID ¶
func (db *DB) LastInsertID(v TableNamer) (int64, error)
func (*DB) LastInsertIDContext ¶
func (*DB) SQLBuilder ¶
func (db *DB) SQLBuilder() *sqlbuilder.SQLBuilder
func (*DB) SelectContext ¶
func (*DB) TablePrefix ¶
TablePrefix 所有数据表拥有的统一表名前缀
当需要在一个数据库中创建不同的实例, 或是同一个数据表结构应用在不同的对象是,可以通过不同的表名前缀对数据表进行区分。
func (*DB) Truncate ¶
func (db *DB) Truncate(v ...TableNamer) error
func (*DB) TruncateContext ¶
func (db *DB) TruncateContext(ctx context.Context, v ...TableNamer) error
func (*DB) UpdateContext ¶
type Engine ¶
type Engine interface { core.Engine // LastInsertIDContext 插入一条数据并返回其自增 ID // // 理论上功能等同于以下两步操作: // rslt, err := engine.Insert(obj) // id, err := rslt.LastInsertId() // 但是实际上部分数据库不支持直接在 [sql.Result] 中获取 LastInsertId, // 比如 postgresql,所以使用此方法比 [sql.Result] 更有效。 // // NOTE: 要求 v 有定义自增列。 LastInsertIDContext(ctx context.Context, v TableNamer) (int64, error) LastInsertID(v TableNamer) (int64, error) // InsertContext 插入数据 // // NOTE: 若需一次性插入多条数据,请使用 [Engine.InsertMany] 。 InsertContext(ctx context.Context, v TableNamer) (sql.Result, error) Insert(v TableNamer) (sql.Result, error) // Delete 删除符合条件的数据 // // 查找条件以结构体定义的主键或是唯一约束(在没有主键的情况下)来查找, // 若两者都不存在,则将返回 error DeleteContext(ctx context.Context, v TableNamer) (sql.Result, error) Delete(v TableNamer) (sql.Result, error) // UpdateContext 更新数据 // // 零值不会被提交,cols 指定的列,即使是零值也会被更新。 // // 查找条件以结构体定义的主键或是唯一约束(在没有主键的情况下)来查找, // 若两者都不存在,则将返回 error UpdateContext(ctx context.Context, v TableNamer, cols ...string) (sql.Result, error) Update(v TableNamer, cols ...string) (sql.Result, error) // SelectContext 查询一个符合条件的数据 // // 查找条件以结构体定义的主键或是唯一约束(在没有主键的情况下 ) 来查找, // 若两者都不存在,则将返回 error // 若没有符合条件的数据,将不会对参数 v 做任何变动。 // // 查找条件的查找顺序是为 自增 > 主键 > 唯一约束, // 如果同时存在多个唯一约束满足条件(可能每个唯一约束查询至的结果是不一样的),则返回错误信息。 SelectContext(context.Context, TableNamer) (found bool, err error) Select(TableNamer) (found bool, err error) CreateContext(context.Context, ...TableNamer) error Create(...TableNamer) error DropContext(context.Context, ...TableNamer) error Drop(...TableNamer) error // TruncateContext 清空表并重置 ai 但保留表结构 TruncateContext(context.Context, ...TableNamer) error Truncate(...TableNamer) error // InsertManyContext 插入多条相同的数据 // // 若需要向某张表中插入多条记录,此方法会比 [Engine.Insert] 性能上好很多。 // // max 表示一次最多插入的数量,如果超过此值,会分批执行,但是依然在一个事务中完成。 InsertManyContext(ctx context.Context, max int, v ...TableNamer) error InsertMany(max int, v ...TableNamer) error // Where 生成 [WhereStmt] 语句 Where(cond string, args ...any) *WhereStmt SQLBuilder() *sqlbuilder.SQLBuilder // contains filtered or unexported methods }
Engine 数据操作引擎
相对于 core.Engine,添加了针对 TableNamer 的操作。 所有针对 TableNamer 的操作与 sqlbuilder 的拼接方式有以下区别:
- 针对 TableNamer 的操作会自动为表名加上 # 表名前缀;
- 针对 TableNamer 的操作会为约束名加上表名,以确保约束名的唯一性;
type TableNamer ¶
type TableNamer = core.TableNamer
type Tx ¶
Tx 事务对象
func (*Tx) Create ¶
func (tx *Tx) Create(v ...TableNamer) error
func (*Tx) CreateContext ¶
func (tx *Tx) CreateContext(ctx context.Context, v ...TableNamer) error
func (*Tx) DeleteContext ¶
func (*Tx) Drop ¶
func (tx *Tx) Drop(v ...TableNamer) error
func (*Tx) DropContext ¶
func (tx *Tx) DropContext(ctx context.Context, v ...TableNamer) error
func (*Tx) ForUpdateContext ¶
func (tx *Tx) ForUpdateContext(ctx context.Context, v TableNamer) error
func (*Tx) InsertContext ¶
func (*Tx) InsertMany ¶
func (tx *Tx) InsertMany(max int, v ...TableNamer) error
func (*Tx) InsertManyContext ¶
func (*Tx) LastInsertID ¶
func (tx *Tx) LastInsertID(v TableNamer) (int64, error)
func (*Tx) LastInsertIDContext ¶
func (*Tx) NewEngine ¶
NewEngine 为当前事务创建一个不同表名前缀的 Engine 对象
如果要复用表模型,可以采此方法创建一个不同表名前缀的 Engine 进行操作表模型。 返回对象的生命周期与 Tx 相同。
func (*Tx) SQLBuilder ¶
func (tx *Tx) SQLBuilder() *sqlbuilder.SQLBuilder
func (*Tx) SelectContext ¶
func (*Tx) Truncate ¶
func (tx *Tx) Truncate(v ...TableNamer) error
func (*Tx) TruncateContext ¶
func (tx *Tx) TruncateContext(ctx context.Context, v ...TableNamer) error
func (*Tx) UpdateContext ¶
type Upgrader ¶
type Upgrader struct {
// contains filtered or unexported fields
}
Upgrader 更新数据表对象
主要针对线上数据与本地模型不相同时,可执行的一些操作。 并没有准确的方法判断线上字段与本地定义的是否相同,比如: varchar(50) 和 text 在 sqlite3 是相同的,但是在其它数据库可能是稍微有差别的, 所以 Upgrader 并不会主动对数据表进行更新,所有更新还是要手动调用相关的函数。
func (*Upgrader) DropConstraint ¶
DropConstraint 删除约束
type WhereStmt ¶
type WhereStmt struct {
// contains filtered or unexported fields
}
func (*WhereStmt) Count ¶
func (stmt *WhereStmt) Count(v TableNamer) (int64, error)
Count 返回符合条件数量
表名来自 v。
func (*WhereStmt) Delete ¶
func (stmt *WhereStmt) Delete(v TableNamer) (sql.Result, error)
Delete 从 v 表中删除符合条件的内容
Directories ¶
Path | Synopsis |
---|---|
Package core 核心功能
|
Package core 核心功能 |
Package dialect 提供了部分数据库对 [core.Dialect] 接口的实现
|
Package dialect 提供了部分数据库对 [core.Dialect] 接口的实现 |
Package fetch 提供了将 [sql.Rows] 导出为几种常用数据格式的方法
|
Package fetch 提供了将 [sql.Rows] 导出为几种常用数据格式的方法 |
internal
|
|
createtable
Package createtable 分析 create table 语句的内容
|
Package createtable 分析 create table 语句的内容 |
model
Package model 管理数据模型
|
Package model 管理数据模型 |
sqltest
Package sqltest 提供对 SQL 内容测试的工具
|
Package sqltest 提供对 SQL 内容测试的工具 |
tags
Package tags 包实现对特定格式的 struct tag 字符串的分析
|
Package tags 包实现对特定格式的 struct tag 字符串的分析 |
test
Package test 提供了整个包的基本测试数据
|
Package test 提供了整个包的基本测试数据 |
Package sqlbuilder 提供一套通过字符串拼接来构成 SQL 语句的工具
|
Package sqlbuilder 提供一套通过字符串拼接来构成 SQL 语句的工具 |
Package types 提供部分存取数据库的类型
|
Package types 提供部分存取数据库的类型 |