皆さん、おかえりなさい?!今日は、データベースとの間でデータをやり取りするときに直面する可能性のある具体的なユースケースについて説明します。まず、今日の課題の境界線を設定しましょう。現実の例に固執するために、アメリカ陸軍からいくつかの概念を借用しましょう。私たちの契約は、警察官のキャリアで達成した成績を保存して読み取るための小さなソフトウェアを作成することです。
Gorm のカスタム データ型
私たちのソフトウェアは、それぞれの等級の陸軍将校を処理する必要があります。一見すると簡単そうに見えますが、おそらくここではカスタム データ タイプは必要ありません。ただし、この機能を示すために、従来とは異なる方法でデータを表現してみましょう。このおかげで、Go 構造体と DB リレーション間のカスタム マッピングを定義するように求められます。さらに、データを解析するための特定のロジックを定義する必要があります。プログラムのターゲットを見て、これをさらに詳しく見てみましょう?
処理するユースケース
作業を簡単にするために、図を使用してコードと SQL オブジェクトの関係を示してみましょう。
各コンテナに 1 つずつ注目してみましょう。
Go 構造体 ?
ここでは 2 つの構造体を定義しました。 Grade 構造体は、軍事等級の非網羅的なリストを保持します ?️。この構造体はデータベース内のテーブルにはなりません。逆に、Officer 構造体には ID、名前、Grade 構造体へのポインタが含まれており、これまでにその役員が達成したグレードを示します。
DB に役員を書き込むときは常に、列 Grade_achieved には、達成した成績 (Grade 構造体で true になっているもの) が入力された文字列の配列が含まれている必要があります。
DB関係?
SQL オブジェクトに関しては、office テーブルのみがあります。 id 列と name 列は一目瞭然です。次に、役員の成績を文字列のコレクションに保持する Grade_achieved 列があります。
データベースから役員をデコードするたびに、grades_achieved 列を解析し、Grade 構造体の一致する「インスタンス」を作成します。
この動作が標準的な動作ではないことに気づいたかもしれません。それを望ましい形で実現するためには、何らかの手配をしなければなりません。
ここでは、モデルのレイアウトが意図的に複雑になりすぎています。可能な限り、より単純な解決策を使用してください。
カスタムデータ型
Gorm はカスタム データ タイプを提供します。これらにより、データベースへの取得とデータベースからの保存を定義する際に、非常に柔軟な対応が可能になります。 Scanner と Valuer という 2 つのインターフェイスを実装する必要があります。前者は、DB からデータをフェッチするときに適用するカスタム動作を指定します。後者は、データベースに値を書き込む方法を示します。どちらも、必要な非従来型のマッピング ロジックを実現するのに役立ちます。
実装する必要がある関数のシグネチャは、Scan(valueinterface{}) error と Value() (driver.Value, error) です。それでは、コードを見てみましょう。
コード
この例のコードは、domain/models.go と main.go の 2 つのファイルに存在します。最初のモデル (Go では構造体として変換される) を扱うことから始めましょう。
ドメイン/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 }
それでは、関連する部分を強調表示してみましょう?:
- Grade 構造体には、ソフトウェアで予測された評点のみがリストされます
- Officer 構造体はエンティティの特性を定義します。このエンティティは DB 内のリレーションです。 2 つの Gorm 記法を適用しました。
- ID フィールドに gorm:"primaryKey" を追加して、リレーションの主キーとして定義します
- gorm:"type:varchar[]" を使用して、フィールド GradesAchieved を DB 内の varchar の配列としてマップします。それ以外の場合は、役員テーブルの別の DB テーブルまたは追加の列として変換されます
- Grade 構造体は Scan 関数を実装します。ここでは、生の値を取得して調整し、g 変数にいくつかのフィールドを設定して、 を返します。
- Grade 構造体は、値レシーバー型として Value 関数も実装します (今回はレシーバーを変更する必要はありません。* 参照は使用しません)。役員テーブル の列grade_achievedに書き込む値を返します。
これら 2 つのメソッドのおかげで、DB インタラクション中に Grade 型を送信および取得する方法を制御できます。次に、main.go ファイルを見てみましょう。
main.go ファイル?
ここでは、DB 接続を準備し、オブジェクトをリレーション (ORM は Object Relation Mapping の略) に移行し、挿入およびフェッチします。ロジックをテストするためのレコード。以下はコードです:
package main import ( "encoding/json" "fmt" "os" "gormcustomdatatype/models" "gorm.io/driver/postgres" "gorm.io/gorm" ) func seedDB(db *gorm.DB, file string) error { data, err := os.ReadFile(file) if err != nil { return err } if err := db.Exec(string(data)).Error; err != nil { return err } return nil } // docker run -d -p 54322:5432 -e POSTGRES_PASSWORD=postgres postgres func main() { dsn := "host=localhost port=54322 user=postgres password=postgres dbname=postgres sslmode=disable" db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) if err != nil { fmt.Fprintf(os.Stderr, "could not connect to DB: %v", err) return } db.AutoMigrate(&models.Officer{}) defer func() { db.Migrator().DropTable(&models.Officer{}) }() if err := seedDB(db, "data.sql"); err != nil { fmt.Fprintf(os.Stderr, "failed to seed DB: %v", err) return } // print all the officers var officers []models.Officer if err := db.Find(&officers).Error; err != nil { fmt.Fprintf(os.Stderr, "could not get the officers from the DB: %v", err) return } data, _ := json.MarshalIndent(officers, "", "\t") fmt.Fprintln(os.Stdout, string(data)) // add a new officer db.Create(&models.Officer{ Name: "Monkey D. Garp", GradesAchieved: &models.Grade{ Lieutenant: true, Captain: true, Colonel: true, General: true, }, }) var garpTheHero models.Officer if err := db.First(&garpTheHero, 4).Error; err != nil { fmt.Fprintf(os.Stderr, "failed to get officer from the DB: %v", err) return } data, _ = json.MarshalIndent(&garpTheHero, "", "\t") fmt.Fprintln(os.Stdout, string(data)) }
Now, let's see the relevant sections of this file. First, we define the seedDB function to add dummy data in the DB. The data lives in the data.sql file with the following content:
INSERT INTO public.officers (id, "name", grades_achieved) VALUES(nextval('officers_id_seq'::regclass), 'john doe', '{captain,lieutenant}'), (nextval('officers_id_seq'::regclass), 'gerard butler', '{general}'), (nextval('officers_id_seq'::regclass), 'chuck norris', '{lieutenant,captain,colonel}');
The main() function starts by setting up a DB connection. For this demo, we used PostgreSQL. Then, we ensure the officers table exists in the database and is up-to-date with the newest version of the models.Officer struct. Since this program is a sample, we did two additional things:
- Removal of the table at the end of the main() function (when the program terminates, we would like to remove the table as well)
- Seeding of some dummy data
Lastly, to ensure that everything works as expected, we do a couple of things:
- Fetching all the records in the DB
- Adding (and fetching back) a new officer
That's it for this file. Now, let's test our work ?.
The Truth Moment
Before running the code, please ensure that a PostgreSQL instance is running on your machine. With Docker ?, you can run this command:
docker run -d -p 54322:5432 -e POSTGRES_PASSWORD=postgres postgres
Now, we can safely run our application by issuing the command: go run . ?
The output is:
[ { "ID": 1, "Name": "john doe", "GradesAchieved": { "Lieutenant": true, "Captain": true, "Colonel": false, "General": false } }, { "ID": 2, "Name": "gerard butler", "GradesAchieved": { "Lieutenant": false, "Captain": false, "Colonel": false, "General": true } }, { "ID": 3, "Name": "chuck norris", "GradesAchieved": { "Lieutenant": true, "Captain": true, "Colonel": true, "General": false } } ] { "ID": 4, "Name": "Monkey D. Garp", "GradesAchieved": { "Lieutenant": true, "Captain": true, "Colonel": true, "General": true } }
Voilà! Everything works as expected. We can re-run the code several times and always have the same output.
That's a Wrap
I hope you enjoyed this blog post regarding Gorm and the Custom Data Types. I always recommend you stick to the most straightforward approach. Opt for this only if you eventually need it. This approach adds flexibility in exchange for making the code more complex and less robust (a tiny change in the structs' definitions might lead to errors and extra work needed).
Keep this in mind. If you stick to conventions, you can be less verbose throughout your codebase.
That's a great quote to end this blog post.
If you realize that Custom Data Types are needed, this blog post should be a good starting point to present you with a working solution.
Please let me know your feelings and thoughts. Any feedback is always appreciated! If you're interested in a specific topic, reach out, and I'll shortlist it. Until next time, stay safe, and see you soon!
以上がGorm: カスタム データ型の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

Golangは迅速な発展と同時プログラミングに適していますが、Cは極端なパフォーマンスと基礎となる制御を必要とするプロジェクトにより適しています。 1)Golangの並行性モデルは、GoroutineとChannelを介した同時性プログラミングを簡素化します。 2)Cのテンプレートプログラミングは、一般的なコードとパフォーマンスの最適化を提供します。 3)Golangのごみ収集は便利ですが、パフォーマンスに影響を与える可能性があります。 Cのメモリ管理は複雑ですが、コントロールは問題ありません。

