Golang + SQLBoiler で Azure SQL Database を使ってみる
GoのORM、SQLBoilerを紹介して頂いたのでSQL Databaseで試してみます。 試した環境がAzure+SQL Databaseですが、特にローカルなどでSQL Serverを使った場合と同じかと思います。
Quickstart: Create an Azure SQL Database single databaseを参考にSQL Databaseとサンプルデータベースを作成して、Firewallの設定は完了している状態です。
今回作成したサンプルコードはこちらとなります。
準備
インストール
プロジェクトを作成して、modファイルの作成とsqlboilerに書かれている通りsqlboilerをインストール。
go mod init golang-azure-sqldb-sqlboiler go get github.com/volatiletech/sqlboiler/v4 go get github.com/volatiletech/null/v8
設定ファイル作成
SQL Databaseの接続情報を記載した設定ファイルを作成します。今回は出力場所を設定ファイルに記載しています。
[sqlboiler.toml]
output = "./common/models" [mssql] dbname = "sample1" host = "*****.database.windows.net" port = 1433 user = "*****" pass = "*****" sslmode = "true" schema = "SalesLT"
モデル作成
sqlboiler mssql
ファイルが作成されましたので、これで準備完了です。
プログラム作成
試しに Product テーブルの CRUD をする API を作成してみます。
DBへの接続はgo-mssqldb/The connection string can be specified in one of three formatsに記載されているフォーマットとなります。今回はドライバーのMSSQLBuildQueryStringを使用して接続文字列を作成しています。
[config.yaml]
database: dbname: "****" host: "****" port: 1433 user: "****" password: "****" sslMode: "****" schema: "****"
[cmd/main.go]
conn := driver.MSSQLBuildQueryString( c.Database.User, c.Database.Password, c.Database.Dbname, c.Database.Host, c.Database.Port, c.Database.SslMode)
[common/database/db.go]
func NewDB(conn string) (*sql.DB, func(), error) { db, err := sql.Open("mssql", conn) if err != nil { return nil, nil, err } cleanup := func() { if err := db.Close(); err != nil { log.Print(err) } } return db, cleanup, nil }
接続すれば、sqlboilerに記載のように、 Find、 insert、 update、 deleteなどが可能です。今回のサンプルでも、以下のように簡単に実装しています。
[app/product_service.go]
Insert
if err := prod.Insert(context.Background(), svc.db, boil.Blacklist(productBlacklist...));err != nil { return nil, err }
Update
if _, err := prod.Update(context.Background(), svc.db, boil.Blacklist(productBlacklist...)); err != nil { return nil, err }
Delete
if _, err := prod.Delete(context.Background(), svc.db); err != nil { return err }
Find
prod, err := models.FindProduct(context.Background(), svc.db, id) if err != nil { return nil, err }
JOINもQuery Mod を指定して簡単にできます。例えば以下のようなSQLを発行する場合
SELECT Product.* FROM [SalesLT].[Product] INNER JOIN SalesLT.ProductCategory AS c on Product.ProductCategoryID = c.ProductCategoryID WHERE (c.Name = @p1);
以下のようなコードになります。
prods, err := models.Products( qm.Select("Product.*"), qm.InnerJoin("SalesLT.ProductCategory AS c on Product.ProductCategoryID = c.ProductCategoryID"), qm.Where("c.Name = ?", "Road Bikes"), ).All(context.Background(), svc.db)
また、生クエリを書くこともできますので同じ処理は以下のようにも書けます。
var prods []models.Product if err := queries.Raw(` SELECT Product.* FROM [SalesLT].[Product] INNER JOIN SalesLT.ProductCategory AS c on Product.ProductCategoryID = c.ProductCategoryID WHERE (c.Name = ?);`, categoryName, ).Bind(context.Background(), svc.db, &prods); err != nil { return nil, err }
そのほか、Eager Loading は Load を使用します。例えば先ほどの例にLoadを追加して以下とした場合、
prods, err := models.Products( qm.Load(models.ProductRels.ProductIDSalesOrderDetails), qm.Select("Product.*"), qm.InnerJoin("SalesLT.ProductCategory AS c on Product.ProductCategoryID = c.ProductCategoryID"), qm.Where("c.Name = ?", categoryName), ).All(context.Background(), svc.db)
以下のようなクエリで関連するSalesOrderDetailを一括で取得します。
SELECT * FROM [SalesLT].[SalesOrderDetail] WHERE ([SalesLT].[SalesOrderDetail].[ProductID] IN (@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p10,@p11,@p12,@p13,@p14,@p15,@p16,@p17,@p18,@p19,@p20,@p21,@p22,@p23,@p24,@p25,@p26,@p27,@p28,@p29,@p30,@p31,@p32,@p33,@p34,@p35,@p36,@p37,@p38,@p39,@p40,@p41,@p42,@p43));
Goでデータベースファーストな開発をする場合、SQLBoilerは便利で使いやすそうです。Go + SQL Database でもうちょっと試していきたいと思います。 まだまだGoもAzureもわからない事が多くお勉強中の日々です。