gorm

package
v0.0.0-...-db1d7af Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2014 License: Apache-2.0 Imports: 16 Imported by: 0

README

GORM

The fantastic ORM library for Golang, aims to be developer friendly.

Install

go get github.com/jinzhu/gorm

Overview

  • Chainable API
  • Relations
  • Callbacks (before/after create/save/update/delete/find)
  • Soft Deletes
  • Auto Migrations
  • Transactions
  • Logger Support
  • Bind struct with tag
  • Iteration Support via Rows
  • Scopes
  • sql.Scanner support
  • Every feature comes with tests
  • Convention Over Configuration
  • Developer Friendly

Conventions

  • Table name is the plural of struct name's snake case. Disable pluralization with db.SingularTable(true), or specify your table name
  • Column name is the snake case of field's name.
  • Use Id int64 field as primary key.
  • Use tag sql to change field's property, change the tag name with db.SetTagIdentifier(new_name).
  • Use CreatedAt to store record's created time if field exists.
  • Use UpdatedAt to store record's updated time if field exists.
  • Use DeletedAt to store record's deleted time if field exists. Soft Delete
  • Gorm uses reflection to know which tables to work with:
// E.g Finding an existing User
var user User
// Gorm will now know to use table "users" ("user" if pluralisation has been disabled) for all operations.
db.First(&user)

// E.g creating a new User
DB.Save(&User{Name: "xxx"}) // table "users"

Getting Started

import (
    "database/sql"
    "time"
)

type User struct {
    Id           int64
    Birthday     time.Time
    Age          int64
    Name         string  `sql:"size:255"`
    CreatedAt    time.Time
    UpdatedAt    time.Time
    DeletedAt    time.Time

    Emails            []Email         // Embedded structs
    BillingAddress    Address         // Embedded struct
    BillingAddressId  sql.NullInt64   // BillingAddress's foreign key
    ShippingAddress   Address         // Another Embedded struct with same type
    ShippingAddressId int64           // ShippingAddress's foreign key
    IgnoreMe          int64 `sql:"-"` // Ignore this field
}

type Email struct {
    Id         int64
    UserId     int64   // Foreign key for User
    Email      string  `sql:"type:varchar(100);"` // Set this field's type
    Subscribed bool
}

type Address struct {
    Id       int64
    Address1 string         `sql:"not null;unique"` // Set this field as not nullable and unique in database
    Address2 string         `sql:"type:varchar(100);unique"`
    Post     sql.NullString `sql:not null`
    // FYI, "NOT NULL" will only work well with NullXXX Scanner, because golang will initalize a default value for most type...
}

Opening a Database


import "github.com/jinzhu/gorm"
import _ "github.com/lib/pq"
// import _ "github.com/go-sql-driver/mysql"
// import _ "github.com/mattn/go-sqlite3"

db, err := Open("postgres", "user=gorm dbname=gorm sslmode=disable")
// db, err = Open("mysql", "gorm:gorm@/gorm?charset=utf8&parseTime=True")
// db, err = Open("sqlite3", "/tmp/gorm.db")

// Get database connection handle [*sql.DB](http://golang.org/pkg/database/sql/#DB)
d := db.DB()

// With it you could use package `database/sql`'s builtin methods
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
db.DB().Ping()

// By default, table name is plural of struct type, you can use struct type as table name with:
db.SingularTable(true)

Gorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere in your project.

// db.go
package db

import (
    "fmt"
    "github.com/jinzhu/gorm"
    _ "github.com/lib/pq"
)

var DB gorm.DB
func init() {
    var err error
    DB, err = gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")

    // Connection string parameters for Postgres - http://godoc.org/github.com/lib/pq, if you are using another
    // database refer to the relevant driver's documentation.

    // * dbname - The name of the database to connect to
    // * user - The user to sign in as
    // * password - The user's password
    // * host - The host to connect to. Values that start with / are for unix domain sockets.
    //   (default is localhost)
    // * port - The port to bind to. (default is 5432)
    // * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq)
    //   Valid SSL modes:
    //    * disable - No SSL
    //    * require - Always SSL (skip verification)
    //    * verify-full - Always SSL (require verification)

    if err != nil {
        panic(fmt.Sprintf("Got error when connect database, the error is '%v'", err))
    }
}

