mirror of https://github.com/go-gorm/gorm.git
Update README
This commit is contained in:
parent
aa352d405b
commit
b4981259de
366
README.md
366
README.md
|
@ -6,167 +6,306 @@ Yet Another ORM library for Go, aims for developer friendly
|
|||
|
||||
* CURD
|
||||
* Chainable API
|
||||
* Embedded structs support
|
||||
* Before/After Create/Save/Update/Delete Callbacks
|
||||
* Order/Select/Limit/Offset Support
|
||||
* Update, Updates Like Rails's update_attribute, update_attributes
|
||||
* FirstOrInit, FirstOrCreate Like Rails's first_or_initialize, first_or_create
|
||||
* Dynamically set table name when search, update, delete...
|
||||
* Order/Select/Limit/Offset Support
|
||||
* Automatically CreatedAt, UpdatedAt
|
||||
* Soft Delete
|
||||
* Create table from struct
|
||||
* Create/Drop table from struct
|
||||
* Dynamically set table name when search, create, update, delete...
|
||||
* Prevent SQL Injection
|
||||
* Goroutines friendly
|
||||
* Database Pool
|
||||
* Convention Over Configuration (CoC)
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```go
|
||||
db, _ = Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
||||
## Opening a Database
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Age int64
|
||||
Birthday time.Time
|
||||
Name string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
```go
|
||||
db, err = Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
||||
|
||||
// Gorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere
|
||||
|
||||
var DB gorm.DB
|
||||
|
||||
func init() {
|
||||
DB, err = gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Got error when connect database, the error is '%v'", err))
|
||||
}
|
||||
}
|
||||
|
||||
// Set database pool
|
||||
db.SetPool(10)
|
||||
// Set the maximum idle database connections
|
||||
db.SetPool(100)
|
||||
```
|
||||
|
||||
// Create
|
||||
user = User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
|
||||
## Conventions
|
||||
|
||||
```go
|
||||
type User struct { // TableName: `users`, gorm will pluralize struct name as table name
|
||||
Id int64 // Id: Database Primary key
|
||||
Birthday time.Time // Time
|
||||
Age int64
|
||||
Name string
|
||||
CreatedAt time.Time // CreatedAt: Time of record is created, will be insert automatically
|
||||
UpdatedAt time.Time // UpdatedAt: Time of record is updated, will be updated automatically
|
||||
DeletedAt time.Time // DeletedAt: Time of record is deleted, refer Soft Delete for more
|
||||
Email []Email // Embedded structs
|
||||
BillingAddress Address // Embedded struct
|
||||
BillingAddressId int64 // Embedded struct's foreign key
|
||||
ShippingAddress Address // Embedded struct
|
||||
ShippingAddressId int64 // Embedded struct's foreign key
|
||||
}
|
||||
|
||||
type Email struct { // TableName: `emails`
|
||||
Id int64
|
||||
UserId int64 // Foreign key for above embedded structs
|
||||
Email string
|
||||
Subscribed bool
|
||||
}
|
||||
|
||||
type Address struct { // TableName: `addresses`
|
||||
Id int64
|
||||
Address1 string
|
||||
Address2 string
|
||||
Post string
|
||||
}
|
||||
```
|
||||
|
||||
## Struct & Database Mapping
|
||||
|
||||
```go
|
||||
// Create table from struct
|
||||
db.CreateTable(User{})
|
||||
|
||||
// Drop table
|
||||
db.DropTable(User{})
|
||||
```
|
||||
|
||||
## Create
|
||||
|
||||
```go
|
||||
user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
|
||||
db.Save(&user)
|
||||
```
|
||||
|
||||
// Update
|
||||
## Update
|
||||
|
||||
```go
|
||||
user.Name = "jinzhu 2"
|
||||
db.Save(&user)
|
||||
```
|
||||
|
||||
// Delete
|
||||
## Delete
|
||||
|
||||
```go
|
||||
db.Delete(&user)
|
||||
```
|
||||
|
||||
// Get First matched record
|
||||
## Query
|
||||
|
||||
```go
|
||||
// Get the first record
|
||||
db.First(&user)
|
||||
//// SELECT * FROM USERS LIMIT 1;
|
||||
|
||||
// Get All records
|
||||
db.Find(&users)
|
||||
//// SELECT * FROM USERS;
|
||||
|
||||
// Using a Primary Key
|
||||
db.First(&user, 10)
|
||||
//// SELECT * FROM USERS WHERE id = 10;
|
||||
```
|
||||
|
||||
### Where (SQL like condition)
|
||||
|
||||
```go
|
||||
// Get the first matched record
|
||||
db.Where("name = ?", "jinzhu").First(&user)
|
||||
//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;
|
||||
|
||||
// Get All matched records
|
||||
// Get all matched records
|
||||
db.Where("name = ?", "jinzhu").Find(&users)
|
||||
//// SELECT * FROM users WHERE name = 'jinzhu';
|
||||
|
||||
// Advanced Where Usage
|
||||
db.Where("name <> ?", "jinzhu").Find(&users)
|
||||
//// users -> select * from users name <> 'jinzhu';
|
||||
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);
|
||||
db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
|
||||
//// users -> select * from users name = 'jinzhu' and age >= 22;
|
||||
//// SELECT * FROM users WHERE name <> 'jinzhu';
|
||||
|
||||
// IN
|
||||
db.Where("name in (?)", []string["jinzhu", "jinzhu 2"]).Find(&users)
|
||||
//// users -> select * from users name in ('jinzhu', 'jinzhu 2');
|
||||
//// SELECT * FROM users WHERE name IN ('jinzhu', 'jinzhu 2');
|
||||
|
||||
// IN For Primary Key
|
||||
db.Where([]int64{20, 21, 22}).Find(&users)
|
||||
//// SELECT * FROM users WHERE id IN (20, 21, 22);
|
||||
|
||||
// LIKE
|
||||
db.Where("name LIKE ?", "%jin%").Find(&users)
|
||||
//// users -> select * from users name LIKE "%jinzhu%";
|
||||
//// SELECT * FROM users WHERE name LIKE "%jin%";
|
||||
|
||||
// Multiple Conditions
|
||||
db.Where("name = ? and age >= ?", "jinzhu", "22").Find(&users)
|
||||
//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
|
||||
```
|
||||
|
||||
### Where (Struct & Map)
|
||||
|
||||
```go
|
||||
// Search with struct
|
||||
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
|
||||
//// user -> select * from users name = "jinzhu" and age = 20 limit 1;
|
||||
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).First(&user)
|
||||
//// user -> select * from users name = "jinzhu" and age = 20 limit 1;
|
||||
db.Where("birthday < ?", time.Now()).Find(&users)
|
||||
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;
|
||||
|
||||
// Not
|
||||
db.Not([]int64{1,2,3}).First(&user)
|
||||
//// user -> select * from users where id NOT IN (1,2,3)
|
||||
db.Not([]int64{}).First(&user)
|
||||
//// user -> select * from users;
|
||||
// Search with map
|
||||
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
|
||||
//// SELECT * FROM users WHERE name = "jinzhu" AND age = 20;
|
||||
```
|
||||
|
||||
### Not
|
||||
|
||||
```go
|
||||
// Not Equal
|
||||
db.Not("name", "jinzhu").First(&user)
|
||||
//// user -> select * from users where name <> "jinzhu"
|
||||
//// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;
|
||||
|
||||
// Not In
|
||||
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
|
||||
//// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
|
||||
|
||||
// Not In for Primary Key
|
||||
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;
|
||||
|
||||
// Normal SQL
|
||||
db.Not("name = ?", "jinzhu").First(&user)
|
||||
//// user -> select * from users where NOT(name = "jinzhu")
|
||||
db.Not("name <> ?", "jinzhu").First(&user)
|
||||
//// user -> select * from users where NOT(name <> "jinzhu")
|
||||
db.Not("name", []string{"jinzhu", "jinzhu 2"}).First(&user)
|
||||
//// user -> select * from users where name NOT IN ("jinzhu", "jinzhu 2")
|
||||
//// SELECT * FROM users WHERE NOT(name = "jinzhu");
|
||||
|
||||
// Not With Struct
|
||||
db.Not(User{Name: "jinzhu"}).First(&user)
|
||||
//// user -> select * from users where name <> "jinzhu";
|
||||
//// SELECT * FROM users WHERE name <> "jinzhu";
|
||||
```
|
||||
|
||||
// Inline search condition
|
||||
### Inline Search Condition
|
||||
|
||||
```go
|
||||
// Find with primary key
|
||||
db.First(&user, 23)
|
||||
//// user -> select * from users where id = 23 limit 1;
|
||||
db.First(&user, "name = ?", "jinzhu")
|
||||
//// user -> select * from users where name = "jinzhu" limit 1;
|
||||
db.Find(&users, "name = ?", "jinzhu")
|
||||
//// users -> select * from users where name = "jinzhu";
|
||||
//// SELECT * FROM users WHERE id = 23 LIMIT 1;
|
||||
|
||||
// Normal SQL
|
||||
db.Find(&user, "name = ?", "jinzhu")
|
||||
//// SELECT * FROM users WHERE name = "jinzhu";
|
||||
|
||||
// Multiple Conditions
|
||||
db.Find(&users, "name <> ? and age > ?", "jinzhu", 20)
|
||||
//// users -> select * from users where name <> "jinzhu" and age > 20;
|
||||
db.Find(&users, &User{Age: 20})
|
||||
//// users -> select * from users where age = 20;
|
||||
//// SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;
|
||||
|
||||
// Inline Search With Struct
|
||||
db.Find(&users, User{Age: 20})
|
||||
//// SELECT * FROM users WHERE age = 20;
|
||||
|
||||
// Inline Search With Map
|
||||
db.Find(&users, map[string]interface{}{"age": 20})
|
||||
//// users -> select * from users where age = 20;
|
||||
//// SELECT * FROM users WHERE age = 20;
|
||||
```
|
||||
|
||||
## FirstOrInit
|
||||
|
||||
Try to load the first record, if fails, initialize struct with search conditions.
|
||||
(only support map or struct conditions, SQL like conditions are not supported)
|
||||
|
||||
```go
|
||||
db.FirstOrInit(&user, User{Name: "non_existing"})
|
||||
//// User{Name: "non_existing"}
|
||||
|
||||
// FirstOrInit
|
||||
db.FirstOrInit(&user, User{Name: "noexisting_user"})
|
||||
//// user -> User{Name: "noexisting_user"}
|
||||
db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
|
||||
//// user -> User{Id: 111, Name: "Jinzhu"}
|
||||
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu", "age": 20})
|
||||
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
|
||||
// FirstOrInit With Attrs
|
||||
db.Where(User{Name: "noexisting_user"}).Attrs(User{Age: 20}).FirstOrInit(&user)
|
||||
//// user -> select * from users where name = 'noexisting_user';
|
||||
//// If no record found, will assign the attrs to user, so user become:
|
||||
//// User{Name: "noexisting_user", Age: 20}
|
||||
db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user)
|
||||
// Same as above
|
||||
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 20}).FirstOrInit(&user)
|
||||
//// user -> select * from users where name = 'jinzhu';
|
||||
//// If found the user, will ingore the attrs:
|
||||
//// User{Id: 111, Name: "Jinzhu", Age: 18}
|
||||
|
||||
// FirstOrInit With Assign
|
||||
db.Where(User{Name: "noexisting_user"}).Assign(User{Age: 20}).FirstOrInit(&user)
|
||||
//// user -> select * from users where name = 'noexisting_user';
|
||||
//// If no record found, will assign the value to user, so user become:
|
||||
//// User{Name: "noexisting_user", Age: 20} (same as FirstOrInit With Attrs)
|
||||
db.Where(User{Name: "noexisting_user"}).Assign("age", 20).FirstOrInit(&user)
|
||||
// Same as above
|
||||
//// user -> User{Name: "noexisting_user", Age: 20}
|
||||
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 20}).FirstOrInit(&user)
|
||||
//// user -> select * from users where name = 'jinzhu';
|
||||
//// If found the user, will assign the value to user, so user become: (different with FirstOrInit With Attrs)
|
||||
//// User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
|
||||
// FirstOrCreate
|
||||
db.FirstOrCreate(&user, User{Name: "noexisting_user"})
|
||||
//// user -> User{Id: 112, Name: "noexisting_user"}
|
||||
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
|
||||
//// User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
```
|
||||
|
||||
### FirstOrInit With Attrs
|
||||
|
||||
Attr's arguments would be used to initialize struct if not record found, but won't be used for search
|
||||
|
||||
```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}
|
||||
|
||||
// Above code could be simplified if have only one attribute
|
||||
db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrInit(&user)
|
||||
|
||||
// If record found, Attrs would be ignored
|
||||
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
|
||||
//// SELECT * FROM USERS WHERE name = jinzhu';
|
||||
//// User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
|
||||
### FirstOrInit With Assign
|
||||
|
||||
Assign's arguments would be used to set the struct even record found, but won't be used for search
|
||||
|
||||
```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 load the first record, if fails, initialize struct with search conditions and save it
|
||||
|
||||
```go
|
||||
db.FirstOrCreate(&user, User{Name: "non_existing"})
|
||||
//// User{Id: 112, Name: "non_existing"}
|
||||
|
||||
db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
|
||||
//// user -> User{Id: 111, Name: "Jinzhu"}
|
||||
db.FirstOrCreate(&user, map[string]interface{}{"name": "jinzhu", "age": 20})
|
||||
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
//// User{Id: 111, Name: "Jinzhu"}
|
||||
|
||||
db.FirstOrCreate(&user, map[string]interface{}{"name": "jinzhu", "age": 30})
|
||||
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
```
|
||||
|
||||
### FirstOrCreate With Attrs
|
||||
|
||||
Attr's arguments would be used to initialize struct if not record found, but won't be used for search
|
||||
|
||||
```go
|
||||
// FirstOrCreate With Attrs
|
||||
db.Where(User{Name: "noexisting_user"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
|
||||
//// user -> select * from users where name = 'noexisting_user';
|
||||
//// If not record found, will assing the attrs to the user first, then create it
|
||||
//// Same as db.Where(User{Name: "noexisting_user"}).FirstOrCreate(&user).Update("age": 20), but one less sql
|
||||
db.Where(User{Name: "noexisting_user"}).Attrs("age", 20).FirstOrCreate(&user)
|
||||
// Save as above
|
||||
//// user -> User{Id: 112, Name: "noexisting_user", Age: 20}
|
||||
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
|
||||
//// user -> select * from users where name = 'jinzhu';
|
||||
//// If found any record, will ignore the attrs
|
||||
//// user -> User{Id: 111, Name: "Jinzhu", Age: 18}
|
||||
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}
|
||||
|
||||
// FirstOrCreate With Assign
|
||||
db.Where(User{Name: "noexisting_user"}).Assign(User{Age: 20}).FirstOrCreate(&user)
|
||||
//// user -> select * from users where name = 'noexisting_user';
|
||||
//// If not record found, will assing the value to the user first, then create it
|
||||
//// user -> User{Id: 112, Name: "noexisting_user", Age: 20} (Same as FirstOrCreate With Attrs)
|
||||
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 20}).FirstOrCreate(&user)
|
||||
//// user -> select * from users where name = 'jinzhu';
|
||||
//// If any record found, will assing the value to the user and update it
|
||||
//// UPDATE users SET age=20 WHERE id = 111;
|
||||
//// user -> User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
|
||||
//// User{Id: 111, Name: "Jinzhu", Age: 20}
|
||||
```
|
||||
|
||||
### FirstOrCreate With Assign
|
||||
|
||||
Assign's arguments would be used to initialize the struct if not record found,
|
||||
If any record found, will assign those values to the record, and save it back to database.
|
||||
|
||||
```go
|
||||
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
|
||||
//// user -> User{Id: 112, Name: "non_existing", Age: 20}
|
||||
|
||||
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
|
||||
//// UPDATE users SET age=30 WHERE id = 111;
|
||||
//// User{Id: 111, Name: "Jinzhu", Age: 30}
|
||||
```
|
||||
|
||||
### SELECT
|
||||
|
||||
```go
|
||||
//// user -> User{Id: 111, Name: "Jinzhu", Age: 18}
|
||||
//// You must noticed that the Attrs is similar to FirstOrInit with Attrs, yes?
|
||||
|
||||
|
@ -350,7 +489,6 @@ db.Where("email = ?", "x@example.org"').Attrs(User{FromIp: "111.111.111.111"}).F
|
|||
```
|
||||
|
||||
## TODO
|
||||
* SubStruct
|
||||
* Index, Unique, Valiations
|
||||
* Auto Migration
|
||||
* SQL Log
|
||||
|
|
46
gorm_test.go
46
gorm_test.go
|
@ -12,12 +12,32 @@ import (
|
|||
)
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Id int64 // Id: Primary key
|
||||
Birthday time.Time // Time
|
||||
Age int64
|
||||
Birthday time.Time
|
||||
Name string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
CreatedAt time.Time // CreatedAt: Time of record is created, will be insert automatically
|
||||
UpdatedAt time.Time // UpdatedAt: Time of record is updated, will be updated automatically
|
||||
DeletedAt time.Time // DeletedAt: Time of record is deleted, refer Soft Delete for more
|
||||
Email []Email // Embedded structs
|
||||
BillingAddress Address // Embedded struct
|
||||
BillingAddressId int64 // Embedded struct's foreign key
|
||||
ShippingAddress Address // Embedded struct
|
||||
ShippingAddressId int64 // Embedded struct's foreign key
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
Id int64
|
||||
UserId int64 // Foreign key for above embedded structs
|
||||
Email string
|
||||
Subscribed bool
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
Id int64
|
||||
Address1 string
|
||||
Address2 string
|
||||
Post string
|
||||
}
|
||||
|
||||
type Product struct {
|
||||
|
@ -1004,10 +1024,12 @@ type Category struct {
|
|||
type Post struct {
|
||||
Id int64
|
||||
CategoryId int64
|
||||
MainCategoryId int64
|
||||
Title string
|
||||
Body string
|
||||
Comments []Comment
|
||||
Category Category
|
||||
MainCategory Category
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
|
@ -1031,6 +1053,7 @@ func TestSubStruct(t *testing.T) {
|
|||
Body: "body 1",
|
||||
Comments: []Comment{{Content: "Comment 1"}, {Content: "Comment 2"}},
|
||||
Category: Category{Name: "Category 1"},
|
||||
MainCategory: Category{Name: "Main Category 1"},
|
||||
}
|
||||
|
||||
if err := db.Save(&post).Error; err != nil {
|
||||
|
@ -1043,7 +1066,7 @@ func TestSubStruct(t *testing.T) {
|
|||
|
||||
var p Post
|
||||
db.First(&p, post.Id)
|
||||
if post.CategoryId == 0 || p.CategoryId == 0 {
|
||||
if post.CategoryId == 0 || p.CategoryId == 0 || post.MainCategoryId == 0 || p.MainCategoryId == 0 {
|
||||
t.Errorf("Category Id should exist")
|
||||
}
|
||||
|
||||
|
@ -1066,3 +1089,16 @@ func TestSubStruct(t *testing.T) {
|
|||
comment3 := Comment{Content: "Comment 3", Post: Post{Title: "Title 3", Body: "Body 3"}}
|
||||
db.Save(&comment3)
|
||||
}
|
||||
|
||||
func TestT(t *testing.T) {
|
||||
user := User{Name: "jinzhu", Age: 18, Birthday: time.Now()}
|
||||
db.Save(&user)
|
||||
|
||||
var user2 User
|
||||
db.Where("name in (?)", []string{"1,2"}).First(&user2).Update("name", "hhh")
|
||||
debug(user2)
|
||||
|
||||
var users []User
|
||||
db.Find(&users, User{Age: 20})
|
||||
debug(users)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue