Go Meddler


原文链接: Go Meddler

Meddler 是一个小工具箱,用于在 sql 查询和结构之间回移数据。
它不是一个完整的 ORM 。 它是为了添加一些方便的方法,同时在程序员的手中留下更多的控制。
软件包文档在以下位置可用:

http://godoc.org/github.com/russross/meddler

软件包位于 github 上,自述文件中有更多信息:

http://github.com/russross/meddler

目前,这是为 SQLite 。MySQL 和 PostgreSQL 配置的,但是它可以配置为与其他数据库一起使用。 如果你成功使用了其他数据库,请与我联系,我将把它添加到预先配置的数据库列表中。
危险

Meddler 仍在开发中,并且对 API 的附加向后不兼容更改很可能是。 最近更改了对多个数据库类型的支持,并且使它们更容易在他们之间进行 switch 。 这很可能会影响初始化库以使用数据库 (请参阅下面的安装部分) 的方式。

另一个最新更新是对主键的 int64 的更改。 这个 MATCHES 在数据库 / sql 中使用的约定,而且更便携,但是它可以能需要一些小的更改。
安装

通常的 go get 命令将把它放在你的 $GOPATH 中:
go get github.com/russross/meddler
如果只使用一种数据库类型,则应将默认设置为 MATCH 的数据库类型,e.g.:
meddler.Default = meddler.PostgreSQL
默认数据库是 MySQL,所以你应该更改它。 若要在单个项目中使用多个数据库,或者使用 MySQL 。PostgreSQL 或者 SQLite 以外的数据库,请参阅以下内容。

注意:如果在 github.com/go-sql-driver/mysql 驱动程序使用 MySQL,则必须在 sql.Open 调用中设置 "parsetime=true",否则时间转换 meddlers 将无法正常工作。
为什么?
这些特性将 meddler 与类似库分开:

它使用标准的数据库 / sql 类型,并且不需要在你的结构中。 这允许你选择性地使用 meddler,而不用 Having 更改项目中已经存在的其他数据库代码。 创建 meddler 后,我将它合并到一个现有项目中,并且可以一次将代码转换为一个结构和一个查询。
它通过整数主键对简单 INSERT/UPDATE/SELECT 查询具有便利性,但超出了查询写入的时间。
它支持 on-the-fly 数据转换。 如果结构中有 map 或者 Slice,可以使用 JSON 或者采空区自动对 meddler 进行编码 / 解码。 
如果有时间字段,可以让 meddler 自动将它们以 UTC 格式写入数据库,并将它们转换为读取时的本地时区。 这些处理器被称为 "meddlers",因为它们调用数据而不是直接传递它。
自动转换空值 -数据库中的空字段可以读取为结构中零值,并且结构中的零值可以写入空值。 这并不总是正确的事情,但它通常足够好,并且比大多数选择简单得多。
它为更复杂的情况开放低级钩子函数。 如果你编写的查询没有很好地映射到主 helper 函数,你仍然可以以使用低级函数来构建自己的helper。

高级功能

Meddler 并不新建或者修改表结构。 它只是提供了一种胶水函数,以便更容易地将结构读写成 SQL 行。 首先定义一个结构:

type Person struct {
	ID      int       `meddler:"id,pk"`
	Name    string    `meddler:"name"`
	Age     int       //自动转换为 `meddler:"Age"`
	salary  int       //不导出 meddler 无法看到它。 将被忽略。
	Created time.Time `meddler:"created,localtime"`
	Closed  time.Time `meddler:",localtimez"`
}
如果提供了可选标记,则tag的第一个字段名是数据库列名 。如果没有标记,字段名将被作为列名。
`ID`标识被标记为主键。 目前只支持整数主键。 这只与加载,保存,插入和更新有关,一些高级函数需要理解主键。 Meddler 将 `pk` 字段在数据库中设置了自动递增机制。
`Age` 使用一个列名为 "Age" 的列 。 只有当列名与字段名不同时,或者者需要选择它的他选项时,才需要使用标记。
`salary` 是小写字母开头,不是导出字段,因此 meddler 无法看到它。 将被忽略。
`Created` 已经用 "localtime" 标记。 这意味着在保存时它将转换成 UTC时区,并在加载时返回到本地时区。[北京时间=UTC时间+8]
`Closed` 使用一个列名为 "Closed" 的列,因为该标记没有指定任何不同的值。 Closed 被标记为 "localtimez"。 这与 "localtime" 具有相同的属性,但是零时间将在数据库中保存为Null(并且Null将作为零时间值加载) 。

Meddlers tag标签 可选配置

meddler 是在保存字段或者加载字段之前对它的进行干预的处理程序。 ""还有"localtimez 是构建 meddlers 的例子。 内置 meddlers 的完整列表包括:
identity:默认 meddler,它不执行任何操作
localtime: 针对 time.Time 和 *time.Time 字段。 在保存时将该值转换为 UTC,并在加载时返回到本地时区。 若要设置本地时区,请确保在启动程序时设置 TZ 环境变量,或者使用类似以下方法:

os.Setenv("TZ","America/Denver")

