Önceki yazılarda Go ve Beego konusunda birşeyler yazmıştım, şuradan bakabilirsiniz:
- http://web.bilecik.edu.tr/murat-ozalp/2015/08/23/go-golang-programlama-dili-maceram/
- http://web.bilecik.edu.tr/murat-ozalp/2015/08/25/go-golang-beego-framework-ve-heroku/
Bu yazının konusu ise Beego framework kullanarak, ORM (Object Relational Mapping) uygulaması olacak.
ORM nedir?
Özetle; veritabanı ile uygulama kodları arasına giren bir katmandır. Uygulama ORM’yi tanır, onunla konuşur. ORM de veritabanını tanır onunla konuşur. Şöyle birşey:
Faydası ise kodların veritabanından bağımsız olmasıdır. SQLite için yazdığınız kodu aynı zamanda MySQL veya PostgreSQL için de kullanabilmenizi sağlar. Veritabanındaki tablolar ve alanlar, kod tarafında struct veya sınıf ve değişkenler gibi yapılar ile eşleştirilir. Bu eşleştirme ORM’nin görevidir. ORM herhangi bir dile özel birşey değildir, güncel dillerin çoğu bir şekilde ORM destekler.
Örnek uygulama
Çok sade bir veritabanına Beego ORM kütüphanesi ile okuyup yazma çalışması yapacağız. Önce sqlite (sürüm 3) ile kodları yazalım, sonra 1 satır kod değişikliği yaparak farklı bir veritabanına geçebileceğimizi göreceğiz zaten. Veritabanını ve tabloları aşağıdaki gibi oluşturalım:
1 2 3 4 5 6 7 8 9 10 11 12 |
$ sqlite3 ornek.db SQLite version 3.8.2 2013-12-06 14:53:30 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> CREATE TABLE userinfo ( ...> Id INTEGER PRIMARY KEY, ...> Username VARCHAR(64) NULL, ...> Departname VARCHAR(64) NULL, ...> Created DATE NULL ...> ); sqlite> .exit |
Yukarıdaki işlemleri yaptığımızda, ornek.db adında bir dosya oluşacak ve bu dosyada SQL kodları ile belirtilen yapı oluşturulacaktır. Eğer sqlite veritabanını komple ekrana dökmek istersek, dump işlemi yapabiliriz. Örnek:
1 2 3 4 5 6 7 8 9 10 11 |
$ sqlite3 ornek.db .dump PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE userinfo ( Id INTEGER PRIMARY KEY, Username VARCHAR(64) NULL, Departname VARCHAR(64) NULL, Created DATE NULL ); COMMIT; $ |
Eğer tablomuzda kayıtlar olsaydı, dump komutu verildiğinde onlar da listelenecekti.
Beego ORM framework katmanı
Bu yazı yazıldığı sırada; sqlite, MySQL ve PostgreSQL veritabanları destekleniyordu. Kullanımı oldukça basit, Go dili yazım tarzı ile uyumlu. Go yazarken farklı birşeyler öğrenmek zorunda kalmayacaksınız. Framework’ün web sayfası şurada: https://github.com/astaxie/beego/tree/master/orm. Sayfada kullanılış şekli de verilmiş. Ben de aşağıda kendi dilimizde açıklamaya çalışayım:
Gerekli kütüphaneleri yükleyelim
Go’nun güzel özelliklerinden birisi, Github üzerinden doğrudan kütüphane kullanabilmesi. Aşağıda bu küçük uygulamamız için kullanılması gereken kütüphanelerin olduğu bölüm gösterilmiştir:
1 2 3 4 5 |
import ( "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" _ "github.com/mattn/go-sqlite3" ) |
Satırların ne amaçla yazıldığı zaten anlaşılıyor. Uygulamadaki veritabanına göre sadece 1 tane veritabanı kütüphanesi (sqlite3, mysql, postgresql) kullanılması yeterli tabiiki. sqlite için çok sayıda kütüphane var, Beego’nun geliştiricisi yukarıda yazılanı tavsiye ediyor. Sebep olarak ta Go içerisinde gelen database/sql paketi ile uyumlu olduğunu belirtiyor. Ben de fazla sorgulamadan aynen uyguladım.
Veritabanındaki alanlar ile eşleşecek olan değişkenler
Bunun için struct kullanıyoruz. Aşağıda örnek kodlar verilmiştir:
1 2 3 4 |
type User struct { Id int `orm:"auto"` Name string `orm:"size(100)"` } |
Anlaşılacağı üzere; User ifadesi tabloya karşılık geliyor. Id ve Name değişkenleri de tablodaki alanlara karşılık geliyor. Go’nun değişken türlerinde; metod içinde private olarak kullanılacak olanların küçük harfle, public olanların ise büyük harfle başlaması gerektiğini unutmayalım. Bu nedenle; id yerine Id yazıyoruz efendim. Değişken tanımlamalarının hemen yanında orm katmanına özel kısımlar geliyor. Id alanına veritabanı tarafından otomatik değer atanacağını ve Name kısmının da veritabanında 100 karakter ile depolanacağını belirtmiş oluyoruz.
Tanımlanan modelin veritabanına bağlanması
Aşağıdaki kodlarda bu işlemin başlangıç (init ~ initialization) fonksiyonunda nasıl yapılacağı gösterilmiştir:
1 2 3 4 5 6 7 |
func init() { // modeli kayıt edelim orm.RegisterModel(new(User)) // varsayılan veritabanını kayıt edelim orm.RegisterDataBase("default", "mysql", "kullanıcı:parola@/veritabanı?charset=utf8", 30) } |
RegisterModel komutu, User yapısı (struct) kullanılarak yeni bir orm modeli oluşturuyor. İkinci komutla da kullanacağımız veritabanını belirtmiş oluyoruz. Parametrelerin anlamı şu şekilde:
- “default” kullanılacak olan varsayılan veritabanı budur.
- “mysql” veritabanının türü. Alternatif seçenekler: sqlite3 ve postgresql
- “kullanıcı:parola@/veritabanı?charset=utf8” SQL bağlantı cümlesi.
- 30: bağlantı zaman aşımı süresi
CRUD işlemleri
CRUD (Create, Read, Update, Delete) işlemleri için yine Astaxie’nin örneğinden faydalanıyoruz:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
func main() { o := orm.NewOrm() user := User{Name: "Hüsamettin"} // ekle id, err := o.Insert(&user) // değiştir user.Name = "Şukubettin" num, err := o.Update(&user) // oku u := User{Id: user.Id} err = o.Read(&u) // sil num, err = o.Delete(&u) } |
Kodlar zaten sade olduğundan detaya girmeyeceğim. “o” isminde yeni bir ORM oluşturuluyor. İsmi “Hüsamettin” olan User tipinde bir struct kayıt oluşturuluyor. o.Insert komutu ile bu kayıt tabloya ekleniyor. Eklenen kayıda veritabanı tarafından otomatik verilen Id değeri, id değişkenine aktarılıyor. İsim, “Şukubettin” olacak şekilde değiştiriliyor. Id alanı falanca olan kayıdı getir diyerekten, çaktırmadan SELECT yapılıyor. Son olarak ta ilgili kayıt siliniyor. Veritabanını açıp baktığımızda birşey göremeyeceğiz yani :)
Çalıştırmak için hazır kod
Yukarıdaki kısımlar, üreticinin (Astaxie) kendi dokümanlarından aktarılmıştı. Şimdi de tam olarak çalışan bir kod verelim. Üst tarafta “örnek uygulama” başlığında verilen veritabanı yapısını (Id, Username, Departname, Created alanları olan) kullanacağız. Bu nedenle öncelikle sqlite3 veritabanının orada anlaıldığı gibi hazırlanması gerekiyor. Veritabanını oluşturduktan sonra, alttaki kodları hedehodo.go şeklinde kaydederseniz, sonra da go build hedehodo.go derseniz, kodu derlemiş olursunuz. Sonra her çalıştırdığınızda, veritabanında birşeyer yapacaktır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
package main import ( "fmt" "github.com/astaxie/beego/orm" _ "github.com/go-sql-driver/mysql" // import your used driver _ "github.com/mattn/go-sqlite3" // import your used driver ) // Model Struct type Userinfo struct { Id int `orm: "pk"` //`orm:"pk"` Username string `orm: "size(30)"` Departname string `orm: "size(30)"` Created string `orm: "size(30)"` } func init() { // Anlaşılacağı üzere aşağıdaki iki satırdan sadece 1 tanesi aktif olmalıdır. // orm.RegisterDataBase("default", "mysql", "kullanıcı:parola@/bigodb?charset=utf8", 30) orm.RegisterDataBase("default", "sqlite3", "orm.db", 30) orm.RegisterModel(new(Userinfo)) orm.Debug = true } func main() { o := orm.NewOrm() user := Userinfo{Username: "portakal"} // insert fmt.Println(user, user.Username) Id, err := o.Insert(&user) // update ///user.Username = "armut" //num, err := o.Update(&user) // read one user = Userinfo{Id: 3} err = o.Read(&user) if err == orm.ErrNoRows { fmt.Println("No result found.") } else if err == orm.ErrMissPK { fmt.Println("No primary key found.") } else { fmt.Println(user.Id, user) } // delete //num, err = o.Delete(&u) _ = Id _ = err //_ = num } |
Alt satırlardaki _ ile başlayan kodların saçma bir gerekçesi var, onu da yazayım. Normalde bu tarz işlere gerek kalmayacak muhtemelen. Go ile kod derlerken bir değişkeni tanımlayıp ta kullanmazsanız, kızıyor. Bu satırlar da onların hiç kullanılmamış olmasını engellemek için yazıldı. CRUD işlemlerinden bazılarını // şeklinde açıklama satırı yaptığımızda orada tanımlanan değişkenleri de “tanımlanmamış” hale getiriyoruz aslında. Alttaki _ ile başlayan kısımlarla bu tarz durumlarda oynamak gerekebiliyor. Özetle; alt çizgi sembolü, tanımlanmış bir değişken kullanılmaz ise Go’nun kızmaması için kullanılıyor.
Sonraki yazıda inşallah CRUD işlemlerinin yine ORM kullanaraktan, varsayılan Beego MVC projesinde ( bee new yeni_proje ) uygulanması şeklinde birşeyler olacak. Tabii önce ben öğrenmeliyim :)