// user.go
package user
import . "db"
...
DB.Save(&User{Name: "xxx"})
...

Struct & Database Mapping

// Create table from struct
db.CreateTable(User{})

// Drop table
db.DropTable(User{})
Automating Migrations

Feel free to update your struct, AutoMigrate will keep your database update to date.

FYI, AutoMigrate will only add new columns, it won't change the current columns' types or delete unused columns, to make sure your data is safe.

If the table doesn't exist when AutoMigrate, gorm will run create the table automatically. (the database first needs to be created manually though...).

(only postgres and mysql supported)

db.AutoMigrate(User{})

Gorm API

Create

user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
db.Save(&user)
NewRecord

Returns true if object hasn’t been saved yet (Id is blank)

user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // => true

db.Save(&user)
db.NewRecord(user) // => false
Create With SubStruct

Refer to Query With Related for how to find associations

user := User{
        Name:            "jinzhu",
        BillingAddress:  Address{Address1: "Billing Address - Address 1"},
        ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
        Emails:          []Email{{Email: "jinzhu@example.com"}, {Email: "jinzhu-2@example@example.com"}},
}

db.Save(&user)
//// BEGIN TRANSACTION;
//// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1");
//// INSERT INTO "addresses" (address1) VALUES ("Shipping Address - Address 1");
//// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
//// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com");
//// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu-2@example.com");
//// COMMIT;

Query

// Get the first record
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;
// Search table `users` is guessed from struct's type

// Get the last record
db.Last(&user)
//// SELECT * FROM users ORDER BY id DESC LIMIT 1;

// Get All records
db.Find(&users)
//// SELECT * FROM users;

