Merge pull request #841 from vCaesar/u13-pr

Fix some md error and Add en syntax highlighting
This commit is contained in:
astaxie
2017-05-14 19:17:09 +08:00
committed by GitHub
8 changed files with 109 additions and 108 deletions

View File

@@ -22,7 +22,7 @@ QQ群148647580
BBS[http://golanghome.com/](http://gocn.io/) BBS[http://golanghome.com/](http://gocn.io/)
##Contributors ## Contributors
- See [contributors page](https://github.com/astaxie/build-web-application-with-golang/graphs/contributors) for full list of contributors. - See [contributors page](https://github.com/astaxie/build-web-application-with-golang/graphs/contributors) for full list of contributors.

View File

@@ -7,7 +7,7 @@ Go doesn't provide any official database drivers, unlike other languages like PH
This function is in the `database/sql` package for registering database drivers when you use third-party database drivers. All of these should call the `Register(name string, driver driver.Driver)` function in `init()` in order to register themselves. This function is in the `database/sql` package for registering database drivers when you use third-party database drivers. All of these should call the `Register(name string, driver driver.Driver)` function in `init()` in order to register themselves.
Let's take a look at the corresponding mymysql and sqlite3 driver code: Let's take a look at the corresponding mymysql and sqlite3 driver code:
```Go
//https://github.com/mattn/go-sqlite3 driver //https://github.com/mattn/go-sqlite3 driver
func init() { func init() {
sql.Register("sqlite3", &SQLiteDriver{}) sql.Register("sqlite3", &SQLiteDriver{})
@@ -20,39 +20,39 @@ Let's take a look at the corresponding mymysql and sqlite3 driver code:
Register("SET NAMES utf8") Register("SET NAMES utf8")
sql.Register("mymysql", &d) sql.Register("mymysql", &d)
} }
```
We see that all third-party database drivers implement this function to register themselves, and Go uses a map to save user drivers inside of `database/sql`. We see that all third-party database drivers implement this function to register themselves, and Go uses a map to save user drivers inside of `database/sql`.
```Go
var drivers = make(map[string]driver.Driver) var drivers = make(map[string]driver.Driver)
drivers[name] = driver drivers[name] = driver
```
Therefore, this registration function can register as many drivers as you may require, each with different names. Therefore, this registration function can register as many drivers as you may require, each with different names.
We always see the following code when we use third-party drivers: We always see the following code when we use third-party drivers:
```Go
import ( import (
"database/sql" "database/sql"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
```
Here, the underscore (also known as a 'blank') `_` can be quite confusing for many beginners, but this is a great feature in Go. We already know that this underscore identifier is used for discarding values from function returns, and also that you must use all packages that you've imported in your code in Go. So when the blank is used with import, it means that you need to execute the init() function of that package without directly using it, which is a perfect fit for the use-case of registering database drivers. Here, the underscore (also known as a 'blank') `_` can be quite confusing for many beginners, but this is a great feature in Go. We already know that this underscore identifier is used for discarding values from function returns, and also that you must use all packages that you've imported in your code in Go. So when the blank is used with import, it means that you need to execute the init() function of that package without directly using it, which is a perfect fit for the use-case of registering database drivers.
## driver.Driver ## driver.Driver
`Driver` is an interface containing an `Open(name string)` method that returns a `Conn` interface. `Driver` is an interface containing an `Open(name string)` method that returns a `Conn` interface.
```Go
type Driver interface { type Driver interface {
Open(name string) (Conn, error) Open(name string) (Conn, error)
} }
```
This is a one-time Conn, which means it can only be used once per goroutine. The following code will cause errors to occur: This is a one-time Conn, which means it can only be used once per goroutine. The following code will cause errors to occur:
```Go
... ...
go goroutineA (Conn) // query go goroutineA (Conn) // query
go goroutineB (Conn) // insert go goroutineB (Conn) // insert
... ...
```
Because Go has no idea which goroutine does which operation, the query operation may get the result of the insert operation, and vice-versa. Because Go has no idea which goroutine does which operation, the query operation may get the result of the insert operation, and vice-versa.
All third-party drivers should have this function to parse the name of Conn and return the correct results. All third-party drivers should have this function to parse the name of Conn and return the correct results.
@@ -60,13 +60,13 @@ All third-party drivers should have this function to parse the name of Conn and
## driver.Conn ## driver.Conn
This is a database connection interface with some methods, and as i've said above, the same Conn can only be used once per goroutine. This is a database connection interface with some methods, and as i've said above, the same Conn can only be used once per goroutine.
```Go
type Conn interface { type Conn interface {
Prepare(query string) (Stmt, error) Prepare(query string) (Stmt, error)
Close() error Close() error
Begin() (Tx, error) Begin() (Tx, error)
} }
```
- `Prepare` returns the prepare status of corresponding SQL commands for querying and deleting, etc. - `Prepare` returns the prepare status of corresponding SQL commands for querying and deleting, etc.
- `Close` closes the current connection and cleans resources. Most third-party drivers implement some kind of connection pool, so you don't need to cache connections which can cause unexpected errors. - `Close` closes the current connection and cleans resources. Most third-party drivers implement some kind of connection pool, so you don't need to cache connections which can cause unexpected errors.
- `Begin` returns a Tx that represents a transaction handle. You can use it for querying, updating, rolling back transactions, etc. - `Begin` returns a Tx that represents a transaction handle. You can use it for querying, updating, rolling back transactions, etc.
@@ -74,14 +74,14 @@ This is a database connection interface with some methods, and as i've said abov
## driver.Stmt ## driver.Stmt
This is a ready status that corresponds with Conn, so it can only be used once per goroutine (as is the case with Conn). This is a ready status that corresponds with Conn, so it can only be used once per goroutine (as is the case with Conn).
```Go
type Stmt interface { type Stmt interface {
Close() error Close() error
NumInput() int NumInput() int
Exec(args []Value) (Result, error) Exec(args []Value) (Result, error)
Query(args []Value) (Rows, error) Query(args []Value) (Rows, error)
} }
```
- `Close` closes the current connection but still returns row data if it is executing a query operation. - `Close` closes the current connection but still returns row data if it is executing a query operation.
- `NumInput` returns the number of obligate arguments. Database drivers should check their caller's arguments when the result is greater than 0, and it returns -1 when database drivers don't know any obligate argument. - `NumInput` returns the number of obligate arguments. Database drivers should check their caller's arguments when the result is greater than 0, and it returns -1 when database drivers don't know any obligate argument.
- `Exec` executes the `update/insert` SQL commands prepared in `Prepare`, returns `Result`. - `Exec` executes the `update/insert` SQL commands prepared in `Prepare`, returns `Result`.
@@ -90,44 +90,44 @@ This is a ready status that corresponds with Conn, so it can only be used once p
## driver.Tx ## driver.Tx
Generally, transaction handles only have submit or rollback methods, and database drivers only need to implement these two methods. Generally, transaction handles only have submit or rollback methods, and database drivers only need to implement these two methods.
```Go
type Tx interface { type Tx interface {
Commit() error Commit() error
Rollback() error Rollback() error
} }
```
## driver.Execer ## driver.Execer
This is an optional interface. This is an optional interface.
```Go
type Execer interface { type Execer interface {
Exec(query string, args []Value) (Result, error) Exec(query string, args []Value) (Result, error)
} }
```
If the driver doesn't implement this interface, when you call DB.Exec, it will automatically call Prepare, then return Stmt. After that it executes the Exec method of Stmt, then closes Stmt. If the driver doesn't implement this interface, when you call DB.Exec, it will automatically call Prepare, then return Stmt. After that it executes the Exec method of Stmt, then closes Stmt.
## driver.Result ## driver.Result
This is the interface for results of `update/insert` operations. This is the interface for results of `update/insert` operations.
```Go
type Result interface { type Result interface {
LastInsertId() (int64, error) LastInsertId() (int64, error)
RowsAffected() (int64, error) RowsAffected() (int64, error)
} }
```
- `LastInsertId` returns auto-increment Id number after a database insert operation. - `LastInsertId` returns auto-increment Id number after a database insert operation.
- `RowsAffected` returns rows that were affected by query operations. - `RowsAffected` returns rows that were affected by query operations.
## driver.Rows ## driver.Rows
This is the interface for the result of a query operation. This is the interface for the result of a query operation.
```Go
type Rows interface { type Rows interface {
Columns() []string Columns() []string
Close() error Close() error
Next(dest []Value) error Next(dest []Value) error
} }
```
- `Columns` returns field information of database tables. The slice has a one-to-one correspondence with SQL query fields only, and does not return all fields of that database table. - `Columns` returns field information of database tables. The slice has a one-to-one correspondence with SQL query fields only, and does not return all fields of that database table.
- `Close` closes Rows iterator. - `Close` closes Rows iterator.
- `Next` returns next data and assigns to dest, converting all strings into byte arrays, and gets io.EOF error if no more data is available. - `Next` returns next data and assigns to dest, converting all strings into byte arrays, and gets io.EOF error if no more data is available.
@@ -135,36 +135,36 @@ This is the interface for the result of a query operation.
## driver.RowsAffected ## driver.RowsAffected
This is an alias of int64, but it implements the Result interface. This is an alias of int64, but it implements the Result interface.
```Go
type RowsAffected int64 type RowsAffected int64
func (RowsAffected) LastInsertId() (int64, error) func (RowsAffected) LastInsertId() (int64, error)
func (v RowsAffected) RowsAffected() (int64, error) func (v RowsAffected) RowsAffected() (int64, error)
```
## driver.Value ## driver.Value
This is an empty interface that can contain any kind of data. This is an empty interface that can contain any kind of data.
```Go
type Value interface{} type Value interface{}
```
The Value must be something that drivers can operate on or nil, so it should be one of the following types: The Value must be something that drivers can operate on or nil, so it should be one of the following types:
```Go
int64 int64
float64 float64
bool bool
[]byte []byte
string [*] Except Rows.Next which cannot return string string [*] Except Rows.Next which cannot return string
time.Time time.Time
```
## driver.ValueConverter ## driver.ValueConverter
This defines an interface for converting normal values to driver.Value. This defines an interface for converting normal values to driver.Value.
```Go
type ValueConverter interface { type ValueConverter interface {
ConvertValue(v interface{}) (Value, error) ConvertValue(v interface{}) (Value, error)
} }
```
This interface is commonly used in database drivers and has many useful features: This interface is commonly used in database drivers and has many useful features:
- Converts driver.Value to a corresponding database field type, for example converts int64 to uint16. - Converts driver.Value to a corresponding database field type, for example converts int64 to uint16.
@@ -174,11 +174,11 @@ This interface is commonly used in database drivers and has many useful features
## driver.Valuer ## driver.Valuer
This defines an interface for returning driver.Value. This defines an interface for returning driver.Value.
```Go
type Valuer interface { type Valuer interface {
Value() (Value, error) Value() (Value, error)
} }
```
Many types implement this interface for conversion between driver.Value and itself. Many types implement this interface for conversion between driver.Value and itself.
At this point, you should know a bit about developing database drivers in Go. Once you can implement interfaces for operations like add, delete, update, etc., there are only a few problems left related to communicating with specific databases. At this point, you should know a bit about developing database drivers in Go. Once you can implement interfaces for operations like add, delete, update, etc., there are only a few problems left related to communicating with specific databases.
@@ -186,7 +186,7 @@ At this point, you should know a bit about developing database drivers in Go. On
## database/sql ## database/sql
database/sql defines even more high-level methods on top of database/sql/driver for more convenient database operations, and it suggests that you implement a connection pool. database/sql defines even more high-level methods on top of database/sql/driver for more convenient database operations, and it suggests that you implement a connection pool.
```Go
type DB struct { type DB struct {
driver driver.Driver driver driver.Driver
dsn string dsn string
@@ -194,7 +194,7 @@ database/sql defines even more high-level methods on top of database/sql/driver
freeConn []driver.Conn freeConn []driver.Conn
closed bool closed bool
} }
```
As you can see, the `Open` function returns a DB that has a freeConn, and this is a simple connection pool. Its implementation is very simple and ugly. It uses `defer db.putConn(ci, err)` in the Db.prepare function to put a connection into the connection pool. Everytime you call the Conn function, it checks the length of freeConn. If it's greater than 0, that means there is a reusable connection and it directly returns to you. Otherwise it creates a new connection and returns. As you can see, the `Open` function returns a DB that has a freeConn, and this is a simple connection pool. Its implementation is very simple and ugly. It uses `defer db.putConn(ci, err)` in the Db.prepare function to put a connection into the connection pool. Everytime you call the Conn function, it checks the length of freeConn. If it's greater than 0, that means there is a reusable connection and it directly returns to you. Otherwise it creates a new connection and returns.
## Links ## Links

View File

@@ -18,7 +18,7 @@ I'll use the first driver in the following examples (I use this one in my person
## Samples ## Samples
In the following sections, I'll use the same database table structure for different databases, then create SQL as follows: In the following sections, I'll use the same database table structure for different databases, then create SQL as follows:
```sql
CREATE TABLE `userinfo` ( CREATE TABLE `userinfo` (
`uid` INT(10) NOT NULL AUTO_INCREMENT, `uid` INT(10) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(64) NULL DEFAULT NULL, `username` VARCHAR(64) NULL DEFAULT NULL,
@@ -26,9 +26,9 @@ In the following sections, I'll use the same database table structure for differ
`created` DATE NULL DEFAULT NULL, `created` DATE NULL DEFAULT NULL,
PRIMARY KEY (`uid`) PRIMARY KEY (`uid`)
); );
```
The following example shows how to operate on a database based on the `database/sql` interface standards. The following example shows how to operate on a database based on the `database/sql` interface standards.
```Go
package main package main
import ( import (
@@ -102,7 +102,7 @@ The following example shows how to operate on a database based on the `database/
panic(err) panic(err)
} }
} }
```
Let me explain a few of the important functions here: Let me explain a few of the important functions here:
- `sql.Open()` opens a registered database driver. The Go-MySQL-Driver registered the mysql driver here. The second argument is the DSN (Data Source Name) that defines information pertaining to the database connection. It supports following formats: - `sql.Open()` opens a registered database driver. The Go-MySQL-Driver registered the mysql driver here. The second argument is the DSN (Data Source Name) that defines information pertaining to the database connection. It supports following formats:

View File

@@ -15,16 +15,16 @@ The first driver is the only one that supports the `database/sql` interface stan
## Samples ## Samples
We create the following SQL: We create the following SQL:
```sql
CREATE TABLE `userinfo` ( CREATE TABLE `userinfo` (
`uid` INTEGER PRIMARY KEY AUTOINCREMENT, `uid` INTEGER PRIMARY KEY AUTOINCREMENT,
`username` VARCHAR(64) NULL, `username` VARCHAR(64) NULL,
`departname` VARCHAR(64) NULL, `departname` VARCHAR(64) NULL,
`created` DATE NULL `created` DATE NULL
); );
```
An example: An example:
```Go
package main package main
import ( import (
@@ -101,11 +101,11 @@ An example:
panic(err) panic(err)
} }
} }
```
You may have noticed that the code is almost the same as in the previous section, and that we only changed the name of the registered driver and called `sql.Open` to connect to SQLite in a different way. You may have noticed that the code is almost the same as in the previous section, and that we only changed the name of the registered driver and called `sql.Open` to connect to SQLite in a different way.
Note that sometimes you can't use the `for` statement because you don't have more than one row, then you can use the `if` statement Note that sometimes you can't use the `for` statement because you don't have more than one row, then you can use the `if` statement
```Go
if rows.Next() { if rows.Next() {
err = rows.Scan(&uid, &username, &department, &created) err = rows.Scan(&uid, &username, &department, &created)
checkErr(err) checkErr(err)
@@ -114,14 +114,14 @@ Note that sometimes you can't use the `for` statement because you don't have mor
fmt.Println(department) fmt.Println(department)
fmt.Println(created) fmt.Println(created)
} }
```
Also you have to do a `rows.Next()`, without using that you can't fetch data in the `Scan` function. Also you have to do a `rows.Next()`, without using that you can't fetch data in the `Scan` function.
Transactions Transactions
=============== ===============
The above example shows how you fetch data from the database, but when you want to write a web application then it will not only be necessary to fetch data from the db but it will also be required to write data into it. For that purpose, you should use transactions because for various reasons, such as having multiple go routines which access the database, the database might get locked. This is undesirable in your web application and the use of transactions is effective in ensuring your database activities either pass or fail completely depending on circumstances. It is clear that using transactions can prevent a lot of things from going wrong with the web app. The above example shows how you fetch data from the database, but when you want to write a web application then it will not only be necessary to fetch data from the db but it will also be required to write data into it. For that purpose, you should use transactions because for various reasons, such as having multiple go routines which access the database, the database might get locked. This is undesirable in your web application and the use of transactions is effective in ensuring your database activities either pass or fail completely depending on circumstances. It is clear that using transactions can prevent a lot of things from going wrong with the web app.
```Go
trashSQL, err := database.Prepare("update task set is_deleted='Y',last_modified_at=datetime() where id=?") trashSQL, err := database.Prepare("update task set is_deleted='Y',last_modified_at=datetime() where id=?")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@@ -137,7 +137,7 @@ The above example shows how you fetch data from the database, but when you want
} else { } else {
tx.Commit() tx.Commit()
} }
```
As it is clear from the above block of code, you first prepare a statement, after which you execute it, depending on the output of that execution then you either roll it back or commit it. As it is clear from the above block of code, you first prepare a statement, after which you execute it, depending on the output of that execution then you either roll it back or commit it.

View File

@@ -15,7 +15,7 @@ I will use the first one in the examples that follow.
## Samples ## Samples
We create the following SQL: We create the following SQL:
```sql
CREATE TABLE userinfo CREATE TABLE userinfo
( (
uid serial NOT NULL, uid serial NOT NULL,
@@ -25,9 +25,9 @@ We create the following SQL:
CONSTRAINT userinfo_pkey PRIMARY KEY (uid) CONSTRAINT userinfo_pkey PRIMARY KEY (uid)
) )
WITH (OIDS=FALSE); WITH (OIDS=FALSE);
```
An example: An example:
```Go
package main package main
import ( import (
@@ -102,20 +102,20 @@ An example:
panic(err) panic(err)
} }
} }
```
Note that PostgreSQL uses the `$1, $2` format instead of the `?` that MySQL uses, and it has a different DSN format in `sql.Open`. Note that PostgreSQL uses the `$1, $2` format instead of the `?` that MySQL uses, and it has a different DSN format in `sql.Open`.
Another thing is that the PostgreSQL driver does not support `sql.Result.LastInsertId()`. Another thing is that the PostgreSQL driver does not support `sql.Result.LastInsertId()`.
So instead of this, So instead of this,
```Go
stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3);") stmt, err := db.Prepare("INSERT INTO userinfo(username,departname,created) VALUES($1,$2,$3);")
res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09") res, err := stmt.Exec("astaxie", "研发部门", "2012-12-09")
fmt.Println(res.LastInsertId()) fmt.Println(res.LastInsertId())
```
use `db.QueryRow()` and `.Scan()` to get the value for the last inserted id. use `db.QueryRow()` and `.Scan()` to get the value for the last inserted id.
```Go
err = db.QueryRow("INSERT INTO TABLE_NAME values($1) returning uid;", VALUE1").Scan(&lastInsertId) err = db.QueryRow("INSERT INTO TABLE_NAME values($1) returning uid;", VALUE1").Scan(&lastInsertId)
fmt.Println(lastInsertId) fmt.Println(lastInsertId)
```
## Links ## Links
- [Directory](preface.md) - [Directory](preface.md)

View File

@@ -32,69 +32,69 @@ You can use `go get` to install beedb locally.
## Initialization ## Initialization
First, you have to import all the necessary packages: First, you have to import all the necessary packages:
```Go
import ( import (
"database/sql" "database/sql"
"github.com/astaxie/beedb" "github.com/astaxie/beedb"
_ "github.com/ziutek/mymysql/godrv" _ "github.com/ziutek/mymysql/godrv"
) )
```
Then you need to open a database connection and create a beedb object (MySQL in this example): Then you need to open a database connection and create a beedb object (MySQL in this example):
```Go
db, err := sql.Open("mymysql", "test/xiemengjun/123456") db, err := sql.Open("mymysql", "test/xiemengjun/123456")
if err != nil { if err != nil {
panic(err) panic(err)
} }
orm := beedb.New(db) orm := beedb.New(db)
```
`beedb.New()` actually has two arguments. The first is the database object, and the second is for indicating which database engine you're using. If you're using MySQL/SQLite, you can just skip the second argument. `beedb.New()` actually has two arguments. The first is the database object, and the second is for indicating which database engine you're using. If you're using MySQL/SQLite, you can just skip the second argument.
Otherwise, this argument must be supplied. For instance, in the case of SQLServer: Otherwise, this argument must be supplied. For instance, in the case of SQLServer:
```Go
orm = beedb.New(db, "mssql") orm = beedb.New(db, "mssql")
```
PostgreSQL: PostgreSQL:
```Go
orm = beedb.New(db, "pg") orm = beedb.New(db, "pg")
```
beedb supports debugging. Use the following code to enable it: beedb supports debugging. Use the following code to enable it:
```Go
beedb.OnDebug=true beedb.OnDebug=true
```
Next, we have a struct for the `Userinfo` database table that we used in previous sections. Next, we have a struct for the `Userinfo` database table that we used in previous sections.
```Go
type Userinfo struct { type Userinfo struct {
Uid int `PK` // if the primary key is not id, you need to add tag `PK` for your customized primary key. Uid int `PK` // if the primary key is not id, you need to add tag `PK` for your customized primary key.
Username string Username string
Departname string Departname string
Created time.Time Created time.Time
} }
```
Be aware that beedb auto-converts camelcase names to lower snake case. For example, if we have `UserInfo` as the struct name, beedb will convert it to `user_info` in the database. The same rule applies to struct field names. Be aware that beedb auto-converts camelcase names to lower snake case. For example, if we have `UserInfo` as the struct name, beedb will convert it to `user_info` in the database. The same rule applies to struct field names.
## Insert data ## Insert data
The following example shows you how to use beedb to save a struct, instead of using raw SQL commands. We use the beedb Save method to apply the change. The following example shows you how to use beedb to save a struct, instead of using raw SQL commands. We use the beedb Save method to apply the change.
```Go
var saveone Userinfo var saveone Userinfo
saveone.Username = "Test Add User" saveone.Username = "Test Add User"
saveone.Departname = "Test Add Departname" saveone.Departname = "Test Add Departname"
saveone.Created = time.Now() saveone.Created = time.Now()
orm.Save(&saveone) orm.Save(&saveone)
```
You can check `saveone.Uid` after the record is inserted; its value is a self-incremented ID, which the Save method takes care of for you. You can check `saveone.Uid` after the record is inserted; its value is a self-incremented ID, which the Save method takes care of for you.
beedb provides another way of inserting data; this is via Go's map type. beedb provides another way of inserting data; this is via Go's map type.
```Go
add := make(map[string]interface{}) add := make(map[string]interface{})
add["username"] = "astaxie" add["username"] = "astaxie"
add["departname"] = "cloud develop" add["departname"] = "cloud develop"
add["created"] = "2012-12-02" add["created"] = "2012-12-02"
orm.SetTable("userinfo").Insert(add) orm.SetTable("userinfo").Insert(add)
```
Insert multiple data: Insert multiple data:
```Go
addslice := make([]map[string]interface{}, 10) addslice := make([]map[string]interface{}, 10)
add:=make(map[string]interface{}) add:=make(map[string]interface{})
add2:=make(map[string]interface{}) add2:=make(map[string]interface{})
@@ -106,7 +106,7 @@ Insert multiple data:
add2["created"] = "2012-12-02" add2["created"] = "2012-12-02"
addslice = append(addslice, add, add2) addslice = append(addslice, add, add2)
orm.SetTable("userinfo").InsertBatch(addslice) orm.SetTable("userinfo").InsertBatch(addslice)
```
The method shown above is similar to a chained query, which you should be familiar with if you've ever used jquery. It returns the original ORM object after calls, then continues doing other jobs. The method shown above is similar to a chained query, which you should be familiar with if you've ever used jquery. It returns the original ORM object after calls, then continues doing other jobs.
The method `SetTable` tells the ORM we want to insert our data into the `userinfo` table. The method `SetTable` tells the ORM we want to insert our data into the `userinfo` table.
@@ -114,18 +114,18 @@ The method `SetTable` tells the ORM we want to insert our data into the `userinf
## Update data ## Update data
Let's continue working with the above example to see how to update data. Now that we have the primary key of saveone(Uid), beedb executes an update operation instead of inserting a new record. Let's continue working with the above example to see how to update data. Now that we have the primary key of saveone(Uid), beedb executes an update operation instead of inserting a new record.
```Go
saveone.Username = "Update Username" saveone.Username = "Update Username"
saveone.Departname = "Update Departname" saveone.Departname = "Update Departname"
saveone.Created = time.Now() saveone.Created = time.Now()
orm.Save(&saveone) // update orm.Save(&saveone) // update
```
Like before, you can also use map for updating data: Like before, you can also use map for updating data:
```Go
t := make(map[string]interface{}) t := make(map[string]interface{})
t["username"] = "astaxie" t["username"] = "astaxie"
orm.SetTable("userinfo").SetPK("uid").Where(2).Update(t) orm.SetTable("userinfo").SetPK("uid").Where(2).Update(t)
```
Let me explain some of the methods used above: Let me explain some of the methods used above:
- `.SetPK()` tells the ORM that `uid` is the primary key records in the `userinfo` table. - `.SetPK()` tells the ORM that `uid` is the primary key records in the `userinfo` table.
@@ -137,54 +137,54 @@ Let me explain some of the methods used above:
The beedb query interface is very flexible. Let's see some examples: The beedb query interface is very flexible. Let's see some examples:
Example 1, query by primary key: Example 1, query by primary key:
```Go
var user Userinfo var user Userinfo
// Where accepts two arguments, supports integers // Where accepts two arguments, supports integers
orm.Where("uid=?", 27).Find(&user) orm.Where("uid=?", 27).Find(&user)
```
Example 2: Example 2:
```Go
var user2 Userinfo var user2 Userinfo
orm.Where(3).Find(&user2) // short form that omits primary key orm.Where(3).Find(&user2) // short form that omits primary key
```
Example 3, other query conditions: Example 3, other query conditions:
```Go
var user3 Userinfo var user3 Userinfo
// Where two arguments are accepted, with support for char type. // Where two arguments are accepted, with support for char type.
orm.Where("name = ?", "john").Find(&user3) orm.Where("name = ?", "john").Find(&user3)
```
Example 4, more complex conditions: Example 4, more complex conditions:
```Go
var user4 Userinfo var user4 Userinfo
// Where three arguments are accepted // Where three arguments are accepted
orm.Where("name = ? and age < ?", "john", 88).Find(&user4) orm.Where("name = ? and age < ?", "john", 88).Find(&user4)
```
Examples to get multiple records: Examples to get multiple records:
Example 1, gets 10 records with `id>3` that starts with position 20: Example 1, gets 10 records with `id>3` that starts with position 20:
```Go
var allusers []Userinfo var allusers []Userinfo
err := orm.Where("id > ?", "3").Limit(10,20).FindAll(&allusers) err := orm.Where("id > ?", "3").Limit(10,20).FindAll(&allusers)
```
Example 2, omits the second argument of limit, so it starts with 0 and gets 10 records: Example 2, omits the second argument of limit, so it starts with 0 and gets 10 records:
```Go
var tenusers []Userinfo var tenusers []Userinfo
err := orm.Where("id > ?", "3").Limit(10).FindAll(&tenusers) err := orm.Where("id > ?", "3").Limit(10).FindAll(&tenusers)
```
Example 3, gets all records: Example 3, gets all records:
```Go
var everyone []Userinfo var everyone []Userinfo
err := orm.OrderBy("uid desc,username asc").FindAll(&everyone) err := orm.OrderBy("uid desc,username asc").FindAll(&everyone)
```
As you can see, the Limit method is for limiting the number of results. As you can see, the Limit method is for limiting the number of results.
- `.Limit()` supports two arguments: the number of results and the starting position. 0 is the default value of the starting position. - `.Limit()` supports two arguments: the number of results and the starting position. 0 is the default value of the starting position.
- `.OrderBy()` is for ordering results. The argument is the order condition. - `.OrderBy()` is for ordering results. The argument is the order condition.
All the examples here are simply mapping records to structs. You can also just put the data into a map as follows: All the examples here are simply mapping records to structs. You can also just put the data into a map as follows:
```Go
a, _ := orm.SetTable("userinfo").SetPK("uid").Where(2).Select("uid,username").FindMap() a, _ := orm.SetTable("userinfo").SetPK("uid").Where(2).Select("uid,username").FindMap()
```
- `.Select()` tells beedb how many fields you want to get from the database table. If unspecified, all fields are returned by default. - `.Select()` tells beedb how many fields you want to get from the database table. If unspecified, all fields are returned by default.
- `.FindMap()` returns the `[]map[string][]byte` type, so you need to convert to other types yourself. - `.FindMap()` returns the `[]map[string][]byte` type, so you need to convert to other types yourself.
@@ -193,27 +193,27 @@ All the examples here are simply mapping records to structs. You can also just p
beedb provides rich methods to delete data. beedb provides rich methods to delete data.
Example 1, delete a single record: Example 1, delete a single record:
```Go
// saveone is the one in above example. // saveone is the one in above example.
orm.Delete(&saveone) orm.Delete(&saveone)
```
Example 2, delete multiple records: Example 2, delete multiple records:
```Go
// alluser is the slice which gets multiple records. // alluser is the slice which gets multiple records.
orm.DeleteAll(&alluser) orm.DeleteAll(&alluser)
```
Example 3, delete records by SQL: Example 3, delete records by SQL:
```Go
orm.SetTable("userinfo").Where("uid>?", 3).DeleteRow() orm.SetTable("userinfo").Where("uid>?", 3).DeleteRow()
```
## Association queries ## Association queries
beedb doesn't support joining between structs. beedb doesn't support joining between structs.
However, since some applications need this feature, here is an implementation: However, since some applications need this feature, here is an implementation:
```Go
a, _ := orm.SetTable("userinfo").Join("LEFT", "userdetail", "userinfo.uid=userdetail.uid") a, _ := orm.SetTable("userinfo").Join("LEFT", "userdetail", "userinfo.uid=userdetail.uid")
.Where("userinfo.uid=?", 1).Select("userinfo.uid,userinfo.username,userdetail.profile").FindMap() .Where("userinfo.uid=?", 1).Select("userinfo.uid,userinfo.username,userdetail.profile").FindMap()
```
We see a new method called `.Join()` that has three arguments: We see a new method called `.Join()` that has three arguments:
- The first argument: Type of Join; INNER, LEFT, OUTER, CROSS, etc. - The first argument: Type of Join; INNER, LEFT, OUTER, CROSS, etc.
@@ -223,9 +223,9 @@ We see a new method called `.Join()` that has three arguments:
## Group By and Having ## Group By and Having
beedb also has an implementation of `group by` and `having`. beedb also has an implementation of `group by` and `having`.
```Go
a, _ := orm.SetTable("userinfo").GroupBy("username").Having("username='astaxie'").FindMap() a, _ := orm.SetTable("userinfo").GroupBy("username").Having("username='astaxie'").FindMap()
```
- `.GroupBy()` indicates the field that is for group by. - `.GroupBy()` indicates the field that is for group by.
- `.Having()` indicates conditions of having. - `.Having()` indicates conditions of having.
@@ -235,11 +235,12 @@ I have received a lot of feedback on beedb from many people all around the world
- Implement an interface design similar to `database/sql/driver` in order to facilitate CRUD operations. - Implement an interface design similar to `database/sql/driver` in order to facilitate CRUD operations.
- Implement relational database associations like one to one, one to many and many to many. Here's a sample: - Implement relational database associations like one to one, one to many and many to many. Here's a sample:
```Go
type Profile struct { type Profile struct {
Nickname string Nickname string
Mobile string Mobile string
} }
type Userinfo struct { type Userinfo struct {
Uid int Uid int
PK_Username string PK_Username string
@@ -247,7 +248,7 @@ I have received a lot of feedback on beedb from many people all around the world
Created time.Time Created time.Time
Profile HasOne Profile HasOne
} }
```
- Auto-create tables and indexes. - Auto-create tables and indexes.
- Implement a connection pool using goroutines. - Implement a connection pool using goroutines.

View File

@@ -98,7 +98,7 @@ I forked the last of these packages, fixed some bugs, and used it in my short UR
- [https://github.com/astaxie/goredis](https://github.com/astaxie/goredis) - [https://github.com/astaxie/goredis](https://github.com/astaxie/goredis)
Let's see how to use the driver that I forked to operate on a database: Let's see how to use the driver that I forked to operate on a database:
```Go
package main package main
import ( import (
@@ -129,7 +129,7 @@ Let's see how to use the driver that I forked to operate on a database:
} }
client.Del("l") client.Del("l")
} }
```
We can see that it is quite easy to operate redis in Go, and it has high performance. It's client commands are almost the same as redis' built-in commands. We can see that it is quite easy to operate redis in Go, and it has high performance. It's client commands are almost the same as redis' built-in commands.
## mongoDB ## mongoDB

View File

@@ -400,7 +400,7 @@ GroupBy:用来指定进行groupby的字段
Having:用来指定having执行的时候的条件 Having:用来指定having执行的时候的条件
##使用原生sql ## 使用原生sql
简单示例: 简单示例: