# CRUD: Reading and Writing Data ## Create ### Create Record ```go user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()} db.NewRecord(user) // => returns `true` as 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; ``` ### Create With Associations Refer [Associations](associations.html) for more details ### Default Values You could define default value in the `gorm` tag, then the inserting SQL will ignore these fields that has default value and its value is blank, and after insert the record into databae, gorm will load those fields's value from database. ```go type Animal struct { ID int64 Name string `gorm:"default:'galeone'"` Age int64 } var animal = Animal{Age: 99, Name: ""} db.Create(&animal) // INSERT INTO animals("age") values('99'); // SELECT name from animals WHERE ID=111; // the returning primary key is 111 // animal.Name => 'galeone' ``` ### Setting Primary Key In Callbacks If you want to set primary field's value in `BeforeCreate` callback, you could use `scope.SetColumn`, for example: ```go func (user *User) BeforeCreate(scope *gorm.Scope) error { scope.SetColumn("ID", uuid.New()) return nil } ``` ### Extra Creating option ```go // Add extra SQL option for inserting SQL db.Set("gorm:insert_option", "ON CONFLICT").Create(&product) // INSERT INTO products (name, code) VALUES ("name", "code") ON CONFLICT; ``` ## Query ```go // Get first record, order by primary key db.First(&user) //// SELECT * FROM users ORDER BY id LIMIT 1; // Get last record, order by primary key 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 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) **NOTE** When query with struct, GORM will only query with those fields has value ```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 **NOTE** When query with primary key, you should carefully check the value you passed is a valid primary key, to avoid SQL injection ```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) ``` ### Extra Querying option ```go // Add extra SQL option for selecting SQL db.Set("gorm:query_option", "FOR UPDATE").First(&user, 10) //// SELECT * FROM users WHERE id = 10 FOR UPDATE; ``` ### FirstOrInit Get first matched record, or initalize a new one with given conditions (only works with struct, map 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 Initalize struct with argument if record haven't been 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 Assign argument to results 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 first matched record, or create a new one with given conditions (only works with struct, map 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 Assgin struct with argument if record haven't been found ```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 Assign it to the record regardless it is found or not, and save back to database. ```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 Specify fields that you want to retrieve from database, by default, will select all fields; ```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 Specify order when retrieve records from database, set reorder to `true` to overwrite defined conditions ```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 Specify the number of records to be retrieved ```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 Specify the number of records to skip before starting to return the records ```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 Get how many records for a model ```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; ``` ### 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 Specify Joins conditions ```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) // multiple joins with parameter db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user) ``` ### Pluck Query single column from a model as a map, if you want to query multiple columns, you could use [`Scan`](#scan) ```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) ``` ### 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) ``` ### Scopes Pass current database connection to `func(*DB) *DB`, which could be used to add conditions dynamically ```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 ``` ### 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'; ``` ## Update ### Update All Fields `Save` will include all fields when perform the Updating SQL, even it is not changed ```go db.First(&user) user.Name = "jinzhu 2" user.Age = 100 db.Save(&user) //// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111; ``` ### Update Changed Fields If you only want to update changed Fields, you could use `Update`, `Updates` ```go // Update single 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; // Update single attribute with combined conditions 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; // Update multiple attributes with `map`, will only update those changed fields db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) //// UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111; // Update multiple attributes with `struct`, will only update those changed & non blank fields 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; // WARNING when update with struct, GORM will only update those fields that with non blank value // For below Update, nothing will be updated as "", 0, false are blank values of their types db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false}) ``` ### Update Selected Fields If you only want to update or ignore some fields when updating, you could use `Select`, `Omit` ```go db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) //// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111; db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false}) //// UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111; ``` ### Update Changed Fields Without Callbacks Above updating operations will perform the mdoel's `BeforeUpdate`, `AfterUpdate` method, update its `UpdatedAt` timestamp, save its `Associations` when updaing, if you don't want to call them, you could use `UpdateColumn`, `UpdateColumns` ```go // Update single attribute, similar with `Update` db.Model(&user).UpdateColumn("name", "hello") //// UPDATE users SET name='hello' WHERE id = 111; // Update multiple attributes, similar with `Updates` db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18}) //// UPDATE users SET name='hello', age=18 WHERE id = 111; ``` ### Batch Updates Callbacks won't run when do batch updates ```go db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18}) //// UPDATE users SET name='hello', age=18 WHERE id IN (10, 11); // 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; // Get updated records count with `RowsAffected` 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 "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 "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; ``` ### Change Updating Values In Callbacks If you want to change updating values in callbacks using `BeforeUpdate`, `BeforeSave`, you could use `scope.SetColumn`, for example: ```go func (user *User) BeforeSave(scope *gorm.Scope) (err error) { if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil { scope.SetColumn("EncryptedPassword", pw) } } ``` ### Extra Updating option ```go // Add extra SQL option for updating SQL db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name, "hello") //// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FOR UNKNOWN); ``` ## Delete **WARNING** When delete a record, you need to ensure it's primary field has value, and GORM will use the primary key to delete the record, if primary field's blank, GORM will delete all records for the model ```go // Delete an existing record db.Delete(&email) //// DELETE from emails where id=10; // Add extra SQL option for deleting SQL db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email) //// DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN); ``` ### Batch Delete Delete all matched records ```go db.Where("email LIKE ?", "%jinzhu%").Delete(Email{}) //// DELETE from emails where email LIKE "%jinhu%"; db.Delete(Email{}, "email LIKE ?", "%jinzhu%") //// DELETE from emails where email LIKE "%jinhu%"; ``` ### Soft Delete If model has `DeletedAt` field, it will get soft delete ability automatically! then it won't be deleted from database permanently when call `Delete`, but only set field `DeletedAt`'s value to current time ```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; // 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; ```