1. ent'e Giriş
Ent, özellikle Go diline yönelik olarak Facebook tarafından geliştirilen bir varlık çerçevesidir. Büyük ölçekli veri modeli uygulamaları oluşturmayı ve sürdürmeyi basitleştirir. ent çerçevesi genellikle aşağıdaki prensipleri izler:
- Veritabanı şemasını grafik yapısı olarak kolayca modelleme.
- Şemayı Go dilinde kod olarak tanımlama.
- Kod üretimi temelli statik tipleri uygulama.
- Veritabanı sorgularını ve grafik dolaşımını yazmak çok kolay.
- Go şablonları kullanarak kolayca genişletilebilir ve özelleştirilebilir.
2. Ortam Kurulumu
Ent çerçevesini kullanmaya başlamak için geliştirme ortamınızda Go dilinin yüklü olduğundan emin olun.
Eğer proje dizininiz GOPATH
'ın dışındaysa veya GOPATH
hakkında bilgi sahibi değilseniz, aşağıdaki komutu kullanarak yeni bir Go modül projesi oluşturabilirsiniz:
go mod init entdemo
Bu, yeni bir Go modülü başlatacak ve entdemo
projeniz için yeni bir go.mod
dosyası oluşturacaktır.
3. İlk Şemanın Tanımlanması
3.1. ent CLI Kullanarak Şema Oluşturma
İlk olarak, kök dizininizde ent CLI aracını kullanarak User adında bir şema oluşturmak için aşağıdaki komutu çalıştırmanız gerekmektedir:
go run -mod=mod entgo.io/ent/cmd/ent new User
Yukarıdaki komut, entdemo/ent/schema/
dizininde User şemasını oluşturacaktır.
Dosya 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. Alan Eklemek
Daha sonra, User Şemasına alan tanımlamaları eklememiz gerekmektedir. Aşağıda, User varlığına iki alan eklemenin bir örneği bulunmaktadır.
Değiştirilmiş dosya entdemo/ent/schema/user.go
:
package schema
import (
"entgo.io/ent"
"entgo.io/ent/schema/field"
)
// User alanları.
func (User) Fields() []ent.Field {
return []ent.Field{
field.Int("age").
Positive(),
field.String("name").
Default("unknown"),
}
}
Bu kod parçası, User modeli için iki alanı tanımlar: age
ve name
. Burada, age
pozitif bir tamsayı iken name
varsayılan olarak "unknown" olan bir dizedir.
3.3. Veritabanı Varlıklarının Oluşturulması
Şemayı tanımladıktan sonra, temel veritabanı erişim mantığını oluşturmak için go generate
komutunu çalıştırmanız gerekmektedir.
Projedeki kök dizininde aşağıdaki komutu çalıştırın:
go generate ./ent
Bu komut, önceden tanımlanan şemaya dayalı olarak ilgili Go kodunu oluşturacak ve aşağıdaki dosya yapısını ortaya çıkaracaktır:
ent
├── client.go
├── config.go
├── context.go
├── ent.go
├── generate.go
├── mutation.go
... (kısalık için birkaç dosya atlandı)
├── 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. Veritabanı Bağlantısını Başlatma
MySQL veritabanına bağlantı kurmak için, ent
çerçevesi tarafından sağlanan Open
işlevini kullanabiliriz. İlk olarak MySQL sürücüsünü içe aktarın ve ardından doğru bağlantı dizesini sağlayarak veritabanı bağlantısını başlatın.
package main
import (
"context"
"log"
"entdemo/ent"
_ "github.com/go-sql-driver/mysql" // MySQL sürücüsünü içe aktar
)
func main() {
// MySQL veritabanı ile bağlantı kurmak için ent.Open kullanın.
// Aşağıdaki yer tutucularını "kullanıcı_adınız", "şifreniz" ve "veritabanınız" ile değiştirmeyi unutmayın.
client, err := ent.Open("mysql", "kullanıcı_adınız:şifreniz@tcp(localhost:3306)/veritabanınız?parseTime=True")
if err != nil {
log.Fatalf("mysql'e bağlantı açma başarısız oldu: %v", err)
}
defer client.Close()
// Otomatik göç (migration) aracını çalıştır
ctx := context.Background()
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("şema kaynakları oluşturma başarısız oldu: %v", err)
}
// Ek iş mantığı burada yazılabilir
}
4.2. Varlıklar Oluşturma
Kullanıcı varlığı oluşturmak, yeni bir varlık nesnesi oluşturmak ve onu veritabanına Save
veya SaveX
yöntemi kullanarak kalıcı hale getirmekle ilgilidir. Aşağıdaki kod, yeni bir Kullanıcı varlığı oluşturmayı ve age
ve name
alanlarını başlatmayı göstermektedir.
// CreateUser işlevi, yeni bir Kullanıcı varlığı oluşturmak için kullanılır
func CreateUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
// Kullanıcı oluşturmak için client.User.Create() kullanın,
// sonra SetAge ve SetName yöntemlerini zincirleyerek varlık alanlarının değerlerini ayarlayın.
u, err := client.User.
Create().
SetAge(30). // Kullanıcı yaşını ayarla
SetName("a8m"). // Kullanıcı adını ayarla
Save(ctx) // Varlığı veritabanına kaydetmek için Save'i çağırın
if err != nil {
return nil, fmt.Errorf("kullanıcı oluşturma başarısız oldu: %w", err)
}
log.Println("kullanıcı oluşturuldu: ", u)
return u, nil
}
main
işlevinde, yeni bir kullanıcı varlığı oluşturmak için CreateUser
işlevini çağırabilirsiniz.
func main() {
// ...Atlanmış veritabanı bağlantısı kurma kodu
// Bir kullanıcı varlığı oluştur
u, err := CreateUser(ctx, client)
if err != nil {
log.Fatalf("kullanıcı oluşturma başarısız oldu: %v", err)
}
log.Printf("oluşturulan kullanıcı: %#v\n", u)
}
4.3. Varlıkları Sorgulama
Varlıkları sorgulamak için, ent
tarafından oluşturulan sorgu oluşturucuyu kullanabiliriz. Aşağıdaki kod, "a8m" adında bir kullanıcıyı sorgulamanın nasıl yapıldığını göstermektedir:
// QueryUser işlevi, belirli bir ada sahip kullanıcı varlığını sorgulamak için kullanılır
func QueryUser(ctx context.Context, client *ent.Client) (*ent.User, error) {
// Kullanıcı için sorgu oluşturmak için client.User.Query() kullanın,
// sonra Where yöntemini zincirleyerek sorgu koşullarını ekleyin, örneğin kullanıcı adına göre sorgulama yapma
u, err := client.User.
Query().
Where(user.NameEQ("a8m")). // Sorgu koşulunu ekleyin, bu durumda ad "a8m"
Only(ctx) // Only yöntemi yalnızca bir sonuç beklediğini gösterir
if err != nil {
return nil, fmt.Errorf("kullanıcı sorgulama başarısız oldu: %w", err)
}
log.Println("getirilen kullanıcı: ", u)
return u, nil
}
main
işlevinde, kullanıcı varlığını sorgulamak için QueryUser
işlevini çağırabilirsiniz.
func main() {
// ...Atlanmış veritabanı bağlantısı kurma ve kullanıcı oluşturma kodu
// Kullanıcı varlığını sorgula
u, err := QueryUser(ctx, client)
if err != nil {
log.Fatalf("kullanıcı sorgulama başarısız oldu: %v", err)
}
log.Printf("sorgulanan kullanıcı: %#v\n", u)
}
5.1. Kenarları ve Ters Kenarları Anlama
ent
çerçevesinde, veri modeli varlık yapısı olarak görselleştirilir, burada varlıklar grafikteki düğümleri temsil eder ve varlıklar arasındaki ilişkiler kenarlarla gösterilir. Bir kenar, bir varlıktan diğerine bir bağlantıdır, örneğin, bir Kullanıcı
birden çok `Araba'ya sahip olabilir.
Ters Kenarlar, varlıklar arasındaki ters referansları temsil ederek, mantıksal olarak varlıklar arasındaki ters ilişkiyi temsil eder, ancak veritabanında yeni bir ilişki oluşturmaz. Örneğin, bir Araba
nın ters kenarı aracılığıyla bu arabaya sahip olan Kullanıcı
yı bulabiliriz.
Kenarların ve ters kenarların temel önemi, ilişkili varlıklar arasında gezinmeyi çok sezgisel ve doğrudan hale getirmesidir.
İpucu:
ent
te, kenarlar geleneksel veritabanı dış anahtarlarına karşılık gelir ve tablolar arasındaki ilişkileri tanımlamak için kullanılır.
5.2. Şemada Kenarları Tanımlama
İlk olarak, ent
CLI'sını kullanarak Car
ve Group
için başlangıç şemasını oluşturacağız:
go run -mod=mod entgo.io/ent/cmd/ent new Car Group
Sonraki adımda, User
şemasında, kullanıcıların arabalarıyla olan ilişkiyi temsil etmek için Car
ile kenarı tanımlarız. Kullanıcı varlığındaki Car
türüne işaret eden cars
adında bir kenar ekleyebiliriz, bu da bir kullanıcının birden çok arabaya sahip olabileceğini gösterir:
// entdemo/ent/schema/user.go
// Kullanıcının kenarları.
func (User) Edges() []ent.Edge {
return []ent.Edge{
edge.To("cars", Car.Type),
}
}
Kenarları tanımladıktan sonra, ilgili kodu oluşturmak için tekrar go generate ./ent
komutunu çalıştırmamız gerekmektedir.
5.3. Kenar Verileri Üzerinde İşlem Yapma
Bir kullanıcıyla ilişkili arabaları oluşturmak basit bir süreçtir. Bir kullanıcı varlığı verildiğinde, yeni bir araba varlığı oluşturabilir ve kullanıcıyla ilişkilendirebiliriz:
import (
"context"
"log"
"entdemo/ent"
// Araba için şema tanımını içe aktardığımızdan emin olun
_ "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("kullanıcı alınamadı: %v", err)
return err
}
// Yeni bir araba oluştur ve kullanıcıyla ilişkilendir
_, err = client.Car.
Create().
SetModel("Tesla").
SetRegisteredAt(time.Now()).
SetOwner(user).
Save(ctx)
if err != nil {
log.Fatalf("kullanıcı için araba oluşturma başarısız: %v", err)
return err
}
log.Println("araba oluşturuldu ve kullanıcıyla ilişkilendirildi")
return nil
}
Benzer şekilde, bir kullanıcının arabalarını sorgulamak da basittir. Bir kullanıcının sahip olduğu tüm arabaların listesini almak istiyorsak, aşağıdakileri yapabiliriz:
func QueryUserCars(ctx context.Context, client *ent.Client, userID int) error {
user, err := client.User.Get(ctx, userID)
if err != nil {
log.Fatalf("kullanıcı alınamadı: %v", err)
return err
}
// Kullanıcının sahip olduğu tüm arabaları sorgula
cars, err := user.QueryCars().All(ctx)
if err != nil {
log.Fatalf("arabaları sorgulama başarısız: %v", err)
return err
}
for _, car := range cars {
log.Printf("araba: %v, model: %v", car.ID, car.Model)
}
return nil
}
Yukarıdaki adımlarla, hem şemada kenarları nasıl tanımlayacağımızı öğrendik hem de kenarlara ilişkin verileri nasıl oluşturup sorgulayacağımızı gösterdik.
6. Grafik Gezginliği ve Sorgulama
6.1. Grafik Yapılarını Anlama
ent
te, grafik yapıları varlıklar ve aralarındaki kenarlardan temsil edilir. Her bir varlık grafikte bir düğüme eşdeğerdir ve varlıklar arasındaki ilişkiler kenarlarla temsil edilir, bu ilişkiler bir-birine, bir-çoka, çok-çoka vb. olabilir. Bu grafik yapısı karmaşık sorguları ve ilişkisel veritabanı üzerinde işlemleri basit ve sezgisel hale getirir.
6.2. Grafik Yapıları Üzerinde Gezinme
Grafik Gezinme kodu yazmak genellikle varlıklar arasındaki kenarlar aracılığıyla veri sorgulama ve ilişkilendirme işlemi içerir. Aşağıda, ent
içindeki grafik yapısını nasıl gezineceğimizi gösteren basit bir örnek bulunmaktadır:
import (
"context"
"log"
"entdemo/ent"
)
// GraphTraversal, grafik yapısını gezmenin bir örneğidir
func GraphTraversal(ctx context.Context, client *ent.Client) error {
// "Ariel" adlı kullanıcıyı sorgula
a8m, err := client.User.Query().Where(user.NameEQ("Ariel")).Only(ctx)
if err != nil {
log.Fatalf("Kullanıcı sorgulanamadı: %v", err)
return err
}
// Ariel'e ait tüm arabaları gez
cars, err := a8m.QueryCars().All(ctx)
if err != nil {
log.Fatalf("Arabalar sorgulanamadı: %v", err)
return err
}
for _, car := range cars {
log.Printf("Ariel'in %s modelinde bir arabası var", car.Model)
}
// Ariel'in üye olduğu tüm grupları gez
groups, err := a8m.QueryGroups().All(ctx)
if err != nil {
log.Fatalf("Gruplar sorgulanamadı: %v", err)
return err
}
for _, g := range groups {
log.Printf("Ariel, %s grubunun üyesi", g.Name)
}
return nil
}
Yukarıdaki kod, grafik gezinmenin temel bir örneğidir. İlk olarak bir kullanıcı sorgulanır ve ardından kullanıcının arabaları ve grupları gezilir.
7. Veritabanı Şemasını Görselleştirme
7.1. Atlas Aracını Yükleme
ent
tarafından oluşturulan veritabanı şemasını görselleştirmek için Atlas aracını kullanabiliriz. Atlas'ın kurulum adımları çok basittir. Örneğin, macOS üzerinde brew
kullanarak şu şekilde yükleyebilirsiniz:
brew install ariga/tap/atlas
Not: Atlas, çeşitli veritabanları için tablo yapı versiyon yönetimini yönetebilen evrensel bir veritabanı göç aracıdır. Atlas'a yönelik detaylı tanıtım ileriki bölümlerde sağlanacaktır.
7.2. ER Diyagramı ve SQL Şeması Oluşturma
Atlas'ı kullanarak şemaları görüntülemek ve dışa aktarmak oldukça basittir. Atlas'ı yükledikten sonra, aşağıdaki komutları kullanabilirsiniz:
Entitiy-Relationship Diyagramını (ERD) görüntülemek için:
atlas schema inspect -d [veritabanı_dsn] --format dot
Veya doğrudan SQL Şeması oluşturabilirsiniz:
atlas schema inspect -d [veritabanı_dsn] --format sql
Burada [veritabanı_dsn]
, veritabanınızın veri kaynağı adını (DSN) belirtir. Örneğin, bir SQLite veritabanı için şöyle olabilir:
atlas schema inspect -d "sqlite://file:ent.db?mode=memory&cache=shared" --format dot
Bu komutlarla oluşturulan çıktı, ilgili araçlar kullanılarak görünümlere veya belgelere dönüştürülebilir.
8. Şema Göçü
8.1. Otomatik ve Versiyonlu Göç
ent, otomatik göç ve versiyonlu göç olmak üzere iki şema göçü stratejisini destekler. Otomatik göç, çalışma zamanında şema değişikliklerini inceleyip uygulama sürecidir; geliştirme ve test için uygundur. Versiyonlu göç ise göç betikleri oluşturmayı içerir ve üretim dağıtımından önce dikkatli bir inceleme ve test gerektirir.
İpucu: Otomatik göç için bölüm 4.1'deki içeriğe bakınız.
8.2. Versiyonlu Göç Yapma
Versiyonlu göç süreci, Atlas aracılığıyla göç dosyaları oluşturmayı içerir. İlgili komutlar aşağıdaki gibidir:
Göç dosyalarını oluşturmak için:
atlas migrate diff -d ent/schema/yol --dir göçler/dizini
Daha sonra bu göç dosyaları veritabanına uygulanabilir:
atlas migrate apply -d göçler/dizini --url veritabanı_dsn
Bu sürecin ardından veritabanı göçlerinin her birinden önce kapsamlı inceleme yapılacak ve versiyon kontrol sisteminde bir göç geçmişi tutulacaktır.
İpucu: Örnek kod için https://github.com/ent/ent/tree/master/examples/start adresine bakınız.