gorm/README.md

1052 lines
28 KiB
Markdown
Raw Normal View History

2013-10-25 12:24:29 +04:00
# GORM
2014-01-03 10:54:47 +04:00
The fantastic ORM library for Golang, aims to be developer friendly.
2013-10-25 12:24:29 +04:00
2014-01-05 17:07:50 +04:00
## Install
```
go get github.com/jinzhu/gorm
```
2013-10-28 06:09:44 +04:00
## Overview
* Chainable API
2013-11-07 08:12:25 +04:00
* Relations
2013-12-30 08:46:37 +04:00
* Callbacks (before/after create/save/update/delete/find)
* Soft Deletes
* Auto Migrations
* Transactions
2013-11-11 13:50:27 +04:00
* Logger Support
2013-11-13 20:03:31 +04:00
* Bind struct with tag
2013-11-17 09:22:09 +04:00
* Iteration Support via [Rows](#row--rows)
2013-11-18 10:35:44 +04:00
* Scopes
2013-11-17 09:22:09 +04:00
* sql.Scanner support
2013-11-07 08:12:25 +04:00
* Every feature comes with tests
2013-11-03 06:09:56 +04:00
* Convention Over Configuration
2013-11-07 08:12:25 +04:00
* Developer Friendly
2013-10-28 06:09:44 +04:00
2013-11-02 17:02:54 +04:00
## Conventions
2013-11-17 08:02:22 +04:00
* Table name is the plural of struct name's snake case.
Disable pluralization with `db.SingularTable(true)`, or [Specifying The Table Name For A Struct Permanently With TableName](#specifying-the-table-name-for-a-struct-permanently-with-tablename)
2013-11-17 08:02:22 +04:00
* 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](#soft-delete)
* Gorm uses reflection to know which tables to work with:
```go
// 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"
```
## Existing Schema
If you have an existing database schema and some of your tables do not follow the conventions, (and you can't rename your table names), please use: [Specifying The Table Name For A Struct Permanently With TableName](#specifying-the-table-name-for-a-struct-permanently-with-tableName).
If your primary key field is different from `id`, you can add a tag to the field structure to specify that this field is a primary key.
```go
type Animal struct { // animals
AnimalId int64 `primaryKey:"yes"`
Birthday time.Time
Age int64
2014-04-10 10:49:00 +04:00
}
```
# Getting Started
2013-11-17 08:02:22 +04:00
2013-10-27 17:37:31 +04:00
```go
import (
"database/sql"
"time"
)
2013-11-15 13:27:16 +04:00
type User struct {
2013-11-26 07:39:07 +04:00
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
2013-10-27 17:37:31 +04:00
}
2013-11-17 08:02:22 +04:00
type Email struct {
2013-11-26 07:39:07 +04:00
Id int64
UserId int64 // Foreign key for User
Email string `sql:"type:varchar(100);"` // Set this field's type
Subscribed bool
2013-11-02 17:02:54 +04:00
}
2013-10-28 06:09:44 +04:00
2013-11-17 08:02:22 +04:00
type Address struct {
2013-11-26 07:39:07 +04:00
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...
2013-11-02 17:02:54 +04:00
}
```
2013-11-03 07:32:25 +04:00
## Opening a Database
```go
2013-11-03 07:38:53 +04:00
import "github.com/jinzhu/gorm"
import _ "github.com/lib/pq"
2013-11-04 16:32:46 +04:00
// import _ "github.com/go-sql-driver/mysql"
2013-11-04 16:47:45 +04:00
// import _ "github.com/mattn/go-sqlite3"
2013-11-03 07:38:53 +04:00
2014-03-15 05:05:08 +04:00
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
// db, err = gorm.Open("mysql", "gorm:gorm@/gorm?charset=utf8&parseTime=True")
// db, err = gorm.Open("sqlite3", "/tmp/gorm.db")
2013-11-04 16:32:46 +04:00
// Get database connection handle [*sql.DB](http://golang.org/pkg/database/sql/#DB)
d := db.DB()
2013-11-03 07:38:53 +04:00
// With it you could use package `database/sql`'s builtin methods
db.DB().SetMaxIdleConns(10)
db.DB().SetMaxOpenConns(100)
db.DB().Ping()
2013-11-15 13:27:16 +04:00
// By default, table name is plural of struct type, you can use struct type as table name with:
2013-11-06 17:43:41 +04:00
db.SingularTable(true)
```
2013-11-03 07:32:25 +04:00
Gorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere in your project.
```go
2013-11-15 13:27:16 +04:00
// db.go
package db
2013-11-03 07:32:25 +04:00
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/lib/pq"
)
2013-11-03 07:32:25 +04:00
var DB gorm.DB
func init() {
2013-11-15 13:37:21 +04:00
var err error
2013-11-03 07:32:25 +04:00
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)
2013-11-03 07:32:25 +04:00
if err != nil {
panic(fmt.Sprintf("Got error when connect database, the error is '%v'", err))
}
}
2013-11-15 13:27:16 +04:00
// user.go
package user
import . "db"
2013-11-15 13:27:16 +04:00
...
DB.Save(&User{Name: "xxx"})
...
2013-11-03 07:32:25 +04:00
```
2013-11-02 17:02:54 +04:00
## Struct & Database Mapping
```go
// Create table from struct
db.CreateTable(User{})
// Drop table
db.DropTable(User{})
```
2013-11-07 07:42:36 +04:00
### Automating Migrations
Feel free to update your struct, AutoMigrate will keep your database up-to-date.
2013-11-07 07:42:36 +04:00
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.
2013-11-07 07:42:36 +04:00
If the table doesn't exist when AutoMigrate is called, gorm will create the table automatically.
(the database first needs to be created manually though...).
2013-11-07 07:42:36 +04:00
2013-11-07 08:23:45 +04:00
(only postgres and mysql supported)
2013-11-07 07:42:36 +04:00
```go
db.AutoMigrate(User{})
```
# Gorm API
2013-11-02 17:02:54 +04:00
## Create
```go
user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
2013-10-27 17:37:31 +04:00
db.Save(&user)
2013-11-02 17:02:54 +04:00
```
2013-10-27 17:37:31 +04:00
2013-11-23 17:38:31 +04:00
### NewRecord
Returns true if object hasnt been saved yet (`Id` is blank)
```go
user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
db.NewRecord(user) // => true
db.Save(&user)
db.NewRecord(user) // => false
```
2013-11-03 06:31:36 +04:00
### Create With SubStruct
Refer to [Query With Related](#query-with-related) for how to find associations
2013-11-05 04:08:42 +04:00
2013-11-03 06:31:36 +04:00
```go
user := User{
2013-11-26 07:39:07 +04:00
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"}},
2013-11-03 06:31:36 +04:00
}
db.Save(&user)
//// BEGIN TRANSACTION;
2013-11-03 06:31:36 +04:00
//// 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;
2013-11-03 06:31:36 +04:00
```
2013-11-02 17:02:54 +04:00
## Query
```go
// Get the first record
db.First(&user)
//// SELECT * FROM users ORDER BY id LIMIT 1;
2013-11-15 13:27:16 +04:00
// Search table `users` is guessed from struct's type
2013-10-27 17:37:31 +04:00
// Get the last record
db.Last(&user)
//// SELECT * FROM users ORDER BY id DESC LIMIT 1;
// Get all records
2013-11-02 17:02:54 +04:00
db.Find(&users)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users;
2013-11-02 17:02:54 +04:00
2013-11-15 13:27:16 +04:00
// Get record with primary key
2013-11-02 17:02:54 +04:00
db.First(&user, 10)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users WHERE id = 10;
2013-11-02 17:02:54 +04:00
```
2013-11-15 13:27:16 +04:00
### Query With Where (SQL)
2013-11-02 17:02:54 +04:00
```go
// Get the first matched record
2013-10-27 17:37:31 +04:00
db.Where("name = ?", "jinzhu").First(&user)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;
2013-10-27 17:37:31 +04:00
2013-11-02 17:02:54 +04:00
// Get all matched records
2013-10-27 17:37:31 +04:00
db.Where("name = ?", "jinzhu").Find(&users)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name = 'jinzhu';
2013-10-27 17:37:31 +04:00
db.Where("name <> ?", "jinzhu").Find(&users)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
2013-11-03 17:19:38 +04:00
db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name IN ('jinzhu', 'jinzhu 2');
// LIKE
2013-10-28 05:05:44 +04:00
db.Where("name LIKE ?", "%jin%").Find(&users)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name LIKE "%jin%";
// Multiple conditions
2013-11-02 17:02:54 +04:00
db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
```
2013-11-03 06:09:56 +04:00
### Query With Where (Struct & Map)
2013-11-02 17:02:54 +04:00
```go
// Search with struct
2013-10-29 13:37:45 +04:00
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
2013-11-02 17:02:54 +04:00
//// 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;
2013-11-03 06:09:56 +04:00
// IN for primary keys
2013-11-03 06:09:56 +04:00
db.Where([]int64{20, 21, 22}).Find(&users)
//// SELECT * FROM users WHERE id IN (20, 21, 22);
2013-11-02 17:02:54 +04:00
```
2013-11-03 06:09:56 +04:00
### Query With Not
2013-11-02 17:02:54 +04:00
```go
2013-11-03 06:09:56 +04:00
// Attribute Not Equal
2013-11-02 17:02:54 +04:00
db.Not("name", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;
2013-10-27 17:37:31 +04:00
2013-11-02 17:02:54 +04:00
// Not In
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
2013-11-15 13:27:16 +04:00
// Not In for primary keys
2013-10-31 14:12:18 +04:00
db.Not([]int64{1,2,3}).First(&user)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE id NOT IN (1,2,3);
2013-10-31 14:12:18 +04:00
db.Not([]int64{}).First(&user)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users;
2013-11-15 13:27:16 +04:00
// SQL string
2013-10-31 18:49:48 +04:00
db.Not("name = ?", "jinzhu").First(&user)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE NOT(name = "jinzhu");
2013-11-15 13:27:16 +04:00
// Not with struct
2013-10-31 14:12:18 +04:00
db.Not(User{Name: "jinzhu"}).First(&user)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name <> "jinzhu";
```
2013-11-03 06:49:09 +04:00
### Query With Inline Condition
2013-10-31 14:12:18 +04:00
2013-11-02 17:02:54 +04:00
```go
// Find with primary key
2013-10-27 18:36:43 +04:00
db.First(&user, 23)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE id = 23 LIMIT 1;
2013-11-15 13:27:16 +04:00
// SQL string
2013-11-02 17:02:54 +04:00
db.Find(&user, "name = ?", "jinzhu")
//// SELECT * FROM users WHERE name = "jinzhu";
// Multiple conditions
2013-10-27 18:36:43 +04:00
db.Find(&users, "name <> ? and age > ?", "jinzhu", 20)
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
2013-11-15 13:27:16 +04:00
// Inline search with struct
2013-11-02 17:02:54 +04:00
db.Find(&users, User{Age: 20})
//// SELECT * FROM users WHERE age = 20;
2013-11-15 13:27:16 +04:00
// Inline search with map
2013-10-29 13:52:37 +04:00
db.Find(&users, map[string]interface{}{"age": 20})
2013-11-02 17:02:54 +04:00
//// SELECT * FROM users WHERE age = 20;
```
2013-11-03 06:09:56 +04:00
### Query With Or
2013-11-03 06:49:09 +04:00
```go
2013-11-03 06:09:56 +04:00
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)
```
### Query With Related
```go
2013-11-15 13:27:16 +04:00
// Find user's emails with guessed foreign key
db.Model(&user).Related(&emails)
//// SELECT * FROM emails WHERE user_id = 111;
2013-11-15 13:27:16 +04:00
// Find user's billing address with specified foreign key 'BillingAddressId'
db.Model(&user).Related(&address1, "BillingAddressId")
2013-11-15 13:27:16 +04:00
//// SELECT * FROM addresses WHERE id = 123; // 123 is user's BillingAddressId
2013-11-15 13:27:16 +04:00
// Find user with guessed primary key value from email
db.Model(&email).Related(&user)
2013-11-15 13:27:16 +04:00
//// SELECT * FROM users WHERE id = 111; // 111 is email's UserId
```
2013-11-03 06:09:56 +04:00
### Query Chains
2013-11-03 06:49:09 +04:00
Gorm has a chainable API, so you could query like this
2013-11-03 06:09:56 +04:00
```go
2013-11-03 06:49:09 +04:00
db.Where("name <> ?","jinzhu").Where("age >= ? and role <> ?",20,"admin").Find(&users)
2013-11-03 06:09:56 +04:00
//// 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
2013-11-03 06:09:56 +04:00
```go
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
2013-11-17 17:39:50 +04:00
//// UPDATE users SET name='jinzhu 2', age=100, updated_at = '2013-11-17 21:34:10' WHERE id=111;
2013-11-03 06:09:56 +04:00
```
### Update One Attribute With `Update`
2013-11-03 06:09:56 +04:00
2013-11-03 06:49:09 +04:00
```go
2013-11-15 13:27:16 +04:00
// Update existing user's name if it is changed
2013-11-03 06:09:56 +04:00
db.Model(&user).Update("name", "hello")
2013-11-17 17:39:50 +04:00
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;
2013-11-03 06:09:56 +04:00
2013-11-15 13:27:16 +04:00
// Find out a user, and update the name if it is changed
2013-11-03 06:09:56 +04:00
db.First(&user, 111).Update("name", "hello")
//// SELECT * FROM users LIMIT 1;
2013-11-17 17:39:50 +04:00
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;
2013-11-03 06:09:56 +04:00
2013-11-15 13:27:16 +04:00
// Update name with search condiation and specified table name
2013-11-03 06:09:56 +04:00
db.Table("users").Where(10).Update("name", "hello")
//// UPDATE users SET name='hello' WHERE id = 10;
```
### Update Multiple Attributes With `Updates`
2013-11-03 06:09:56 +04:00
2013-11-03 06:49:09 +04:00
```go
2013-11-15 13:27:16 +04:00
// Update user's name and age if they are changed
2013-11-03 06:09:56 +04:00
db.Model(&user).Updates(User{Name: "hello", Age: 18})
2013-11-17 17:39:50 +04:00
//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
2013-11-03 06:09:56 +04:00
2013-11-03 06:49:09 +04:00
// Updates with Map
2013-11-03 06:09:56 +04:00
db.Table("users").Where(10).Updates(map[string]interface{}{"name": "hello", "age": 18})
//// UPDATE users SET name='hello', age=18 WHERE id = 10;
2013-11-03 06:49:09 +04:00
// Updates with Struct
2013-11-03 06:09:56 +04:00
db.Model(User{}).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18;
```
### Update Attributes Without Callbacks
2013-11-17 17:39:50 +04:00
```go
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;
```
2013-11-03 06:09:56 +04:00
## Delete
### Delete An Existing Struct
2013-11-03 06:09:56 +04:00
```go
db.Delete(&email)
// DELETE from emails where id=10;
```
### Batch Delete With Search
2013-11-03 06:09:56 +04:00
```go
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 a soft delete ability automatically!
2013-11-03 06:49:09 +04:00
Structs that don't have a DeletedAt field will be deleted from the database permanently.
2013-11-03 06:09:56 +04:00
```go
db.Delete(&user)
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
// Delete with search condition
2013-11-03 06:09:56 +04:00
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
2013-11-03 06:09:56 +04:00
db.Where("age = 20").Find(&user)
//// SELECT * FROM users WHERE age = 100 AND (deleted_at IS NULL AND deleted_at <= '0001-01-02');
2013-11-15 13:27:16 +04:00
// Find soft deleted records with Unscoped
2013-11-03 06:09:56 +04:00
db.Unscoped().Where("age = 20").Find(&users)
//// SELECT * FROM users WHERE age = 20;
2013-11-15 13:27:16 +04:00
// Delete record permanently with Unscoped
2013-11-03 06:09:56 +04:00
db.Unscoped().Delete(&order)
// DELETE FROM orders WHERE id=10;
```
2013-11-02 17:02:54 +04:00
## FirstOrInit
2013-11-15 13:27:16 +04:00
Try to get the first record, if failed, will initialize the struct with search conditions.
2013-11-03 06:49:09 +04:00
(only supports search conditions map and struct)
2013-11-02 17:02:54 +04:00
```go
db.FirstOrInit(&user, User{Name: "non_existing"})
//// User{Name: "non_existing"}
2013-10-27 18:18:06 +04:00
2013-10-29 16:32:27 +04:00
db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
2013-11-02 17:02:54 +04:00
//// User{Id: 111, Name: "Jinzhu", Age: 20}
2013-10-29 16:32:27 +04:00
2013-11-02 17:02:54 +04:00
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.
2013-11-02 17:02:54 +04:00
```go
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}
2013-11-15 13:27:16 +04:00
// Or write it like this if has only one attribute
db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user)
2013-11-02 17:02:54 +04:00
2013-11-15 13:27:16 +04:00
// If a record found, Attrs would be ignored
2013-11-02 17:02:54 +04:00
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
//// SELECT * FROM USERS WHERE name = jinzhu';
2013-11-03 06:09:56 +04:00
//// User{Id: 111, Name: "Jinzhu", Age: 20}
2013-11-03 06:18:16 +04:00
```
2013-10-30 11:33:34 +04:00
2013-11-02 17:02:54 +04:00
### FirstOrInit With Assign
Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not.
2013-11-02 17:02:54 +04:00
```go
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.
2013-11-02 17:02:54 +04:00
```go
db.FirstOrCreate(&user, User{Name: "non_existing"})
//// User{Id: 112, Name: "non_existing"}
2013-10-29 16:32:27 +04:00
db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
2013-11-02 17:02:54 +04:00
//// User{Id: 111, Name: "Jinzhu"}
db.FirstOrCreate(&user, map[string]interface{}{"name": "jinzhu", "age": 30})
2013-11-03 06:09:56 +04:00
//// user -> User{Id: 111, Name: "jinzhu", Age: 20}
2013-11-02 17:02:54 +04:00
```
### FirstOrCreate With Attrs
2013-10-29 16:32:27 +04:00
Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.
2013-11-02 17:02:54 +04:00
```go
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}
2013-10-31 05:34:27 +04:00
2013-11-03 06:09:56 +04:00
db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
//// User{Id: 111, Name: "jinzhu", Age: 20}
2013-11-02 17:02:54 +04:00
```
### FirstOrCreate With Assign
2013-10-31 05:34:27 +04:00
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.
2013-10-31 05:34:27 +04:00
2013-11-02 17:02:54 +04:00
```go
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
//// user -> User{Id: 112, Name: "non_existing", Age: 20}
2013-11-03 06:09:56 +04:00
db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
//// SELECT * FROM users WHERE name = 'jinzhu';
2013-11-02 17:02:54 +04:00
//// UPDATE users SET age=30 WHERE id = 111;
2013-11-03 06:09:56 +04:00
//// User{Id: 111, Name: "jinzhu", Age: 30}
2013-11-02 17:02:54 +04:00
```
2013-11-03 06:09:56 +04:00
## Select
2013-11-02 17:02:54 +04:00
```go
2013-11-03 06:09:56 +04:00
db.Select("name, age").Find(&users)
//// SELECT name, age FROM users;
```
2013-10-30 11:33:34 +04:00
2013-11-03 06:09:56 +04:00
## Order
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
```go
2013-10-27 17:37:31 +04:00
db.Order("age desc, name").Find(&users)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users ORDER BY age desc, name;
2013-11-15 13:27:16 +04:00
// Multiple orders
2013-10-27 18:18:06 +04:00
db.Order("age desc").Order("name").Find(&users)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users ORDER BY age desc, name;
2013-10-28 05:18:34 +04:00
// ReOrder
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users ORDER BY age desc; (users1)
//// SELECT * FROM users ORDER BY age; (users2)
```
2013-10-28 05:18:34 +04:00
2013-11-03 06:09:56 +04:00
## Limit
```go
2013-10-27 17:37:31 +04:00
db.Limit(3).Find(&users)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users LIMIT 3;
2013-10-27 17:37:31 +04:00
2013-11-15 13:27:16 +04:00
// Remove limit with -1
2013-11-03 06:09:56 +04:00
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
//// SELECT * FROM users LIMIT 10; (users1)
//// SELECT * FROM users; (users2)
```
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
## Offset
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
```go
db.Offset(3).Find(&users)
//// SELECT * FROM users OFFSET 3;
2013-10-27 17:37:31 +04:00
2013-11-15 13:27:16 +04:00
// Remove offset with -1
2013-11-03 06:09:56 +04:00
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
//// SELECT * FROM users OFFSET 10; (users1)
//// SELECT * FROM users; (users2)
```
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
## Count
2013-10-27 18:18:06 +04:00
2013-11-03 06:09:56 +04:00
```go
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)
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
// 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)
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
// Set table name with Table
db.Table("deleted_users").Count(&count)
//// SELECT count(*) FROM deleted_users;
```
## Pluck
2013-10-27 18:18:06 +04:00
Get struct's selected attributes as a map.
2013-11-03 06:09:56 +04:00
```go
2013-10-27 17:37:31 +04:00
var ages []int64
2013-10-28 05:05:44 +04:00
db.Find(&users).Pluck("age", &ages)
2013-11-03 06:09:56 +04:00
// Set Table With Model
2013-10-27 18:18:06 +04:00
var names []string
db.Model(&User{}).Pluck("name", &names)
2013-11-03 06:09:56 +04:00
//// SELECT name FROM users;
2013-10-27 17:37:31 +04:00
2013-11-03 06:09:56 +04:00
// Set Table With Table
db.Table("deleted_users").Pluck("name", &names)
//// SELECT name FROM deleted_users;
2013-11-11 17:55:44 +04:00
// Plucking more than one column? Do it like this:
2013-11-11 17:55:44 +04:00
db.Select("name, age").Find(&users)
2013-11-03 06:09:56 +04:00
```
## Transactions
All individual save and delete operations are run in a transaction by default.
```go
tx := db.Begin()
user := User{Name: "transcation"}
tx.Save(&u)
tx.Update("age": 90)
// do whatever
// rollback
tx.Rollback()
// commit
tx.Commit()
```
2013-11-03 06:09:56 +04:00
## Callbacks
2013-10-27 17:37:31 +04:00
Callbacks are methods defined on the struct's pointer.
If any callback returns an error, gorm will stop future operations and rollback all changes.
2013-11-03 06:09:56 +04:00
Here is a list of all available callbacks,
2013-11-24 04:29:56 +04:00
listed in the same order in which they will get called during the respective operations.
2013-11-03 06:09:56 +04:00
### Creating An Object
2013-11-03 06:09:56 +04:00
2013-11-24 04:29:56 +04:00
```go
BeforeSave
BeforeCreate
// save before associations
// save self
// save after associations
AfterCreate
AfterSave
```
### Updating An Object
2013-11-24 04:29:56 +04:00
```go
BeforeSave
BeforeUpdate
// save before associations
// save self
// save after associations
AfterUpdate
AfterSave
```
### Destroying An Object
2013-11-24 04:29:56 +04:00
```go
BeforeDelete
// delete self
AfterDelete
```
2013-12-30 08:46:37 +04:00
### After Find
```go
// load record/records from database
AfterFind
```
2013-11-24 04:29:56 +04:00
Here is an example:
2013-11-15 13:27:16 +04:00
2013-11-03 06:09:56 +04:00
```go
func (u *User) BeforeUpdate() (err error) {
if u.readonly() {
2013-11-15 13:27:16 +04:00
err = errors.New("Read Only User!")
2013-11-03 06:09:56 +04:00
}
return
}
2013-11-11 15:06:26 +04:00
// Rollback the insertion if there are more than 1000 users (hypothetical example)
2013-11-11 15:06:26 +04:00
func (u *User) AfterCreate() (err error) {
if (u.Id > 1000) { // Just as an example, don't use Id to count users!
2013-11-15 13:27:16 +04:00
err = errors.New("Only 1000 users allowed!")
2013-11-11 15:06:26 +04:00
}
return
}
2013-11-03 06:09:56 +04:00
```
2013-10-27 17:37:31 +04:00
2013-11-24 04:29:56 +04:00
```go
// 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
2013-11-24 04:29:56 +04:00
// If you want your changes in callbacks be run in the same transaction
// you have to pass the transaction as argument to the function
2013-11-24 04:29:56 +04:00
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
tx.Model(u).Update("role", "admin")
return
}
```
## Specifying The Table Name
2013-11-01 11:01:39 +04:00
2013-11-03 07:32:25 +04:00
```go
2013-11-15 13:27:16 +04:00
// Create `deleted_users` table with User's fields
2013-10-28 16:27:25 +04:00
db.Table("deleted_users").CreateTable(&User{})
2013-11-03 06:09:56 +04:00
// Search from table `deleted_users`
2013-10-28 16:27:25 +04:00
var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM deleted_users;
2013-10-28 16:27:25 +04:00
2013-11-15 13:27:16 +04:00
// Delete results from table `deleted_users` with search conditions
2013-11-03 06:09:56 +04:00
db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
//// DELETE FROM deleted_users WHERE name = 'jinzhu';
```
2013-10-28 17:52:22 +04:00
### Specifying The Table Name For A Struct Permanently with TableName
```go
type Cart struct {
}
func (c Cart) TableName() string {
2013-11-26 07:39:07 +04:00
return "shopping_cart"
}
2013-11-06 18:20:26 +04:00
func (u User) TableName() string {
if u.Role == "admin" {
return "admin_users"
} else {
return "users"
}
}
```
2013-11-18 10:35:44 +04:00
## Scopes
```go
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
```
2013-11-11 13:50:27 +04:00
## Logger
Gorm has built-in logger support, enable it with:
2013-11-11 13:50:27 +04:00
```go
db.LogMode(true)
```
2013-11-11 13:51:39 +04:00
![logger](https://raw.github.com/jinzhu/gorm/master/images/logger.png)
2013-11-11 13:50:27 +04:00
```go
// 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
2013-11-11 13:50:27 +04:00
db.SetLogger(log.New(os.Stdout, "\r\n", 0))
2014-01-03 15:23:41 +04:00
// 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
2013-11-11 13:50:27 +04:00
db.LogMode(false)
// Enable logging for a single operation, to make debugging easy
2013-11-11 13:50:27 +04:00
db.Debug().Where("name = ?", "jinzhu").First(&User{})
```
2013-11-17 08:09:40 +04:00
## Row & Rows
Row & Rows is not chainable, it works just like `QueryRow` and `Query`.
2013-11-17 08:09:40 +04:00
```go
2014-01-03 14:14:51 +04:00
row := db.Table("users").Where("name = ?", "jinzhu").select("name, age").Row() // (*sql.Row)
2013-11-17 08:09:40 +04:00
row.Scan(&name, &age)
2014-01-03 14:14:51 +04:00
rows, err := db.Model(User{}).Where("name = ?", "jinzhu").select("name, age, email").Rows() // (*sql.Rows, error)
2013-11-17 08:09:40 +04:00
defer rows.Close()
for rows.Next() {
...
rows.Scan(&name, &age, &email)
...
}
2014-01-03 14:14:51 +04:00
// 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 a struct.
2014-01-03 14:14:51 +04:00
```go
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)
2013-11-17 08:09:40 +04:00
```
2013-11-17 09:22:09 +04:00
## Group & Having
```go
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() {
...
}
2014-01-04 10:23:55 +04:00
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
```go
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)
2013-11-17 09:22:09 +04:00
```
## Run Raw SQL
2013-11-03 06:09:56 +04:00
```go
// 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})
2013-11-03 06:09:56 +04:00
```
2013-10-28 07:24:51 +04:00
2013-11-03 06:09:56 +04:00
## Error Handling
```go
2013-10-28 07:24:51 +04:00
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
2013-11-03 06:09:56 +04:00
// I often use some code like below to do error handling when writing applications
2013-11-15 13:27:16 +04:00
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
// ...
}
2013-11-15 14:36:39 +04:00
// If no record is found, gorm will return RecordNotFound error, you could check it with
2013-11-15 14:36:39 +04:00
db.Where("name = ?", "hello world").First(&User{}).Error == gorm.RecordNotFound
// Or use the shortcut method
2013-11-24 07:29:37 +04:00
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")
}
2013-10-27 17:37:31 +04:00
```
## Advanced Usage With Query Chaining
2013-10-27 18:18:06 +04:00
Already excited with what gorm has to offer? Let's see some magic!
2013-10-27 18:18:06 +04:00
2013-11-03 06:09:56 +04:00
```go
2013-10-27 18:18:06 +04:00
db.First(&first_article).Count(&total_count).Limit(10).Find(&first_page_articles).Offset(10).Find(&second_page_articles)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM articles LIMIT 1; (first_article)
2013-11-15 13:27:16 +04:00
//// SELECT count(*) FROM articles; (total_count)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM articles LIMIT 10; (first_page_articles)
//// SELECT * FROM articles LIMIT 10 OFFSET 10; (second_page_articles)
2013-10-27 18:18:06 +04:00
2013-11-15 13:27:16 +04:00
// Mix where conditions with inline conditions
2013-11-03 06:09:56 +04:00
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)
2013-10-27 18:18:06 +04:00
2013-11-15 13:27:16 +04:00
// 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")
2013-11-15 13:27:16 +04:00
// Search with shared conditions from different tables
2013-11-03 06:09:56 +04:00
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)
2013-10-27 18:18:06 +04:00
2013-11-15 13:27:16 +04:00
// Search with shared conditions from different tables with specified table
db.Where("mail_type = ?", "TEXT").Find(&users1).Table("deleted_users").Find(&users2)
2013-11-03 06:09:56 +04:00
//// SELECT * FROM users WHERE mail_type = 'TEXT'; (users1)
//// SELECT * FROM deleted_users WHERE mail_type = 'TEXT'; (users2)
2013-10-28 16:27:25 +04:00
2013-10-30 11:33:34 +04:00
// An example on how to use FirstOrCreate
2013-11-15 13:27:16 +04:00
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
2013-10-27 18:18:06 +04:00
```
2013-10-26 11:18:44 +04:00
## TODO
2014-01-03 10:54:47 +04:00
* 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
2013-11-17 16:38:43 +04:00
* Includes
2014-01-03 10:54:47 +04:00
* AlertColumn, DropColumn, AddIndex, RemoveIndex
2013-10-25 12:24:29 +04:00
2013-10-26 11:18:44 +04:00
# Author
2013-10-25 12:24:29 +04:00
2013-11-03 06:09:56 +04:00
**jinzhu**
2013-10-25 12:24:29 +04:00
2013-10-26 11:18:44 +04:00
* <http://github.com/jinzhu>
* <wosmvp@gmail.com>
* <http://twitter.com/zhangjinzhu>
2013-11-12 03:13:58 +04:00
## License
Released under the [MIT License](http://www.opensource.org/licenses/MIT).
2014-01-04 09:38:41 +04:00
[![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.png)](http://godoc.org/github.com/jinzhu/gorm)