speed、効率、およびシンプル性をspeedsped.1)speed:gocompilesquilesquicklyandrunseffictient、理想的なlargeprojects.2)効率:等系dribribraryreducesexexternaldedenciess、開発効果を高める3)シンプルさ:

Cは、ハードウェアリソースと高性能の最適化が必要なシナリオにより適していますが、Golangは迅速な開発と高い並行性処理が必要なシナリオにより適しています。 1.Cの利点は、ハードウェア特性と高い最適化機能に近いものにあります。これは、ゲーム開発などの高性能ニーズに適しています。 2.Golangの利点は、その簡潔な構文と自然な並行性サポートにあり、これは高い並行性サービス開発に適しています。

Golangは実際のアプリケーションに優れており、そのシンプルさ、効率性、並行性で知られています。 1)同時プログラミングはゴルチンとチャネルを通じて実装されます。2)柔軟なコードは、インターフェイスと多型を使用して記述されます。3)ネット/HTTPパッケージを使用したネットワークプログラミングを簡素化、4)効率的な同時クローラーを構築する、5)ツールと最高の実践を通じてデバッグと最適化。

GOのコア機能には、ガベージコレクション、静的リンク、並行性サポートが含まれます。 1. GO言語の並行性モデルは、GoroutineとChannelを通じて効率的な同時プログラミングを実現します。 2.インターフェイスと多型は、インターフェイスメソッドを介して実装されているため、異なるタイプを統一された方法で処理できます。 3.基本的な使用法は、関数定義と呼び出しの効率を示しています。 4。高度な使用法では、スライスは動的なサイズ変更の強力な機能を提供します。 5.人種条件などの一般的なエラーは、Getest Raceを通じて検出および解決できます。 6.パフォーマンス最適化Sync.Poolを通じてオブジェクトを再利用して、ゴミ収集圧力を軽減します。

GO言語は、効率的でスケーラブルなシステムの構築においてうまく機能します。その利点には次のものがあります。1。高性能:マシンコードにコンパイルされ、速度速度が速い。 2。同時プログラミング:ゴルチンとチャネルを介してマルチタスクを簡素化します。 3。シンプルさ:簡潔な構文、学習コストとメンテナンスコストの削減。 4。クロスプラットフォーム:クロスプラットフォームのコンパイル、簡単な展開をサポートします。

SQLクエリの結果の並べ替えについて混乱しています。 SQLを学習する過程で、しばしば混乱する問題に遭遇します。最近、著者は「Mick-SQL Basics」を読んでいます...

テクノロジースタックの収束とテクノロジーの選択の関係ソフトウェア開発におけるテクノロジーの選択、テクノロジースタックの選択と管理は非常に重要な問題です。最近、一部の読者が提案しています...


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

Dreamweaver Mac版
ビジュアル Web 開発ツール

ドリームウィーバー CS6
ビジュアル Web 開発ツール