// Get record with primary key
db.First(&user, 10)
//// SELECT * FROM users WHERE id = 10;
Query With Where (SQL)
// Get the first matched record
db.Where("name = ?", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;

// Get all matched records
db.Where("name = ?", "jinzhu").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu';

db.Where("name <> ?", "jinzhu").Find(&users)
//// SELECT * FROM users WHERE name <> 'jinzhu';

// IN
db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name IN ('jinzhu', 'jinzhu 2');

// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
//// SELECT * FROM users WHERE name LIKE "%jin%";

// Multiple Conditions
db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
Query With Where (Struct & Map)
// Search with struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;

// Search with map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;

// IN for primary Keys
db.Where([]int64{20, 21, 22}).Find(&users)
//// SELECT * FROM users WHERE id IN (20, 21, 22);
Query With Not
// Attribute Not Equal
db.Not("name", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;

// Not In
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");

// Not In for primary keys
db.Not([]int64{1,2,3}).First(&user)
//// SELECT * FROM users WHERE id NOT IN (1,2,3);

db.Not([]int64{}).First(&user)
//// SELECT * FROM users;

// SQL string
db.Not("name = ?", "jinzhu").First(&user)
//// SELECT * FROM users WHERE NOT(name = "jinzhu");

// Not with struct
db.Not(User{Name: "jinzhu"}).First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu";
Query With Inline Condition
// Find with primary key
db.First(&user, 23)
//// SELECT * FROM users WHERE id = 23 LIMIT 1;

// SQL string
db.Find(&user, "name = ?", "jinzhu")
//// SELECT * FROM users WHERE name = "jinzhu";

// Multiple Conditions
db.Find(&users, "name <> ? and age > ?", "jinzhu", 20)
//// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;

// Inline search with struct
db.Find(&users, User{Age: 20})
//// SELECT * FROM users WHERE age = 20;

// Inline search with map
db.Find(&users, map[string]interface{}{"age": 20})
//// SELECT * FROM users WHERE age = 20;
Query With Or
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
//// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';

// Or With Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

// Or With Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
// Find user's emails with guessed foreign key
db.Model(&user).Related(&emails)
//// SELECT * FROM emails WHERE user_id = 111;

// Find user's billing address with specified foreign key 'BillingAddressId'
db.Model(&user).Related(&address1, "BillingAddressId")
//// SELECT * FROM addresses WHERE id = 123; // 123 is user's BillingAddressId

// Find user with guessed primary key value from email
db.Model(&email).Related(&user)
//// SELECT * FROM users WHERE id = 111; // 111 is email's UserId
Query Chains

Gorm has a chainable API, so you could query like this

db.Where("name <> ?","jinzhu").Where("age >= ? and role <> ?",20,"admin").Find(&users)
//// SELECT * FROM users WHERE name <> 'jinzhu' AND age >= 20 AND role <> 'admin';

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Not("name = ?", "jinzhu").Find(&users)

Update

Update an existing struct
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
//// UPDATE users SET name='jinzhu 2', age=100, updated_at = '2013-11-17 21:34:10' WHERE id=111;
Update one attribute with Update
// Update existing user's name if it is changed
db.Model(&user).Update("name", "hello")
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;

// Find out a user, and update the name if it is changed
db.First(&user, 111).Update("name", "hello")
//// SELECT * FROM users LIMIT 1;
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;

// Update name with search condiation and specified table name
db.Table("users").Where(10).Update("name", "hello")
//// UPDATE users SET name='hello' WHERE id = 10;
Update multiple attributes with Updates
// Update user's name and age if they are changed
db.Model(&user).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;

// Updates with Map
db.Table("users").Where(10).Updates(map[string]interface{}{"name": "hello", "age": 18})
//// UPDATE users SET name='hello', age=18 WHERE id = 10;

// Updates with Struct
db.Model(User{}).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18;
Update attributes without callbacks
db.Model(&user).UpdateColumn("name", "hello")
//// UPDATE users SET name='hello' WHERE id = 111;

db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18 WHERE id = 111;

Delete

Delete an existing struct
db.Delete(&email)
// DELETE from emails where id=10;
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
// DELETE from emails where email LIKE "%jinhu%";
Soft Delete

If a struct has a DeletedAt field, it will get soft delete ability automatically!

Structs that don't have a DeletedAt field will be deleted from the database permanently

db.Delete(&user)
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// Delete with search condition
db.Where("age = ?", 20).Delete(&User{})
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// Soft deleted records will be ignored when searched
db.Where("age = 20").Find(&user)
//// SELECT * FROM users WHERE age = 100 AND (deleted_at IS NULL AND deleted_at <= '0001-01-02');

// Find soft deleted records with Unscoped
db.Unscoped().Where("age = 20").Find(&users)
//// SELECT * FROM users WHERE age = 20;

// Delete record permanently with Unscoped
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;

FirstOrInit

Try to get the first record, if failed, will initialize the struct with search conditions.

(only supports search conditions map and struct)

db.FirstOrInit(&user, User{Name: "non_existing"})
//// User{Name: "non_existing"}

db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
//// User{Id: 111, Name: "Jinzhu", Age: 20}

db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
//// User{Id: 111, Name: "Jinzhu", Age: 20}
FirstOrInit With Attrs

Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.

db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = 'non_existing';
//// User{Name: "non_existing", Age: 20}

// Or write it like this if has only one attribute
db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user)

// If a record found, Attrs would be ignored
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = jinzhu';
//// User{Id: 111, Name: "Jinzhu", Age: 20}
FirstOrInit With Assign

Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not.

db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
//// User{Name: "non_existing", Age: 20}

db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
//// User{Id: 111, Name: "Jinzhu", Age: 30}

FirstOrCreate

Try to get the first record, if failed, will initialize the struct with the search conditions and insert it in the database.

db.FirstOrCreate(&user, User{Name: "non_existing"})
//// User{Id: 112, Name: "non_existing"}

db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
//// User{Id: 111, Name: "Jinzhu"}

db.FirstOrCreate(&user, map[string]interface{}{"name": "jinzhu", "age": 30})
//// user -> User{Id: 111, Name: "jinzhu", Age: 20}
FirstOrCreate With Attrs

Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.

db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'non_existing';
//// User{Id: 112, Name: "non_existing", Age: 20}

db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
//// User{Id: 111, Name: "jinzhu", Age: 20}
FirstOrCreate With Assign

Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not, then save it back to the database.

db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
//// user -> User{Id: 112, Name: "non_existing", Age: 20}

db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'jinzhu';
//// UPDATE users SET age=30 WHERE id = 111;
//// User{Id: 111, Name: "jinzhu", Age: 30}

Select

db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;

Order

db.Order("age desc, name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;

// Multiple orders
db.Order("age desc").Order("name").Find(&users)
//// SELECT * FROM users ORDER BY age desc, name;

// ReOrder
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
//// SELECT * FROM users ORDER BY age desc; (users1)
//// SELECT * FROM users ORDER BY age; (users2)

Limit

db.Limit(3).Find(&users)
//// SELECT * FROM users LIMIT 3;

// Remove limit with -1
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
//// SELECT * FROM users LIMIT 10; (users1)
//// SELECT * FROM users; (users2)

Offset

db.Offset(3).Find(&users)
//// SELECT * FROM users OFFSET 3;

// Remove offset with -1
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
//// SELECT * FROM users OFFSET 10; (users1)
//// SELECT * FROM users; (users2)

Count

db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
//// SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)

// Set table name with Model
db.Model(User{}).Where("name = ?", "jinzhu").Count(&count)
//// SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)

// Set table name with Table
db.Table("deleted_users").Count(&count)
//// SELECT count(*) FROM deleted_users;

Pluck

Get struct's selected attributes as a map

var ages []int64
db.Find(&users).Pluck("age", &ages)

// Set Table With Model
var names []string
db.Model(&User{}).Pluck("name", &names)
//// SELECT name FROM users;

// Set Table With Table
db.Table("deleted_users").Pluck("name", &names)
//// SELECT name FROM deleted_users;

// Plucking more than one column? Do it like this:
db.Select("name, age").Find(&users)

Transactions

All individual save and delete operations are run in a transaction by default.

tx := db.Begin()

user := User{Name: "transcation"}

tx.Save(&u)
tx.Update("age": 90)
// do whatever

// rollback
tx.Rollback()

// commit
tx.Commit()

Callbacks

Callbacks are methods defined on the struct's pointer. If any callback returns an error, gorm will stop future operations and rollback all changes.

Here is a list of all available callbacks, listed in the same order in which they will get called during the respective operations.

Creating an Object
BeforeSave
BeforeCreate
// save before associations
// save self
// save after associations
AfterCreate
AfterSave
Updating an Object
BeforeSave
BeforeUpdate
// save before associations
// save self
// save after associations
AfterUpdate
AfterSave
Destroying an Object
BeforeDelete
// delete self
AfterDelete
After Find
// load record/records from database
AfterFind

Here is an example:

func (u *User) BeforeUpdate() (err error) {
    if u.readonly() {
        err = errors.New("Read Only User!")
    }
    return
}

// Rollback the insertion if there are more than 1000 users (hypothetical example)
func (u *User) AfterCreate() (err error) {
    if (u.Id > 1000) { // Just as an example, don't use Id to count users!
        err = errors.New("Only 1000 users allowed!")
    }
    return
}
// As you know, the save/delete operations are running in a transaction
// This is means that all your changes will be rolled back if there are any errors
// If you want your changes in callbacks be run in the same transaction
// you have to pass the transaction as argument to the function
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
    tx.Model(u).Update("role", "admin")
    return
}

Specifying the Table Name

// Create `deleted_users` table with User's fields
db.Table("deleted_users").CreateTable(&User{})

// Search from table `deleted_users`
var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
//// SELECT * FROM deleted_users;

// Delete results from table `deleted_users` with search conditions
db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
//// DELETE FROM deleted_users WHERE name = 'jinzhu';
Specifying the Table Name for Struct permanently with TableName
type Cart struct {
}

func (c Cart) TableName() string {
    return "shopping_cart"
}

func (u User) TableName() string {
    if u.Role == "admin" {
        return "admin_users"
    } else {
        return "users"
    }
}

Scopes

func AmountGreaterThan1000(d *gorm.DB) *gorm.DB {
  d.Where("amount > ?", 1000)
}

func PaidWithCreditCard(d *gorm.DB) *gorm.DB {
  d.Where("pay_mode_sign = ?", "C")
}

func PaidWithCod(d *gorm.DB) *gorm.DB {
  d.Where("pay_mode_sign = ?", "C")
}

func OrderStatus(status []string) func (d *gorm.DB) *gorm.DB {
  return func (d *gorm.DB) *gorm.DB {
     return d.Scopes(AmountGreaterThan1000).Where("status in (?)", status)
  }
}

db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
// Find all credit card orders and amount greater than 1000

db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
// Find all COD orders and amount greater than 1000

db.Scopes(OrderStatus([]string{"paid", "shipped"})).Find(&orders)
// Find all paid, shipped orders and amount greater than 1000

Logger

Gorm has built-in logger support, enable it with:

db.LogMode(true)

logger

// Use your own logger
// Refer to gorm's default logger for how to format messages: https://github.com/jinzhu/gorm/blob/master/logger.go#files
db.SetLogger(log.New(os.Stdout, "\r\n", 0))

// If you want to use gorm's default log format, then you could just do it like this
db.SetLogger(gorm.Logger{revel.TRACE})

// Disable logging
db.LogMode(false)

// Enable logging for a single operation, to make debugging easy
db.Debug().Where("name = ?", "jinzhu").First(&User{})

Row & Rows

Row & Rows is not chainable, it works just like QueryRow and Query

row := db.Table("users").Where("name = ?", "jinzhu").select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)

