Оригинальный автор, публичный аккаунт [программист чтение], прошу обратить внимание на паблик-аккаунт, просьба указывать источник перепечатываемой статьи.
Следует сказать, что программирование баз данных — это базовый функциональный модуль, предоставляемый любым языком программирования, будь то встроенная поддержка языка программирования или реализованная через внешние библиотеки; конечно, API-интерфейсы программирования баз данных, предоставляемые разными языками программирования не одинаковы, и базы данных, которые необходимо поддерживать, также разнообразны, например, часто используемыеMySQL
,SQLServer
,Postgres
база данных.
Оставив в стороне другие языки программирования, в этой статье мы поговорим о программировании базы данных на языке Go и узнаем, как использовать стандартную библиотеку, предоставляемую языком Go, для написания кода базы данных общего назначения.
Подключение к базе данных и драйвер
база данных/sql и база данных/sql/драйвер
стандартная библиотекаdatabase/sql
Это уровень абстракции операций с базами данных языка Go, и конкретная реализация логики операций с базами данных выполняется различными сторонними пакетами, в то время как стандартная библиотекаdatabase/sql/driver
Предоставляются стандартные спецификации, реализованные этими сторонними пакетами.
Поэтому для программирования баз данных на языке Go обычно требуется только импорт стандартной библиотеки.database/sql
пакет.Этот пакет предоставляет все необходимые структуры, функции и методы для работы с базой данных.Следующее является оператором для импорта этого пакета:
import database/sql
Преимущество этого в языке Go заключается в том, что при миграции из одной базы данных в другую (например,SQL Server
переехал вMySQL
), вам нужно только изменить пакет драйверов.
Пакеты драйверов баз данных, поддерживаемые Go
Ранее мы говорили, что операции с данными языка Go реализуются различными сторонними пакетами, поэтому, если мы хотим подключиться к базе данных MySQL, как нам реализовать такой пакет? Фактически, стандартная библиотека языка Godatabase/sql/driver
Определяет все интерфейсы, которые реализуют сторонний пакет драйверов, мы импортируем только реализациюdatabase/sql/driver
Достаточно соответствующего пакета интерфейсных драйверов.
Ниже приведен список драйверов баз данных, поддерживающих Golang:
Установите сторонние пакеты драйверов
В качестве примера возьмем пакет драйвера базы данных MySQL:
$ go get -u github.com/go-sql-driver/mysql
импортировать пакет драйверов
import database/sql
import _ "github.com/go-sql-driver/mysql"
Структура sql.DB
sql.DB
структураsql/database
Объект операции с базой данных, инкапсулированный пакетом, включая основные методы работы с базой данных.
DSN
DSN
полное имяData Source Name
, указывающий источник подключения к базе данных, используемый для определения способа подключения к базе данных. Формат DSN для разных баз данных отличается, что зависит от реализации драйвера базы данных. Ниже приведеноgo-sql-driver/sql
Формат DSN выглядит следующим образом:
//[用户名[:密码]@][协议(数据库服务器地址)]]/数据库名称?参数列表
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
Инициализировать sql.DB
database/sql
В пакете есть две функции, которые инициализируют и возвращают*sql.DB
Структурный объект:
//driverName表示驱动名,如mysql,dataSourceName为上文介绍的DSN
func Open(driverName, dataSourceName string) (*DB, error)
func OpenDB(c driver.Connector) *DB
Ниже показано, как использовать эти две функции:
Как правило, мы используемOpen()
функция может инициализировать и возвращать*sql.DB
экземпляр структуры, используйтеOpen()
Функция должна только передать имя драйвера и соответствующий DSN.Он очень прост в использовании и очень общий.Когда вам нужно подключиться к разным базам данных, вам нужно только изменить имя драйвера и DSN.
import "database/sql"
import _ "github.com/go-sql-driver/mysql" //注意前面有_
func open(){
const DRIVER = "mysql"
var DSN = "root:123456@tcp(localhost:3306)/test?charset=utf8&parseTime=True&loc=Local"
var err error
db, err = sql.Open(DRIVER, DSN)
if err != nil {
panic(err)
}
if err = db.Ping();err != nil{
panic(err)
}
}
OpenDB()
Функция зависит от реализации пакета драйверовsql/database/driver
в упаковкеConnector
интерфейс, этот метод не является обобщенным и не рекомендуется использовать, в следующей демонстрации используетсяmysql
пакет драйверовdriver.Connector
инициализировать и вернуть*sql.DB
Пример структуры:
import "database/sql"
import "github.com/go-sql-driver/mysql"//注意前面没有_
func openConnector() {
Connector, err := mysql.NewConnector(&mysql.Config{
User: "root",
Passwd: "123456",
Net: "tcp",
Addr: "localhost:3306",
DBName: "test",
AllowNativePasswords:true,
Collation:"utf8_general_ci",
ParseTime:true,
Loc:time.Local,
})
if err != nil {
panic(err)
}
db = sql.OpenDB(Connector)
if err = db.Ping();err != nil{
panic(err)
}
}
Используя метод, определенный ранее, инициализируйте*sql.DB
структура указателя:
var db *sql.DB
//在init方法初始化`*sql.DB`
func init(){
open()
//或者
openConnector()
}
Вот что я хочу сказать,sql.DB
даsql/database
Структура, инкапсулированная пакетом, но не представляющая собой объект подключения к базе данных.sql.DB
В качестве простого пула соединений с базой данных мы устанавливаем соответствующие параметры пула соединений с базой данных следующими методами:
func (db *DB) SetMaxIdleConns(n int)//设置连接池中最大空闲数据库连接数,<=0表示不保留空闲连接,默认值2
func (db *DB) SetMaxOpenConns(n int)//设置连接池最大打开数据库连接数,<=表示不限制打开连接数,默认为0
func (db *DB) SetConnMaxLifetime(d time.Duration)//设置连接超时时间
демонстрация кода
db.SetMaxOpenConns(100)//设置最多打开100个数据连连接
db.SetMaxIdleConns(0)//设置为0表示
db.SetConnMaxLifetime(time.Second * 5)//5秒超时
Основные операции с базой данных
Ниже мы демонстрируем основные операции добавления, удаления, модификации и запроса базы данных (CURD) на языке Go.users
таблица данных, которая создалаSQL
Заявление выглядит следующим образом:
CREATE TABLE users(
id INT NOT NULL AUTO_INCREMENT COMMENT 'ID',
username VARCHAR(32) NOT NULL COMMENT '用户名',
moeny INT DEFAULT 0 COMMENT '账户余额',
PRIMARY KEY(id)
);
INSERT INTO users VALUES(1,'小明',1000);
INSERT INTO users VALUES(2,'小红',2000);
INSERT INTO users VALUES(3,'小刚',1400);
Запрос
Запрос — самая основная функция работы с базой данных.В языке Go вы можете использоватьsql.DB
серединаQuery()
илиQueryContext()
методы, которые определяются следующим образом:
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error)
Query()
иQueryContext()
метод возвращаетsql.Rows
структура, представляющая набор результатов запроса,sql.Rows
Определение и содержащиеся в нем методы следующие:
type Rows struct {
//contains filtered or unexported fields
}
func (rs *Rows) Close() error //关闭结果集
func (rs *Rows) ColumnTypes() ([]*ColumnType, error)//返回数据表的列类型
func (rs *Rows) Columns() ([]string, error)//返回数据表列的名称
func (rs *Rows) Err() error//错误集
func (rs *Rows) Next() bool//游标,下一行
func (rs *Rows) NextResultSet() bool
func (rs *Rows) Scan(dest ...interface{}) error //扫描结构体
использоватьsql.Rows
изNext()
иScan
метод, но он может проходить по возвращаемому набору результатов, ниже приведен пример кода:
func query() {
selectText := "SELECT * FROM users WHERE id = ?"
rows, _ := db.Query(selectText, 2)
defer rows.Close()
for rows.Next() {
var (
id int
username string
money int
)
_ = rows.Scan(&id, &username,&money)
fmt.Println(id, username,money)
}
}
также можно использоватьsql.DB
серединаQueryRow()
илиQueryRowContext()
метод, определения этих двух методов следующие:
func (db *DB) QueryRow(query string, args ...interface{}) *Row
func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row
QueryRow
иQueryRowContext
вернутьsql.Row
структура, представляющая строку таблицы данных,sql.Row
определяется следующим образом, видно, чтоsql.Row
только одна структураScan()
способ сканированияsql.Row
данные в структуре.
type Row struct{
}
func (r *Row) Scan(dest ...interface{}) error
демонстрация кода
func queryRow(){
selectText := "SELECT * FROM users WHERE id = ?"
row := db.QueryRow(selectText, 2)
var (
id int
username string
money int
)
_ = row.Scan(&id, &username,&money)
fmt.Println(id, username,money)
}
Кроме того, используйтеsql.DB
серединаPrepare()
илиPrepareContext()
метод, который возвращаетsql.Stmt
структура`.
Примечание. Опыт работы со структурой sql.Stmt сначала помещается в
Prepare()
илиPrepareContext()
Определенный оператор SQL отправляется в базу данных для выполнения, параметры, требуемые в операторе SQL, отправляются в базу данных, и возвращается результат обработки.
func (db *DB) Prepare(query string) (*Stmt, error)
func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error)
sql.Stmt
представлено сsql.DB
Метод запроса и возврата набора результатов, см. пример ниже:
func queryStmt(){
stmt,err := db.Prepare("SELECT * FROM users WHERE id = ?")
if err != nil{
return
}
defer stmt.Close()
rows,err := stmt.Query(2)
defer rows.Close()
for rows.Next() {
var (
id int
username string
money int
)
_ = rows.Scan(&id, &username,&money)
fmt.Println(id, username,money)
}
}
Добавить к
Добавьте записи базы данных, вы можете использоватьsql.DB
серединаExec()
илиExecContext()
методы, эти два метода определяются следующим образом:
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
func (db *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error)
Пример кода:
func insert(){
insertText := "INSERT INTO users values(?,?,?)"
rs,err := db.Exec(insertText,4,"juejin",1000)
if err != nil{
fmt.Println(err)
return
}
if id,_ := rs.LastInsertId();id > 0 {
fmt.Println("插入成功")
}
/*也可以这样判断是否插入成功
if n,_ := rs.RowsAffected();n > 0 {
fmt.Println("插入成功")
}
*/
}
Exec()
илиExecContext()
Первое возвращаемое значение метода является реализованнымsql.Result
Тип интерфейса,sql.Result
определяется следующим образом:
Уведомление
LastInsertId()
Метод возвращает значение идентификатора автоинкремента только тогда, когда используется оператор INSERT и таблица данных имеет идентификатор автоинкремента, в противном случае он возвращает 0.
type Result interface {
LastInsertId() (int64, error)//使用insert向数据插入记录,数据表有自增id时,该函数有返回值
RowsAffected() (int64, error)//表示影响的数据表行数
}
мы можем использоватьsql.Result
серединаLastInsertId()
метод илиRowsAffected()
судитьSQL
Был ли оператор выполнен успешно.
Помимо использованияsql.DB
серединаExec()
иExecContext()
метод, вы также можете использоватьPrepare()
илиPrepareContext()
возвращениеsql.Stmt
структуру, затем пройтиsql.Stmt
серединаExec()
способ записи данных в таблицу данных.
использоватьsql.Stmt
Демонстрация записи данных в таблицу данных:
func insertStmt(){
stmt,err := db.Prepare("INSERT INTO users VALUES(?,?,?)")
defer stmt.Close()
if err != nil{
return
}
rs,err := stmt.Exec(5,"juejin",1000)
if id,_ := rs.LastInsertId(); id > 0 {
fmt.Println("插入成功")
}
}
Обратите внимание, что использование
sql.Stmt
серединаExec()
илиExecContext()
воплощать в жизньSQL
Он также подходит для операторов обновления и удаления и не будет демонстрироваться при обновлении и удалении, описанном ниже.
возобновить
Точно так же, как добавление данных в таблицу данных, вы можете использоватьsql.DB
изExec()
илиExecContext()
метод, но с использованием базы данныхUPDATE
Когда оператор обновляет данные, мы можем передать толькоsql.Result
в структуреRowsAffected()
метод, чтобы определить количество затронутых строк данных, а затем определить, успешно ли выполнено выполнение.
func update() {
updateText := "UPDATE users SET username = ? WHERE id = ?"
rs,err := db.Exec(updateText,"database",2)
if err != nil{
fmt.Println(err)
return
}
if n,_ := rs.RowsAffected();n > 0 {
fmt.Println("更新成功")
}
}
Удалить
использоватьDELETE
Операция оператора по удалению записей таблицы данных такая же, как и в приведенном выше операторе обновления, см. следующую демонстрацию:
func del() {
delText := "DELETE FROM users WHERE id = ?"
rs,err := db.Exec(delText,1)
if err != nil{
fmt.Println(err)
return
}
fmt.Println(rs.RowsAffected())
}
дела
В предыдущих примерах мы не открывали транзакцию, если транзакция не открыта, то по умолчанию каждый отправленный товар будетSQL
Операторы рассматриваются как транзакция.Если несколько операторов выполняются вместе, когда один из операторов выполняется неправильно, ранее выполненныйSQL
Заявление нельзя откатить.
Для некоторой бизнес-логики со строгими требованиями (такими как оплата заказа, перевод пользователя и т. д.) в одной и той же транзакции должно быть представлено несколько записей.SQL
заявление, чтобы избежать ситуации, когда транзакция не может быть отменена из-за ошибки выполнения.
открытая транзакция
Как начать новую транзакцию? можно использоватьsql.DB
в структуреBegin()
илиBeginTx()
методы, которые определяются следующим образом:
func (db *DB) Begin() (*Tx, error)
func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error)
BeginTx()
Второй параметр методаTxOptions
, который определяется следующим образом:
type TxOptions struct {
// Isolation is the transaction isolation level.
// If zero, the driver or database's default level is used.
Isolation IsolationLevel
ReadOnly bool
}
TxOptions
изIsolation
Поле используется для определения уровня изоляции транзакции и его типа.IsolationLevel
,Ioslation
Диапазон значений может быть следующими константами:
const (
LevelDefault IsolationLevel = iota
LevelReadUncommitted
LevelReadCommitted
LevelWriteCommitted
LevelRepeatableRead
LevelSnapshot
LevelSerializable
LevelLinearizable
)
Begin()
иBeginTxt()
метод возвращаетsql.Tx
структура, использованиеsql.Tx
Операции с базой данных будут зафиксированы в той же транзакции.Следующий демонстрационный код:
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
//Begin方法实际上是调用BeginTx()方法,db.BeginTx(context.Background(), nil)
tx, err := db.Begin()
Основные операции, поддерживаемые sql.Tx
Нижеsql.Tx
Основной метод работы в структуре используется так же, как в примере, который мы продемонстрировали ранее.
func (tx *Tx) Exec(query string, args ...interface{}) (Result, error)
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error)
func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error)
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error)
func (tx *Tx) QueryRow(query string, args ...interface{}) *Row
func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row
фиксация транзакции
когда используешьsql.Tx
После обработки данных нам нужно использоватьsql.Tx
изCommit()
метод явно фиксирует транзакцию, и если есть ошибка, вы можете использоватьsql.Tx
серединаRollback()
Метод откатывает транзакцию и поддерживает согласованность данных.Ниже приведены определения этих двух методов:
func (tx *Tx) Commit() error
func (tx *Tx) Rollback() error
предварительно скомпилировано
sql.Tx
в структуреStmt()
иStmtContext()
можетsql.Stmt
Инкапсулирован как поддерживающий транзакциюsql.Stmt
Структура и возврат, эти два метода определяются следующим образом:
func (tx *Tx) Stmt(stmt *Stmt) *Stmt
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt
при использованииsql.Tx
серединаPrepare()
иPrepareContext()
метод может напрямую возвращать транзакционныйsql.Stmt
структура
func (tx *Tx) Prepare(query string) (*Stmt, error)
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error)
Пример
//修改
func txUpdate(){
tx,_ := db.Begin()
rs,err := tx.Exec("UPDATE users SET username = ? WHERE id = ?","sssss",2)
if err != nil{
panic(err)
}
err = tx.Commit()
if err != nil{
panic(err)
}
if n,_ := rs.RowsAffected();n > 0{
fmt.Println("成功")
}
}
//使用Stmt修改
func txStmt(){
tx,err := db.Begin()
if err != nil{
panic(err)
}
stmt,err := db.Prepare("UPDATE users SET username = ? WHERE id = ?")
stmtTx := tx.Stmt(stmt)
defer stmtTx.Close()
rs,_ := stmtTx.Exec("test",2)
_ = tx.Commit()
if n,_ := rs.RowsAffected();n > 0{
fmt.Println("成功")
}
}
Связанная структура ORM
Ранее мы представили нативную поддержку программирования баз данных на языке Go, однако более удобно, что мы можем напрямую использовать некоторые открытые исходные коды.ORM
(Object Relational Mapping
)Рамка,ORM
Фреймворки могут инкапсулировать базовыеSQL
оператор и сопоставляется непосредственно сStruct
,Map
и другие типы данных, избавьте нас от необходимости писать напрямуюSQL
Операторская работа, очень простая и удобная.
Ниже приведены некоторые из наиболее часто используемых фреймворков ORM:
GORM
GORM
Это очень полная структура ORM.Помимо основной поддержки изменения и проверки, она также поддерживает ассоциации, включая одну, несколько, принадлежащие и многие-ко-многим таблицы данных.Кроме того, ее также можно создавать/сохранять/ обновляется/удаляется/искается до или затем пишет обратные вызовы ловушек, а также поддерживает транзакции.
В настоящее время GORM поддерживает работу с базой данных.MySQL
,SQLite3
,SQL Server
,Postgres
.
Xorm
Xorm
Это также простая и мощная структура ORM, и ее функции такжеGORM
похож, но поддерживает больше драйверов баз данных, чемGORM
больше, поддержкаMySQL
,SQL Server
,SQLite3
,Postgres
,MyMysql
,Tidb
,Oracle
и т. д. управляемая базой данных.
Beego ORM
Beego ORM
Это веб-фреймворк, разработанный китайскимиBeego
модуль в , хотя этоBeego
Модуль , но может использоваться самостоятельно, но в настоящее времяBeego ORM
только поддержкаMySQL
,SQLite3
,Postgres
и т. д. управляемая базой данных.
В дополнение к трем фреймворкам ORM, которые мы представили выше, на самом деле существует много хороших фреймворков ORM, вы можете взглянуть на них, когда у вас будет время.
резюме
Язык Go абстрагирует и инкапсулирует работу базы данных вsql/database
В пакете он предоставляет нам единый API для работы с разными базами данных, что очень удобно, как мы объясняли в этой статье.sql/database
в упаковкеsql.DB
,sql.Rows
,sql.Stmt
,sql.Tx
Использование других структур, я думаю, вы сможете освоить метод работы с базой данных на языке Go с помощью приведенных выше примеров.
Ваше внимание — самое большое поощрение на моем писательском пути!