Rust ile MariaDB / Mysql Veritabanı İşlemleri
Bildiğiniz üzere Rust ile geliştirmelerimizi Linux üzerinde yapıyoruz. Tabii ki Pardus kullanıyoruz. Mysql kurulumunu daha önceki .net core makalemde anlatmıştım. Buradan o makaleye erişip mysql kurulum adımlarını tamamlayabilirsiniz. Başlangıçta makaleleri daha basit tutacağım. Günün sonunda Rust ile web servis katmanı ile güzel bir mini uygulama yazacağız.
MySql daha doğrusu MariaDB üzerinde Demo veritabanını oluşturarak işe başlayabiliriz. Daha sonra aşağıdaki tabloyu oluşturup veri ekleme scriptlerini çalıştırabilirsiniz. Ben CALISAN adında bir tablo ekledim. o tablo üzerinden basit sorgular oluşturacağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
create table CALISAN ( SICIL INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, KIMLIK_NO VARCHAR(11) NOT NULL UNIQUE, MAAS DOUBLE NOT NULL, AD_SOYAD VARCHAR(1024) NOT NULL, BOLUM VARCHAR(25), TARIH DATE NOT NULL ); insert into CALISAN (KIMLIK_NO, MAAS, AD_SOYAD, BOLUM,TARIH) values ('33556677889', 5800.75, 'Ali Velioğlu', 'MUHASEBE',CURRENT_DATE()); insert into CALISAN (KIMLIK_NO, MAAS, AD_SOYAD, BOLUM,TARIH) values ('22445577884', 8000.85, 'Gülden Nezaket','BT', CURRENT_DATE()); insert into CALISAN (KIMLIK_NO, MAAS, AD_SOYAD, BOLUM,TARIH) values ('31156677889', 7800.75, 'Ali Bilgin', 'BT',CURRENT_DATE()); insert into CALISAN (KIMLIK_NO, MAAS, AD_SOYAD, BOLUM,TARIH) values ('24545577884', 6000.85, 'Murat Zade', 'IK',CURRENT_DATE()); |
Projeyi oluşturma
Visual Studio Code açtıktan sonra terminal kısmına gelelim. Buradan yeni Rust projemizi cargo komutları ile oluşturabiliriz.
1 2 3 4 5 6 7 8 9 10 |
kubilay@pardusVm:~$ mkdir codes kubilay@pardusVm:~$ cd codes kubilay@pardusVm:~/codes$ mkdir mysqlDemo kubilay@pardusVm:~/codes$ cd mysqlDemo/ kubilay@pardusVm:~/codes/mysqlDemo$ cargo new RustMySqlDemo Created binary (application) `RustMySqlDemo` package |
Daha sonra VS.Code üzerinden ilgili klasörü açabiliriz. Cargo.toml dosyasını aşağıdaki gibi düzenleyelim. Aşağıdaki kısımda karşımıza edition denilen yeni bir alan geliyor. Rust 2015 ve 2018 olmak üzere iki ana versiyondan oluşmaktadır. Paketler cargo ile yüklenirken biz en son versiyonu olan 2018 sürümü ve paket mantığıyla ilerliyor olacağız.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[package] name = "RustMySqlDemo" version = "0.1.0" authors = ["kubilay"] edition = "2018" <br /> [dependencies] mysql="*" chrono="0.4" |
Artık projemizi oluşturduk. Kod yazmaya başlayabiliriz.
Hadi Kodlayalım
Src altındaki main.rs dosyasını açalım ve ilk olarak aşağıdaki satırları ekleyelim. Böylece gerekli olan namespaceleri eklemiş olacağız.
1 2 3 4 5 6 7 8 9 |
use mysql::*; use mysql::prelude::*; use chrono::prelude::*; use std::time::{Duration, Instant}; use std::thread; |
Uygulamada önce mariadb için bağlantı kısımlarını birlikte girelim. ilk olarak yapacağımız bağlantıyı deneme olacaktır. main fonksiyonu içine aşağıdaki satırları yazalım .
1 2 3 4 5 6 7 |
let url = "mysql://dbUser:parola@localhost:3306/Demo"; letpool = Pool::new(url).unwrap(); letmutconn = pool.get_conn().unwrap(); |
1 2 3 4 5 |
cargo run |
Komutu çalıştıralım. Paketlerin indiğini ve uygulamanın çalıştığını görelim.
Sorgular
Rust tarafında çeşitli sorgulama teknikleri bulunuyor. Bunları birlikte kullanacağız. Ben zaman ölçümlerini de ekleyerek bunların performans farklarınıda görmenizi istedim. Hadi o zaman kaldığımız yerden devam edelim. Main fonksiyonunda kaldığımız yerden bir enter yapıp aşağıdaki kodları eklemeye devam edelim.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
let start1 = Instant::now(); println!("query iteration ******************"); conn.query_iter("select sicil, ad_soyad, tarih from CALISAN") .unwrap() .for_each(|row| { let r:(u64, String,NaiveDate) = from_row(row.unwrap()); println!("{}, {}, {:?}", r.0, r.1, r.2); }); letduration1= start1.elapsed(); println!("query iteration ***************time is : {:?}",duration1); |
query_iter ile sonuç verileri satır satır okunur. Tüm veriler asla hafızada saklanmaz. Dışa veri aktarmak istiyorsanız veya büyük veri ile çalışacaksanız bu tekniği kullanabilirsiniz. Fakat zaman sürelerine baktığınızda yavaş olduğunu göreceksiniz.
Gerçek hayatta uygulamalarında çalışırken ise hem hızı hem de kullanışlığı yönünden query_map yapısını kullanmak daha iyi görünüyor. Ama unutmayın kullanılan teknikler işe özel olabilir.Verilerimizi struct bir yapıya atarak daha kullanışlı hale getirebiliriz.
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 |
println!("query map **************"); let start2 = Instant::now(); struct Calisan { sicil: u64, kimlik_no: String, maas: f32, ad_soyad: String, bolum:String, tarih: NaiveDate, } let res = conn.query_map( "select sicil, kimlik_no, maas, ad_soyad, bolum,tarih from CALISAN", |(sicil, kimlik_no, maas, ad_soyad, bolum,tarih)| Calisan { sicil:sicil, kimlik_no: kimlik_no, maas: maas, ad_soyad: ad_soyad, bolum:bolum, tarih: tarih, } ).expect("Query failed."); for r in res { println!("{}, {}, {:?}", r.sicil,r.ad_soyad, r.tarih); } let duration2= start2.elapsed(); println!("query map end *************** time is : {:?}",duration3); |
Uygulamamızı çalıştırdığımızda ise aşağıdaki sonuçları alacağız. Gördüğünüz üzere map yapısı çok daha hızlı çalışmaktadır.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
query iteration ****************** 1, Ali Velioğlu, 2021-02-12 2, Gülden Nezaket, 2021-02-12 3, Ali Bilgin, 2021-02-12 4, Murat Zade, 2021-02-12 query iteration ***************time is : 11.281007ms query map ************** 1, Ali Velioğlu, 2021-02-12 2, Gülden Nezaket, 2021-02-12 3, Ali Bilgin, 2021-02-12 4, Murat Zade, 2021-02-12 query map end *************** time is : 4.929459ms |
Veri Ekleme
Listeleme işlemlerine baktıktan sonra ekleme işlemlerinede bakabiliriz.Öncelikle hem bir fonksiyon tanımının nasıl yapıldığını anlamak hemde insert işlemindeki tarih bilgisini daha düzgün oluşturmak için main fonksiyonu dışına aşağıdaki today fonksiyonunu ekleyelim.
1 2 3 4 5 6 7 8 |
fn today() -> NaiveDate { let l = Local::today(); NaiveDate::from_ymd(l.year(), l.month(), l.day()) } |
Fonksiyonumuzu oluşturduktan sonra insert işlemimize dönebiliriz.Insert işlemini daha hızlı çalışabilmesi için veritabanında önceden compile edilmiş bir sorgu mantığını kullanmak daha iyi oluyor.
1 2 3 4 5 6 |
let stmt = conn.prep("insert into CALISAN (kimlik_no, maas, ad_soyad, bolum,tarih) values (:kimlik_no, :maas, :ad_soyad, :bolum,:tarih)") .unwrap(); |
İşlem bilgisini veritabanına yolladıktan sonra parametrik insert işlemimiz aşağıdadır. Delete ve update işlemleride buna benzer olarak yapılmaktadır.
1 2 3 4 5 6 7 8 9 10 11 |
conn.exec_drop(&stmt, params! { "kimlik_no" => "31554375941", "maas" => 13000.99, "ad_soyad" => "Tarık Bin Ziyad", "bolum" => "Danışman", "tarih" => today(), }).unwrap(); |
Veri tabanı tarafında tablomuzun oluşma şeklini hatırlarsanız sicil kolonunu otomatik artan bir sayı olarak yapmıştık. Aşağıda insert işlemi sonrasında bize dönen bu otomatik artan id bilgisini nasıl alacağımızı görebilirsiniz.
1 2 3 4 5 6 |
let sicil=conn.last_insert_id(); println!("id : {}", sicil); |
Bu kısım aslında bir tekrar kod olarak yazdım. insert ettiğimiz veriyi yeniden geri listeliyoruz. Burada bu kodu tekrarlı olarak yazmamın sebebi Rust ile ilgilenen arkadaşların yukarıda örnek olarak verdiğim today() fonksiyonu gibi bu kod parçacığını da ona çevirmeleri içindir. Kod hakimiyeti ve el yatkınlığınız için bunu bu şekilde yapabilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
let res = conn.query_map( "select sicil, kimlik_no, maas, ad_soyad, bolum,tarih from CALISAN ", |(sicil, kimlik_no, maas, ad_soyad, bolum,tarih)| Calisan { sicil:sicil, kimlik_no: kimlik_no, maas: maas, ad_soyad: ad_soyad, bolum:bolum, tarih: tarih, } ).expect("Query failed."); for r in res { println!("{}, {}, {:?}", r.sicil,r.ad_soyad, r.tarih); } |