rows, err := db.Model(User{}).Where("name = ?", "jinzhu").select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
  ...
  rows.Scan(&name, &age, &email)
  ...
}

// Rows() with raw sql
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {
  ...
  rows.Scan(&name, &age, &email)
  ...
}

Scan

Scan sql results into strcut

type Result struct {
	Name string
	Age  int
}

var result Result
db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)

// Scan raw sql
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)

Group & Having

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {
  ...
}

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {
  ...
}

type Result struct {
	Date  time.Time
	Total int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

Joins

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {
  ...
}

db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)

Run Raw SQL

// Raw SQL
db.Exec("DROP TABLE users;")

// Raw SQL with arguments
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})

Error Handling

query := db.Where("name = ?", "jinzhu").First(&user)
query := db.First(&user).Limit(10).Find(&users)
//// query.Error returns the last error
//// query.Errors returns all errors that have taken place
//// If an error has taken place, gorm will stop all following operations

// I often use some code like below to do error handling when writing applications
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
  // ...
}

// If no record is found, gorm will return RecordNotFound error, you could check it with
db.Where("name = ?", "hello world").First(&User{}).Error == gorm.RecordNotFound

// Or use shortcut method
if db.Where("name = ?", "hello world").First(&user).RecordNotFound() {
  panic("no record found")
} else {
  user.Blalala()
}

