Golang SQL操作初体験
概要
Golangはdatabase/sqlパッケージを提供してSQLのデータベースへのアクセスに使用して、このパッケージの中で最も重要なのは自然にsqlです.DBだsqlについて.DBは、既存のデータベースの抽象アクセスインタフェースであることを強調する必要があります.sql.DBは2つの重要な機能を提供しています. sql.DBは、データベース駆動により、最下位のデータベース接続の開閉操作を管理する. sql.DBは、データベース接続プール を管理しています.
一つ注意しなければならないのは、sqlのせいだ.DBは接続プールとしてデータベース接続を管理するものであり、データベース操作を行うたびに接続プールから接続を取り出す必要があり、操作タスクが完了すると、この接続を接続プールに戻す必要があるため、接続プールに正しく戻すとdbになる.SQLはデータベース接続を開きすぎて、データベース接続資源を消耗させる.
MySQLデータベースの基本操作
データベースドライバのインポート
データベースの開発経験のある友人は知っています.具体的なデータベースと接続するには、データベースの駆動を借りなければなりません.これはGolangにおいても例外ではない.たとえば、MySQLデータベースを例に挙げます.
通常、駆動によって提供する方法を直接使用するのではなく、sqlを使用するべきであることに注意すべきである.DBであるためmysqlドライバを導入する際に匿名で導入する方式(パケットパスの前に_を追加)を用いた.データベースドライバをインポートすると、Golangのdatabase/sqlコンテキストに独自に初期化して登録するので、database/sqlパッケージで提供する方法でデータベースにアクセスできます.
データベースの接続
MySQLドライバをインポートすると、データベース接続を開きます.
sqlを通ります.Open関数は、データベース抽象操作インタフェースを作成することができ、正常に開くとsqlを返す.DBポインタsql.Open関数の署名は次のとおりです.
2つのパラメータを受信します. driverName、使用するドライバ名.この名前は、データベースドライバがdatabase/sqlに登録する際に使用する名前です. dataSourceName、2番目のデータベース接続のリンク.このリンクには、データベースのユーザ名、パスワード、データベースホスト、接続するデータベース名などの情報が含まれている.
sql.DBのベストプラクティス:sql.DBオブジェクトは長期生存のオブジェクトとして使用するため、Open()とClose()を頻繁に呼び出すことは避けるべきである.すなわち、一般的に、データベースを操作するときにsqlを作成する.DBはそれを保存する、このデータベースを操作するたびにこのsqlを渡す.DBオブジェクトでよい、最後にこのデータベースにアクセスする必要がある場合には、対応するsqlを閉じる.DBオブジェクト
データベースのクエリー
データベース・クエリーの一般的な手順は次のとおりです. dbが呼び出される.QueryはSQL文を実行し、クエリの結果としてRowsを返す rowsを通過する.Next()反復クエリデータ. rowsを通過する.Scan()は、各ローの値 を読み出す. dbが呼び出される.Close()クエリー を閉じる
たとえば、次のデータベース・テーブルがあります.
レコードを挿入します.
dbを呼び出す.Query、INSERT文を実行してデータを挿入しました.実行が完了すると、まず文が成功したかどうかを確認し、エラーがない場合はrowsを通過する必要があります.Scanは実行結果を取得する.INSERTは挿入するデータの行数を返すので、私たちが印刷する文は「insert result 0」です.
次に、データベースから挿入したデータを取り出します.
上のコードの流れは基本的に大きな違いはありませんが、rowsに注意する必要があります.Scanパラメータの順序は重要で、クエリの結果のcolumnに対応する必要がある.例えば「SELECT*From user where id=1」クエリの行のcolumn順序は「id,name,age」であるためrows.Scanもこの順序でrowsする必要がある.Scan(&id,&name,&age)は、データ読み出しのずれをもたらす.データベース操作ごとに、エラーがあるかどうかを確認する必要があります. に戻ります.毎回db.Query操作後、rowsを呼び出す必要があります.Close(). なぜならdb.Query()は、rowsを呼び出していない場合、データベース接続プールから接続を取得します.Close()の場合、この接続は常に占有されます.通常はdefer rowsを使用しますClose()は、データベース接続が接続プールに正しく戻すことを保証する. rowsを複数回呼び出す.Close()は副作用がないので、rowsを表示的に呼び出してもよい.Close()はdefer rowsを使うべきだClose()を使用してクエリーを閉じます.
完全な例は次のとおりです.
プリコンパイル文(Prepared Statement)
プレコンパイル文(PreparedStatement)は多くのメリットを提供するので、開発ではできるだけ使用します.以下に、プリコンパイル文を使用して提供される機能を示します. PreparedStatementは、カスタムパラメータのクエリ を実装することができる PreparedStatementは、通常、手動文字列SQL文よりも効率的である. PreparedStatement SQL注入攻撃 を防止
次に、前節の例をPrepared Statementで書き換えます.
Golangはdatabase/sqlパッケージを提供してSQLのデータベースへのアクセスに使用して、このパッケージの中で最も重要なのは自然にsqlです.DBだsqlについて.DBは、既存のデータベースの抽象アクセスインタフェースであることを強調する必要があります.sql.DBは2つの重要な機能を提供しています.
一つ注意しなければならないのは、sqlのせいだ.DBは接続プールとしてデータベース接続を管理するものであり、データベース操作を行うたびに接続プールから接続を取り出す必要があり、操作タスクが完了すると、この接続を接続プールに戻す必要があるため、接続プールに正しく戻すとdbになる.SQLはデータベース接続を開きすぎて、データベース接続資源を消耗させる.
MySQLデータベースの基本操作
データベースドライバのインポート
データベースの開発経験のある友人は知っています.具体的なデータベースと接続するには、データベースの駆動を借りなければなりません.これはGolangにおいても例外ではない.たとえば、MySQLデータベースを例に挙げます.
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
通常、駆動によって提供する方法を直接使用するのではなく、sqlを使用するべきであることに注意すべきである.DBであるためmysqlドライバを導入する際に匿名で導入する方式(パケットパスの前に_を追加)を用いた.データベースドライバをインポートすると、Golangのdatabase/sqlコンテキストに独自に初期化して登録するので、database/sqlパッケージで提供する方法でデータベースにアクセスできます.
データベースの接続
MySQLドライバをインポートすると、データベース接続を開きます.
func main() {
db, err := sql.Open("mysql",
"user:password@tcp(127.0.0.1:3306)/test")
if err != nil {
log.Fatal(err)
}
defer db.Close()
}
sqlを通ります.Open関数は、データベース抽象操作インタフェースを作成することができ、正常に開くとsqlを返す.DBポインタsql.Open関数の署名は次のとおりです.
func Open(driverName, dataSourceName string) (*DB, error)
2つのパラメータを受信します.
最初のデータベースクエリー操作を行うと、ネットワーク接続が確立されます.データベース接続が使用可能かどうかをすぐに確認するには、sqlを使用します.DBのPingメソッドは、次のようになります.err = db.Ping()
if err != nil {
log.Fatal(err)
}
sql.DBのベストプラクティス:sql.DBオブジェクトは長期生存のオブジェクトとして使用するため、Open()とClose()を頻繁に呼び出すことは避けるべきである.すなわち、一般的に、データベースを操作するときにsqlを作成する.DBはそれを保存する、このデータベースを操作するたびにこのsqlを渡す.DBオブジェクトでよい、最後にこのデータベースにアクセスする必要がある場合には、対応するsqlを閉じる.DBオブジェクト
データベースのクエリー
データベース・クエリーの一般的な手順は次のとおりです.
たとえば、次のデータベース・テーブルがあります.
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT '',
`age` int(11) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4
レコードを挿入します.
func insertData(db *sql.DB) {
rows, err := db.Query(`INSERT INTO user (id, name, age) VALUES (1, "xys", 20)`)
defer rows.Close()
if err != nil {
log.Fatalf("insert data error: %v
", err)
}
var result int
rows.Scan(&result)
log.Printf("insert result %v
", result)
}
dbを呼び出す.Query、INSERT文を実行してデータを挿入しました.実行が完了すると、まず文が成功したかどうかを確認し、エラーがない場合はrowsを通過する必要があります.Scanは実行結果を取得する.INSERTは挿入するデータの行数を返すので、私たちが印刷する文は「insert result 0」です.
次に、データベースから挿入したデータを取り出します.
func selectData(db *sql.DB) {
var id int
var name string
var age int
rows, err := db.Query(`SELECT * From user where id = 1`)
if err != nil {
log.Fatalf("insert data error: %v
", err)
return
}
for rows.Next() {
rows.Scan(&id, &age, &name)
if err != nil {
log.Fatal(err)
}
log.Printf("get data, id: %d, name: %s, age: %d", id, name, age)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
上のコードの流れは基本的に大きな違いはありませんが、rowsに注意する必要があります.Scanパラメータの順序は重要で、クエリの結果のcolumnに対応する必要がある.例えば「SELECT*From user where id=1」クエリの行のcolumn順序は「id,name,age」であるためrows.Scanもこの順序でrowsする必要がある.Scan(&id,&name,&age)は、データ読み出しのずれをもたらす.
, golang (lazy init), sql.Open , , sql.DB .
: 完全な例は次のとおりです.
func insertData(db *sql.DB) {
rows, err := db.Query(`INSERT INTO user (id, name, age) VALUES (1, "xys", 20)`)
defer rows.Close()
if err != nil {
log.Fatalf("insert data error: %v
", err)
}
var result int
rows.Scan(&result)
log.Printf("insert result %v
", result)
}
func selectData(db *sql.DB) {
var id int
var name string
var age int
rows, err := db.Query(`SELECT id, name, age From user where id = 1`)
if err != nil {
log.Fatalf("insert data error: %v
", err)
return
}
for rows.Next() {
err = rows.Scan(&id, &name, &age)
if err != nil {
log.Fatal(err)
}
log.Printf("get data, id: %d, name: %s, age: %d", id, name, age)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
defer db.Close()
if err != nil {
fmt.Printf("connect to db 127.0.0.1:3306 error: %v
", err)
return
}
insertData(db)
selectData(db)
}
プリコンパイル文(Prepared Statement)
プレコンパイル文(PreparedStatement)は多くのメリットを提供するので、開発ではできるだけ使用します.以下に、プリコンパイル文を使用して提供される機能を示します.
次に、前節の例をPrepared Statementで書き換えます.
func deleteData(db *sql.DB) {
stmt, _ := db.Prepare(`DELETE FROM user WHERE id = ?`)
rows, err := stmt.Query(1)
defer stmt.Close()
rows.Close()
if err != nil {
log.Fatalf("delete data error: %v
", err)
}
rows, err = stmt.Query(2)
rows.Close()
if err != nil {
log.Fatalf("delete data error: %v
", err)
}
}
func insertData(db *sql.DB) {
stmt, _ := db.Prepare(`INSERT INTO user (id, name, age) VALUES (?, ?, ?)`)
rows, err := stmt.Query(1, "xys", 20)
defer stmt.Close()
rows.Close()
if err != nil {
log.Fatalf("insert data error: %v
", err)
}
rows, err = stmt.Query(2, "test", 19)
var result int
rows.Scan(&result)
log.Printf("insert result %v
", result)
rows.Close()
}
func selectData(db *sql.DB) {
var id int
var name string
var age int
stmt, _ := db.Prepare(`SELECT * From user where age > ?`)
rows, err := stmt.Query(10)
defer stmt.Close()
defer rows.Close()
if err != nil {
log.Fatalf("select data error: %v
", err)
return
}
for rows.Next() {
err = rows.Scan(&id, &name, &age)
if err != nil {
log.Fatal(err)
}
log.Printf("get data, id: %d, name: %s, age: %d", id, name, age)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
func main() {
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test")
defer db.Close()
if err != nil {
fmt.Printf("connect to db 127.0.0.1:3306 error: %v
", err)
return
}
deleteData(db)
insertData(db)
selectData(db)
}