Update README

This commit is contained in:
Jinzhu 2013-11-02 21:02:54 +08:00
parent aa352d405b
commit b4981259de
2 changed files with 311 additions and 137 deletions

378
README.md
View File

@ -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}
//// 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.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)
// 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)
// 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}
// FirstOrCreate
db.FirstOrCreate(&user, User{Name: "noexisting_user"})
//// user -> User{Id: 112, Name: "noexisting_user"}
### 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?
@ -227,10 +366,10 @@ Below callbacks are defined now:
Callbacks is a function defined to a model, if the function return error, will prevent the database operations.
func (u *User) BeforeUpdate() (err error) {
if u.readonly() {
err = errors.New("Read Only User")
}
return
if u.readonly() {
err = errors.New("Read Only User")
}
return
}
// Pluck (get users's age as map)
@ -282,11 +421,11 @@ db.First(&user, 20).Updates(User{Name: "hello", Age: 18})
// 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
Id int64
Amount int64
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt time.Time
}
order := order{Id:10}
db.Delete(&order)
@ -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

View File

@ -12,12 +12,32 @@ import (
)
type User struct {
Id int64
Age int64
Birthday time.Time
Name string
CreatedAt time.Time
UpdatedAt time.Time
Id int64 // Id: 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 {
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 {
@ -1002,12 +1022,14 @@ type Category struct {
}
type Post struct {
Id int64
CategoryId int64
Title string
Body string
Comments []Comment
Category Category
Id int64
CategoryId int64
MainCategoryId int64
Title string
Body string
Comments []Comment
Category Category
MainCategory Category
}
type Comment struct {
@ -1027,10 +1049,11 @@ func TestSubStruct(t *testing.T) {
db.CreateTable(Comment{})
post := Post{
Title: "post 1",
Body: "body 1",
Comments: []Comment{{Content: "Comment 1"}, {Content: "Comment 2"}},
Category: Category{Name: "Category 1"},
Title: "post 1",
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)
}