おかえりなさい、皆さん?!今日は、データベースとの間でデータをやり取りするときに直面する可能性のある具体的なユースケースについて説明します。まず、今日の課題の境界線を設定しましょう。現実の例に固執するために、アメリカ陸軍からいくつかの概念を借用しましょう。私たちの契約は、役員のキャリアで達成した成績を保存して読み取るための小さなソフトウェアを作成することです。
私たちのソフトウェアは、それぞれの等級の陸軍将校を処理する必要があります。一見すると簡単そうに見えますが、おそらくここではカスタム データ タイプは必要ありません。ただし、この機能を示すために、従来とは異なる方法でデータを表現してみましょう。このおかげで、Go 構造体と DB リレーション間のカスタム マッピングを定義するように求められます。さらに、データを解析するための特定のロジックを定義する必要があります。プログラムのターゲットを見て、これをさらに詳しく見てみましょう ?.
作業を簡単にするために、図を使用してコードと SQL オブジェクトの関係を示しましょう:
各コンテナに 1 つずつ注目してみましょう。
ここでは 2 つの構造体を定義しました。 Grade 構造体は、軍事等級の非網羅的なリストを保持します ?️。この構造体はデータベース内のテーブルにはなりません。逆に、Officer 構造体には ID、名前、Grade 構造体へのポインタが含まれており、これまでにその役員が達成したグレードを示します。
DB に役員を書き込むときは常に、列 Grade_achieved には、達成された成績 (Grade 構造体で true を持つもの) が入力された文字列の配列が含まれている必要があります。
データベースから役員をデコードするたびに、grades_achieved 列を解析し、Grade 構造体の一致する「インスタンス」を作成します。
この動作が標準的な動作ではないことに気づいたかもしれません。望ましい方法でそれを実現するには、何らかの手配をしなければなりません。
ここでは、モデルのレイアウトを意図的に複雑にしすぎています。可能な限り、より単純な解決策を使用してください。カスタムデータ型
実装する必要がある関数のシグネチャは、Scan(valueinterface{}) error と Value() (driver.Value, error) です。それでは、コードを見てみましょう。
ドメイン/models.go ファイル
package models import ( "database/sql/driver" "slices" "strings" ) type Grade struct { Lieutenant bool Captain bool Colonel bool General bool } type Officer struct { ID uint64 `gorm:"primaryKey"` Name string GradesAchieved *Grade `gorm:"type:varchar[]"` } func (g *Grade) Scan(value interface{}) error { // we should have utilized the "comma, ok" idiom valueRaw := value.(string) valueRaw = strings.Replace(strings.Replace(valueRaw, "{", "", -1), "}", "", -1) grades := strings.Split(valueRaw, ",") if slices.Contains(grades, "lieutenant") { g.Lieutenant = true } if slices.Contains(grades, "captain") { g.Captain = true } if slices.Contains(grades, "colonel") { g.Colonel = true } if slices.Contains(grades, "general") { g.General = true } return nil } func (g Grade) Value() (driver.Value, error) { grades := make([]string, 0, 4) if g.Lieutenant { grades = append(grades, "lieutenant") } if g.Captain { grades = append(grades, "captain") } if g.Colonel { grades = append(grades, "colonel") } if g.General { grades = append(grades, "general") } return grades, nil }それでは、関連する部分を強調表示しましょう ?:
main.go ファイル?
Object Relation Mapping の略) に移行し、挿入およびフェッチします。ロジックをテストするためのレコード。以下はコードです:
package models import ( "database/sql/driver" "slices" "strings" ) type Grade struct { Lieutenant bool Captain bool Colonel bool General bool } type Officer struct { ID uint64 `gorm:"primaryKey"` Name string GradesAchieved *Grade `gorm:"type:varchar[]"` } func (g *Grade) Scan(value interface{}) error { // we should have utilized the "comma, ok" idiom valueRaw := value.(string) valueRaw = strings.Replace(strings.Replace(valueRaw, "{", "", -1), "}", "", -1) grades := strings.Split(valueRaw, ",") if slices.Contains(grades, "lieutenant") { g.Lieutenant = true } if slices.Contains(grades, "captain") { g.Captain = true } if slices.Contains(grades, "colonel") { g.Colonel = true } if slices.Contains(grades, "general") { g.General = true } return nil } func (g Grade) Value() (driver.Value, error) { grades := make([]string, 0, 4) if g.Lieutenant { grades = append(grades, "lieutenant") } if g.Captain { grades = append(grades, "captain") } if g.Colonel { grades = append(grades, "colonel") } if g.General { grades = append(grades, "general") } return grades, nil }次に、このファイルの関連セクションを見てみましょう。まず、DBにダミーデータを追加するseedDB関数を定義します。データは、次の内容の data.sql ファイル内に存在します:
package models import ( "database/sql/driver" "slices" "strings" ) type Grade struct { Lieutenant bool Captain bool Colonel bool General bool } type Officer struct { ID uint64 `gorm:"primaryKey"` Name string GradesAchieved *Grade `gorm:"type:varchar[]"` } func (g *Grade) Scan(value interface{}) error { // we should have utilized the "comma, ok" idiom valueRaw := value.(string) valueRaw = strings.Replace(strings.Replace(valueRaw, "{", "", -1), "}", "", -1) grades := strings.Split(valueRaw, ",") if slices.Contains(grades, "lieutenant") { g.Lieutenant = true } if slices.Contains(grades, "captain") { g.Captain = true } if slices.Contains(grades, "colonel") { g.Colonel = true } if slices.Contains(grades, "general") { g.General = true } return nil } func (g Grade) Value() (driver.Value, error) { grades := make([]string, 0, 4) if g.Lieutenant { grades = append(grades, "lieutenant") } if g.Captain { grades = append(grades, "captain") } if g.Colonel { grades = append(grades, "colonel") } if g.General { grades = append(grades, "general") } return grades, nil }main() 関数は、DB 接続を設定することから始まります。このデモでは、PostgreSQL を使用しました。次に、officer テーブルがデータベースに存在し、models.Officer 構造体の最新バージョンで最新であることを確認します。このプログラムはサンプルなので、さらに 2 つのことを行いました:
真実の瞬間
package models import ( "database/sql/driver" "slices" "strings" ) type Grade struct { Lieutenant bool Captain bool Colonel bool General bool } type Officer struct { ID uint64 `gorm:"primaryKey"` Name string GradesAchieved *Grade `gorm:"type:varchar[]"` } func (g *Grade) Scan(value interface{}) error { // we should have utilized the "comma, ok" idiom valueRaw := value.(string) valueRaw = strings.Replace(strings.Replace(valueRaw, "{", "", -1), "}", "", -1) grades := strings.Split(valueRaw, ",") if slices.Contains(grades, "lieutenant") { g.Lieutenant = true } if slices.Contains(grades, "captain") { g.Captain = true } if slices.Contains(grades, "colonel") { g.Colonel = true } if slices.Contains(grades, "general") { g.General = true } return nil } func (g Grade) Value() (driver.Value, error) { grades := make([]string, 0, 4) if g.Lieutenant { grades = append(grades, "lieutenant") } if g.Captain { grades = append(grades, "captain") } if g.Colonel { grades = append(grades, "colonel") } if g.General { grades = append(grades, "general") } return grades, nil }これで、 go run コマンドを発行することでアプリケーションを安全に実行できるようになりました。 ?
出力は次のとおりです:
package models import ( "database/sql/driver" "slices" "strings" ) type Grade struct { Lieutenant bool Captain bool Colonel bool General bool } type Officer struct { ID uint64 `gorm:"primaryKey"` Name string GradesAchieved *Grade `gorm:"type:varchar[]"` } func (g *Grade) Scan(value interface{}) error { // we should have utilized the "comma, ok" idiom valueRaw := value.(string) valueRaw = strings.Replace(strings.Replace(valueRaw, "{", "", -1), "}", "", -1) grades := strings.Split(valueRaw, ",") if slices.Contains(grades, "lieutenant") { g.Lieutenant = true } if slices.Contains(grades, "captain") { g.Captain = true } if slices.Contains(grades, "colonel") { g.Colonel = true } if slices.Contains(grades, "general") { g.General = true } return nil } func (g Grade) Value() (driver.Value, error) { grades := make([]string, 0, 4) if g.Lieutenant { grades = append(grades, "lieutenant") } if g.Captain { grades = append(grades, "captain") } if g.Colonel { grades = append(grades, "colonel") } if g.General { grades = append(grades, "general") } return grades, nil }ほら!すべてが期待どおりに機能します。コードを数回再実行すると、常に同じ出力が得られます。
それはラップです
Gorm と カスタム データ タイプ に関するこのブログ投稿を楽しんでいただけたでしょうか。私は常に、最も単純なアプローチに固執することをお勧めします。最終的に必要になった場合にのみ、これを選択してください。このアプローチでは、コードがより複雑になり堅牢性が低下する代わりに柔軟性が追加されます (構造体の定義のわずかな変更によりエラーが発生し、追加の作業が必要になる可能性があります)。
これを覚えておいてください。規則に従えば、コードベース全体の冗長性を減らすことができます。これはこのブログ投稿を締めくくるのに最適な引用です。
カスタム データ型が必要であることがわかった場合は、このブログ投稿が有効なソリューションを示す良い出発点となるはずです。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3