1923

都内と業界の隅っこで生活しているエンジニアのノート

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

ファイルが作成されましたので、これで準備完了です。

f:id:taka1923:20210220122948p:plain

プログラム作成

試しに 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に記載のように、 Findinsertupdatedeleteなどが可能です。今回のサンプルでも、以下のように簡単に実装しています。

[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もわからない事が多くお勉強中の日々です。