まさひlog

趣味で行っていることをのんびりとログとして残していきます

RustでMySQLを操作してみる(書き込み)

前回はRustでMySQLの読み込み操作について書きました。

mshr-t.hatenablog.jp

引き続き、書き込み操作について書いていきます。
今回もdieselチュートリアル参考にしていきます。
http://diesel.rs/guides/getting-started/

読み込みではチュートリアルとの違いはそこまでなかったのですが、
書き込みに関しては、異なる部分がほんの少し増えました。

書き込み処理追加

  • src/models.rs
#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}

// 以下、追加部分
use super::schema::posts;

#[derive(Insertable)]
#[table_name="posts"]
pub struct NewPost<'a> {
    pub title: &'a str,
    pub body: &'a str,
}

modelsには、postsテーブルを使うことを宣言しています。
そのため、schema.rsからpostsを読み込んでいます。

NewPostの構造体には、titleとbody定義しています。
これにより、DBに入力する際はtitleとbobyに対して値を代入することができます。

  • src/lib.rs
#[macro_use]
extern crate diesel;
extern crate dotenv;

pub mod schema;
pub mod models;

use diesel::prelude::*;
use dotenv::dotenv;
use diesel::mysql::MysqlConnection;
use std::env;

pub fn establish_connection() -> MysqlConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    MysqlConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}

// 以下、追加部分
use self::models::{NewPost, Post};
pub fn create_post(conn: &MysqlConnection, title: &str, body: &str) -> Post {
    use self::schema::posts::dsl::{id, posts};

    let new_post = NewPost {
        title: title,
        body: body,
    };

    diesel::insert_into(posts)
        .values(&new_post)
        .execute(conn)
        .expect("Error saving new post");

    posts.order(id.desc()).first(conn).unwrap()
}

postsテーブルに書き込みを行うために必要な処理を書いています。
読み込み同様にPgConnectionの部分をMysqlConnectionに置き換えます。

チュートリアルではdiesel::insert_into以下の処理は次のようになっています。

diesel::insert_into(posts::table)
        .values(&new_post)
        .get_result(conn)
        .expect("Error saving new post")

PostgreSQLでは.get_result(conn)となっていますが、 MySQLでは execute(conn)を使います。

dieselのドキュメントには、PostgreSQLなどのReturning句をサポートするものはexecuteの代わりにget_resultsを使うと書いてあります。 https://docs.diesel.rs/diesel/fn.insert_into.html#examples

下記に関しては、自分で付け加えた部分になります。 戻り値をPostにしているため、行っていることとしてはidで降順にし、Postが返るようにしています。 ここでidを使用しているため、use self::schema::posts::dsl::{id, posts};でidを宣言している感じですね。

posts.order(id.desc()).first(conn).unwrap()

以下のwrite_post.rsに関してはチュートリアルと変更はありません。

  • src/bin/write_post.rs
extern crate diesel_demo;
extern crate diesel;

use self::diesel_demo::*;
use std::io::{stdin, Read};

fn main() {
    let connection = establish_connection();

    println!("What would you like your title to be?");
    let mut title = String::new();
    stdin().read_line(&mut title).unwrap();
    let title = &title[..(title.len() - 1)]; // Drop the newline character
    println!("\nOk! Let's write {} (Press {} when finished)\n", title, EOF);
    let mut body = String::new();
    stdin().read_to_string(&mut body).unwrap();

    let post = create_post(&connection, title, &body);
    println!("\nSaved draft {} with id {}", title, post.id);
}

#[cfg(not(windows))]
const EOF: &str = "CTRL+D";

#[cfg(windows)]
const EOF: &str = "CTRL+Z";

ここまでできたら
docker-compose run --rm rust cargo run --bin write_postで実行します。

What would you like your title to be?
write_test

Ok! Let's write write_test (Press CTRL+D when finished)

hello  
Saved draft write_test with id 1

sqlに登録されているか確認すると

mysql> select * from posts;
+----+------------+-------+-----------+
| id | title      | body  | published |
+----+------------+-------+-----------+
|  1 | write_test | hello |         0 |
+----+------------+-------+-----------+
1 row in set (0.00 sec)

ちゃんと書き込みできていますね!

まとめ

前回に引き続き、dieselを用いたMySQL操作を行いました。 チュートリアルには、更新と削除処理もあるので試してみてください。

チュートリアルを一通り試すと、簡単なTODOリスト的なものはできそうですね!