「TAURI」でデータベースを使ってみよう
RustでSQL文の実行
データベースを実行するにはSQL文を実行する必要があります。SQL文はシンプルなプログラムの文字列をテキスト形式で書くだけです。
SQL文の最も基本的なものに「CRUD」の4つがあります。「C」は「Create」の頭文字で「作成」、「R」は「Read」の頭文字で「読み出し」、「U」は「Update」の頭文字で「更新」、「D」は「Delete」の頭文字で「削除」です。具体的なSQL文は、作成には「CREATE」文を、読み出しには「SELECT」文を、更新には「UPDATE」文を、削除には「DELETE」文を使います。
ここからバックエンド側のRustをプログラミングしていきます。SQL文を実行する処理がメインになります。サンプルコードではID番号と動物名、動物の種類を1行の「レコード」としてテーブルに追加したり読み込んだり、更新したり削除したりします。
SQL文は一種のプログラミング言語のようなもので「"」で囲った文字列で表現しますが、Rustとは文法が違います。今回解説するSQL文はとても短いものなので、テーブル名やカラム名を変えただけでも使いまわせると思います。
TABLEを作成するSQL文
データベースの「TABLE」とHTMLの「table」タグは全く別のものですが、テーブル表に表すとそっくりです。下図のように「行(レコード)」と「列(カラム)」でできている二次元配列のようなものです。
テーブルは「CREATE TABLE テーブル名 (カラム名 型,カラム名 型);」文で作成しますが、作成したい名前のテーブルが既に存在する場合はエラーになります。そこで「IF NOT EXISTS テーブル名」でテーブル名のテーブルが存在していない場合だけ作成します。ここでは「$ cargo-tauri dev」コマンドを実行すると「id」カラムと「name」カラムと「category」カラムのある「animal」テーブルを作成します。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // データベースファイル名 const TAURI_DB:&str = "../tauri.db"; // SQL文でTABLE作成 fn create() { let connection = sqlite::open(TAURI_DB).unwrap(); let query = " CREATE TABLE IF NOT EXISTS animal (id INTEGER PRIMARY KEY AUTOINCREMENT,name TEXT,category TEXT); "; connection.execute(query).unwrap(); } // メイン関数 fn main() { // TABLE作成 create(); tauri::Builder::default() .invoke_handler(tauri::generate_handler![]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
【サンプルコードの解説】
「TAURI_DB」定数がデータベースのファイル名です。最初にCREATE TABLEした際に"tauri.db"というファイルが作成されます。リリースビルドする際は保存するパスを変更すべきでしょう。
「create」関数で「sqlite」クレートの「open」関数でデータベースを開いて「connection」変数に代入し、「query」変数がTABLEを作成するSQL文の「execute」メソッドでそれを実行します。
メイン関数でcreate関数を呼び出します。
行を挿入するSQL文
「INSERT INTO テーブル名(カラム名,カラム名) VALUES (値,値);」のSQL文でテーブルに値を入れたカラムのレコードを追加します。ここでは「$ cargo-tauri dev」コマンドを実行すると、animalテーブルのnameカラムに鯛をcategoryカラムに魚類を入れるなどしています。idカラムは「オートインクリメント」される「主キー」なので自動で1から1ずつ増えた正の整数が入力されます。
他のプログラミング言語の場合、SQLite3のSQL文を実行する際にexecuteメソッドでデータを別の引数で渡せるのですが、sqliteクレートでは見つからなかったので、仕方なく「format!」マクロでSQL文の文字列にデータの変数を挿入しました。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード(前略) // SQL文で行挿入 fn insert(name:&str,category:&str) { let connection = sqlite::open(TAURI_DB).unwrap(); let query = format!(" INSERT INTO animal(name,category) VALUES (\"{}\",\"{}\"); ",name,category); connection.execute(query).unwrap(); } // メイン関数 fn main() { // 行挿入 insert("鯛","魚類"); insert("カエル","両生類"); insert("トカゲ","爬虫類"); insert("鷹","鳥類"); insert("トイプードル","哺乳類"); tauri::Builder::default() .invoke_handler(tauri::generate_handler![]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
【サンプルコードの解説】
「insert」関数で「sqlite」クレートの「open」関数でデータベースを開き、「connection」変数に代入して「query」変数がレコードを挿入するSQL文の「execute」メソッドでそれを実行します。
メイン関数でinsert関数を呼び出します。
行を取得するSQL文
「SELECT カラム名 FROM テーブル名;」のSQL文でテーブル名のカラム名のレコードを取得します。ここでは「$ cargo-tauri dev」コマンドを実行するとanimalテーブルの全てのカラム(*)を取得します。
ここでSQL文を実行するのに「prepare」メソッドを使いました。prepareメソッドはレコードを取得するだけでデータベースに変更を加えない場合に使うのに対し、executeメソッドはデータベースに変更を加える場合に使います。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // データベース名 const TAURI_DB:&str = "../tauri.db"; // TAURIコマンドget_data関数 #[tauri::command] fn select() -> Vec<(i64,String,String)> { let connection = sqlite::open(TAURI_DB).unwrap(); let query = "SELECT * FROM animal"; let mut data = Vec::new(); // SQL文で行取得 for row in connection .prepare(query) .unwrap() .into_iter() .map(|row| row.unwrap()) { let id = row.read::<i64, _>("id"); let name = row.read::<&str, _>("name").to_string(); let category = row.read::<&str, _>("category").to_string(); data.push((id.clone(),name.clone(),category.clone())); println!("id = {},name = {},category = {}",id,name,category); } data } (中略) // メイン関数 fn main() { // 行データ取得 select(); // TAURIコマンドselect関数の登録 tauri::Builder::default() .invoke_handler(tauri::generate_handler![select]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
【サンプルコードの解説】
TAURIコマンド「select」関数で「sqlite」クレートの「open」関数でデータベースを開いて「connection」変数に代入し、「query」変数がレコードを取得するSQL文の「prepare」メソッドでそれを実行します。
メイン関数でTAURIコマンドselect関数を呼び出し、TAURIビルダーにTAURIコマンドselect関数を登録します。
行を更新するSQL文
「UPDATE テーブル名 SET カラム名=値,カラム名=値 WHERE 条件;」のSQL文で、条件に当てはまるレコードのテーブルのカラムに値を入れ替えます。ここでは「$ cargo-tauri dev」コマンドを実行するとanimalテーブルのidカラムが1のnameカラムを秋刀魚に、categoryカラムを魚類に更新します。
ここで「WHERE」に続く条件文で指定したレコードだけ値を変更します。idカラムはオートインクリメントする一意の値なので、必ず1つのレコードだけを指定できます。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード(前略) // SQL文で行更新 fn update(id:i64,name:&str,category:&str) { let connection = sqlite::open(TAURI_DB).unwrap(); let query = format!(" UPDATE animal SET name=\"{}\",category=\"{}\" WHERE id = {}; ",name,category,id); connection.execute(query).unwrap(); } // メイン関数 fn main() { // 1行目を書き換え update(1,"秋刀魚","魚類"); select(); tauri::Builder::default() .invoke_handler(tauri::generate_handler![select]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
【サンプルコードの解説】
「update」関数で「sqlite」クレートの「open」関数でデータベースを開いて「connection」変数に代入し、「query」変数がレコードを更新するSQL文の「execute」メソッドでそれを実行します。
メイン関数でupdate関数を呼び出します。
行を削除するSQL文
「DELETE FROM テーブル名 WHERE 条件;」のSQL文で条件に当てはまるレコードをテーブルから削除します。ここでは「$ cargo-tauri dev」コマンドを実行すると、animalテーブルのidカラムが2のレコードを削除します。
このSQL文は全て開発者が操作しているため改ざんや誤動作させる悪意のあるSQL文にはなりませんが、もし値をユーザーが操作できるようにした場合は悪意のあるデータにも対処しなければなりません。
・「src-tauri」→「src」→「main.rs」ファイルのサンプルコード(前略) // SQL文で行削除 fn delete(id:i64) { let connection = sqlite::open(TAURI_DB).unwrap(); let query = format!(" DELETE FROM animal WHERE id = {}; ",id); connection.execute(query).unwrap(); } // メイン関数 fn main() { // id=2の行削除 delete(2); select(); tauri::Builder::default() .invoke_handler(tauri::generate_handler![select]) .run(tauri::generate_context!()) .expect("error while running tauri application"); }
【サンプルコードの解説】
「delete」関数で「sqlite」クレートの「open」関数でデータベースを開いて「connection」変数に代入し、「query」変数がレコードを削除するSQL文の「execute」メソッドでそれを実行します。
メイン関数でdelete関数を呼び出します。
どんなデータベースでもSQL文には大文字も小文字も使えますが、筆者は文法のCREATEやSELECTなどは全て大文字で、テーブル名やカラム名などの名前は全て小文字で書くことで、SQL文を見やすくするように心がけています。
おわりに
最終回の今回は、「SQLite3」を「sqlite」クレートからデータベースを扱う解説をしました。SQL文はほとんどのデータベースと同じような文法でした。
今回で、約9ヶ月間にわたって解説した「TAURI」+「Rust」ではじめるデスクトップアプリ開発の連載も終了となります。長い間お付き合いいただき、ありがとうございました。TAURIはRustだけでなくWebアプリの知識も必要ですが、Think ITの読者にはWebインフラに強い方が多いようなので、むしろとっつきやすかったかも知れませんね。
それでは、また次の連載でお会いしましょう!
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- 「TAURI」でExcelのデータを読み書きしてWebページに表示してみよう
- 「TAURI」で「簡易RSSリーダー」を開発してみよう
- 「TAURI」で気象庁の「CSVデータ」を解析する
- 「Ace」を使って「TAURI」で「テキストエディタ」アプリを作ろう
- 「TAURI」で「ピアノ音楽」アプリを作ろう
- 「TAURI」にも必要な「Rust」の「クレート」を使う
- 「TAURI」で「画像ビューア」のサンプルアプリを作ろう
- 「TAURI」と「Rust」の「Option型」と「Result型」を使いこなそう
- 「TAURI」と「Rust」の「テスト」機能を試してみよう
- データベース:サーバにデータを保存しよう