はじめに
タイトルの通り、Goでデータベースを使用する際にDSN(Data Source Name)のオプションを適切に設定していなかったため詰まったためメモとして残します。
sql-driverはmysqlを使用しています。
結論
Date型を扱う時はparseTimeをTrueにしよう(誰でも知ってるかも笑)
理由
Todoテーブルからsqlxのselectメソッドでデータを取得した時のこと
err := db.Select(&todos, "SELECT id,task,due_date, status,created_at,updated_at FROM todos")
sqlxのselectメソッドは構造体のフィールド名、もしくはタグ名と一致していれば自動的にマッピングしてくれます。
type Todo struct {
Id int `json:"id" db:"id"`
Task string `json:"task" db:"task"`
DueDate time.Time `json:"due_date" db:"due_date"`
Status int `json:"status" db:"status"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
とても便利だったので使用したところ以下のようなエラーが発生
Failed to get todos sql: Scan error on column index 2, name "due_date": unsupported Scan, storing driver.Value type []uint8 into type *time.Time
[]uint8を型*time.Timeに入れることはできないよと怒られています。
なぜByte型?
とりあえずtime.Time型にしていたのをuint8型にしてみる
[
{
"id": 1,
"task": "テスト",
"due_date": "MjAyMy0wOS0wOCAwMDowMDowMA==",
"status": 0,
"created_at": "MjAyNC0wNS0wNiAxMjowNTo0Mg==",
"updated_at": "MjAyNC0wNS0wNiAxMjowNTo0Mg=="
},
{
"id": 2,
"task": "テスト",
"due_date": "MjAyNC0wOS0wOCAwMDowMDowMA==",
"status": 0,
"created_at": "MjAyNC0wNS0wNiAxMjowNjowMA==",
"updated_at": "MjAyNC0wNS0wNiAxMjowNjowMA=="
}
]
正常に処理が行われByte型のデータが返却されてきました
取得時にByte型からTime型に変換して格納しないといけないのか?と思い調べていると以下の情報を発見
parseTime
以下公式ドキュメントより抜粋
parseTime
Type: bool Valid Values: true, false Default: false
parseTime=true changes the output type of DATE and DATETIME values to > > > time.Time instead of []byte / string The date or datetime like 0000-00-00 > 00:00:00 is converted into zero value of time.Time.
なんとデフォルトではDATE型は[]Byte型で返却されるようになっていました。
parseTime=trueを設定すると、DATE型とDATETIME型の値の出力型が
[]byte/string型 ⇨ time.Time型に変換してくれるようになるようです。
ということで以下のように変更
変更前
- dbConf := fmt.Sprintf("%s:%s@tcp(%s)/%s", MySqlConf.User, MySqlConf.Pass, MySqlConf.Host, MySqlConf.Name)
- db, err := sqlx.Connect("mysql", dbConf)
変更後
+ dbConf := fmt.Sprintf("%s:%s@tcp(%s)/%s?parseTime=true", MySqlConf.User, MySqlConf.Pass, MySqlConf.Host, MySqlConf.Name)
+ db, err := sqlx.Connect("mysql", dbConf)
mysqlのsqlドライバーでは?の後にオプションを設定します。
sql-driverとは?
Go言語では、SQLデータベースと通信するためのdatabase/sql パッケージがある。ただこのパッケージ自体はデータベースと通信する機能は持っていない。
SQLデータベースとの実際の通信は、専用のドライバーが行う。
SQLドライバー = 特定のデータベースシステム(MySQL、PostgreSQL、SQLiteなど)と通信するためのプログラム
import(
"github.com/jmoiron/sqlx" //⇦sqlパッケージを拡張したsqlx
_ "github.com/go-sql-driver/mysql" //⇦ドライバー
)
こんな感じにドライバーもインストールして読み込まないと使えない。
設定変更後
[
{
"id": 1,
"task": "テスト",
"due_date": "2023-09-08T00:00:00Z",
"status": 0,
"created_at": "2024-05-06T12:05:42Z",
"updated_at": "2024-05-06T12:05:42Z"
},
{
"id": 2,
"task": "テスト",
"due_date": "2024-09-08T00:00:00Z",
"status": 0,
"created_at": "2024-05-06T12:06:00Z",
"updated_at": "2024-05-06T12:06:00Z"
}
]
正常に返却されるようになりました。
まとめ
最近、Go言語を勉強を始めたのですが、初歩的なところで躓き時間を浪費してしまいました。
同じところで躓く方はいないかも知れませんがメモとして残します笑