A better ORM for Go and database/sql
.
It uses non-empty interfaces, code generation (go generate
), and initialization-time reflection
as opposed to interface{}
, type system sidestepping, and runtime reflection. It will be kept simple.
Supported SQL dialects:
RDBMS | Library and drivers | Tested with |
---|---|---|
PostgreSQL | github.com/lib/pq (postgres ) |
All supported versions. |
MySQL | github.com/go-sql-driver/mysql (mysql ) |
All supported versions. |
SQLite3 | github.com/mattn/go-sqlite3 (sqlite3 ) |
|
Microsoft SQL Server | github.com/denisenkom/go-mssqldb (mssql , sqlserver ) |
Windows: SQL2008R2SP2, SQL2012SP1, SQL2014, SQL2016. Linux: microsoft/mssql-server-linux:latest Docker image. |
Note that for MySQL clientFoundRows=true
flag is required.
-
Make sure you are using Go 1.7+. Install or update
reform
package,reform
andreform-db
commands (see about versioning below):go get -u gopkg.in/reform.v1/...
-
Use
reform-db
command to generate models for your existing database schema. For example:reform-db -db-driver=sqlite3 -db-source=example.sqlite3 init
-
Update generated models or write your own –
struct
representing a table or view row. For example, store this in fileperson.go
://go:generate reform //reform:people type Person struct { ID int32 `reform:"id,pk"` Name string `reform:"name"` Email *string `reform:"email"` CreatedAt time.Time `reform:"created_at"` UpdatedAt *time.Time `reform:"updated_at"` }
Magic comment
//reform:people
links this model topeople
table or view in SQL database. The first value in field'sreform
tag is a column name.pk
marks primary key. Use value-
or omit tag completely to skip a field. Use pointers for nullable fields. -
Run
reform [package or directory]
orgo generate [package or file]
. This will createperson_reform.go
in the same package with typePersonTable
and methods onPerson
. -
See documentation how to use it. Simple example:
// Use reform.NewDB to create DB. // Save record (performs INSERT or UPDATE). person := &Person{ Name: "Alexey Palazhchenko", Email: pointer.ToString("alexey.palazhchenko@gmail.com"), } if err := DB.Save(person); err != nil { log.Fatal(err) } // ID is filled by Save. person2, err := DB.FindByPrimaryKeyFrom(PersonTable, person.ID) if err != nil { log.Fatal(err) } fmt.Println(person2.(*Person).Name) // Delete record. if err = DB.Delete(person); err != nil { log.Fatal(err) } // Find records by IDs. persons, err := DB.FindAllFrom(PersonTable, "id", 1, 2) if err != nil { log.Fatal(err) } for _, p := range persons { fmt.Println(p) }
reform was born during summer 2014 out of frustrations with existing Go ORMs. All of them have a method
Save(record interface{})
which can be used like this:
orm.Save(User{Name: "gopher"})
orm.Save(&User{Name: "gopher"})
orm.Save(nil)
orm.Save("Batman!!")
Now you can say that last invocation is obviously invalid, and that it's not hard to make an ORM to accept both first and second versions. But there are two problems:
- Compiler can't check it. Method's signature in
godoc
will not tell us how to use it. We are essentially working against those tools by sidestepping type system. - First version is still invalid, since one would expect
Save()
method to set record's primary key afterINSERT
, but this change will be lost due to passing by value.
First proprietary version of reform was used in production even before go generate
announcement.
This free and open-source version is the fourth milestone on the road to better and idiomatic API.
We are following Semantic Versioning, using gopkg.in and filling a changelog.
We use branch v1-stable
(default on Github) for v1 development and tags v1.Y.Z
for releases.
All v1 releases are SemVer-compatible, breaking changes will not be applied.
Canonical import path is gopkg.in/reform.v1
.
go get -u gopkg.in/reform.v1/reform
will install latest released version.
To install not yet released v1 version one can do checkout manually while preserving import path:
cd $GOPATH/src/gopkg.in/reform.v1
git fetch
git checkout origin/v1-stable
go install -v gopkg.in/reform.v1/reform
Branch v2-unstable
is used for v2 development. It doesn't have any releases yet, and no compatibility is guaranteed.
Canonical import path is gopkg.in/reform.v2-unstable
.
- github.com/AlekSi/pointer is very useful for working with reform structs with pointers.
- There should be zero
pk
fields for Struct and exactly onepk
field for Record. Composite primary keys are not supported (#114). pk
field can't be a pointer (== nil
doesn't work).- Database row can't have a Go's zero value (0, empty string, etc.) in primary key column.
Code is covered by standard MIT-style license. Copyright (c) 2016-2017 Alexey Palazhchenko. See LICENSE for details. Note that generated code is covered by the terms of your choice.
The reform gopher was drawn by Natalya Glebova. Please use it only as reform logo. It is based on the original design by Renée French, released under Creative Commons Attribution 3.0 USA license.