はじめに
今回の記事では、 gorp を使ってDBを操作する方法を紹介します。
GoのORM
GoでDBにアクセスする場合、今回紹介する gorp の他にも様々な選択肢があります。ORMの比較についてはこちらの記事が参考になります。
gorpの紹介
gorp では、シンプルなインタフェースを通してCRUD操作、構造体へのマッピング、トランザクションなどの処理ができます。
insert、update、deleteおよびPK指定のselectの場合、クエリを書くことなく処理できるほか、SQLを書いて実行することもできます。
他にも、テーブルの作成やインデックスを貼ることもできます。
gorpの基本操作
まず初めに、 gorp でのCRUD操作の書き方を見ていきましょう。前提として、以下のような構造体が定義されているものとします。
1 2 3 4 5 6 7 8 9 | type User struct { Id int64 `db:"id, primarykey, autoincrement` FirstName string `db:"firstname, notnull, size:16` LastName string `db:"lastname, notnull, size:16` Age int `db:"age, notnull` gender int `db:"gender, notnull"` CreatedAt time.Time `db:"created_at, notnull"` UpdatedAt time.Time `db:"updated_at, notnull"` } |
db:xxx
については後ほど説明します。
insert
insertは以下のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() timeNow := time.Now() user1 := User{ "Taro", "Yamada", 25, 1, timeNow, timeNow } user2 := User{ "Hanako", "Yamada", 20, 2, timeNow, timeNow } err := dbMap.Insert(&user1, &user2) if err != nil { fmt.printf("error! %v", err) } } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
select
selectは以下のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() var users []User _, err := dbMap.Select(&users, "select * from users where gender = ?", 1) if err != nil { fmt.printf("error! %v", err) } } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
さらに、以下のようにパラメータを map でbindingすることもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() user := User{} err := dbMap.Select( &user, "select * from users where age > :age and gender = :gender", map[string]interface{}{ "age": 20, "gender": 1 }, ) if err != nil { fmt.printf("error! %v", err) } } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
1件だけ取得したい場合は以下のように記述します。ただし、1件も該当しない場合もしくは、複数件該当する場合はエラーが返ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() user := User{} err := dbMap.SelectOne(&user, "select * from users where id = ?", 1) if err != nil { fmt.printf("error! %v", err) } } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
PKを指定してselectする場合は以下のように記述できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() obj, err := dbMap.Get(User{}, 1) if err != nil { fmt.printf("error! %v", err) } user := obj.(*User) } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
update
updateは以下のように記述します。count には更新したレコード数が返ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() user := User{} err := dbMap.SelectOne(&user, "select * from users where id = ?", 1) if err != nil { fmt.printf("error! %v", err) } user.Age = 30 user.UpdatedAt = time.Now() count, err := dbMap.Update(&user) if err != nil { fmt.printf("error! %v", err) } } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
delete
deleteは以下のように記述します。ただし、構造体にPKを定義しておく必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { dbMap := initDb() user := User{} err := dbMap.SelectOne(&user, "select * from users where id = ?", 1) if err != nil { fmt.printf("error! %v", err) } count, err := dbMap.Delete(&user) if err != nil { fmt.printf("error! %v", err) } } func initDb() *gorp.DbMap { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") if err != nil { fmt.printf("error! %v", err) } dbMap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} return dbMap } |
構造体のmapping
gorpではSQLの実行結果をmappingする構造体をあらかじめ定義しておく必要があります。先ほど定義した構造体をもとに説明します。
1 2 3 4 5 6 7 8 9 | type User struct { Id int64 `db:"id, primarykey, autoincrement` FirstName string `db:"firstname, notnull, size:16` LastName string `db:"lastname, notnull, size:16` Age int `db:"age, notnull` gender int `db:"gender, notnull"` CreatedAt time.Time `db:"created_at, notnull"` UpdatedAt time.Time `db:"updated_at, notnull"` } |
db: の次の文字列でbindするカラムを指定します。PKの場合は primarykey を記述します。文字列型の場合は size でサイズを指定できます。Nullを許容しない場合は notnull を記述します。
テーブルの作成
gorpではテーブルを作成する関数が用意されています。以下のように記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package main import ( "gopkg.in/gorp.v2" "time" ) func main() { db, err := sql.Open("mysql", "tcp:localhost:3306*database/user/password") dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "UTF8"}} t := dbmap.AddTableWithName(Invoice{}, "invoice_test").SetKeys(true, "Id") err = dbmap.CreateTablesIfNotExists() if err != nil { fmt.printf("error! %v", err) } } |
さいごに
gorpの基本的な使い方を紹介しました。他にもインデックスの作成やupdateなどのhookなどの機能がありますので、興味がありましたらgo docなどをご覧ください。