「Tech Do Advent Calendar 2018」25日目、最終日です。
今回はgolangのORMライブラリであるgormの使い方を簡単にご紹介します。
この記事はGORM公式ページを参考にしています。
ORM(オブジェクト関係マッピング)とは?
オブジェクト関係マッピング(英: Object-relational mapping、O/RM、ORM)とは、データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である。オブジェクト関連マッピングとも呼ぶ。実際には、オブジェクト指向言語から使える「仮想」オブジェクトデータベースを構築する手法である。オブジェクト関係マッピングを行うソフトウェアパッケージは商用のものもフリーなものもあるが、場合によっては独自に開発することもある。
オブジェクト言語からデータベースへデータを渡すための橋渡しをするラッパー的なもの、らしいです。
golang ORM 略してgormです。
より詳しい説明は割愛。
説明の準備
今回説明に利用するデータはこちら。
テーブル名:employee
論理名 | 物理名 | データ型 | 制約等 |
---|---|---|---|
社員番号 | emp_num | integer | 主キー、not null |
社員名 | emp_name | varchar(255) | not null |
社員名のカナ | emp_name_kana | varchar(255) | |
年齢 | age | integer | |
入社日 | hire_date | timestamp | not null |
登録日 | inserted_date | timestamp | not null |
データ
emp_num | emp_name | emp_name_kana | age | hire_date | inserted_date |
---|---|---|---|---|---|
1 | hoge | ホゲ | 22 | 2018/04/01 | 2018/12/25 |
2 | foo | フー | 30 | 2018/12/24 | 2018/12/25 |
とりあえず使ってみる
gormをインストール
go get -u github.com/jinzhu/gorm
操作対象となるデータの作成
//構造体を定義
type Employee struct {
EmpNum int `gorm:"primary_key" "column:emp_num"`
EmpName string `gorm:"column:emp_name"`
EmpNameKana sql.NullString `gorm:"column:emp_name_kana"`
Age sql.NullInt64 `gorm:"column:age"`
HireDate time.Time `gorm:"column:hire_date"`
InsertedDate time.Time `gorm:"column:inserted_date"`
}
構造体の名称をテーブル名と同じ「employee」に。
各フィールド変数を、「{変数名} {型名} {gormの定義}」の形式で定義していきます。
gormの定義として、プライマリーキー、NOT NULL制約、型、カラム名等のタグを設定することが可能です。
注意点として、変数にNULL値が格納される場合、それに適応した変数型で定義する必要があります。
例:varchar型がNULL → sql.NullString型
timestamp型がNULL → pq.NullTime型
//sql.NullStringの構造
type NullString struct {
String string
Valid bool
}
データベースのコネクション取得
db, err := gorm.Open("postgres",
"host=localhost port=5432 user=hoge password=hogehoge dbname=hoge_db sslmode=disable")
if err != nil {
fmt.Println("error")
}
//処理完了後クローズ
defer db.Close()
これで準備は完了。
Selectしてみる
//データを格納する変数を定義
employee := []Employee{}
//全取得
db.Find(&employee)
//表示
for _, emp := range employee {
fmt.Println(emp.EmpNum)
fmt.Println(emp.EmpName)
fmt.Println(emp.EmpNameKana.String)
fmt.Println(emp.Age.Int64)
fmt.Println(emp.HireDate.Time)
fmt.Println(emp.InsertedDate)
}
結果
1
hoge
ホゲ
22
2018-04-01 00:00:00 +0000 +0000
2018-12-25 00:00:00 +0000 +0000
実行例
実行例を少しだけ紹介
Select
最初の一行を取得
db.First(&employee)
全取得
db.Find(&employee)
条件に合致するデータを取得
db.Where("empName = ?", "hoge").Find(&employee)
条件に合致するデータを取得2
db.Where("age between ? and ?" "20", "30").Find(&employee)
いずれかの条件に合致するデータを取得
b.Where("empName = ?", "hoge").Or("empName = ?", "foo").Find(&employee)
取得カラムを指定して取得
db.Select("empName, hire_date").Find(&employee)
order by
db.Order("age desc").Find(&employee)
offset&limit
db.Offset(1).Limit(3).Find(&employee)
件数をカウント
var count int
db.Where("age > 20").Count(&count)
group&having
db.Select("empName, age").Group("age").Having("age > 25").Find(&employee)
Join
db.Table("employee emp")Join("inner join publisher pub on emp.publisher_id = pub.publisher_id")
Insert
データを挿入
//挿入するデータを定義
employee := Employee{EmpNum: 3,
EmpName: "bar",
EmpNameKana: sql.NullString{"バー", true},
Age: sql.NullInt64{int64(33), true},
HireDate: time.Now(),
InsertedDate: time.Now()
}
db.Create(&employee)
Update
データを更新
//更新するデータを定義
employee := Employee{EmpNum: 3,
EmpName: "bar",
EmpNameKana: sql.NullString{"バー", true},
Age: sql.NullInt64{int64(33), true},
HireDate: time.Now(),
InsertedDate: time.Now()
}
db.Save(&employee)
Delete
データを削除
db.Delete(&employee)
その他用法は公式ページで確認してください。
おまけ:開発中の失敗例と解決方法
実際に開発をしている中で想定通り動作しなかった例とその解決方法を挙げます。
取得対象のテーブル名が複数形になってしまう
取得したいテーブルは「employee」だが、実際に実行されるSQL分では「employees」になってしまっていたケース。
type employee struct{
...
}
//理想
select * from employee;
//実際
select * from employees;
原因は、gormのデフォルト設定で扱うテーブルは全て複数形で扱うようになっているため。
解決方法1:単数形で扱うように設定する
下記の記述を追加することで単数形のままSQLが実行されます。
db.SingularTable(true)
解決方法2:データベースの定義を複数形にする
gormによって複数形で扱われてしまうならデータベース側の定義を変えてしまおうという話です。
employee →employees
所感
gormを使わず素のSQL文を長々と書くよりも直感的に理解しやすい。
時間短縮にもなって良い。
gormが使える状況なら悩むことなく使うことをおすすめします。