Golang ORMs

A survey of the current state of Golang ORMs

30 January 2014

Jesse Szwedko

Software Engineer, ModCloth Inc.

Why ORMs?

Evaluation metrics

ORMs

database/sql

type User struct {
  Id        int
  FirstName string
  LastName  string
  Age       int
}

db, _ := sql.Open('sqlite3', 'sqlite:///tmp/tmp.db')

database/sql CRUD

db.Exec(
  "INSERT INTO users (first_name, last_name, age) VALUES (?, ?, ?)",
  "John", "Doe", 25
)

row := db.QueryRow("SELECT id, first_name, last_name, age FROM users WHERE id = ?", 1)

row.Scan(&user.Id, &user.FirstName, &user.LastName, &user.Age)

user.FirstName = "James"

db.Exec(
  "UPDATE users SET first_name=?, last_name=?, age=? WHERE id = ?",
  user.Id, user.FirstName, user.LastName, user.Age
)

db.Exec(
  "DELETE FROM users WHERE id = ?",
  user.Id
)

coopernurse/gorp

coopernurse/gorp CRUD

type User struct {
    Id        int    `db:"id"`
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Age       int    `db:"age"`
}
    dbMap.AddTableWithName(User{}, "users").SetKeys(true, "Id")
package main

import (
	"database/sql"
	"fmt"
	"github.com/coopernurse/gorp"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int    `db:"id"`
	FirstName string `db:"first_name"`
	LastName  string `db:"last_name"`
	Age       int    `db:"age"`
}

//END STRUCT OMIT

func SetupDb() (dbMap *gorp.DbMap) {
	var (
		err error
		db  *sql.DB
	)

	if db, err = sql.Open("sqlite3", ":memory:"); err != nil {
		panic(nil)
	}

	dbMap = &gorp.DbMap{
		Db:      db,
		Dialect: gorp.SqliteDialect{},
	}

	//START REGISTER OMIT

	dbMap.AddTableWithName(User{}, "users").SetKeys(true, "Id")

	//END REGISTER OMIT

	if err = dbMap.CreateTablesIfNotExists(); err != nil {
		panic(err)
	}

	return dbMap
}

func PrintTable(dbMap *gorp.DbMap) {
	var users []User

	dbMap.Select(&users, "SELECT * FROM users")

	fmt.Printf("%+v\n", users)
}

func main() {
	var (
		dbMap *gorp.DbMap
		user  *User
		iUser interface{}
	)

	dbMap = SetupDb()

    dbMap.Insert(&User{Id: 1, FirstName: "John", LastName: "Doe"})
    PrintTable(dbMap)

    iUser, _ = dbMap.Get(User{}, 1)
    user = iUser.(*User)

    user.FirstName = "James"
    dbMap.Update(user)
    PrintTable(dbMap)

    dbMap.Delete(user)
    PrintTable(dbMap)
}

coopernurse/gorp Complex query creation

type User struct {
    Id        int    `db:"id"`
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Age       int    `db:"age"`
}
package main

import (
	"database/sql"
	"fmt"
	"github.com/coopernurse/gorp"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int    `db:"id"`
	FirstName string `db:"first_name"`
	LastName  string `db:"last_name"`
	Age       int    `db:"age"`
}

//END STRUCT OMIT
func SetupDb() (dbMap *gorp.DbMap) {
	var (
		err error
		db  *sql.DB
	)

	if db, err = sql.Open("sqlite3", ":memory:"); err != nil {
		panic(nil)
	}

	dbMap = &gorp.DbMap{
		Db:      db,
		Dialect: gorp.SqliteDialect{},
	}

	//START REGISTER OMIT

	dbMap.AddTableWithName(User{}, "users").SetKeys(true, "Id")

	//END REGISTER OMIT

	if err = dbMap.CreateTablesIfNotExists(); err != nil {
		panic(err)
	}

	return dbMap
}