在初始设置中,在开始使用时间函数之前。
localtimez: 相同,但仅用于 time.Time, 并将零时间视为空字段( 转换两种方法)
utctime: 与 localtime 类似,但在加载时保持该值为 UTC 。 这确保保存时Time总是 coverted 到 UTC,这是在数据库中节省时间值的理智方法。
utctimez: 相同,但零时间意味着空。
zeroisnull: 对于它的他类型,应该将零值插入为 null,并且空值应该读为零值。 适用于整数。无符号整数。浮点数。复数和字符串类型。 注意:不是指针类型。
JSON: 在保存时将字段值封装到 JSON 中,并在加载时进行反引用。
jsongzip: 同样,但在保存时使用 gzip 压缩,在加载时解压
采空区:在保存时对场地值进行编码,并对载荷进行解码。
gobgzip: 同样,但在保存时使用 gzip 压缩,在加载时解压
你也可以通过实现 Meddler 接口来实现定制的 meddlers 。 参见 medder.go 中的现有实现示例。

Meddler 提供了几个高级功能( 请注意: DB 是与 *sql.DB 或者 *sql.Tx 一起工作的接口):

1. Load(db DB, table string, dst interface{}, pk int64) error

Load函数 通过主键加载单个记录。 例如:

p := new(Person)
err := meddler.Load(db,"person", p, 15)
// db 可以是 *sql.DB 或者 *sql.Tx. 
// table是表名,
// dst 是指向该表的结构的指针。
// pk 是主 key-value,

注意:这个调用要求结构有一个整数主键字段标记为。

2. Insert(db DB, table string, src interface{}) error

这将在数据库中插入新行。 如果结构值具有主键字段,则必须为零( 并且将从 insert 语句中省略,提示默认的自动增量值) 。

elt := &Person{
 Name:"Alice",
 Age: 22,
//...
}
err := meddler.Insert(db,"person", elt)
//elt.ID is updated to the value assigned by the database

3. Update(db DB, table string, src interface{}) error

根据主键更新现有行。 它必须有一个主键,并且主键必须不为零。

注意:这个调用要求结构有一个整数主键字段标记为。

4. Save(db DB, table string, src interface{}) error

自动选取插入或者更新。 如果存在非零主键,则使用更新,否则使用插入。

注意:这个调用要求结构有一个整数主键字段标记为。

5. QueryRow(db DB, dst interface{}, query string, args ...interface) error

执行给定查询,并将单行结果扫描为 dst,这必须是指向结构的指针。

    p := new(Person)
    err := meddler.QueryRow(db, p,"select * from person where name =?","bob")

6. QueryAll(db DB, dst interface{}, query string, args ...interface) error

执行给定的查询,并将结果扫描到 dst,它必须是一个指向结构指针 Slice 的指针。

    var people []*Person
    err := meddler.QueryAll(db, &people,"select * from person")

7. Scan(rows *sql.Rows, dst interface{}) error

将一行数据扫描到一个结构中,并通过干预完成。 可以重复调用以遍历结果集中的所有行。 当没有更多数据时返回 sql.ErrNoRows 。

8. ScanRow(rows *sql.Rows, dst interface{}) error

类似于扫描,但保证在返回行对象时对它的进行关闭。 如果没有行,也返回 sql.ErrNoRows 。

9. ScanAll(rows *sql.Rows, dst interface{}) error

指向指向结构的结构 / 指针片段的指针,并在行集中追加多个元素。 结束时关闭行设置。 不在空集上返回 sql.ErrNoRows ;相反,它只是不向 Slice 添加任何内容。

注意:所有这些函数都可以用作数据库对象的方法。 当用作包函数时,它们使用默认的数据库对象,这是 MySQL,除非你更改它。

获取列名

    p := new(Person)
	names, err := meddler.Columns(p, true)
	if err != nil {
		t.Errorf("Error getting Columns: %v", err)
	}
	fmt.Println(names)
    //[]string{"id", "name", "Email", "Age", "opened", "closed", "updated", "height"}
    //[id name Email Age opened closed updated height]

获取Quoted之后的列名

    names, err := meddler.ColumnsQuoted(p, true)
    fmt.Println(names)
    `id`,`name`,`Email`,`Age`,`opened`,`closed`,`updated`,`height`

查找对象的主键

p := new(Person)
p.ID = 56
name, val, err := meddler.PrimaryKey(p)
fmt.Println(name, val) // id 56

获取所有的对象值

alice.ID = 15
lst, err := meddler.Values(alice, true)

使用不同的数据库类型

Meddler 可以同时处理多个数据库类型。 数据库特定参数存储在数据库结构中,并且为 MySQL 。PostgreSQL 和 SQLite 预先定义结构。

不要依赖包级别函数,而是使用适当的数据库类型 e.g. 上的方法窗体:

err = meddler.PostgreSQL.Load(...)

代替

err = meddler.Load(...)

或者要保存键入,请为每个数据库定义你自己的缩写 NAME:

ms := meddler.MySQL
pg := meddler.PostgreSQL
err = ms.Load(...)
err = pg.QueryAll(...)

如果需要其他数据库,请使用适当的参数集创建自己的数据库实例。 如果一切正常,请与我联系使用的参数,以便我可以添加新的数据库到预先定义的列表。
低级函数

如果使用更复杂的查询,并且只想减少读写值的烦恼,那么还有一些低级 helper 函数。 有关详细信息,请参阅包文档,并查看高级函数的实现,以了解它们是如何使用的。

`