if db.Model(&user).Related(&credit_card).RecordNotFound() {
  panic("no credit card found")
}

Advanced Usage With Query Chaining

Already excited with what gorm has to offer? Let's see some magic!

db.First(&first_article).Count(&total_count).Limit(10).Find(&first_page_articles).Offset(10).Find(&second_page_articles)
//// SELECT * FROM articles LIMIT 1; (first_article)
//// SELECT count(*) FROM articles; (total_count)
//// SELECT * FROM articles LIMIT 10; (first_page_articles)
//// SELECT * FROM articles LIMIT 10 OFFSET 10; (second_page_articles)


// Mix where conditions with inline conditions
db.Where("created_at > ?", "2013-10-10").Find(&cancelled_orders, "state = ?", "cancelled").Find(&shipped_orders, "state = ?", "shipped")
//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'cancelled'; (cancelled_orders)
//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'shipped'; (shipped_orders)


// Use variables to keep query chain
todays_orders := db.Where("created_at > ?", "2013-10-29")
cancelled_orders := todays_orders.Where("state = ?", "cancelled")
shipped_orders := todays_orders.Where("state = ?", "shipped")


// Search with shared conditions from different tables
db.Where("product_name = ?", "fancy_product").Find(&orders).Find(&shopping_carts)
//// SELECT * FROM orders WHERE product_name = 'fancy_product'; (orders)
//// SELECT * FROM carts WHERE product_name = 'fancy_product'; (shopping_carts)