func main() {
	var (
		dbMap *gorp.DbMap
	)

	dbMap = SetupDb()

	//START SETUP OMIT
	dbMap.Insert(&User{
		Id:        1,
		FirstName: "John",
		LastName:  "Doe",
		Age:       24,
	})

	dbMap.Insert(&User{
		Id:        2,
		FirstName: "Jane",
		LastName:  "Doe",
		Age:       52,
	})

	dbMap.Insert(&User{
		Id:        3,
		FirstName: "Joe",
		LastName:  "Shmoe",
		Age:       10,
	})
	//END SETUP OMIT

    var users []User
    dbMap.Select(&users, "SELECT * FROM users WHERE last_name = ? OR age < ? ORDER BY age DESC LIMIT 2", "Doe", 12)
    fmt.Printf("%+v\n", users)
}

coopernurse/gorp SQL Migration

coopernurse/gorp Hooks

coopernurse/gorp Notes

eaigner/hood

eaigner/hood CRUD

type User struct {
  Id        hood.Id `db:"id"`
  FirstName string  `db:"first_name"`
  LastName  string  `db:"last_name"`
  Age       int     `db:"age"`
}

hd.Save(&User{Id: 1, FirstName: "John", LastName: "Doe"})

hd.Where("id", "=", 1)

user.FirstName = "James"
hd.Save(user)

hd.Delete(user)

eaigner/hood Complex query creation

type User struct {
  Id        int    `db:"id"`
  FirstName string `db:"first_name"`
  LastName  string `db:"last_name"`
  Age       int    `db:"age"`
}

hd.Where("last_name", "=", "Doe").Or("age", "<", 12).OrderBy("age").Limit(2).Find(&users)

eaigner/hood SQL Migration

func (m *M) CreateUserTable_1357605106_Up(hd *hood.Hood) {
  type Users struct {
    Id      hood.Id
      First   string
      Last    string
  }
  hd.CreateTable(&Users{})
}

eaigner/hood Hooks

eaigner/hood Notes

coocood/qbs

coocood/qbs CRUD

type User struct {
    Id        int64
    FirstName string
    LastName  string
    Age       int
}
package main

import (
	"fmt"
	"github.com/coocood/qbs"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"os"
)

//START STRUCT OMIT

type User struct {
	Id        int64
	FirstName string
	LastName  string
	Age       int
}

//END STRUCT OMIT

func init() {
	os.Remove("/tmp/tmp.db")
	qbs.Register("sqlite3", "/tmp/tmp.db", "", qbs.NewSqlite3())
}

func SetupDb() (q *qbs.Qbs) {
	var (
		err       error
		migration *qbs.Migration
	)

	qbs.SetLogger(log.New(os.Stdout, "query:", log.Lmicroseconds), log.New(os.Stdout, "error:", log.Lmicroseconds))

	q, err = qbs.GetQbs()

	if q, err = qbs.GetQbs(); err != nil {
		panic(err)
	}

	if migration, err = qbs.GetMigration(); err != nil {
		panic(err)
	}
	defer migration.Close()
	if err = migration.CreateTableIfNotExists(new(User)); err != nil {
		panic(err)
	}

	return q
}

func PrintTable(q *qbs.Qbs) {
	var users []*User

	q.FindAll(&users)

	for _, user := range users {
		fmt.Printf("%+v,", user)
	}
	fmt.Println()
}

func main() {
	var (
		q    *qbs.Qbs
		user *User
	)

	q = SetupDb()
	defer q.Close()

    q.Save(&User{Id: 1, FirstName: "John", LastName: "Doe", Age: 25})
    PrintTable(q)

    user = &User{Id: 1}
    q.Find(user)

    user.FirstName = "James"
    q.Save(user)
    PrintTable(q)

    q.Delete(user)
    PrintTable(q)
}

coocood/qbs Complex query creation

type User struct {
    Id        int64
    FirstName string
    LastName  string
    Age       int
}
package main

import (
	"fmt"
	"github.com/coocood/qbs"
	_ "github.com/mattn/go-sqlite3"
	"log"
	"os"
)

//START STRUCT OMIT

type User struct {
	Id        int64
	FirstName string
	LastName  string
	Age       int
}

//END STRUCT OMIT

func init() {
	os.Remove("/tmp/tmp.db")
	qbs.Register("sqlite3", "/tmp/tmp.db", "", qbs.NewSqlite3())
}

