rem:复古的Golang ORM


rem是Retro Entity Mapper三个字母简写。一种新的Golang ORM。

特点:

  • PostgreSQL 和 MySQL 方言。SQLite 即将推出。
  • 使用相同模型语法的数据和架构迁移。
  • 优化外键和一对多预取。
  • 接口可扩展查询生成器。可用于特定于数据库的功能。
  • 与直接使用数据库/sql 的性能差异可以忽略不计。
  • 与数据库/sql 连接和驱动程序解耦。
  • 根据需要部分或完全回退到安全参数化的 SQL 格式。
  • 零代码生成 模型只是可能具有您自己的字段和方法的结构。
  • 具有显式 null 和非 null 类型的标准化安全性。
  • 事务和 golang 上下文支持。
  • 子查询、连接、选择性获取、地图扫描等。

模型是定义表模式的结构:

type Accounts struct {
    Id   int64  `db:"id" primary_key:"true"`
    Name string `db:
"name" db_max_length:"100"`
    Junk string
}

在上述结构定义中,Id 和 Name 是账户中的列。它们的列由 db 字段标记定义。Id 也是自动递增主键。Name 的最大字符长度为 100。REM 会忽略 Junk 字段。

定义模型后,在应用程序启动时注册一次,然后查询数据库。

// rem.Register[To]() 会缓存模型的计算结构。
rem.Register[Accounts]()

// rem.Use[To]() 返回模型的查询创建器.
rows, err := rem.Use[Accounts]().All(db)

// 您还可以重复使用 rem.Register[To]() 和 rem.Use[To]() 返回的 Model[To] 实例。
accounts := rem.Use[Accounts]()
rows1, err1 := accounts.Filter(
"name", "=", "foo").All(db)
rows2, err2 := accounts.Filter(
"name", "=", "bar").All(db)

// 注册并使用同一型号的不同台面。
rem.Register[Accounts](rem.Config{Table:
"groups"})
groups := rem.Use[Accounts](rem.Config{Table:
"groups"})


一对多关系表

type Accounts struct {
    Group rem.NullForeignKey[Groups] `db:"group_id"`
    Id    int64                      `db:
"id" primary_key:"true"`
    Name  string                     `db:
"name"`
}

type Groups struct {
    Accounts rem.OneToMany[Accounts] `related_column:
"group_id"`
    Id       int64                   `db:
"id" primary_key:"true"`
    Name     string                  `db:
"name" db_max_length:"100"`
}

// 只需执行一次额外查询即可获取所有相关account。
groups, err := rem.Use[Groups]().
    FetchRelated(
"Accounts").
    Filter(
"id", "IN", interface{}{10, 20, 30}).
    Sort(
"name", "-id").
    All(db)

if err != nil {
    panic(err)
}
for _, group := range groups {
    
// group *Groups
    
// group.Accounts.Rows *Accounts
}

REM 可以优化外键和一对多记录查找。这是通过 FetchRelated 方法实现的,该方法可以获取任意数量的字符串,这些字符串代表要预取的关系字段。

无论从关系的哪一边开始,也无论最初要获取多少条记录,REM 都只会执行一次额外的查询来进行预获取。

事务
REM 通过 Transaction(*sql.Tx) 方法支持事务。

tx, _ := db.Begin()

_, err := rem.Use[Accounts]().
    Filter("id", "=", 100).
    Transaction(tx).
    Delete(db)

if err != nil {
    tx.Rollback()
    panic(err)
}

err = tx.Commit()
if err != nil {
    panic(err)
}