PostgreSQLへの接続

PostgreSQLへの接続を標準の[database/sql]を利用した場合と
ORMのGORMを利用した場合の比較しました。

#標準
まずは、標準[database/sql]を利用した場合

```go:main.go
package main

import (
	"database/sql"
	"fmt"
	"log"
	_ "github.com/lib/pq"
)

type Sale struct {
	Id   string
	OrderId string
}

func main() {
	db, err := sql.Open("postgres", "postgres://user:pass@host:port/dbname")
	if err != nil {
		log.Fatalln("接続失敗", err)
	}
	defer db.Close()

	// 取得件数を条件[$1]にする
	// ?だとエラーが発生
	//cmd := "select id, order_id from final_sales where id like $1"
	cmd := "select id, order_id from final_sales where id like $1"
	//取得するデータが1件の場合は、QueryRowも利用できる
	rows, _ := db.Query(cmd, "T00%")
	defer rows.Close()

	var sales []Sale // 取得するデータの構造体を用意（複数取得するのでスライス）
	for rows.Next() {
		var tmp Sale // 取得の格納用
		err := rows.Scan(&tmp.Id, &tmp.OrderId)
		if err != nil {
			log.Fatalln("取得失敗", err)
		}
		sales = append(sales, tmp)
	}
	for _, sale := range sales {
		fmt.Println(sale.Id, sale.OrderId)
	}
}
```


#ORM

次にORM[gorm]を利用した場合

```go:main.go
package main

import (
	"fmt"
	"log"

	"github.com/jinzhu/gorm"

	_ "github.com/lib/pq"
)

// `デフォルトのテーブル名は`sales`と自動判断
type Sale struct {
	Id   string `gorm:"column:sale_id"`
	OrderId string `gorm:"column:order_id"`
}

// 今回は、デフォルトではないので指定する
func (Sale) TableName() string {
	return "final_sales"
}

func main() {
	db, err := gorm.Open("postgres", "postgres://user:pass@host:port/dbname")
	if err != nil {
		log.Fatalln("接続失敗", err)
	}
	defer db.Close()

	var sales []Sale // 取得するデータの構造体を用意（複数取得するのでスライス）
	err = db.Where("sale_id like ?", "T00%").Find(&sales).Error

	if err != nil {
		log.Fatalln("取得失敗", err)
	}

	fmt.Println(sales)
}
```

#相違点

##import
`database/sql`の代わりに`github.com/jinzhu/gorm`をimportします。

##接続方法
接続を `sql.Open` から `gorm.Open` に変更します。

##構造体の宣言
構造体の宣言に``gorm:"column:sale_id"``を付与します。
これは、gormが自動でデータ取得する際の判断に用いられます。

なお、`OrderId`はスネークケースとキャメルケースの変換が自動で行われます。
そのため、以下のようにカラム名の指定が無くても問題ありません。

```go
type Sale struct {
	Id   string `gorm:"column:sale_id"`
	OrderId string
}
```

以下、サイトを参照
[カラム名の指定方法](http://gorm.io/ja_JP/docs/conventions.html#%E3%82%B9%E3%83%8D%E3%83%BC%E3%82%AF%E3%82%B1%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%AB%E3%83%A9%E3%83%A0%E5%90%8D)

##テーブル名の指定
gormでは宣言した構造体の複数形がテーブル名として自動判断されます。
今回は自動判断の名称と違うため、テーブル名を宣言しています。

```go
func (Sale) TableName() string {
    return "final_sales"
}
```

以下を参照
[複数形のテーブル名](http://gorm.io/ja_JP/docs/conventions.html#%E8%A4%87%E6%95%B0%E5%BD%A2%E3%81%AE%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E5%90%8D)

##データ取得方法

ORMなので、SQLを記述する必要がなくなります。
もちろん、SQLを記述することもできます。

標準だとfor文でデータを1件ずつ取得して構造体のスライスに格納していましたが、ORMを利用すると1発で取得できます。

```go
err = db.Where("sale_id like ?", "T00%").Find(&sales).Error
```

以下を参照
[WHERE](http://gorm.io/ja_JP/docs/query.html#Where)
[エラーハンドリング](http://gorm.io/ja_JP/docs/error_handling.html#%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%8F%E3%83%B3%E3%83%89%E3%83%AA%E3%83%B3%E3%82%B0)

#まとめ
ローカルのSQLiteとサーバーにあるSQLSeverでSQLを別々に書いているプロジェクトを経験したことがあります。
ORMを知っていれば、このような煩わしい行為をしなくてすみそうです。
現在は、PostgreSQLへの接続情報を環境変数に用意して、環境変数を取得できない場合、ローカルの開発環境と判断し、SQLiteへ接続するようにしています。
生のSQLを記述していないため、今のところデータベースが変わってもエラーは発生していません。

ただ、通常のプロジェクトであれば、サーバーとローカルに同じデータベースを用意した方が、データベースの差異による挙動の違いも防げるので良いかと思います。
また、ORM特有の記述も覚える必要があるし。


#参照
[カラム名の指定方法](http://gorm.io/ja_JP/docs/conventions.html#%E3%82%B9%E3%83%8D%E3%83%BC%E3%82%AF%E3%82%B1%E3%83%BC%E3%82%B9%E3%81%AE%E3%82%AB%E3%83%A9%E3%83%A0%E5%90%8D)
[複数形のテーブル名](http://gorm.io/ja_JP/docs/conventions.html#%E8%A4%87%E6%95%B0%E5%BD%A2%E3%81%AE%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E5%90%8D)
[WHERE](http://gorm.io/ja_JP/docs/query.html#Where)
[エラーハンドリング](http://gorm.io/ja_JP/docs/error_handling.html#%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%8F%E3%83%B3%E3%83%89%E3%83%AA%E3%83%B3%E3%82%B0)