func SetupDb() (q *qbs.Qbs) {
	var (
		err       error
		migration *qbs.Migration
	)

	qbs.SetLogger(log.New(os.Stdout, "query:", log.Lmicroseconds), log.New(os.Stdout, "error:", log.Lmicroseconds))

	q, err = qbs.GetQbs()

	if q, err = qbs.GetQbs(); err != nil {
		panic(err)
	}

	if migration, err = qbs.GetMigration(); err != nil {
		panic(err)
	}
	defer migration.Close()
	if err = migration.CreateTableIfNotExists(new(User)); err != nil {
		panic(err)
	}

	return q
}

func main() {
	var (
		q *qbs.Qbs
	)

	q = SetupDb()
	defer q.Close()

	//START SETUP OMIT
	q.Save(&User{
		Id:        1,
		FirstName: "John",
		LastName:  "Doe",
		Age:       24,
	})

	q.Save(&User{
		Id:        2,
		FirstName: "Jane",
		LastName:  "Doe",
		Age:       52,
	})

	q.Save(&User{
		Id:        3,
		FirstName: "Joe",
		LastName:  "Shmoe",
		Age:       10,
	})
	//END SETUP OMIT

    var users []*User
    q.Condition(qbs.NewCondition("last_name = ?", "Doe").Or("age < ?", 12)).Limit(2).OrderByDesc("age").FindAll(&users)
    for _, user := range users {
        fmt.Printf("%+v,", user)
    }
    fmt.Println()
}

coocood/qbs SQL Migration

coocood/qbs Relationships

type User struct {
    Id        int64
    FirstName string
    LastName  string
    Age       int
    Profile   *Profile
    ProfileId int64
}

type Profile struct {
    Id        int64
    Homepage  string
    Interests string
}
package main

import (
	"fmt"
	"github.com/coocood/qbs"
	_ "github.com/mattn/go-sqlite3"
	"os"
)

//START STRUCT OMIT

type User struct {
	Id        int64
	FirstName string
	LastName  string
	Age       int
	Profile   *Profile
	ProfileId int64
}

type Profile struct {
	Id        int64
	Homepage  string
	Interests string
}

//END STRUCT OMIT

func init() {
	os.Remove("/tmp/tmp.db")
	qbs.Register("sqlite3", "/tmp/tmp.db", "", qbs.NewSqlite3())
}

func SetupDb() (q *qbs.Qbs) {
	var (
		err       error
		migration *qbs.Migration
	)

	q, err = qbs.GetQbs()

	if q, err = qbs.GetQbs(); err != nil {
		panic(err)
	}

	if migration, err = qbs.GetMigration(); err != nil {
		panic(err)
	}
	defer migration.Close()

	if err = migration.CreateTableIfNotExists(new(User)); err != nil {
		panic(err)
	}

	if err = migration.CreateTableIfNotExists(new(Profile)); err != nil {
		panic(err)
	}

	return q
}

func PrintTable(q *qbs.Qbs) {
	var users []*User

	q.FindAll(&users)

	for _, user := range users {
		fmt.Printf("%+v,", user)
	}
	fmt.Println()
}

func main() {
	var (
		q    *qbs.Qbs
		user *User
	)

	q = SetupDb()
	defer q.Close()

	profile := &Profile{Homepage: "www.example.com", Interests: "Golang", Id: 1}
	q.Save(profile)
	q.Save(&User{Id: 1, FirstName: "John", LastName: "Doe", Age: 25, ProfileId: 1})

    user = &User{Id: 1}
    q.Find(user)

    fmt.Printf("%+v\n", user)
    fmt.Printf("%+v\n", user.Profile)
}

coocood/qbs Hooks

coocood/qbs Notes

astaxie/beedb

astaxie/beedb CRUD

type User struct {
    Id        int
    FirstName string
    LastName  string
    Age       int
}
package main

import (
	"database/sql"
	"fmt"
	"github.com/astaxie/beedb"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int
	FirstName string
	LastName  string
	Age       int
}

//END STRUCT OMIT

func init() {
	beedb.PluralizeTableNames = true
}

