こんにちは。現役エンジニアの”はやぶさ”@Cpp_Learningです。Go言語が好きです。
XORMによるデータベース操作を勉強しながら、ORM入門したので、備忘録も兼ねて本記事を書きます。
Contents
ORM(Object Relational Mapping)とは
ORM(Object Relational Mapping)とは、プログラミング言語で扱う型とデータベースから抽出したレコードを紐づける処理のことです。
SQL検索結果をGoの構造体にマッピングする処理という説明の方が直感的で分かり易いかもしれません。
ORMを使うことで、Goの型とデータベースのテーブルを透過的に扱うことができるため、生のSQL文を使わずに関数を使って SELECT や INSERT などのSQLクエリを組み立てる機能(クエリビルダ)をもつライブラリもあります。
- SQL検索結果とGoの型を紐づけるマッパー
- クエリビルダ※
※クエリビルダがないライブラリもあります
人気のORM for Golang
GitHubスター数やブログ記事の多さから、以下のORMが有名どころです。
他にもありますが、紹介しきれないので、良質なGoの記事を数多く公開しているフューチャー技術ブログの中から一部をピックアップして紹介します。
XORMの採用した理由
今回は数多くあるORMの中から XORM を採用しました。いくつか理由はありますが、公式リポジトリに「(試験的とはいえ)Oracle の Driver をサポートしている」という説明があったのが決め手となりました。
普段メインで使っている RDB は Postgres や SQLite ですが、Oracle を使用する機会もあるので、1つのORMで全てのRDBをカバーしたいと考えました。
実践 XORMでデータベース操作 -ORM入門-
ここからはXORMの基本的な使い方を説明します。
プロジェクトフォルダの構成
プロジェクトフォルダの構成は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
workspace └─docker-xorm │──db # SQLite │ └──user.db # create.pyで生成 │ │──Dockerfile │──go.mod │──go.sum │ │──create.go │──read.go │──update.go └──delete.go |
※今回は SQLite を使います(手軽なので)。user.db は create.py で生成します
go.mod -バージョン管理-
go.mod は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
module example.com/myapp go 1.17 require ( github.com/go-xorm/xorm v0.7.9 github.com/mattn/go-sqlite3 v1.14.8 ) require ( xorm.io/builder v0.3.6 // indirect xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb // indirect ) |
Create(テーブルおよびレコード作成)
XORMでCRUD(Create/Read/Update/Delete)やっていきます。まずは create.go から。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
package main import ( "fmt" "os" "time" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) // User describes a user type User struct { Id int64 Name string Age int64 Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } func main() { f := "./db/user.db" os.Remove(f) // Create engine engine, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) } defer engine.Close() engine.ShowSQL(true) // Create tables err = engine.CreateTables(&User{}) if err != nil { fmt.Println(err) } // Insert multiple object user := &User{Id: 1, Name: "Kururu", Age: 5} _, err = engine.Insert(user) if err != nil { fmt.Println(err) } // Insert multiple objects by Slice on one table users := make([]User, 2) users[0].Id = 2 users[0].Name = "Hayabusa" users[0].Age = 300 users[1].Id = 3 users[1].Name = "Nyoro" users[1].Age = 4 affected, err := engine.Insert(&users) if err != nil { fmt.Println(err) } fmt.Println(affected) // Insert multiple records by Slice of pointer on one table m_users := make([]*User, 2) m_users[0] = new(User) m_users[0].Id = 4 m_users[0].Name = "Pocha" m_users[0].Age = 5 m_users[1] = new(User) m_users[1].Id = 5 m_users[1].Name = "Mohumi" m_users[1].Age = 1 m_affected, err := engine.Insert(&m_users) if err != nil { fmt.Println(err) } fmt.Println(m_affected) c_users := make([]User, 0) err = engine.Find(&c_users) if err != nil { fmt.Println(err) } fmt.Println("================") fmt.Println(c_users) } |
このコードで作成したテーブルを適当なツールで可視化したものが下図です。
色んな書き方で Insert(レコード作成) しているように見えますが、やっていることはGoで定義したUserという構造体にデータを格納し、それをテーブルに登録しているだけです。
engine.ShowSQL(true)としておけば、下記などの生SQLを確認できます。
INSERT INTO user
(id
,name
,age
,created
,updated
) VALUES (?, ?, ?, ?, ?) []interface {}{1, “Kururu”, 5, “2021-09-11 02:13:50”, “2021-09-11 02:13:50”}
Read(読み出し)
続いて read.go で任意のレコードを検索して読み出します。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
package main import ( "fmt" // "os" "time" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) // Column definition type User struct { Id int64 Name string Age int64 Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } func main() { f := "./db/user.db" // os.Remove(f) // Create engine engine, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) } defer engine.Close() // engine.ShowSQL(true) // Hyababusaという名前で登録されているレコードを読み出し user := make([]User, 0) err = engine.Where("name = ?", "Hayabusa").Find(&user) if err != nil { fmt.Println(err) } fmt.Println("======== Hayabusa info =========") fmt.Println(user) // テーブルから上3行のレコードを読み出し users := make([]User, 0) err = engine.Limit(3).Find(&users) if err != nil { fmt.Println(err) } fmt.Println("========= users info ==========") fmt.Println(users) // 構造体にマッピングしたデータに対する処理 fmt.Println("======= user id=1 info ========") // {1 Kururu 5 2021-09-11 02:13:50 +0000 UTC 2021-09-11 02:13:50 +0000 UTC} fmt.Println(users[0]) // Kururu fmt.Println("I love", users[0].Name) // 生sqlによる検索もできる name := "Kururu" sql := "SELECT id, name, age FROM user where name = ?" results, err := engine.QueryString(sql, name) if err != nil { fmt.Println(err) } fmt.Println("========= Kururu info ==========") fmt.Println(results) } |
======== Hayabusa info =========
[{2 Hayabusa 300 2021-09-11 02:13:50 +0000 UTC 2021-09-11 02:13:50 +0000 UTC}]
========= users info ==========
[{1 Kururu 5 2021-09-11 02:13:50 +0000 UTC 2021-09-11 02:13:50 +0000 UTC} {2 Hayabusa 300 2021-09-11 02:13:50 +0000 UTC 2021-09-11 02:13:50 +0000 UTC} {3 Nyoro 4 2021-09-11 02:13:50 +0000 UTC 2021-09-11 02:13:50 +0000 UTC}]
======= user id=1 info ========
{1 Kururu 5 2021-09-11 02:13:50 +0000 UTC 2021-09-11 02:13:50 +0000 UTC}
I love Kururu
========= Kururu info ==========
[map[age:5 id:1 name:Kururu]]
分かり易いクエリビルド、かつ検索結果が構造体にマッピングされるのでGoでの扱いが楽です。
生SQLも使えるので、過去に書いたSQLをそのまま流用できます。
Update(更新)
今度は update.go で任意のレコードを更新します。
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package main import ( "fmt" // "os" "time" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) // Column definition type User struct { Id int64 Name string Age int64 Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } func main() { f := "./db/user.db" // os.Remove(f) // Create engine engine, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) } defer engine.Close() engine.ShowSQL(true) // Get id=2 info var user User engine.ID(2).Get(&user) fmt.Println("======== Id=2 info =========") fmt.Println(user) fmt.Println(user.Age) // Update user.Age = 30 engine.ID(user.Id).Update(&user) fmt.Println("======== Update Id=2 info =========") fmt.Println(user) fmt.Println(user.Age) } |
更新前
更新後
「構造体のデータ編集=レコード更新」という手軽さです。
engine.ID(2).Get(&user)とすれば任意Idのレコードを読み出せます。
Delete(削除)
最後に delete.go でId=3のレコードを削除します。
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 33 34 35 36 37 38 39 40 41 42 |
package main import ( "fmt" // "os" "time" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) // Column definition type User struct { Id int64 Name string Age int64 Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } func main() { f := "./db/user.db" // os.Remove(f) // Create engine engine, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) } defer engine.Close() engine.ShowSQL(true) // Get id=2 info var user User engine.ID(3).Get(&user) fmt.Println("======== Id=3 info =========") fmt.Println(user) fmt.Println(user.Name) // Delete engine.ID(user.Id).Delete(&user) } |
削除前
削除後
更新と同様の手軽さです。
その他のデータベース操作
その他の操作については、公式サイトをご参照ください。
まとめ
本記事で以下のことを紹介しました。
- ORMについて
- ORM for Goの1つ XORM の基本的な使い方(CRUD)
ORM入門したい人やGo言語(Glang)でデータベース操作したい人の参考になれば嬉しいです。
以下 Go言語の本紹介。最初の一冊は「みんなのGo言語」がオススメです。