// Search with shared conditions from different tables with specified table
db.Where("mail_type = ?", "TEXT").Find(&users1).Table("deleted_users").Find(&users2)
//// SELECT * FROM users WHERE mail_type = 'TEXT'; (users1)
//// SELECT * FROM deleted_users WHERE mail_type = 'TEXT'; (users2)


// An example on how to use FirstOrCreate
db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111"}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE email = 'x@example.org';
//// INSERT INTO "users" (email,registered_ip) VALUES ("x@example.org", "111.111.111.111")  // if no record found

TODO

  • Support plugin BeforeQuery BeforeSave BeforeCreate BeforeUpdate BeforeDelete AfterQuery AfterSave AfterCreate AfterUpdate SoftDelete BeforeQuery BeforeSave BeforeDelete

    db.RegisterPlugin("xxx") db.RegisterCallback("BeforeQuery", func() {}) db.RegisterCallback("BeforeSave", func() {}) db.RegisterFuncation("Search", func() {}) db.Model(&[]User{}).Limit(10).Do("Search", "vip", "china") db.Mode(&User{}).Do("EditForm").Get("edit_form_html")

    DefaultValue, DefaultTimeZone, R/W Splitting, Validation

  • Getter/Setter share or not? transaction?

  • Github Pages

  • Includes

  • AlertColumn, DropColumn, AddIndex, RemoveIndex

Author

jinzhu

License

Released under the MIT License.

GoDoc

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	RecordNotFound       = errors.New("Record Not Found")
	InvalidSql           = errors.New("Invalid SQL")
	NoNewAttrs           = errors.New("No new Attributes")
	NoValidTransaction   = errors.New("No valid transaction")
	CantStartTransaction = errors.New("Can't start transaction")
)
View Source
var DefaultCallback = &callback{processors: []*callback_processor{}}

Functions

func AfterCreate

func AfterCreate(scope *Scope)

func AfterDelete

func AfterDelete(scope *Scope)

func AfterQuery

func AfterQuery(scope *Scope)

func AfterUpdate

func AfterUpdate(scope *Scope)

func AssignUpdateAttributes

func AssignUpdateAttributes(scope *Scope)

func BeforeCreate

func BeforeCreate(scope *Scope)

func BeforeDelete

func BeforeDelete(scope *Scope)

func BeforeUpdate

func BeforeUpdate(scope *Scope)

func BeginTransaction

func BeginTransaction(scope *Scope)

func CommitOrRollbackTransaction

func CommitOrRollbackTransaction(scope *Scope)

func Create

func Create(scope *Scope)

func Delete

func Delete(scope *Scope)

func Query

func Query(scope *Scope)

func SaveAfterAssociations

func SaveAfterAssociations(scope *Scope)

func SaveBeforeAssociations

func SaveBeforeAssociations(scope *Scope)

func Update

func Update(scope *Scope)

func UpdateTimeStampWhenCreate

func UpdateTimeStampWhenCreate(scope *Scope)

func UpdateTimeStampWhenUpdate

func UpdateTimeStampWhenUpdate(scope *Scope)

Types

type DB

type DB struct {
	Value interface{}
	Error error
	// contains filtered or unexported fields
}

func Open

func Open(driver, source string) (DB, error)

func (*DB) AddIndex

func (s *DB) AddIndex(column string, indexName ...string) *DB

func (*DB) Assign

func (s *DB) Assign(attrs ...interface{}) *DB

func (*DB) Attrs

func (s *DB) Attrs(attrs ...interface{}) *DB

func (*DB) AutoMigrate

func (s *DB) AutoMigrate(value interface{}) *DB

func (*DB) Begin

func (s *DB) Begin() *DB

func (*DB) Callback

func (s *DB) Callback() *callback

func (*DB) Commit