func SetupDb() (orm beedb.Model) {
	var (
		err error
		db  *sql.DB
	)

	if db, err = sql.Open("sqlite3", ":memory:"); err != nil {
		panic(err)
	}

	if _, err = db.Exec(`CREATE TABLE users (
    id INTEGER,
    first_name VARCHAR(80),
    last_name VARCHAR(80),
    age INTEGER
  );
  `); err != nil {
		panic(err)
	}

	return beedb.New(db)
}

func PrintTable(orm beedb.Model) {
	var users []User

	if err := orm.FindAll(&users); err != nil {
		panic(err)
	}

	fmt.Printf("%+v,", users)
	for _, user := range users {
		fmt.Printf("%+v,", user)
	}
	fmt.Println()
}

func main() {
	var (
		orm  beedb.Model
		user *User
	)

	user = new(User)

	orm = SetupDb()

    orm.SetTable("users").Insert(map[string]interface{}{"id": 1, "first_name": "John", "last_name": "Doe", "age": 25})
    PrintTable(orm)

    orm.Where(1).Find(user)

    user.FirstName = "James"
    orm.Save(user)
    PrintTable(orm)

    orm.Delete(user)
    PrintTable(orm)
}

astaxie/beedb Complex query creation

type User struct {
    Id        int64
    FirstName string
    LastName  string
    Age       int
}
package main

import (
	"database/sql"
	"fmt"
	"github.com/astaxie/beedb"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int64
	FirstName string
	LastName  string
	Age       int
}

//END STRUCT OMIT
//
func init() {
	beedb.PluralizeTableNames = true
}

func SetupDb() (orm beedb.Model) {
	var (
		err error
		db  *sql.DB
	)

	if db, err = sql.Open("sqlite3", ":memory:"); err != nil {
		panic(err)
	}

	if _, err = db.Exec(`CREATE TABLE users (
    id INTEGER,
    first_name VARCHAR(80),
    last_name VARCHAR(80),
    age INTEGER
  );
  `); err != nil {
		panic(err)
	}

	return beedb.New(db)
}

func main() {
	var (
		orm beedb.Model
	)

	orm = SetupDb()

	//START SETUP OMIT
	orm.SetTable("users").Insert(map[string]interface{}{
		"id":         1,
		"first_name": "John",
		"last_name":  "Doe",
		"age":        24,
	})
	orm.SetTable("users").Insert(map[string]interface{}{
		"id":         2,
		"first_name": "Jane",
		"last_name":  "Doe",
		"age":        52,
	})
	orm.SetTable("users").Insert(map[string]interface{}{
		"id":         1,
		"first_name": "Joe",
		"last_name":  "Shmoe",
		"age":        10,
	})
	//END SETUP OMIT

    var users []User
    orm.Where("last_name = ? OR age < ?", "Doe", 12).Limit(2).OrderBy("age").FindAll(&users)
    for _, user := range users {
        fmt.Printf("%+v,", user)
    }
    fmt.Println()
}

astaxie/beedb SQL Migration

astaxie/beedb Notes

upper.io/db (replaces gosexy/db)

upper.io/db CRUD

type User struct {
    Id        int    `field:"id"`
    FirstName string `field:"first_name"`
    LastName  string `field:"last_name"`
}
package main

import (
	"database/sql"
	"fmt"

	"upper.io/db"
	_ "upper.io/db/sqlite"
)

//START STRUCT OMIT

type User struct {
	Id        int    `field:"id"`
	FirstName string `field:"first_name"`
	LastName  string `field:"last_name"`
}

//END STRUCT OMIT

func SetupCollection() (col db.Collection) {
	var (
		err      error
		sess     db.Database
		settings db.Settings
	)

	settings = db.Settings{
		Database: `:memory:`,
	}

	if sess, err = db.Open("sqlite", settings); err != nil {
		panic(err)
	}

	if _, err = sess.Driver().(*sql.DB).Exec(`CREATE TABLE users (
    id INTEGER,
    first_name VARCHAR(80),
    last_name VARCHAR(80)
  );
  `); err != nil {
		panic(err)
	}

	if col, err = sess.Collection("users"); err != nil {
		panic(err)
	}

	return col
}

