Gorm: Tinjau Menyelinap Jenis Data Tersuai
Sep 13, 2024 pm 08:15 PMSelamat kembali, kawan-kawan?! Hari ini, kita membincangkan kes penggunaan khusus yang mungkin kita hadapi apabila memindahkan data ke sana ke mari dari/ke pangkalan data. Pertama, izinkan saya menetapkan sempadan untuk cabaran hari ini. Untuk berpegang kepada contoh kehidupan sebenar, mari kita meminjam beberapa konsep daripada Tentera A.S.?. Perjanjian kami adalah untuk menulis perisian kecil untuk menyimpan dan membaca pegawai dengan gred yang telah mereka capai dalam kerjaya mereka.
Jenis Data Tersuai Gorm
Perisian kami perlu mengendalikan pegawai tentera dengan gred masing-masing. Pada pandangan pertama, ia mungkin kelihatan mudah, dan kami mungkin tidak memerlukan sebarang Jenis Data Tersuai di sini. Walau bagaimanapun, untuk menunjukkan ciri ini, mari gunakan cara bukan konvensional untuk mewakili data. Terima kasih kepada ini, kami diminta untuk menentukan pemetaan tersuai antara struct Go dan hubungan DB. Tambahan pula, kita mesti menentukan logik khusus untuk menghuraikan data. Mari kita kembangkan perkara ini dengan melihat sasaran program ?.
Gunakan Sarung untuk Mengendalikan
Untuk meredakan keadaan, mari kita gunakan lukisan untuk menggambarkan hubungan antara kod dan objek SQL:
Jom fokus pada setiap bekas satu persatu.
The Go Structs ?
Di sini, kami menentukan dua struct. Struktur Gred mempunyai senarai gred tentera yang tidak lengkap ?️. Struktur ini tidak akan menjadi jadual dalam pangkalan data. Sebaliknya, struct Pegawai mengandungi ID, nama dan penunjuk kepada struct Gred, yang menunjukkan gred yang telah dicapai oleh pegawai setakat ini.
Setiap kali kami menulis pegawai kepada DB, lajur gred_capaian mesti mengandungi tatasusunan rentetan yang diisi dengan gred yang dicapai (yang benar dalam struktur Gred).
Hubungan DB?
Mengenai objek SQL, kami hanya mempunyai jadual pegawai. Lajur id dan nama adalah jelas. Kemudian, kami mempunyai lajur gred_capaian yang menyimpan gred pegawai dalam koleksi rentetan.
Setiap kali kami menyahkod pegawai daripada pangkalan data, kami menghuraikan lajur grades_achieved dan mencipta "contoh" padanan struktur Gred.
Anda mungkin perasan bahawa tingkah laku itu bukan yang standard. Kita mesti membuat beberapa pengaturan untuk memenuhinya dengan cara yang diingini.
Di sini, reka letak model sengaja dibuat terlalu rumit. Sila berpegang kepada penyelesaian yang lebih mudah apabila boleh.
Jenis Data Tersuai
Gorm memberikan kami Jenis Data Tersuai. Mereka memberi kami fleksibiliti yang hebat dalam menentukan perolehan semula dan menyimpan ke/dari pangkalan data. Kita mesti melaksanakan dua antara muka: Pengimbas dan Penilai ?. Yang pertama menentukan tingkah laku tersuai untuk digunakan semasa mengambil data daripada DB. Yang terakhir menunjukkan cara menulis nilai dalam pangkalan data. Kedua-duanya membantu kami dalam mencapai logik pemetaan bukan konvensional yang kami perlukan.
Tandatangan fungsi yang mesti kami laksanakan ialah ralat Scan(antara muka nilai{}) dan Value() (driver.Value, ralat). Sekarang, mari lihat kodnya.
Kod
Kod untuk contoh ini tinggal dalam dua fail: domain/models.go dan main.go. Mari kita mulakan dengan yang pertama, berurusan dengan model (diterjemahkan sebagai struct dalam Go).
Fail domain/models.go
Pertama, izinkan saya membentangkan kod untuk fail ini:
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 }
Sekarang, mari kita serlahkan bahagian yang berkaitan dengannya ?:
- Struktur Gred hanya menyenaraikan gred yang kami ramalkan dalam perisian kami
- Struktur Pegawai mentakrifkan ciri-ciri entiti. Entiti ini ialah hubungan dalam DB. Kami menggunakan dua notasi Gorm:
- gorm:"primaryKey" pada medan ID untuk mentakrifkannya sebagai kunci utama perhubungan kami
- gorm:"type:varchar[]" untuk memetakan medan GradesAchieved sebagai tatasusunan varchar dalam DB. Jika tidak, ia diterjemahkan sebagai jadual DB yang berasingan atau lajur tambahan dalam jadual pegawai
- Struktur Gred melaksanakan fungsi Imbasan. Di sini, kami mendapat nilai mentah, kami melaraskannya, kami menetapkan beberapa medan pada pembolehubah g, dan kami kembali
- Struktur Gred juga melaksanakan fungsi Nilai sebagai jenis penerima nilai (kami tidak perlu menukar penerima kali ini, kami tidak menggunakan rujukan *). Kami mengembalikan nilai untuk ditulis dalam gred lajur_yang dicapai dalam jadual pegawai
Terima kasih kepada dua kaedah ini, kami boleh mengawal cara menghantar dan mendapatkan semula jenis Gred semasa interaksi DB. Sekarang, mari lihat fail main.go.
Fail main.go ?
Di sini, kami menyediakan sambungan DB, memindahkan objek kepada hubungan (ORM bermaksud Object Relation Mapping), dan masukkan serta ambil rekod untuk menguji logik. Di bawah ialah kod:
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!
Atas ialah kandungan terperinci Gorm: Tinjau Menyelinap Jenis Data Tersuai. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Artikel Panas

Alat panas Tag

Artikel Panas

Tag artikel panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas

GO Language Pack Import: Apakah perbezaan antara garis bawah dan tanpa garis bawah?

Bagaimana untuk melaksanakan pemindahan maklumat jangka pendek antara halaman dalam kerangka beego?

Bagaimana saya menulis objek dan stub untuk ujian di GO?

Bagaimana saya boleh menggunakan alat pengesanan untuk memahami aliran pelaksanaan aplikasi saya?

Bagaimana cara menukar senarai hasil pertanyaan mysql ke dalam slice struktur tersuai dalam bahasa Go?

Bagaimana saya boleh menentukan kekangan jenis tersuai untuk generik di GO?

Bagaimana cara menulis fail dalam bahasa Go dengan mudah?

Bagaimanakah saya boleh menggunakan alat linter dan analisis statik untuk meningkatkan kualiti dan pemeliharaan kod pergi saya?
