まさひlog

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

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

RustでAPIサーバを作成するにあたり、MySQLを使いたいと思ったため どんな感じで使うことができるのか試してみました。

個人的には今までRailsで開発を行っていたこともあり、 ActiveRecordのようなORマッパーがよかったため、dieselを使用します。

今回dieselチュートリアルを参考にしています

http://diesel.rs/guides/getting-started/

環境

プロジェクト作成

ディレクトリ作成

mkdir diesel_demo
cd diesel_demo

この中にdockerの準備していきます

docker

  • docker/rust/Dockerfile
FROM rust:1.31

WORKDIR /diesel_demo

RUN apt-get update -y && apt-get upgrade -y
RUN apt-get install -y mysql-client

RUN cargo install diesel_cli --no-default-features --features mysql

imageは公式が配布しているもの
https://hub.docker.com/_/rust/

docker内からMySQLを操作するため、mysql-clientをインストール

diesel_cliをインストールすることでスキーマ管理できます。

  • docker-compose.yml
version: '2'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - 3306:3306
    volumes:
      - ./docker/mysql:/var/lib/mysql
    container_name: demo_sql
  rust:
    build:
      context: .
      dockerfile: ./docker/rust/Dockerfile
    environment:
      USER: root
    volumes:
      - ./:/diesel_demo
    depends_on:
      - db
    container_name: demo_rust

ここまで準備できたら以下を実行しプロジェクトを作成していきます。

docker-compose build
docker-compose run --rm rust cargo init 

生成されたCargo.tomlに以下を追記し、docker-compose run --rm rust cargo install

[dependencies]
diesel = { version = "1.4.0", features = ["mysql"] }
dotenv = "0.9.0

diesel_cliによるセットアップ

初めにdieselMySQLに接続するために.envを用意

echo DATABASE_URL=mysql://root:password@db/diesel_demo > .env

次に以下コマンドを実行します

docker-compose run --rm rust diesel setup

これでデータベースにdiesel_demoが作成されます

docker-compose run --rm rust diesel migration generate create_posts

上記コマンドで以下のファイルが生成されるのでそれぞれ記述していきます。 migrations/20160815133237_create_posts/up.sql migrations/20160815133237_create_posts/down.sql

CREATE TABLE posts (
  id INTEGER AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(255) NOT NULL,
  body TEXT NOT NULL,
  published BOOLEAN NOT NULL DEFAULT 0
) DEFAULT CHARACTER SET= utf8mb4;
DROP TABLE posts

sqlファイルが書けたあと実行し、テーブル作成

docker-compose run --rm rust  diesel migration run

作成したテーブルを削除するときはdiesel migration revertでできる

DBの読み込み

以下のファイルを用意していきます 基本的にチュートリアルと変わらないため説明は省きます。 異なる部分としては、チュートリアルPostgreSQLのため、 部分的にMySQLに書き換える必要があります。

  • src/models.rs
#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub published: bool,
}
  • 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))
}
  • src/bin/show_posts.rs
extern crate diesel_demo;
extern crate diesel;

use self::diesel_demo::*;
use self::models::*;
use self::diesel::prelude::*;

fn main() {
    use diesel_demo::schema::posts::dsl::*;

    let connection = establish_connection();
    let results = posts.filter(published.eq(true))
        .limit(5)
        .load::<Post>(&connection)
        .expect("Error loading posts");
    
    println!("Displaying {} posts", results.len());
    for post in results {
        println!("{}", post.title);
        println!("-----------\n");
        println!("{}", post.body);
    }
}

実行する

サンプルデータをpostsテーブルに用意し、確認します。

insert into posts(title, body, published) values("test","おはよう",1);
insert into posts(title, body, published) values("test2","こんにちは",0);
insert into posts(title, body, published) values("test3","こんばんは",1);

mysql> select * from posts;
+----+-------+-----------------+-----------+
| id | title | body            | published |
+----+-------+-----------------+-----------+
|  1 | test  | おはよう        |         1 |
|  2 | test2 | こんにちは      |         0 |
|  3 | test3 | こんばんは      |         1 |
+----+-------+-----------------+-----------+
3 rows in set (0.01 sec)
docker-compose run --rm rust cargo run --bin show_posts

=> Displaying 2 posts
test
-----------

おはよう
test3
-----------

こんばんは

実行結果を見るとpublishedがtrue(1)のものが表示されましたね。 これはposts.filter(published.eq(true))publishedがtrueのものだけに絞り込んでいるからですね この部分をfalseに置き換えるとpublishedが0のレコードが表示されます。

Displaying 1 posts
test2
-----------

こんにちは

まとめ

diesel_cliスキーマ管理が簡単にできますね!
今回は読み込みだけですがチュートリアルもあるので割と簡単にDB読み込みができました。