func PrintTable(col db.Collection) {
	var users []User

	col.Find().All(&users)

	fmt.Printf("%+v\n", users)
}

func main() {
	var (
		col  db.Collection
		user User
	)

	col = SetupCollection()

    col.Append(User{Id: 1, FirstName: "John", LastName: "Doe"})
    PrintTable(col)

    col.Find(db.Cond{"id": 1}).One(&user)

    user.FirstName = "James"
    col.Find(user.Id).Update(user)
    PrintTable(col)

    col.Find(db.Cond{"id": user.Id}).Remove()
    PrintTable(col)
}

upper.io/db Complex query creation

type User struct {
    Id        int    `field:"id"`
    FirstName string `field:"first_name"`
    LastName  string `field:"last_name"`
    Age       int    `field:"age"`
}
package main

import (
	"database/sql"
	"fmt"
	"upper.io/db"
	_ "upper.io/db/sqlite"
)

//START STRUCT OMIT

type User struct {
	Id        int    `field:"id"`
	FirstName string `field:"first_name"`
	LastName  string `field:"last_name"`
	Age       int    `field:"age"`
}

//END STRUCT OMIT

func SetupCollection() (col db.Collection) {
	var (
		err      error
		sess     db.Database
		settings db.Settings
	)

	settings = db.Settings{
		Database: `:memory:`,
	}

	if sess, err = db.Open("sqlite", settings); err != nil {
		panic(err)
	}

	if _, err = sess.Driver().(*sql.DB).Exec(`CREATE TABLE users (
    id INTEGER,
    first_name VARCHAR(80),
    last_name VARCHAR(80),
    age INTEGER
  );
  `); err != nil {
		panic(err)
	}

	if col, err = sess.Collection("users"); err != nil {
		panic(err)
	}

	return col
}

func main() {
	var (
		col db.Collection
	)

	col = SetupCollection()

	//START SETUP OMIT
	col.Append(User{
		Id:        1,
		FirstName: "John",
		LastName:  "Doe",
		Age:       24,
	})

	col.Append(User{
		Id:        2,
		FirstName: "Jane",
		LastName:  "Doe",
		Age:       52,
	})

	col.Append(User{
		Id:        3,
		FirstName: "Joe",
		LastName:  "Shmoe",
		Age:       10,
	})
	//END SETUP OMIT

    var users []User
    col.Find(db.Or{
        db.Cond{"last_name": "Doe"},
        db.Cond{"age <": "12"},
    }).
        Limit(2).
        Sort("-age").
        All(&users)

    fmt.Printf("%+v\n", users)
}

upper.io/db Notes

jinzhu/gorm

jinzhu/gorm CRUD

type User struct {
    Id        int64
    FirstName string
    LastName  string
    Age       int
}
package main

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int64
	FirstName string
	LastName  string
	Age       int
}

//END STRUCT OMIT

func SetupDb() (db gorm.DB) {
	var (
		err error
	)

	if db, err = gorm.Open("sqlite3", ":memory:"); err != nil {
		panic(err)
	}

	db.CreateTable(User{})

	return db
}

func PrintTable(db gorm.DB) {
	var users []User

	db.Find(&users)

	fmt.Printf("%+v\n", users)
}

func main() {
	var (
		db   gorm.DB
		user *User
	)

	user = new(User)

	db = SetupDb()

    db.Save(&User{FirstName: "John", LastName: "Doe", Age: 25})
    PrintTable(db)

    db.First(user, 1)

    user.FirstName = "James"
    db.Save(user)
    PrintTable(db)

    db.Delete(user)
    PrintTable(db)
}

jinzhu/gorm Complex query creation

type User struct {
    Id        int64
    FirstName string
    LastName  string
    Age       int
}
package main

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int64
	FirstName string
	LastName  string
	Age       int
}

//END STRUCT OMIT

func SetupDb() (db gorm.DB) {
	var (
		err error
	)

	if db, err = gorm.Open("sqlite3", ":memory:"); err != nil {
		panic(err)
	}

	db.CreateTable(User{})

	return db
}

