Fix typos and grammar for 05.1.md
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
# 5.1 database/sql interface
|
||||
|
||||
Go doesn't provide any official database driver which PHP does, but it does have some database driver interface standards for developers develop database drivers. There is an advantage that if your code is developed according to these interface standards, you will not change any code when your database changes. Let's see what these database interface standards are.
|
||||
Go doesn't provide any official database drivers, unlike other languages like PHP which do. However, it does have some database driver interface standards for developers to develop database drivers with. The advantage is that if your code is developed according to these interface standards, you will not need to change any code if your database changes. Let's see what these database interface standards are.
|
||||
|
||||
## sql.Register
|
||||
|
||||
This function is in package `database/sql` for registering database drivers when you use third-party database drivers. In all those drivers, they should call function `Register(name string, driver driver.Driver)` in `init()` function in order to register their drivers.
|
||||
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 corresponding code in drivers of mymysql and sqlite3:
|
||||
Let's take a look at the corresponding mymysql and sqlite3 driver code:
|
||||
|
||||
//https://github.com/mattn/go-sqlite3 driver
|
||||
func init() {
|
||||
@@ -21,45 +21,45 @@ Let's take a look at corresponding code in drivers of mymysql and sqlite3:
|
||||
sql.Register("mymysql", &d)
|
||||
}
|
||||
|
||||
We see that all third-party database drivers implemented this function to register themselves, and Go uses a map to save user drivers inside of `databse/sql`.
|
||||
We see that all third-party database drivers have implemented this function to register themselves, and Go uses a map to save user drivers inside of `databse/sql`.
|
||||
|
||||
var drivers = make(map[string]driver.Driver)
|
||||
|
||||
drivers[name] = driver
|
||||
|
||||
Therefore, this register function can register drivers as many as you want with all different name.
|
||||
Therefore, this register function can register drivers as many as you want with different names.
|
||||
|
||||
We always see following code when we use third-party drivers:
|
||||
We always see the following code when we use third-party drivers:
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
Here the blank `_` is quite confusing for many beginners, and this is a great design in Go. We know this identifier is for discarding values from function return, and you have to use all imported packages in your code in Go. So when the blank is used with import means you need to execute init() function of that package without directly using it, which is for registering database driver.
|
||||
Here the underscore `_` can be quite confusing for many beginners, but this is a great feature in Go. We already know that this identifier is 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 underscore is used with import, it means that you need to execute the init() function of that package without directly using it, which exactly fits the use-case for registering database drivers.
|
||||
|
||||
## driver.Driver
|
||||
|
||||
`Driver` is a interface that has a method `Open(name string)` that returns a interface of `Conn`.
|
||||
`Driver` is an interface containing an `Open(name string)` method that returns a `Conn` interface.
|
||||
|
||||
type Driver interface {
|
||||
Open(name string) (Conn, error)
|
||||
}
|
||||
|
||||
This is a one-time Conn, which means it can be used only once in one goroutine. The following code will occur errors:
|
||||
This is a one-time Conn, which means it can only be used once in one goroutine. The following code will cause errors to occur:
|
||||
|
||||
...
|
||||
go goroutineA (Conn) // query
|
||||
go goroutineB (Conn) // insert
|
||||
...
|
||||
|
||||
Because Go has no idea about which goroutine does what operation, so the query operation may get result of insert, vice-versa.
|
||||
Because Go has no idea which goroutine does what 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 name of Conn and return results correctly.
|
||||
All third-party drivers should have this function to parse the name of Conn and return the correct results.
|
||||
|
||||
## driver.Conn
|
||||
|
||||
This is a database connection interface with some methods, and as I said above, same Conn can only be used in one goroutine.
|
||||
This is a database connection interface with some methods, and as i've said above, the same Conn can only be used in one goroutine.
|
||||
|
||||
type Conn interface {
|
||||
Prepare(query string) (Stmt, error)
|
||||
@@ -67,13 +67,13 @@ This is a database connection interface with some methods, and as I said above,
|
||||
Begin() (Tx, error)
|
||||
}
|
||||
|
||||
- `Prepare` returns prepare status of corresponding SQL commands for querying and deleting, etc.
|
||||
- `Close` closes current connection and clean resources. Most of third-party drivers implemented some kinds of connection pool, so you don't need to cache connections unless you want to have unexpected errors.
|
||||
- `Begin` returns a Tx that represents a affair handle, you can use it for querying, updating or affair roll back 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 unless you want to have unexpected errors.
|
||||
- `Begin` returns a Tx that represents a transaction handle. You can use it for querying, updating, rolling back transactions, etc.
|
||||
|
||||
## driver.Stmt
|
||||
|
||||
This is a ready status and is corresponding with Conn, so it can only be used in one goroutine like Conn.
|
||||
This is a ready status that corresponds with Conn, so it can only be used in one goroutine like Conn.
|
||||
|
||||
type Stmt interface {
|
||||
Close() error
|
||||
@@ -82,14 +82,14 @@ This is a ready status and is corresponding with Conn, so it can only be used in
|
||||
Query(args []Value) (Rows, error)
|
||||
}
|
||||
|
||||
- `Close` closes current connection, but it still returns rows data if it is doing query operation.
|
||||
- `NumInput` returns the number of obligate arguments, database drivers should check 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 SQL commands of `update/insert` that are prepared in `Prepare`, returns `Result`.
|
||||
- `Query` executes SQL commands of `select` that are prepared in `Prepare`, returns rows data.
|
||||
- `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.
|
||||
- `Exec` executes the `update/insert` SQL commands prepared in `Prepare`, returns `Result`.
|
||||
- `Query` executes the `select` SQL command prepared in `Prepare`, returns row data.
|
||||
|
||||
## driver.Tx
|
||||
|
||||
Generally, affair handle only have submit or roll back, 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.
|
||||
|
||||
type Tx interface {
|
||||
Commit() error
|
||||
@@ -104,23 +104,23 @@ This is an optional interface.
|
||||
Exec(query string, args []Value) (Result, error)
|
||||
}
|
||||
|
||||
If the driver doesn't implement this interface, then when you call DB.Exec, it automatically calls Prepare and returns Stmt, then executes Exec 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
|
||||
|
||||
This is the interface for result of `update/insert` operations.
|
||||
This is the interface for results of `update/insert` operations.
|
||||
|
||||
type Result interface {
|
||||
LastInsertId() (int64, error)
|
||||
RowsAffected() (int64, error)
|
||||
}
|
||||
|
||||
- `LastInsertId` returns auto-increment Id number after insert operation from database.
|
||||
- `RowsAffected` returns rows that affected after query operation.
|
||||
- `LastInsertId` returns auto-increment Id number after a database insert operation.
|
||||
- `RowsAffected` returns rows that were affected by query operations.
|
||||
|
||||
## driver.Rows
|
||||
|
||||
This is the interface for result set of query operation.
|
||||
This is the interface for the result of a query operation.
|
||||
|
||||
type Rows interface {
|
||||
Columns() []string
|
||||
@@ -128,13 +128,13 @@ This is the interface for result set of query operation.
|
||||
Next(dest []Value) error
|
||||
}
|
||||
|
||||
- `Columns` returns fields information of database tables, the slice is one-to-one correspondence to SQL query field, not all fields in 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.
|
||||
- `Next` returns next data and assigns to dest, all string should be converted to byte array, and gets io.EOF error if no more data 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.
|
||||
|
||||
## diriver.RowsAffected
|
||||
## driver.RowsAffected
|
||||
|
||||
This is a alias of int64, but it implemented Result interface.
|
||||
This is an alias of int64, but it implements the Result interface.
|
||||
|
||||
type RowsAffected int64
|
||||
|
||||
@@ -144,11 +144,11 @@ This is a alias of int64, but it implemented Result interface.
|
||||
|
||||
## driver.Value
|
||||
|
||||
This is a empty interface that can contain any kind of data.
|
||||
This is an empty interface that can contains any kind of data.
|
||||
|
||||
type Value interface{}
|
||||
|
||||
The Value must be somewhat that drivers can operate or nil, so it should be one of following types:
|
||||
The Value must be something that drivers can operate on or nil, so it should be one of following types:
|
||||
|
||||
int64
|
||||
float64
|
||||
@@ -159,33 +159,33 @@ The Value must be somewhat that drivers can operate or nil, so it should be one
|
||||
|
||||
## driver.ValueConverter
|
||||
|
||||
This defines a interface for converting normal values to driver.Value.
|
||||
This defines an interface for converting normal values to driver.Value.
|
||||
|
||||
type ValueConverter interface {
|
||||
ConvertValue(v interface{}) (Value, error)
|
||||
}
|
||||
|
||||
This is commonly used in database drivers and has many good features:
|
||||
This interface is commonly used in database drivers and has many useful features:
|
||||
|
||||
- Convert driver.Value to corresponding database field type, for example convert int64 to uint16.
|
||||
- Convert database query results to driver.Value.
|
||||
- Convert driver.Value to user defined value in `scan` function.
|
||||
- Converts driver.Value to a corresponding database field type, for example converts int64 to uint16.
|
||||
- Converts database query results to driver.Value.
|
||||
- Converts driver.Value to a user defined value in the `scan` function.
|
||||
|
||||
## driver.Valuer
|
||||
|
||||
This defines a interface for returning driver.Value.
|
||||
This defines an interface for returning driver.Value.
|
||||
|
||||
type Valuer interface {
|
||||
Value() (Value, error)
|
||||
}
|
||||
|
||||
Many types implemented 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 have concepts about how to develop a database driver. Once you implement about interfaces for operations like add, delete, update, etc. There only left problems about communicating with specific database.
|
||||
At this point, you should know a bit about developping 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.
|
||||
|
||||
## database/sql
|
||||
|
||||
databse/sql defines more high-level methods above database/sql/driver for more convenient operations with databases, and it suggests you to implement a connection pool.
|
||||
databse/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.
|
||||
|
||||
type DB struct {
|
||||
driver driver.Driver
|
||||
@@ -195,7 +195,7 @@ databse/sql defines more high-level methods above database/sql/driver for more c
|
||||
closed bool
|
||||
}
|
||||
|
||||
As you can see, Open function returns a DB that has a freeConn, and this is the simple connection pool. Its implementation is very simple or ugly, it uses `defer db.putConn(ci, err)` in function Db.prepare to put connection into connection pool. Every time you call Conn function, it checks length of freeCoon, if it's greater than 0 means there is a reusable connection and 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 freeCoon. 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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user