func (s *DB) Commit() *DB

func (*DB) Count

func (s *DB) Count(value interface{}) *DB

func (*DB) CreateTable

func (s *DB) CreateTable(value interface{}) *DB

Migrations

func (*DB) DB

func (s *DB) DB() *sql.DB

func (*DB) Debug

func (s *DB) Debug() *DB

func (*DB) Delete

func (s *DB) Delete(value interface{}) *DB

func (*DB) DropColumn

func (s *DB) DropColumn(column string) *DB

func (*DB) DropTable

func (s *DB) DropTable(value interface{}) *DB

func (*DB) Exec

func (s *DB) Exec(sql string, values ...interface{}) *DB

func (*DB) Find

func (s *DB) Find(out interface{}, where ...interface{}) *DB

func (*DB) First

func (s *DB) First(out interface{}, where ...interface{}) *DB

func (*DB) FirstOrCreate

func (s *DB) FirstOrCreate(out interface{}, where ...interface{}) *DB

func (*DB) FirstOrInit

func (s *DB) FirstOrInit(out interface{}, where ...interface{}) *DB

func (*DB) Group

func (s *DB) Group(query string) *DB

func (*DB) Having

func (s *DB) Having(query string, values ...interface{}) *DB

func (*DB) Includes

func (s *DB) Includes(value interface{}) *DB

func (*DB) Joins

func (s *DB) Joins(query string) *DB

func (*DB) Last

func (s *DB) Last(out interface{}, where ...interface{}) *DB

func (*DB) Limit

func (s *DB) Limit(value interface{}) *DB

func (*DB) LogMode

func (s *DB) LogMode(b bool) *DB

func (*DB) Model

func (s *DB) Model(value interface{}) *DB

func (*DB) ModifyColumn

func (s *DB) ModifyColumn(column string, typ string) *DB

func (*DB) NewRecord

func (s *DB) NewRecord(value interface{}) bool

func (*DB) NewScope

func (db *DB) NewScope(value interface{}) *Scope

NewScope create scope for callbacks, including DB's search information

func (*DB) Not

func (s *DB) Not(query interface{}, args ...interface{}) *DB

func (*DB) Offset

func (s *DB) Offset(value interface{}) *DB

func (*DB) Or

func (s *DB) Or(query interface{}, args ...interface{}) *DB

func (*DB) Order

func (s *DB) Order(value string, reorder ...bool) *DB

func (*DB) Pluck

func (s *DB) Pluck(column string, value interface{}) *DB

func (*DB) Raw

func (s *DB) Raw(sql string, values ...interface{}) *DB

func (*DB) RecordNotFound

func (s *DB) RecordNotFound() bool

func (*DB) Related

func (s *DB) Related(value interface{}, foreignKeys ...string) *DB

func (*DB) RemoveIndex

func (s *DB) RemoveIndex(column string) *DB

func (*DB) Rollback

func (s *DB) Rollback() *DB

func (*DB) Row

func (s *DB) Row() *sql.Row

func (*DB) Rows

func (s *DB) Rows() (*sql.Rows, error)

func (*DB) Save

func (s *DB) Save(value interface{}) *DB

func (*DB) Scan

func (s *DB) Scan(dest interface{}) *DB

func (*DB) Scopes

func (s *DB) Scopes(funcs ...func(*DB) *DB) *DB

func (*DB) Select

func (s *DB) Select(value interface{}) *DB

func (*DB) SetLogger

func (s *DB) SetLogger(l logger)

func (*DB) SetTagIdentifier

func (s *DB) SetTagIdentifier(str string)

func (*DB) SingularTable

func (s *DB) SingularTable(b bool)

func (*DB) Table

func (s *DB) Table(name string) *DB

func (*DB) Unscoped

func (s *DB) Unscoped() *DB

func (*DB) Update

func (s *DB) Update(attrs ...interface{}) *DB

func (*DB) UpdateColumn

func (s *DB) UpdateColumn(attrs ...interface{}) *DB

func (*DB) UpdateColumns

func (s *DB) UpdateColumns(values interface{}) *DB