func main() {
	var (
		db gorm.DB
	)

	db = SetupDb()

	db.Save(&User{
		FirstName: "John",
		LastName:  "Doe",
		Age:       24,
	})

	db.Save(&User{
		FirstName: "Jane",
		LastName:  "Doe",
		Age:       52,
	})
	db.Save(&User{
		FirstName: "Joe",
		LastName:  "Shmoe",
		Age:       10,
	})

    var users []User
    db.Where("last_name = ?", "Doe").Or("age < ?", 12).Limit(2).Order("age", true).Find(&users)
    for _, user := range users {
        fmt.Printf("%+v,", user)
    }
    fmt.Println()
}

jinzhu/gorm SQL Migration

jinzhu/gorm Hooks

jinzhu/gorm Notes

func AmountGreaterThan1000(d *gorm.DB) *gorm.DB {
  d.Where("amount > ?", 1000)
}
func PaidWithCod(d *gorm.DB) *gorm.DB {
  d.Where("pay_mode_sign = ?", "C")
}
db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)

lunny/xorm

lunny/xorm CRUD

type User struct {
    Id        int    `db:"id"`
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Age       int    `db:"age"`
}
package main

import (
	"fmt"
	"github.com/lunny/xorm"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int    `db:"id"`
	FirstName string `db:"first_name"`
	LastName  string `db:"last_name"`
	Age       int    `db:"age"`
}

//END STRUCT OMIT

func SetupDb() (engine *xorm.Engine) {
	var (
		err error
	)

	if engine, err = xorm.NewEngine("sqlite3", ":memory:"); err != nil {
		panic(err)
	}

	if err = engine.Sync(new(User)); err != nil {
		panic(err)
	}

	return engine
}

func PrintTable(engine *xorm.Engine) {
	var users []User

	engine.Find(&users)

	fmt.Printf("%+v\n", users)
}

func main() {
	var (
		engine *xorm.Engine
		user   *User
	)

	engine = SetupDb()

    engine.Insert(&User{Id: 1, FirstName: "John", LastName: "Doe"})
    PrintTable(engine)

    user = &User{Id: 1}
    engine.Get(user)

    user.FirstName = "James"
    engine.Update(user)
    PrintTable(engine)

    engine.Delete(user)
    PrintTable(engine)
}

lunny/xorm Complex query creation

type User struct {
    Id        int    `db:"id"`
    FirstName string `db:"first_name"`
    LastName  string `db:"last_name"`
    Age       int    `db:"age"`
}
package main

import (
	"fmt"
	"github.com/lunny/xorm"
	_ "github.com/mattn/go-sqlite3"
)

//START STRUCT OMIT

type User struct {
	Id        int    `db:"id"`
	FirstName string `db:"first_name"`
	LastName  string `db:"last_name"`
	Age       int    `db:"age"`
}

//END STRUCT OMIT

func SetupDb() (engine *xorm.Engine) {
	var (
		err error
	)

	if engine, err = xorm.NewEngine("sqlite3", "/tmp/tmp.db"); err != nil {
		panic(err)
	}

	if err = engine.Sync(new(User)); err != nil {
		panic(err)
	}

	return engine
}

func PrintTable(engine *xorm.Engine) {
	var users []User

	engine.Find(&users)

	fmt.Printf("%+v\n", users)
}

func main() {
	var (
		engine *xorm.Engine
	)

	engine = SetupDb()

	engine.Insert(&User{
		Id:        1,
		FirstName: "John",
		LastName:  "Doe",
		Age:       24,
	})

	engine.Insert(&User{
		Id:        2,
		FirstName: "Jane",
		LastName:  "Doe",
		Age:       52,
	})

	engine.Insert(&User{
		Id:        3,
		FirstName: "Joe",
		LastName:  "Shmoe",
		Age:       10,
	})

    var users []User
    engine.Where("last_name = ? OR age < ?", "Doe", "12").OrderBy("age").Limit(2).Find(&users)
    fmt.Printf("%+v\n", users)
}

lunny/xorm SQL Migration

lunny/xorm Hooks

lunny/xorm Notes

Overall notes

Thank you

Jesse Szwedko

Software Engineer, ModCloth Inc.

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)