2013-10-25 12:24:29 +04:00
|
|
|
# GORM
|
|
|
|
|
2013-10-26 11:18:44 +04:00
|
|
|
Yet Another ORM library for Go, aims for developer friendly
|
2013-10-25 12:24:29 +04:00
|
|
|
|
2013-10-28 06:09:44 +04:00
|
|
|
## Overview
|
|
|
|
|
|
|
|
* CURD
|
|
|
|
* Chainable API
|
|
|
|
* Before/After Create/Save/Update/Delete Callbacks
|
|
|
|
* Order/Select/Limit/Offset Support
|
2013-10-28 17:52:22 +04:00
|
|
|
* Update, Updates Like Rails's update_attribute, update_attributes
|
|
|
|
* Dynamically set table name when search, update, delete...
|
2013-10-29 07:01:51 +04:00
|
|
|
* Automatically CreatedAt, UpdatedAt
|
|
|
|
* Soft Delete
|
2013-10-28 06:09:44 +04:00
|
|
|
* Create table from struct
|
|
|
|
* Prevent SQL Injection
|
|
|
|
* Goroutines friendly
|
|
|
|
* Database Pool
|
|
|
|
|
2013-10-27 18:18:06 +04:00
|
|
|
## Basic Usage
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
```go
|
|
|
|
db, _ = Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
|
|
|
|
|
|
|
type User struct {
|
2013-10-28 16:27:25 +04:00
|
|
|
Id int64
|
|
|
|
Age int64
|
|
|
|
Birthday time.Time
|
|
|
|
Name string
|
|
|
|
CreatedAt time.Time
|
|
|
|
UpdatedAt time.Time
|
2013-10-27 17:37:31 +04:00
|
|
|
}
|
|
|
|
|
2013-10-28 06:09:44 +04:00
|
|
|
// Set database pool
|
|
|
|
db.SetPool(10)
|
|
|
|
|
2013-10-27 17:37:31 +04:00
|
|
|
// Create
|
2013-10-28 06:09:44 +04:00
|
|
|
user = User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
|
2013-10-27 17:37:31 +04:00
|
|
|
db.Save(&user)
|
|
|
|
|
|
|
|
// Update
|
|
|
|
user.Name = "jinzhu 2"
|
|
|
|
db.Save(&user)
|
|
|
|
|
|
|
|
// Delete
|
|
|
|
db.Delete(&user)
|
|
|
|
|
|
|
|
// Get First matched record
|
|
|
|
db.Where("name = ?", "jinzhu").First(&user)
|
|
|
|
|
|
|
|
// Get All matched records
|
|
|
|
db.Where("name = ?", "jinzhu").Find(&users)
|
|
|
|
|
|
|
|
// Advanced Where Usage
|
|
|
|
db.Where("name <> ?", "jinzhu").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users name <> 'jinzhu';
|
2013-10-29 11:37:58 +04:00
|
|
|
db.Where(20).First(&user)
|
|
|
|
//// users -> select * from users where id = 20;
|
|
|
|
db.Where([]int64{20, 21, 22}).Find(&user)
|
|
|
|
//// users -> select * from users where id in (20, 21, 22);
|
2013-10-27 18:36:43 +04:00
|
|
|
db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users name = 'jinzhu' and age >= 22;
|
2013-10-27 17:37:31 +04:00
|
|
|
db.Where("name in (?)", []string["jinzhu", "jinzhu 2"]).Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users name in ('jinzhu', 'jinzhu 2');
|
|
|
|
db.Where("name LIKE ?", "%jin%").Find(&users)
|
|
|
|
//// users -> select * from users name LIKE "%jinzhu%";
|
2013-10-29 13:37:45 +04:00
|
|
|
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
|
|
|
|
//// user -> select * from users name = "jinzhu" and age = 20 limit 1;
|
2013-10-29 13:52:37 +04:00
|
|
|
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).First(&user)
|
|
|
|
//// user -> select * from users name = "jinzhu" and age = 20 limit 1;
|
2013-10-27 17:37:31 +04:00
|
|
|
db.Where("birthday < ?", time.Now()).Find(&users)
|
|
|
|
|
2013-10-27 18:18:06 +04:00
|
|
|
// Inline search condition
|
2013-10-27 18:36:43 +04:00
|
|
|
db.First(&user, 23)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// user -> select * from users where id = 23 limit 1;
|
2013-10-27 18:36:43 +04:00
|
|
|
db.First(&user, "name = ?", "jinzhu")
|
2013-10-28 05:05:44 +04:00
|
|
|
//// user -> select * from users where name = "jinzhu" limit 1;
|
2013-10-27 18:36:43 +04:00
|
|
|
db.Find(&users, "name = ?", "jinzhu")
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users where name = "jinzhu";
|
2013-10-27 18:36:43 +04:00
|
|
|
db.Find(&users, "name <> ? and age > ?", "jinzhu", 20)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users where name <> "jinzhu" and age > 20;
|
2013-10-29 13:37:45 +04:00
|
|
|
db.Find(&users, &User{Age: 20})
|
|
|
|
//// users -> select * from users where age = 20;
|
2013-10-29 13:52:37 +04:00
|
|
|
db.Find(&users, map[string]interface{}{"age": 20})
|
|
|
|
//// users -> select * from users where age = 20;
|
2013-10-27 18:18:06 +04:00
|
|
|
|
2013-10-27 17:37:31 +04:00
|
|
|
// Select
|
|
|
|
db.Select("name").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select name from users;
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// Order
|
|
|
|
db.Order("age desc, name").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users order by age desc, name;
|
2013-10-27 18:18:06 +04:00
|
|
|
db.Order("age desc").Order("name").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> 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)
|
|
|
|
//// users1 -> select * from users order by age desc;
|
|
|
|
//// users2 -> select * from users order by age;
|
|
|
|
|
2013-10-27 17:37:31 +04:00
|
|
|
// Limit
|
|
|
|
db.Limit(3).Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users limit 3;
|
|
|
|
db.Limit(10).Find(&users1).Limit(20).Find(&users2).Limit(-1).Find(&users3)
|
|
|
|
//// users1 -> select * from users limit 10;
|
|
|
|
//// users2 -> select * from users limit 20;
|
|
|
|
//// users3 -> select * from users;
|
|
|
|
|
2013-10-27 17:37:31 +04:00
|
|
|
// Offset
|
2013-10-28 04:08:45 +04:00
|
|
|
//// select * from users offset 3;
|
2013-10-27 17:37:31 +04:00
|
|
|
db.Offset(3).Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
db.Offset(10).Find(&users1).Offset(20).Find(&users2).Offset(-1).Find(&users3)
|
|
|
|
//// user1 -> select * from users offset 10;
|
|
|
|
//// user2 -> select * from users offset 20;
|
|
|
|
//// user3 -> select * from users;
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// Or
|
2013-10-27 18:18:06 +04:00
|
|
|
db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users where role = 'admin' or role = 'super_admin';
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// Count
|
2013-10-27 18:18:06 +04:00
|
|
|
db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users where name = 'jinzhu' or name = 'jinzhu 2';
|
|
|
|
//// count -> select count(*) from users where name = 'jinzhu' or name = 'jinzhu 2';
|
2013-10-27 18:18:06 +04:00
|
|
|
db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// CreatedAt (auto insert current time on create)
|
2013-10-27 18:18:06 +04:00
|
|
|
If your struct has field CreatedAt,
|
|
|
|
it will be filled with the current time when insert into database
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// UpdatedAt (auto update the time on save)
|
2013-10-27 18:18:06 +04:00
|
|
|
If your struct has field UpdatedAt,
|
|
|
|
it will be filled with the current time when update it
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// Callbacks
|
2013-10-27 18:18:06 +04:00
|
|
|
Below callbacks are defined now:
|
|
|
|
|
|
|
|
`BeforeCreate`, `BeforeUpdate`, `BeforeSave`, `AfterCreate`, `AfterUpdate`, `AfterSave`
|
|
|
|
`BeforeDelete`, `AfterDelete`
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
Callbacks is a function defined to a model, if the function return error, will prevent the database operations.
|
|
|
|
|
2013-10-28 16:27:25 +04:00
|
|
|
func (u *User) BeforeUpdate() (err error) {
|
|
|
|
if u.readonly() {
|
|
|
|
err = errors.New("Read Only User")
|
2013-10-27 18:18:06 +04:00
|
|
|
}
|
2013-10-28 16:27:25 +04:00
|
|
|
return
|
|
|
|
}
|
2013-10-27 18:18:06 +04:00
|
|
|
|
|
|
|
// Pluck (get users's age as map)
|
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)
|
|
|
|
//// ages -> select age from users;
|
2013-10-27 18:18:06 +04:00
|
|
|
var names []string
|
|
|
|
db.Model(&User{}).Pluck("name", &names)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// names -> select name from users;
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// Query Chains
|
|
|
|
db.Where("name <> ?", "jinzhu").Where("age >= ? and role <> ?", 20, "admin").Find(&users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// users -> select * from users where name <> 'jinzhu' andd age >= 20 and role <> 'admin';
|
2013-10-27 17:37:31 +04:00
|
|
|
|
|
|
|
// Create Table with struct
|
|
|
|
db.CreateTable(&User{})
|
|
|
|
|
2013-10-28 16:27:25 +04:00
|
|
|
// Specify Table Name
|
|
|
|
db.Table("deleted_users").CreateTable(&User{})
|
|
|
|
db.Table("users").Pluck("age", &ages)
|
|
|
|
//// ages -> select age from users;
|
|
|
|
var deleted_users []User
|
|
|
|
db.Table("deleted_users").Find(&deleted_users)
|
|
|
|
//// deleted_users -> select * from deleted_users;
|
|
|
|
db.Table("deleted_users").Find(&deleted_user)
|
|
|
|
//// deleted_user -> select * from deleted_users limit 1;
|
|
|
|
|
2013-10-28 17:52:22 +04:00
|
|
|
// Update
|
|
|
|
db.Table("users").Where(10).Update("name", "hello")
|
|
|
|
//// update users set name='hello' where id = 10;
|
|
|
|
db.Table("users").Update("name", "hello")
|
|
|
|
//// update users set name='hello';
|
|
|
|
|
|
|
|
// Updates
|
|
|
|
db.Table("users").Where(10).Updates(map[string]interface{}{"name": "hello", "age": 18})
|
|
|
|
//// update users set name='hello', age=18 where id = 10;
|
|
|
|
db.Table("users").Updates(map[string]interface{}{"name": "hello", "age": 18})
|
|
|
|
//// update users set name='hello', age=18;
|
|
|
|
|
2013-10-29 07:01:51 +04:00
|
|
|
// Soft Delete
|
|
|
|
// For those struct have DeletedAt field, they will get soft delete ability automatically!
|
|
|
|
type Order struct {
|
|
|
|
Id int64
|
|
|
|
Amount int64
|
|
|
|
CreatedAt time.Time
|
|
|
|
UpdatedAt time.Time
|
|
|
|
DeletedAt time.Time
|
|
|
|
}
|
|
|
|
order := order{Id:10}
|
|
|
|
db.Delete(&order)
|
|
|
|
//// UPDATE orders SET deleted_at="2013-10-29 10:23" WHERE id = 10;
|
|
|
|
db.Where("amount = ?", 0).Delete(&Order{})
|
|
|
|
//// UPDATE orders SET deleted_at="2013-10-29 10:23" WHERE amount = 0;
|
|
|
|
db.Where("amount = 100").Find(&order)
|
|
|
|
//// order -> select * from orders where amount = 100 and (deleted_at is null and deleted_at <= '0001-01-02');
|
|
|
|
// And you are possible to query soft deleted orders with Unscoped method
|
|
|
|
db.Unscoped().Where("amount = 100").Find(&order)
|
|
|
|
//// order -> select * from orders where amount = 100;
|
|
|
|
// Of course, you could permanently delete a record with Unscoped
|
|
|
|
db.Unscoped().Delete(&order)
|
|
|
|
// DELETE from orders where id=10;
|
|
|
|
|
2013-10-27 17:37:31 +04:00
|
|
|
// Run Raw SQL
|
|
|
|
db.Exec("drop table users;")
|
2013-10-28 07:24:51 +04:00
|
|
|
|
|
|
|
// Error Handling
|
|
|
|
query := db.Where("name = ?", "jinzhu").First(&user)
|
|
|
|
query := db.First(&user).Limit(10).Find(&users)
|
|
|
|
//// query.Error -> the last error happened
|
|
|
|
//// query.Errors -> all errors happened
|
|
|
|
//// If an error happened, gorm will stop do insert, update, delete operations
|
2013-10-27 17:37:31 +04:00
|
|
|
```
|
|
|
|
|
2013-10-28 04:08:45 +04:00
|
|
|
## Advanced Usage With Query Chain
|
2013-10-27 18:18:06 +04:00
|
|
|
|
|
|
|
```go
|
|
|
|
// Already excited about the basic usage? Let's see some magic!
|
|
|
|
|
|
|
|
db.First(&first_article).Count(&total_count).Limit(10).Find(&first_page_articles).Offset(10).Find(&second_page_articles)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// first_article -> select * from articles limit 1
|
|
|
|
//// total_count -> select count(*) from articles
|
|
|
|
//// first_page_articles -> select * from articles limit 10
|
|
|
|
//// second_page_articles -> select * from articles limit 10 offset 10
|
2013-10-27 18:18:06 +04:00
|
|
|
|
|
|
|
db.Where("created_at > ?", "2013/10/10").Find(&cancelled_orders, "state = ?", "cancelled").Find(&shipped_orders, "state = ?", "shipped")
|
2013-10-28 05:05:44 +04:00
|
|
|
//// cancelled_orders -> select * from orders where created_at > '2013/10/10' and state = 'cancelled'
|
|
|
|
//// shipped_orders -> select * from orders where created_at > '2013/10/10' and state = 'shipped'
|
2013-10-27 18:18:06 +04:00
|
|
|
|
|
|
|
db.Model(&Order{}).Where("amount > ?", 10000).Pluck("user_id", &paid_user_ids)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// paid_user_ids -> select user_id from orders where amount > 10000
|
2013-10-27 18:18:06 +04:00
|
|
|
db.Where("user_id = ?", paid_user_ids).Find(&:paid_users)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// paid_users -> select * from users where user_id in (10, 20, 99)
|
2013-10-27 18:18:06 +04:00
|
|
|
|
|
|
|
db.Where("product_name = ?", "fancy_product").Find(&orders).Find(&shopping_cart)
|
2013-10-28 05:05:44 +04:00
|
|
|
//// orders -> select * from orders where product_name = 'fancy_product'
|
|
|
|
//// shopping_cart -> select * from carts where product_name = 'fancy_product'
|
2013-10-28 04:08:45 +04:00
|
|
|
// Do you noticed the search table is different for above query, yay
|
2013-10-27 18:18:06 +04:00
|
|
|
|
2013-10-28 16:27:25 +04:00
|
|
|
db.Where("mail_type = ?", "TEXT").Find(&users1).Table("deleted_users").First(&user2)
|
|
|
|
//// users1 -> select * from users where mail_type = 'TEXT';
|
|
|
|
//// users2 -> select * from deleted_users where mail_type = 'TEXT';
|
|
|
|
|
2013-10-27 18:18:06 +04:00
|
|
|
// Open your mind, add more cool examples
|
|
|
|
```
|
|
|
|
|
2013-10-26 11:18:44 +04:00
|
|
|
## TODO
|
2013-10-29 13:52:37 +04:00
|
|
|
* FindOrInitialize / FindOrCreate
|
2013-10-29 03:39:26 +04:00
|
|
|
* SubStruct
|
|
|
|
* Index, Unique, Valiations
|
|
|
|
* Auto Migration
|
2013-10-26 11:18:44 +04:00
|
|
|
* SQL Log
|
2013-10-28 04:08:45 +04:00
|
|
|
* SQL Query with goroutines
|
|
|
|
* Only tested with postgres, confirm works with other database adaptors
|
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-10-26 11:18:44 +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>
|