# GORM [![Join the chat at https://gitter.im/jinzhu/gorm](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/jinzhu/gorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) The fantastic ORM library for Golang, aims to be developer friendly. [![wercker status](https://app.wercker.com/status/0cb7bb1039e21b74f8274941428e0921/s/master "wercker status")](https://app.wercker.com/project/bykey/0cb7bb1039e21b74f8274941428e0921) ## Overview * Full-Featured ORM (almost) * Chainable API * Auto Migrations * Relations (Has One, Has Many, Belongs To, Many To Many, [Polymorphism](#polymorphism)) * Callbacks (Before/After Create/Save/Update/Delete/Find) * Preloading (eager loading) * Transactions * Embed Anonymous Struct * Soft Deletes * Customizable Logger * Iteration Support via [Rows](#row--rows) * Every feature comes with tests * Developer Friendly # Getting Started ## Install ``` go get -u github.com/jinzhu/gorm ``` ## Table of Contents - [Define Models (Structs)](#define-models-structs) - [Conventions](#conventions) - [Initialize Database](#initialize-database) - [Migration](#migration) - [Basic CRUD](#basic-crud) - [Create](#create-record) - [Query](#query) - [Query With Where (Plain SQL)](#query-with-where-plain-sql) - [Query With Where (Struct & Map)](#query-with-where-struct--map) - [Query With Not](#query-with-not) - [Query With Inline Condition](#query-with-inline-condition) - [Query With Or](#query-with-or) - [Query Chains](#query-chains) - [Preloading (Eager loading)](#preloading-eager-loading) - [Update](#update) - [Update Without Callbacks](#update-without-callbacks) - [Batch Updates](#batch-updates) - [Update with SQL Expression](#update-with-sql-expression) - [Delete](#delete) - [Batch Delete](#batch-delete) - [Soft Delete](#soft-delete) - [Associations](#associations) - [Has One](#has-one) - [Belongs To](#belongs-to) - [Has Many](#has-many) - [Many To Many](#many-to-many) - [Polymorphism](#polymorphism) - [Association Mode](#association-mode) - [Advanced Usage](#advanced-usage) - [FirstOrInit](#firstorinit) - [FirstOrCreate](#firstorcreate) - [Select](#select) - [Order](#order) - [Limit](#limit) - [Offset](#offset) - [Count](#count) - [Pluck](#pluck) - [Raw SQL](#raw-sql) - [Row & Rows](#row--rows) - [Scan](#scan) - [Group & Having](#group--having) - [Joins](#joins) - [Transactions](#transactions) - [Scopes](#scopes) - [Callbacks](#callbacks) - [Specifying The Table Name](#specifying-the-table-name) - [Error Handling](#error-handling) - [Logger](#logger) - [Existing Schema](#existing-schema) - [Composite Primary Key](#composite-primary-key) - [Database Indexes & Foreign Key](#database-indexes--foreign-key) - [Default values](#default-values) - [More examples with query chain](#more-examples-with-query-chain) ## Define Models (Structs) ```go type User struct { ID int Birthday time.Time Age int Name string `sql:"size:255"` // Default size for string is 255, you could reset it with this tag Num int `sql:"AUTO_INCREMENT"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time CreditCard CreditCard // One-To-One relationship (has one - use CreditCard's UserID as foreign key) Emails []Email // One-To-Many relationship (has many - use Email's UserID as foreign key) BillingAddress Address // One-To-One relationship (belongs to - use BillingAddressID as foreign key) BillingAddressID sql.NullInt64 ShippingAddress Address // One-To-One relationship (belongs to - use ShippingAddressID as foreign key) ShippingAddressID int IgnoreMe int `sql:"-"` // Ignore this field Languages []Language `gorm:"many2many:user_languages;"` // Many-To-Many relationship, 'user_languages' is join table } type Email struct { ID int UserID int `sql:"index"` // Foreign key (belongs to), tag `index` will create index for this field when using AutoMigrate Email string `sql:"type:varchar(100);unique_index"` // Set field's sql type, tag `unique_index` will create unique index Subscribed bool } type Address struct { ID int Address1 string `sql:"not null;unique"` // Set field as not nullable and unique Address2 string `sql:"type:varchar(100);unique"` Post sql.NullString `sql:"not null"` } type Language struct { ID int Name string `sql:"index:idx_name_code"` // Create index with name, and will create combined index if find other fields defined same name Code string `sql:"index:idx_name_code"` // `unique_index` also works } type CreditCard struct { gorm.Model UserID uint Number string } ``` ## Conventions * Table name is the plural of struct name's snake case, you can 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) ```go type User struct{} // struct User's database table name is "users" by default, will be "user" if you disabled pluralisation ``` * Column name is the snake case of field's name * Use `ID` field as primary key * 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 provide a default model struct, you could embed it in your struct ```go type Model struct { ID uint `gorm:"primary_key"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time } type User struct { gorm.Model Name string } ``` ## Initialize Database ```go import ( "github.com/jinzhu/gorm" _ "github.com/lib/pq" _ "github.com/go-sql-driver/mysql" _ "github.com/mattn/go-sqlite3" ) db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable") // db, err := gorm.Open("foundation", "dbname=gorm") // FoundationDB. // db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local") // db, err := gorm.Open("sqlite3", "/tmp/gorm.db") // You can also use an existing database connection handle // dbSql, _ := sql.Open("postgres", "user=gorm dbname=gorm sslmode=disable") // db, _ := gorm.Open("postgres", dbSql) // Get database connection handle [*sql.DB](http://golang.org/pkg/database/sql/#DB) db.DB() // Then you could invoke `*sql.DB`'s functions with it db.DB().Ping() db.DB().SetMaxIdleConns(10) db.DB().SetMaxOpenConns(100) // Disable table name's pluralization db.SingularTable(true) ``` ## Migration ```go // Create table db.CreateTable(&User{}) db.Set("gorm:table_options", "ENGINE=InnoDB").CreateTable(&User{}) // Drop table db.DropTable(&User{}) // ModifyColumn db.Model(&User{}).ModifyColumn("description", "text") // DropColumn db.Model(&User{}).DropColumn("description") // Automating Migration db.AutoMigrate(&User{}) db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{}) db.AutoMigrate(&User{}, &Product{}, &Order{}) // Feel free to change your struct, AutoMigrate will keep your database up-to-date. // AutoMigrate will ONLY add *new columns* and *new indexes*, // WON'T update current column's type or delete unused columns, to protect your data. // If the table is not existing, AutoMigrate will create the table automatically. ``` # Basic CRUD ## Create Record ```go user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()} db.NewRecord(user) // => returns `true` if primary key is blank db.Create(&user) db.NewRecord(user) // => return `false` after `user` created // Associations will be inserted automatically when save the record 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"}}, Languages: []Language{{Name: "ZH"}, {Name: "EN"}}, } db.Create(&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"); //// INSERT INTO "languages" ("name") VALUES ('ZH'); //// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 1); //// INSERT INTO "languages" ("name") VALUES ('EN'); //// INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2); //// COMMIT; ``` Refer [Associations](#associations) for more details ## Query ```go // Get the first record db.First(&user) //// SELECT * FROM users ORDER BY id LIMIT 1; // 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 (Plain SQL) ```go // 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) // IN db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users) // LIKE db.Where("name LIKE ?", "%jin%").Find(&users) // AND db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users) // Time db.Where("updated_at > ?", lastWeek).Find(&users) db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users) ``` ### Query With Where (Struct & Map) ```go // Struct db.Where(&User{Name: "jinzhu", Age: 20}).First(&user) //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1; // Map db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users) //// SELECT * FROM users WHERE name = "jinzhu" AND age = 20; // Slice of primary keys db.Where([]int64{20, 21, 22}).Find(&users) //// SELECT * FROM users WHERE id IN (20, 21, 22); ``` ### Query With Not ```go 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 slice of 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; // Plain SQL db.Not("name = ?", "jinzhu").First(&user) //// SELECT * FROM users WHERE NOT(name = "jinzhu"); // Struct db.Not(User{Name: "jinzhu"}).First(&user) //// SELECT * FROM users WHERE name <> "jinzhu"; ``` ### Query With Inline Condition ```go // Get by primary key db.First(&user, 23) //// SELECT * FROM users WHERE id = 23 LIMIT 1; // Plain SQL db.Find(&user, "name = ?", "jinzhu") //// SELECT * FROM users WHERE name = "jinzhu"; db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20) //// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20; // Struct db.Find(&users, User{Age: 20}) //// SELECT * FROM users WHERE age = 20; // Map db.Find(&users, map[string]interface{}{"age": 20}) //// SELECT * FROM users WHERE age = 20; ``` ### Query With Or ```go db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users) //// SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin'; // Struct db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users) //// SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; // Map db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users) ``` ### Query Chains Gorm has a chainable API, you could use it like this ```go 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) ``` ### Preloading (Eager loading) ```go db.Preload("Orders").Find(&users) //// SELECT * FROM users; //// SELECT * FROM orders WHERE user_id IN (1,2,3,4); db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users) //// SELECT * FROM users; //// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled'); db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users) //// SELECT * FROM users WHERE state = 'active'; //// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled'); db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users) //// SELECT * FROM users; //// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many //// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one //// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to ``` #### Nested Preloading ```go db.Preload("Orders.OrderItems").Find(&users) db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users) ``` ## Update ```go // Update an existing struct db.First(&user) 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; db.Where("active = ?", true).Save(&user) //// UPDATE users SET name='jinzhu 2', age=100, updated_at = '2013-11-17 21:34:10' WHERE id=111 AND active = true; // Update an attribute 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; db.Model(&user).Where("active = ?", true).Update("name", "hello") //// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 AND active = true; 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 multiple attributes if they are changed db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) // Update multiple attributes if they are changed (update with struct only works with none zero values) 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; ``` ### Update Without Callbacks By default, update will call BeforeUpdate, AfterUpdate callbacks, if you want to update w/o callbacks and w/o saving associations: ```go db.Model(&user).UpdateColumn("name", "hello") //// UPDATE users SET name='hello' WHERE id = 111; // Update with struct only works with none zero values, or use map[string]interface{} db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18}) //// UPDATE users SET name='hello', age=18 WHERE id = 111; ``` ### Batch Updates ```go db.Table("users").Where("id = ?", 10).Updates(map[string]interface{}{"name": "hello", "age": 18}) //// UPDATE users SET name='hello', age=18 WHERE id = 10; // Update with struct only works with none zero values, or use map[string]interface{} db.Model(User{}).Updates(User{Name: "hello", Age: 18}) //// UPDATE users SET name='hello', age=18; // Callbacks won't run when do batch updates // Use `RowsAffected` to get the count of affected records db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected ``` ### Update with SQL Expression ```go DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100)) //// UPDATE "products" SET "code" = 'L1212', "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2'; DB.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)}) //// UPDATE "products" SET "code" = 'L1212', "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2'; DB.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1)) //// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2'; DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1)) //// UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2' AND quantity > 1; ``` ## Delete ```go // Delete an existing record db.Delete(&email) //// DELETE from emails where id=10; ``` ### Batch Delete ```go db.Where("email LIKE ?", "%jinzhu%").Delete(Email{}) //// DELETE from emails where email LIKE "%jinhu%"; ``` ### Soft Delete If struct has `DeletedAt` field, it will get soft delete ability automatically! Then it won't be deleted from database permanently when call `Delete`. ```go db.Delete(&user) //// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111; // Batch Delete 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 query them db.Where("age = 20").Find(&user) //// SELECT * FROM users WHERE age = 20 AND (deleted_at IS NULL OR 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; ``` ## Associations ### Has One ```go // User has one CreditCard, UserID is the foreign key type User struct { gorm.Model CreditCard CreditCard } type CreditCard struct { gorm.Model UserID uint Number string } var card CreditCard db.Model(&user).Related(&card, "CreditCard") //// SELECT * FROM credit_cards WHERE user_id = 123; // 123 is user's primary key // CreditCard is user's field name, it means get user's CreditCard relations and fill it into variable card // If the field name is same as the variable's type name, like above example, it could be omitted, like: db.Model(&user).Related(&creditCard, "CreditCard") ``` ### Belongs To ```go // User belongs to a profile, ProfileID is the foreign key type User struct { gorm.Model Profile Profile ProfileID int } type Profile struct { gorm.Model Name string } db.Model(&user).Related(&profile) //// SELECT * FROM profiles WHERE id = 111; // 111 is user's foreign key ProfileID ``` ### Has Many ```go // User has many emails, UserID is the foreign key type User struct { gorm.Model Emails []Email } type Email struct { gorm.Model Email string UserID uint } db.Model(&user).Related(&emails) //// SELECT * FROM emails WHERE user_id = 111; // 111 is user's primary key ``` ### Many To Many ```go // User has and belongs to many languages, use `user_languages` as join table type User struct { gorm.Model Languages []Language `gorm:"many2many:user_languages;"` } type Language struct { gorm.Model Name string } db.Model(&user).Related(&languages) //// SELECT * FROM "languages" INNER JOIN "user_languages" ON "user_languages"."language_id" = "languages"."id" WHERE "user_languages"."user_id" = 111 ``` ### Polymorphism Supports polymorphic has-many and has-one associations. ```go type Cat struct { Id int Name string Toy Toy `gorm:"polymorphic:Owner;"` } type Dog struct { Id int Name string Toy Toy `gorm:"polymorphic:Owner;"` } type Toy struct { Id int Name string OwnerId int OwnerType string } ``` Note: polymorphic belongs-to and many-to-many are explicitly NOT supported, and will throw errors. ## Association Mode Association Mode contains some helper methods to handle relationship things easily. ```go // Start Association Mode var user User db.Model(&user).Association("Languages") // `user` is the source, it need to be a valid record (contains primary key) // `Languages` is source's field name for a relationship. // If those conditions not matched, will return an error, check it with: // db.Model(&user).Association("Languages").Error // Query - Find out all related associations db.Model(&user).Association("Languages").Find(&languages) // Append - Append new associations for many2many, has_many, will replace current association for has_one, belongs_to db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN}) db.Model(&user).Association("Languages").Append(Language{Name: "DE"}) // Delete - Remove relationship between source & passed arguments, won't delete those arguments db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN}) db.Model(&user).Association("Languages").Delete(languageZH, languageEN) // Replace - Replace current associations with new one db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN}) db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN) // Count - Return the count of current associations db.Model(&user).Association("Languages").Count() // Clear - Remove relationship between source & current associations, won't delete those associations db.Model(&user).Association("Languages").Clear() ``` ## Advanced Usage ## FirstOrInit Get the first matched record, or initialize a record with search conditions. ```go // Unfound db.FirstOrInit(&user, User{Name: "non_existing"}) //// user -> User{Name: "non_existing"} // Found db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user) //// user -> User{Id: 111, Name: "Jinzhu", Age: 20} db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"}) //// user -> User{Id: 111, Name: "Jinzhu", Age: 20} ``` ### Attrs Ignore some values when searching, but use them to initialize the struct if record is not found. ```go // Unfound db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user) //// SELECT * FROM USERS WHERE name = 'non_existing'; //// user -> User{Name: "non_existing", Age: 20} db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user) //// SELECT * FROM USERS WHERE name = 'non_existing'; //// user -> User{Name: "non_existing", Age: 20} // Found db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user) //// SELECT * FROM USERS WHERE name = jinzhu'; //// user -> User{Id: 111, Name: "Jinzhu", Age: 20} ``` ### Assign Ignore some values when searching, but assign it to the result regardless it is found or not. ```go // Unfound db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user) //// user -> User{Name: "non_existing", Age: 20} // Found db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user) //// SELECT * FROM USERS WHERE name = jinzhu'; //// user -> User{Id: 111, Name: "Jinzhu", Age: 30} ``` ## FirstOrCreate Get the first matched record, or create with search conditions. ```go // Unfound db.FirstOrCreate(&user, User{Name: "non_existing"}) //// INSERT INTO "users" (name) VALUES ("non_existing"); //// user -> User{Id: 112, Name: "non_existing"} // Found db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user) //// user -> User{Id: 111, Name: "Jinzhu"} ``` ### Attrs Ignore some values when searching, but use them to create the struct if record is not found. like `FirstOrInit` ```go // Unfound db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user) //// SELECT * FROM users WHERE name = 'non_existing'; //// INSERT INTO "users" (name, age) VALUES ("non_existing", 20); //// user -> User{Id: 112, Name: "non_existing", Age: 20} // Found db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user) //// SELECT * FROM users WHERE name = 'jinzhu'; //// user -> User{Id: 111, Name: "jinzhu", Age: 20} ``` ### Assign Ignore some values when searching, but assign it to the record regardless it is found or not, then save back to database. like `FirstOrInit` ```go // Unfound db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user) //// SELECT * FROM users WHERE name = 'non_existing'; //// INSERT INTO "users" (name, age) VALUES ("non_existing", 20); //// user -> User{Id: 112, Name: "non_existing", Age: 20} // Found 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 -> User{Id: 111, Name: "jinzhu", Age: 30} ``` ## Select ```go db.Select("name, age").Find(&users) //// SELECT name, age FROM users; db.Select([]string{"name", "age"}).Find(&users) //// SELECT name, age FROM users; db.Table("users").Select("COALESCE(age,?)", 42).Rows() //// SELECT COALESCE(age,'42') FROM users; ``` ## Order ```go 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 ```go db.Limit(3).Find(&users) //// SELECT * FROM users LIMIT 3; // Cancel limit condition with -1 db.Limit(10).Find(&users1).Limit(-1).Find(&users2) //// SELECT * FROM users LIMIT 10; (users1) //// SELECT * FROM users; (users2) ``` ## Offset ```go db.Offset(3).Find(&users) //// SELECT * FROM users OFFSET 3; // Cancel offset condition with -1 db.Offset(10).Find(&users1).Offset(-1).Find(&users2) //// SELECT * FROM users OFFSET 10; (users1) //// SELECT * FROM users; (users2) ``` ## Count ```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) db.Model(User{}).Where("name = ?", "jinzhu").Count(&count) //// SELECT count(*) FROM users WHERE name = 'jinzhu'; (count) db.Table("deleted_users").Count(&count) //// SELECT count(*) FROM deleted_users; ``` ## Pluck Get selected attributes as map ```go var ages []int64 db.Find(&users).Pluck("age", &ages) var names []string db.Model(&User{}).Pluck("name", &names) db.Table("deleted_users").Pluck("name", &names) // Requesting more than one column? Do it like this: db.Select("name, age").Find(&users) ``` ## Raw SQL ```go db.Exec("DROP TABLE users;") db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33}) ``` ## Row & Rows It is even possible to get query result as `*sql.Row` or `*sql.Rows` ```go 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) ... } // 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 results into another struct. ```go type Result struct { Name string Age int } var result Result db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result) // Raw SQL db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result) ``` ## 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() { ... } 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) // find a user by email address db.Joins("inner join emails on emails.user_id = users.id").Where("emails.email = ?", "x@example.org").Find(&user) // find all email addresses for a user db.Joins("left join users on users.id = emails.user_id").Where("users.name = ?", "jinzhu").Find(&emails) ``` ## Transactions To perform a set of operations within a transaction, the general flow is as below. The database handle returned from ``` db.Begin() ``` should be used for all operations within the transaction. (Note that all individual save and delete operations are run in a transaction by default.) ```go // begin tx := db.Begin() // do some database operations (use 'tx' from this point, not 'db') tx.Create(...) ... // rollback in case of error tx.Rollback() // Or commit if all is ok tx.Commit() ``` ### A Specific Example ``` func CreateAnimals(db *gorm.DB) err { tx := db.Begin() // Note the use of tx as the database handle once you are within a transaction if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil { tx.Rollback() return err } if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil { tx.Rollback() return err } tx.Commit() return nil } ``` ## Scopes ```go func AmountGreaterThan1000(db *gorm.DB) *gorm.DB { return db.Where("amount > ?", 1000) } func PaidWithCreditCard(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } func PaidWithCod(db *gorm.DB) *gorm.DB { return db.Where("pay_mode_sign = ?", "C") } func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB { return func (db *gorm.DB) *gorm.DB { return db.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 ``` ## Callbacks Callbacks are methods defined on the pointer of struct. If any callback returns an error, gorm will stop future operations and rollback all changes. Here is the list of all available callbacks: (listed in the same order in which they will get called during the respective operations) ### Creating An Object ```go BeforeSave BeforeCreate // save before associations // save self // save after associations AfterCreate AfterSave ``` ### Updating An Object ```go BeforeSave BeforeUpdate // save before associations // save self // save after associations AfterUpdate AfterSave ``` ### Destroying An Object ```go BeforeDelete // delete self AfterDelete ``` ### After Find ```go // load data from database AfterFind ``` ### Example ```go func (u *User) BeforeUpdate() (err error) { if u.readonly() { err = errors.New("read only user") } return } // Rollback the insertion if user's id greater than 1000 func (u *User) AfterCreate() (err error) { if (u.Id > 1000) { err = errors.New("user id is already greater than 1000") } return } ``` Save/delete operations in gorm are running in a transaction. Changes made in that transaction are not visible unless it is commited. So if you want to use those changes in your callbacks, you need to run your SQL in the same transaction. For this Gorm supports passing transactions to callbacks like this: ```go func (u *User) AfterCreate(tx *gorm.DB) (err error) { tx.Model(u).Update("role", "admin") return } ``` ## Specifying The Table Name ```go // Create `deleted_users` table with struct User's definition db.Table("deleted_users").CreateTable(&User{}) var deleted_users []User db.Table("deleted_users").Find(&deleted_users) //// SELECT * FROM deleted_users; db.Table("deleted_users").Where("name = ?", "jinzhu").Delete() //// DELETE FROM deleted_users WHERE name = 'jinzhu'; ``` ### Specifying The Table Name For A Struct Permanently with TableName ```go 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" } } ``` ## Error Handling ```go query := db.Where("name = ?", "jinzhu").First(&user) query := db.First(&user).Limit(10).Find(&users) // query.Error will return the last happened error // So you could do error handing in your application like this: if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil { // error handling... } // RecordNotFound // If no record found when you query data, gorm will return RecordNotFound error, you could check it like this: db.Where("name = ?", "hello world").First(&User{}).Error == gorm.RecordNotFound // Or use the shortcut method db.Where("name = ?", "hello world").First(&user).RecordNotFound() if db.Model(&user).Related(&credit_card).RecordNotFound() { // no credit card found error handling } ``` ## Logger Gorm has built-in logger support ```go // Enable Logger db.LogMode(true) // Diable Logger db.LogMode(false) // Debug a single operation db.Debug().Where("name = ?", "jinzhu").First(&User{}) ``` ![logger](https://raw.github.com/jinzhu/gorm/master/doc/logger.png) ### Customize Logger ```go // Refer gorm's default logger for how to: https://github.com/jinzhu/gorm/blob/master/logger.go#files db.SetLogger(gorm.Logger{revel.TRACE}) db.SetLogger(log.New(os.Stdout, "\r\n", 0)) ``` ## Existing Schema If you have an existing database schema, and the 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 { AnimalId int64 `gorm:"primary_key"` Birthday time.Time `sql:"DEFAULT:current_timestamp"` Name string `sql:"default:'galeone'"` Age int64 } ``` If your column names differ from the struct fields, you can specify them like this: ```go type Animal struct { AnimalId int64 `gorm:"column:beast_id;primary_key"` Birthday time.Time `gorm:"column:day_of_the_beast"` Age int64 `gorm:"column:age_of_the_beast"` } ``` ## Composite Primary Key ```go type Product struct { ID string `gorm:"primary_key"` LanguageCode string `gorm:"primary_key"` } ``` ## Database Indexes & Foreign Key ```go // Add foreign key // 1st param : foreignkey field // 2nd param : destination table(id) // 3rd param : ONDELETE // 4th param : ONUPDATE db.Model(&User{}).AddForeignKey("city_id", "cities(id)", "RESTRICT", "RESTRICT") // Add index db.Model(&User{}).AddIndex("idx_user_name", "name") // Multiple column index db.Model(&User{}).AddIndex("idx_user_name_age", "name", "age") // Add unique index db.Model(&User{}).AddUniqueIndex("idx_user_name", "name") // Multiple column unique index db.Model(&User{}).AddUniqueIndex("idx_user_name_age", "name", "age") // Remove index db.Model(&User{}).RemoveIndex("idx_user_name") ``` ## Default values ```go type Animal struct { ID int64 Name string `sql:"default:'galeone'"` Age int64 } ``` If you have defined a default value in the `sql` tag, the generated create SQL will ignore these fields if it is blank. Eg. ```go db.Create(&Animal{Age: 99, Name: ""}) ``` The generated SQL will be: ```sql INSERT INTO animals("age") values('99'); ``` The same thing occurs in update statements. ## More examples with query chain ```go 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) 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 for 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) // FirstOrCreate example 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 record not found ``` ## Documentation [![GoDoc](https://godoc.org/github.com/jinzhu/gorm?status.svg)](https://godoc.org/github.com/jinzhu/gorm) `go doc` format documentation for this project can be viewed online without installing the package by using the GoDoc page at: http://godoc.org/github.com/jinzhu/gorm ## TODO * Github Pages # Author **jinzhu** * * * # Contributors https://github.com/jinzhu/gorm/graphs/contributors ## License Released under the [MIT License](https://github.com/jinzhu/gorm/blob/master/License).