1. Pengenalan ent
Ent adalah kerangka kerja entitas yang dikembangkan oleh Facebook khusus untuk bahasa Go. Ini menyederhanakan proses membangun dan memelihara aplikasi model data berskala besar. Kerangka kerja ent terutama mengikuti prinsip-prinsip berikut:
- Dapat dengan mudah memodelkan skema basis data sebagai struktur graf.
- Mendefinisikan skema dalam bentuk kode bahasa Go.
- Melaksanakan tipe statis berdasarkan pembangkitan kode.
- Menulis kueri basis data dan traversal graf sangat sederhana.
- Dapat dengan mudah diperluas dan disesuaikan menggunakan template Go.
2. Persiapan Lingkungan
Untuk mulai menggunakan kerangka kerja ent, pastikan bahwa bahasa Go sudah terpasang di lingkungan pengembangan Anda.
Jika direktori proyek Anda berada di luar GOPATH
, atau jika Anda tidak terbiasa dengan GOPATH
, Anda dapat menggunakan perintah berikut untuk membuat proyek modul Go baru:
go mod init entdemo
Ini akan menginisialisasi modul Go baru dan membuat file go.mod
baru untuk proyek entdemo
Anda.
3. Mendefinisikan Skema Pertama
3.1. Membuat Skema Menggunakan ent CLI
Pertama, Anda perlu menjalankan perintah berikut di direktori root proyek Anda untuk membuat skema bernama User menggunakan alat CLI ent:
go run -mod=mod entgo.io/ent/cmd/ent new User
Perintah di atas akan menghasilkan skema User di direktori entdemo/ent/schema/
:
File entdemo/ent/schema/user.go
:
package schema
import "entgo.io/ent"
// User holds the schema definition for the User entity.
type User struct {
ent.Schema
}
// Fields of the User.
func (User) Fields() []ent.Field {
return nil
}
// Edges of the User.
func (User) Edges() []ent.Edge {
return nil
}
3.2. Menambahkan Kolom
Selanjutnya, kita perlu menambahkan definisi kolom ke Skema User. Berikut adalah contoh penambahan dua kolom ke entitas User.
File yang diubah entdemo/ent/schema/user.go
:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// Fields of the User.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Positive(),
field.String("name").
Default("unknown"),
}
}
Potongan kode ini mendefinisikan dua kolom untuk model User: age
dan name
, di mana age
merupakan bilangan bulat positif dan name
merupakan string dengan nilai default "unknown".
3.3. Menghasilkan Entitas Basis Data
Setelah mendefinisikan skema, Anda perlu menjalankan perintah go generate
untuk menghasilkan logika akses basis data yang mendasarinya.
Jalankan perintah berikut di direktori root proyek Anda:
go generate ./ent
Perintah ini akan menghasilkan kode Go yang sesuai berdasarkan skema yang sebelumnya ditentukan, menghasilkan struktur file berikut:
ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── generate.go
├── mutation.go
... (beberapa file dihapus untuk singkat)
├── schema
│ └── user.go
├── tx.go
├── user
│ ├── user.go
│ └── where.go
├── user.go
├── user_create.go
├── user_delete.go
├── user_query.go
└── user_update.go
4.1. Menginisialisasi Koneksi Database
Untuk menghubungkan ke database MySQL, kita dapat menggunakan fungsi Open
yang disediakan oleh framework ent
. Pertama, impor driver MySQL dan kemudian berikan string koneksi yang benar untuk menginisialisasi koneksi database.
package main
import (
"context"
"log"
"entdemo/ent"
_ "github.com/go-sql-driver/mysql" // Impor driver MySQL
)
func main() {
// Gunakan ent.Open untuk membuat koneksi dengan basis data MySQL.
// Ingat untuk mengganti tempat kosong "your_username", "your_password", dan "your_database" di bawah.
client, err := ent.Open("mysql", "your_username:your_password@tcp(localhost:3306)/your_database?parseTime=True")
if err != nil {
log.Fatalf("gagal membuka koneksi ke mysql: %v", err)
}
defer client.Close()
// Jalankan alat migrasi otomatis
ctx := context.Background()
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("gagal membuat sumber skema: %v", err)
}
// Logika bisnis tambahan dapat ditulis di sini
}
4.2. Membuat Entitas
Membuat entitas Pengguna melibatkan membangun objek entitas baru dan menyimpannya ke database menggunakan metode Save
atau SaveX
. Kode berikut menunjukkan bagaimana membuat entitas Pengguna baru dan menginisialisasi dua bidang umur
dan nama
.
// Fungsi CreateUser digunakan untuk membuat entitas Pengguna baru
func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
// Gunakan client.User.Create() untuk membangun permintaan pembuatan Pengguna,
// kemudian rantai metode SetAge dan SetName untuk mengatur nilai bidang entitas.
u, err := client.User.
Create().
SetAge(30). // Atur usia pengguna
SetName("a8m"). // Atur nama pengguna
Save(ctx) // Panggil Save untuk menyimpan entitas ke database
if err != nil {
return nil, fmt.Errorf("gagal membuat pengguna: %w", err)
}
log.Println("pengguna telah dibuat: ", u)
return u, nil
}
Di dalam fungsi main
, Anda dapat memanggil fungsi CreateUser
untuk membuat entitas pengguna baru.
func main() {
// ...Kode pembuatan koneksi database dihilangkan
// Buat entitas pengguna
u, err := CreateUser(ctx, client)
if err != nil {
log.Fatalf("gagal membuat pengguna: %v", err)
}
log.Printf("pengguna telah dibuat: %#v\n", u)
}
4.3. Melakukan Kueri pada Entitas
Untuk melakukan kueri pada entitas, kita dapat menggunakan pembangun kueri yang dihasilkan oleh ent
. Kode berikut menunjukkan bagaimana melakukan kueri pada pengguna bernama "a8m":
// Fungsi QueryUser digunakan untuk mengkueri entitas pengguna dengan nama yang ditentukan
func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
// Gunakan client.User.Query() untuk membangun kueri untuk Pengguna,
// kemudian rantai metode Where untuk menambahkan kondisi kueri, seperti melakukan kueri berdasarkan nama pengguna
u, err := client.User.
Query().
Where(user.NameEQ("a8m")). // Tambahkan kondisi kueri, dalam hal ini, namanya adalah "a8m"
Only(ctx) // Metode Only menunjukkan bahwa hanya satu hasil yang diharapkan
if err != nil {
return nil, fmt.Errorf("gagal melakukan kueri pengguna: %w", err)
}
log.Println("pengguna yang dikembalikan: ", u)
return u, nil
}
Di dalam fungsi main
, Anda dapat memanggil fungsi QueryUser
untuk melakukan kueri pada entitas pengguna.
func main() {
// ...Kode pembuatan koneksi database dan pembuatan pengguna dihilangkan
// Lakukan kueri pada entitas pengguna
u, err := QueryUser(ctx, client)
if err != nil {
log.Fatalf("gagal melakukan kueri pengguna: %v", err)
}
log.Printf("pengguna yang dikueri: %#v\n", u)
}
5.1. Memahami Ujung dan Ujung Terbalik
Pada kerangka kerja ent
, model data divisualisasikan sebagai struktur graf, di mana entitas mewakili simpul dalam graf, dan hubungan antara entitas diwakili oleh ujung. Sebuah ujung merupakan koneksi dari satu entitas ke entitas lainnya, misalnya, seorang Pengguna
dapat memiliki beberapa Mobil
.
Ujung Terbalik adalah referensi terbalik ke ujung, secara logis mewakili hubungan terbalik antara entitas, tetapi tidak membuat hubungan baru dalam basis data. Misalnya, melalui ujung terbalik dari sebuah Mobil
, kita dapat menemukan Pengguna
yang memiliki mobil ini.
Signifikansi utama dari ujung dan ujung terbalik terletak pada membuat navigasi antara entitas terkait sangat intuitif dan mudah dimengerti.
Tips: Dalam
ent
, ujung sesuai dengan kunci asing basis data tradisional dan digunakan untuk mendefinisikan hubungan antara tabel.
5.2. Mendefinisikan Ujung dalam Skema
Pertama, kita akan menggunakan CLI ent
untuk membuat skema awal untuk Mobil
dan Grup
:
go run -mod=mod entgo.io/ent/cmd/ent new Car Group
Selanjutnya, dalam skema Pengguna
, kita mendefinisikan ujung dengan Mobil
untuk merepresentasikan hubungan antara pengguna dan mobil. Kita dapat menambahkan ujung cars
yang menunjuk ke tipe Car
dalam entitas pengguna, menunjukkan bahwa seorang pengguna dapat memiliki beberapa mobil:
// entdemo/ent/schema/user.go
// Ujung dari Pengguna.
func (Pengguna) Edges() []ent.Edge {
return []ent.Edge{
edge.To("cars", Car.Type),
}
}
Setelah mendefinisikan ujung, kita perlu menjalankan go generate ./ent
lagi untuk menghasilkan kode yang sesuai.
5.3. Operasi pada Data Ujung
Membuat mobil yang terkait dengan seorang pengguna adalah proses yang sederhana. Diberikan entitas pengguna, kita dapat membuat entitas mobil baru dan mengaitkannya dengan pengguna:
import (
"context"
"log"
"entdemo/ent"
// Pastikan untuk mengimpor definisi skema untuk Car
_ "entdemo/ent/schema"
)
func CreateCarsForUser(ctx context.Context, client *ent.Client, userID int) error {
user, err := client.User.Get(ctx, userID)
if err != nil {
log.Fatalf("gagal mengambil pengguna: %v", err)
return err
}
// Membuat mobil baru dan mengaitkannya dengan pengguna
_, err = client.Car.
Create().
SetModel("Tesla").
SetRegisteredAt(time.Now()).
SetOwner(user).
Save(ctx)
if err != nil {
log.Fatalf("gagal membuat mobil untuk pengguna: %v", err)
return err
}
log.Println("mobil telah dibuat dan dikaitkan dengan pengguna")
return nil
}
Demikian pula, melakukan kueri terhadap mobil pengguna sangatlah mudah. Jika kita ingin mengambil daftar semua mobil yang dimiliki oleh seorang pengguna, kita dapat melakukan hal berikut:
func QueryUserCars(ctx context.Context, client *ent.Client, userID int) error {
user, err := client.User.Get(ctx, userID)
if err != nil {
log.Fatalf("gagal mengambil pengguna: %v", err)
return err
}
// Kueri semua mobil yang dimiliki oleh pengguna
cars, err := user.QueryCars().All(ctx)
if err != nil {
log.Fatalf("gagal melakukan kueri mobil: %v", err)
return err
}
for _, car := range cars {
log.Printf("mobil: %v, model: %v", car.ID, car.Model)
}
return nil
}
Melalui langkah-langkah di atas, kita tidak hanya belajar bagaimana mendefinisikan ujung dalam skema tetapi juga mendemonstrasikan bagaimana membuat dan menanyakan data terkait ujung.
6. Traversal Graf dan Pengkuerian
6.1. Memahami Struktur Graf
Dalam ent
, struktur graf direpresentasikan oleh entitas dan ujung di antara mereka. Setiap entitas setara dengan simpul dalam graf, dan hubungan antara entitas direpresentasikan oleh ujung, yang bisa satu-ke-satu, satu-ke-banyak, banyak-ke-banyak, dll. Struktur graf ini membuat kueri kompleks dan operasi pada basis data relasional menjadi sederhana dan intuitif.
6.2. Melintasi Struktur Grafik
Menulis kode Traversal Grafik utamanya melibatkan pengambilan data dan mengasosiasikannya melalui sisi antara entitas-entitas. Berikut adalah contoh sederhana yang menunjukkan bagaimana melintasi struktur grafik dalam ent
:
import (
"context"
"log"
"entdemo/ent"
)
// GraphTraversal adalah contoh dari melintasi struktur grafik
func GraphTraversal(ctx context.Context, client *ent.Client) error {
// Query pengguna dengan nama "Ariel"
a8m, err := client.User.Query().Where(user.NameEQ("Ariel")).Only(ctx)
if err != nil {
log.Fatalf("Gagal melakukan query pengguna: %v", err)
return err
}
// Melintasi semua mobil milik Ariel
cars, err := a8m.QueryCars().All(ctx)
if err != nil {
log.Fatalf("Gagal melakukan query mobil: %v", err)
return err
}
for _, car := range cars {
log.Printf("Ariel memiliki mobil dengan model: %s", car.Model)
}
// Melintasi semua grup tempat Ariel menjadi anggota
groups, err := a8m.QueryGroups().All(ctx)
if err != nil {
log.Fatalf("Gagal melakukan query grup: %v", err)
return err
}
for _, g := range groups {
log.Printf("Ariel menjadi anggota dari grup: %s", g.Name)
}
return nil
}
Kode di atas adalah contoh dasar dari melintasi grafik, yang pertama-tama melakukan query pengguna dan kemudian melintasi mobil-mobil dan grup-grup pengguna tersebut.
7. Memvisualisasikan Skema Database
7.1. Memasang Alat Atlas
Untuk memvisualisasikan skema database yang dihasilkan oleh ent
, kita dapat menggunakan alat Atlas. Langkah-langkah pemasangan Atlas sangat sederhana. Sebagai contoh, pada macOS, Anda dapat memasangnya menggunakan brew
:
brew install ariga/tap/atlas
Catatan: Atlas adalah alat migrasi database universal yang dapat menangani manajemen versi struktur tabel untuk berbagai database. Pengenalan rinci tentang Atlas akan disediakan pada bab-bab berikutnya.
7.2. Menghasilkan ERD dan Skema SQL
Menggunakan Atlas untuk melihat dan mengekspor skema sangat mudah. Setelah memasang Atlas, Anda dapat menggunakan perintah berikut untuk melihat Diagram Entitas-Hubungan (ERD):
atlas schema inspect -d [database_dsn] --format dot
Atau langsung menghasilkan Skema SQL:
atlas schema inspect -d [database_dsn] --format sql
Di mana [database_dsn]
merujuk pada nama sumber data (DSN) dari database Anda. Misalnya, untuk database SQLite, bisa jadi:
atlas schema inspect -d "sqlite://file:ent.db?mode=memory&cache=shared" --format dot
Output yang dihasilkan oleh perintah-perintah ini dapat ditransformasikan lebih lanjut menjadi tampilan atau dokumen menggunakan alat-alat yang sesuai.
8. Migrasi Skema
8.1. Migrasi Otomatis dan Migrasi Berkelompok
ent mendukung dua strategi migrasi skema: migrasi otomatis dan migrasi berkelompok. Migrasi otomatis adalah proses pemeriksaan dan penerapan perubahan skema secara langsung, cocok untuk pengembangan dan pengujian. Migrasi berkelompok melibatkan generasi skrip migrasi dan memerlukan peninjauan dan pengujian yang teliti sebelum implementasi produksi.
Tip: Untuk migrasi otomatis, lihat konten di bagian 4.1.
8.2. Melakukan Migrasi Berkelompok
Proses migrasi berkelompok melibatkan generasi file-file migrasi melalui Atlas. Berikut adalah perintah-perintah terkait:
Untuk menghasilkan file-file migrasi:
atlas migrate diff -d ent/schema/path --dir migrations/dir
Kemudian, file-file migrasi ini dapat diterapkan pada database:
atlas migrate apply -d migrations/dir --url database_dsn
Setelah proses ini, Anda dapat menjaga riwayat migrasi database dalam sistem kontrol versi dan memastikan peninjauan teliti sebelum setiap migrasi.
Tip: Lihat contoh kode di https://github.com/ent/ent/tree/master/examples/start