func (*DB) Updates

func (s *DB) Updates(values interface{}, ignoreProtectedAttrs ...bool) *DB

func (*DB) Where

func (s *DB) Where(query interface{}, args ...interface{}) *DB

type Field

type Field struct {
	Name              string
	DBName            string
	Value             interface{}
	IsBlank           bool
	IsIgnored         bool
	Tag               reflect.StructTag
	SqlTag            string
	ForeignKey        string
	BeforeAssociation bool
	AfterAssociation  bool
	// contains filtered or unexported fields
}

func (*Field) IsScanner

func (f *Field) IsScanner() bool

func (*Field) IsTime

func (f *Field) IsTime() bool

type Logger

type Logger struct {
	*log.Logger
}

func (Logger) Print

func (logger Logger) Print(v ...interface{})

type Scope

type Scope struct {
	Value   interface{}
	Search  *search
	Sql     string
	SqlVars []interface{}
	// contains filtered or unexported fields
}

func (*Scope) AddToVars

func (scope *Scope) AddToVars(value interface{}) string

AddToVars add value as sql's vars, gorm will escape them

func (*Scope) Begin

func (scope *Scope) Begin() *Scope

Begin start a transaction

func (*Scope) CallMethod

func (scope *Scope) CallMethod(name string)

CallMethod invoke method with necessary argument

func (*Scope) CombinedConditionSql

func (scope *Scope) CombinedConditionSql() string

CombinedConditionSql get combined condition sql

func (*Scope) CommitOrRollback

func (scope *Scope) CommitOrRollback() *Scope

CommitOrRollback commit current transaction if there is no error, otherwise rollback it

func (*Scope) DB

func (scope *Scope) DB() sqlCommon

DB get *sql.DB

func (*Scope) Dialect

func (scope *Scope) Dialect() dialect.Dialect

Dialect get dialect

func (*Scope) Err

func (scope *Scope) Err(err error) error

Err write error

func (*Scope) Exec

func (scope *Scope) Exec() *Scope

Exec invoke sql

func (*Scope) FieldByName

func (scope *Scope) FieldByName(name string) (interface{}, bool)

FieldByName to get column's value and existence

func (*Scope) Fields

func (scope *Scope) Fields() []*Field

Fields get value's fields

func (*Scope) Get

func (scope *Scope) Get(name string) (value interface{}, ok bool)

Get get value by name

func (*Scope) HasColumn

func (scope *Scope) HasColumn(name string) bool

HasColumn to check if has column

func (*Scope) HasError

func (scope *Scope) HasError() bool

HasError check if there are any error

func (*Scope) Log

func (scope *Scope) Log(v ...interface{})

Log print log message

func (*Scope) New

func (scope *Scope) New(value interface{}) *Scope

New create a new Scope without search information

func (*Scope) NewDB

func (scope *Scope) NewDB() *DB

NewDB create a new DB without search information

func (*Scope) PrimaryKey

func (scope *Scope) PrimaryKey() string

PrimaryKey get the primary key's column name

func (*Scope) PrimaryKeyValue

func (scope *Scope) PrimaryKeyValue() interface{}

PrimaryKeyValue get the primary key's value

func (*Scope) PrimaryKeyZero

func (scope *Scope) PrimaryKeyZero() bool

PrimaryKeyZero check the primary key is blank or not

func (*Scope) Quote

func (scope *Scope) Quote(str string) string

Quote used to quote database column name according to database dialect

func (*Scope) Raw

func (scope *Scope) Raw(sql string) *Scope

Raw set sql

func (*Scope) Set

func (scope *Scope) Set(name string, value interface{}) *Scope

Set set value by name

func (*Scope) SetColumn

func (scope *Scope) SetColumn(column string, value interface{})

SetColumn to set the column's value

func (*Scope) SkipLeft

func (scope *Scope) SkipLeft()

SkipLeft skip remaining callbacks

func (*Scope) TableName

func (scope *Scope) TableName() string

TableName get table name

func (*Scope) Trace

func (scope *Scope) Trace(t time.Time)

Trace print sql log

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL