forked from mirror/gorm
Made some small improvements to the documentation
This commit is contained in:
parent
fb05f2fde2
commit
4203bc15c6
177
README.md
177
README.md
|
@ -1,15 +1,15 @@
|
|||
# GORM
|
||||
|
||||
Yet Another ORM library for Go, aims for developer friendly
|
||||
Yet Another ORM library for Go, aims to be developer friendly.
|
||||
|
||||
## Overview
|
||||
|
||||
* Chainable API
|
||||
* Relations
|
||||
* Callbacks (before/after create/save/update/delete/find)
|
||||
* Soft Delete
|
||||
* Auto Migration
|
||||
* Transaction
|
||||
* Soft Deletes
|
||||
* Auto Migrations
|
||||
* Transactions
|
||||
* Logger Support
|
||||
* Bind struct with tag
|
||||
* Iteration Support via [Rows](#row--rows)
|
||||
|
@ -26,11 +26,16 @@ Yet Another ORM library for Go, aims for developer friendly
|
|||
* Column name is the snake case of field's name.
|
||||
* Use `Id int64` field as primary key.
|
||||
* Use tag `sql` to change field's property, change the tag name with `db.SetTagIdentifier(new_name)`.
|
||||
* Use `CreatedAt` to store record's created time if it exist.
|
||||
* Use `UpdatedAt` to store record's updated time if it exist.
|
||||
* Use `DeletedAt` to store record's deleted time if it exist. [Soft Delete](#soft-delete)
|
||||
* 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)
|
||||
|
||||
```go
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Id int64
|
||||
Birthday time.Time
|
||||
|
@ -60,13 +65,14 @@ type Address struct {
|
|||
Address1 string `sql:"not null;unique"` // Set this field as not nullable and unique in database
|
||||
Address2 string `sql:"type:varchar(100);unique"`
|
||||
Post sql.NullString `sql:not null`
|
||||
// FYI, "NOT NULL" will only works well with NullXXX Scanner, because golang will initalize a default value for most type...
|
||||
// FYI, "NOT NULL" will only work well with NullXXX Scanner, because golang will initalize a default value for most type...
|
||||
}
|
||||
```
|
||||
|
||||
## Opening a Database
|
||||
|
||||
```go
|
||||
|
||||
import "github.com/jinzhu/gorm"
|
||||
import _ "github.com/lib/pq"
|
||||
// import _ "github.com/go-sql-driver/mysql"
|
||||
|
@ -86,16 +92,39 @@ db.DB().Ping()
|
|||
|
||||
// By default, table name is plural of struct type, you can use struct type as table name with:
|
||||
db.SingularTable(true)
|
||||
```
|
||||
|
||||
|
||||
// Gorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere in your project
|
||||
Gorm is goroutines friendly, so you can create a global variable to keep the connection and use it everywhere in your project.
|
||||
```go
|
||||
// db.go
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
var DB gorm.DB
|
||||
func init() {
|
||||
var err error
|
||||
DB, err = gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
||||
|
||||
// Connection string parameters for Postgres - http://godoc.org/github.com/lib/pq, if you are using another
|
||||
// database refer to the relevant driver's documentation.
|
||||
|
||||
// * dbname - The name of the database to connect to
|
||||
// * user - The user to sign in as
|
||||
// * password - The user's password
|
||||
// * host - The host to connect to. Values that start with / are for unix domain sockets.
|
||||
// (default is localhost)
|
||||
// * port - The port to bind to. (default is 5432)
|
||||
// * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq)
|
||||
// Valid SSL modes:
|
||||
// * disable - No SSL
|
||||
// * require - Always SSL (skip verification)
|
||||
// * verify-full - Always SSL (require verification)
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Got error when connect database, the error is '%v'", err))
|
||||
}
|
||||
|
@ -121,11 +150,12 @@ db.DropTable(User{})
|
|||
|
||||
### Automating Migrations
|
||||
|
||||
Feel Free to update your struct, AutoMigrate will keep your database update to date.
|
||||
Feel free to update your struct, AutoMigrate will keep your database update to date.
|
||||
|
||||
FYI, AutoMigrate will only add new columns, won't change current column's type or delete unused columns, to make sure your data is safe
|
||||
FYI, AutoMigrate will only add new columns, it won't change the current columns' types or delete unused columns, to make sure your data is safe.
|
||||
|
||||
If table doesn't exist when AutoMigrate, gorm will run create table automatically.
|
||||
If the table doesn't exist when AutoMigrate, gorm will run create the table automatically.
|
||||
(the database first needs to be created manually though...).
|
||||
|
||||
(only postgres and mysql supported)
|
||||
|
||||
|
@ -154,7 +184,7 @@ db.NewRecord(user) // => false
|
|||
|
||||
### Create With SubStruct
|
||||
|
||||
Refer [Query With Related](#query-with-related) for how to find associations
|
||||
Refer to [Query With Related](#query-with-related) for how to find associations
|
||||
|
||||
```go
|
||||
user := User{
|
||||
|
@ -165,11 +195,13 @@ user := User{
|
|||
}
|
||||
|
||||
db.Save(&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");
|
||||
//// COMMIT;
|
||||
```
|
||||
|
||||
## Query
|
||||
|
@ -400,19 +432,19 @@ db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
|
|||
|
||||
### Soft Delete
|
||||
|
||||
If a struct has DeletedAt field, it will get soft delete ability automatically!
|
||||
If a struct has a DeletedAt field, it will get soft delete ability automatically!
|
||||
|
||||
For those don't have the filed, will be deleted from database permanently
|
||||
Structs that don't have a DeletedAt field will be deleted from the database permanently
|
||||
|
||||
```go
|
||||
db.Delete(&user)
|
||||
//// UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;
|
||||
|
||||
// Delete with search condiation
|
||||
// Delete with search condition
|
||||
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 search
|
||||
// Soft deleted records will be ignored when searched
|
||||
db.Where("age = 20").Find(&user)
|
||||
//// SELECT * FROM users WHERE age = 100 AND (deleted_at IS NULL AND deleted_at <= '0001-01-02');
|
||||
|
||||
|
@ -429,7 +461,7 @@ db.Unscoped().Delete(&order)
|
|||
|
||||
Try to get the first record, if failed, will initialize the struct with search conditions.
|
||||
|
||||
(only support search conditions map and struct)
|
||||
(only supports search conditions map and struct)
|
||||
|
||||
```go
|
||||
db.FirstOrInit(&user, User{Name: "non_existing"})
|
||||
|
@ -444,7 +476,7 @@ db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
|
|||
|
||||
### FirstOrInit With Attrs
|
||||
|
||||
Ignore Attrs's arguments when search, but use them to initialize the struct if no record found.
|
||||
Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.
|
||||
|
||||
```go
|
||||
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
|
||||
|
@ -462,7 +494,7 @@ db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
|
|||
|
||||
### FirstOrInit With Assign
|
||||
|
||||
Ignore Assign's arguments when search, but use them to fill the struct regardless record found or not
|
||||
Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not.
|
||||
|
||||
```go
|
||||
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
|
||||
|
@ -474,7 +506,7 @@ db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
|
|||
|
||||
## FirstOrCreate
|
||||
|
||||
Try to get the first record, if failed, will initialize the struct with search conditions and insert it to database
|
||||
Try to get the first record, if failed, will initialize the struct with the search conditions and insert it in the database.
|
||||
|
||||
```go
|
||||
db.FirstOrCreate(&user, User{Name: "non_existing"})
|
||||
|
@ -489,7 +521,7 @@ db.FirstOrCreate(&user, map[string]interface{}{"name": "jinzhu", "age": 30})
|
|||
|
||||
### FirstOrCreate With Attrs
|
||||
|
||||
Ignore Attrs's arguments when search, but use them to initialize the struct if no record found.
|
||||
Ignore Attrs's arguments when searching, but use them to initialize the struct if no record is found.
|
||||
|
||||
```go
|
||||
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
|
||||
|
@ -502,7 +534,7 @@ db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
|
|||
|
||||
### FirstOrCreate With Assign
|
||||
|
||||
Ignore Assign's arguments when search, but use them to fill the struct regardless record found or not, then save it back to database
|
||||
Ignore Assign's arguments when searching, but use them to fill the struct regardless, whether the record is found or not, then save it back to the database.
|
||||
|
||||
```go
|
||||
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
|
||||
|
@ -579,7 +611,7 @@ db.Table("deleted_users").Count(&count)
|
|||
|
||||
## Pluck
|
||||
|
||||
Get struct's attribute as map
|
||||
Get struct's selected attributes as a map
|
||||
|
||||
```go
|
||||
var ages []int64
|
||||
|
@ -594,16 +626,35 @@ db.Model(&User{}).Pluck("name", &names)
|
|||
db.Table("deleted_users").Pluck("name", &names)
|
||||
//// SELECT name FROM deleted_users;
|
||||
|
||||
// Pluck more than one column? Do it like this
|
||||
// Plucking more than one column? Do it like this:
|
||||
db.Select("name, age").Find(&users)
|
||||
```
|
||||
|
||||
## Transactions
|
||||
All individual save and delete operations are run in a transaction by default.
|
||||
|
||||
```go
|
||||
tx := db.Begin()
|
||||
|
||||
user := User{Name: "transcation"}
|
||||
|
||||
tx.Save(&u)
|
||||
tx.Update("age": 90)
|
||||
// do whatever
|
||||
|
||||
// rollback
|
||||
tx.Rollback()
|
||||
|
||||
// commit
|
||||
tx.Commit()
|
||||
```
|
||||
|
||||
## Callbacks
|
||||
|
||||
Callbacks are functions defined to struct's pointer, they would be run when save a struct to database.
|
||||
If any callback return error, gorm will stop future operations and rollback all changes
|
||||
Callbacks are methods defined on the struct's pointer.
|
||||
If any callback returns an error, gorm will stop future operations and rollback all changes.
|
||||
|
||||
Here is a list with all available callbacks,
|
||||
Here is a list of all available callbacks,
|
||||
listed in the same order in which they will get called during the respective operations.
|
||||
|
||||
### Creating an Object
|
||||
|
@ -654,9 +705,9 @@ func (u *User) BeforeUpdate() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Rollback the insertion if have more than 1000 users
|
||||
// Rollback the insertion if there are more than 1000 users (hypothetical example)
|
||||
func (u *User) AfterCreate() (err error) {
|
||||
if (u.Id > 1000) { // Just as example, don't use Id to count users!
|
||||
if (u.Id > 1000) { // Just as an example, don't use Id to count users!
|
||||
err = errors.New("Only 1000 users allowed!")
|
||||
}
|
||||
return
|
||||
|
@ -665,22 +716,22 @@ func (u *User) AfterCreate() (err error) {
|
|||
|
||||
```go
|
||||
// As you know, the save/delete operations are running in a transaction
|
||||
// This is means all your changes will be rollbacked if get any errors
|
||||
// This is means that all your changes will be rolled back if there are any errors
|
||||
// If you want your changes in callbacks be run in the same transaction
|
||||
// You have to pass the transaction as argument to the function
|
||||
// you have to pass the transaction as argument to the function
|
||||
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
|
||||
tx.Model(u).Update("role", "admin")
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
## Specify Table Name
|
||||
## Specifying the Table Name
|
||||
|
||||
```go
|
||||
// Create `deleted_users` table with User's fields
|
||||
db.Table("deleted_users").CreateTable(&User{})
|
||||
|
||||
// Search from table `deleted_users`, and fill results to []User
|
||||
// Search from table `deleted_users`
|
||||
var deleted_users []User
|
||||
db.Table("deleted_users").Find(&deleted_users)
|
||||
//// SELECT * FROM deleted_users;
|
||||
|
@ -690,7 +741,7 @@ db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
|
|||
//// DELETE FROM deleted_users WHERE name = 'jinzhu';
|
||||
```
|
||||
|
||||
### Specify Table Name for Struct permanently with TableName method
|
||||
### Specifying the Table Name for Struct permanently with TableName
|
||||
|
||||
```go
|
||||
type Cart struct {
|
||||
|
@ -709,24 +760,6 @@ func (u User) TableName() string {
|
|||
}
|
||||
```
|
||||
|
||||
## Transaction
|
||||
|
||||
```go
|
||||
tx := db.Begin()
|
||||
|
||||
user := User{Name: "transcation"}
|
||||
|
||||
tx.Save(&u)
|
||||
tx.Update("age": 90)
|
||||
// do whatever
|
||||
|
||||
// rollback
|
||||
tx.Rollback()
|
||||
|
||||
// commit
|
||||
tx.Commit()
|
||||
```
|
||||
|
||||
## Scopes
|
||||
|
||||
```go
|
||||
|
@ -760,7 +793,7 @@ db.Scopes(OrderStatus([]string{"paid", "shipped"})).Find(&orders)
|
|||
|
||||
## Logger
|
||||
|
||||
Grom has builtin logger support, enable it with:
|
||||
Gorm has built-in logger support, enable it with:
|
||||
|
||||
```go
|
||||
db.LogMode(true)
|
||||
|
@ -770,13 +803,13 @@ db.LogMode(true)
|
|||
|
||||
```go
|
||||
// Use your own logger
|
||||
// Refer gorm's default logger for how to format messages: https://github.com/jinzhu/gorm/blob/master/logger.go#files
|
||||
// Refer to gorm's default logger for how to format messages: https://github.com/jinzhu/gorm/blob/master/logger.go#files
|
||||
db.SetLogger(log.New(os.Stdout, "\r\n", 0))
|
||||
|
||||
// Disable log
|
||||
// Disable logging
|
||||
db.LogMode(false)
|
||||
|
||||
// Enable log for a single operation, make debug easy
|
||||
// Enable logging for a single operation, to make debugging easy
|
||||
db.Debug().Where("name = ?", "jinzhu").First(&User{})
|
||||
```
|
||||
|
||||
|
@ -811,14 +844,14 @@ for rows.Next() {
|
|||
}
|
||||
```
|
||||
|
||||
## Run Raw SQl
|
||||
## Run Raw SQL
|
||||
|
||||
```go
|
||||
// Raw sql
|
||||
db.Exec("drop table users;")
|
||||
// Raw SQL
|
||||
db.Exec("DROP TABLE users;")
|
||||
|
||||
// Raw sql with arguments
|
||||
db.Exec("update orders set shipped_at=? where id in (?)", time.Now, []int64{11,22,33})
|
||||
// Raw SQL with arguments
|
||||
db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now, []int64{11,22,33})
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
@ -826,16 +859,16 @@ db.Exec("update orders set shipped_at=? where id in (?)", time.Now, []int64{11,2
|
|||
```go
|
||||
query := db.Where("name = ?", "jinzhu").First(&user)
|
||||
query := db.First(&user).Limit(10).Find(&users)
|
||||
//// query.Error keep the latest error happened
|
||||
//// query.Errors keep all errors happened
|
||||
//// If an error happened, gorm will stop following operations
|
||||
//// query.Error returns the last error
|
||||
//// query.Errors returns all errors that have taken place
|
||||
//// If an error has taken place, gorm will stop all following operations
|
||||
|
||||
// I often use some code like below to do error handling when writting applicatoins
|
||||
// I often use some code like below to do error handling when writing applications
|
||||
if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {
|
||||
// ...
|
||||
}
|
||||
|
||||
// If no record found, gorm will return RecordNotFound error, you could check it with
|
||||
// If no record is found, gorm will return RecordNotFound error, you could check it with
|
||||
db.Where("name = ?", "hello world").First(&User{}).Error == gorm.RecordNotFound
|
||||
|
||||
// Or use shortcut method
|
||||
|
@ -850,9 +883,9 @@ if db.Model(&user).Related(&credit_card).RecordNotFound() {
|
|||
}
|
||||
```
|
||||
|
||||
## Advanced Usage With Query Chain
|
||||
## Advanced Usage With Query Chaining
|
||||
|
||||
Already excited about above usage? Let's see some magic!
|
||||
Already excited with what gorm has to offer? Let's see some magic!
|
||||
|
||||
```go
|
||||
db.First(&first_article).Count(&total_count).Limit(10).Find(&first_page_articles).Offset(10).Find(&second_page_articles)
|
||||
|
@ -868,7 +901,7 @@ db.Where("created_at > ?", "2013-10-10").Find(&cancelled_orders, "state = ?", "c
|
|||
//// SELECT * FROM orders WHERE created_at > '2013/10/10' AND state = 'shipped'; (shipped_orders)
|
||||
|
||||
|
||||
// Use variable to keep query chain
|
||||
// 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")
|
||||
|
@ -886,7 +919,7 @@ db.Where("mail_type = ?", "TEXT").Find(&users1).Table("deleted_users").Find(&use
|
|||
//// SELECT * FROM deleted_users WHERE mail_type = 'TEXT'; (users2)
|
||||
|
||||
|
||||
// An example for how to use FirstOrCreate
|
||||
// An example on how to use FirstOrCreate
|
||||
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 no record found
|
||||
|
|
Loading…
Reference in New Issue