。
4 数据结构设计
在今天的实践中,我们希望可以把weid保存到本地数据库中。
我们这次选择的是 Sqlite 数据库,在Rust – ORM 选择上,我们选择的是 Diesel,这个项目有 6.8k Stars。
https://github.com/diesel-rs/diesel
Tips: 接触新库时,我们可以通过学习 Repo 中的 Examples,来掌握 Repo 的用法。
因为weid中,前半部分did:weid
是不变的,所以我们只需保存chain_id
和`bs-specific-string
即可。
在Rust中数据结构如下:
pub struct Weid { id: i32, chain_id: i32, //当然也可以同样设置为 String addr: String, created_at: NaiveDateTime, // 创建时间 updated_at: NaiveDateTime, // 更新时间 }
数据库的创建语句如下:
CREATE TABLE weids ( id INTEGER PRIMARY KEY AUTOINCREMENT, addr TEXT NOT NULL, chain_id INTEGER, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP );
我们用标准规范创建数据库迁移文件夹migration
:
migrations └── 2020-05-13-105400_create_weids ├── down.sql └── up.sql
其中,up.sql
的内容即是上面的内容:
CREATE TABLE weids ( id INTEGER PRIMARY KEY AUTOINCREMENT, addr TEXT NOT NULL, chain_id INTEGER, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP );
down.sql
的内容就是移除weids
表:
DROP TABLE weids;
5 数据库的创建与建表(Create&Migrate)
此处我们使用diesel
使用的命令行工具。
(1)复制必要文件到项目目录下
从 Diesel 的代码仓库中 Clone 代码到本地。
https://github.com/diesel-rs/diesel
将 Repo 中的 diesel
、diesel_cli
、diesel_derives
、diesel_migrations
复制到项目根目录下。
在项目根目录下新建bin
文件夹。
编译 Diesel Repo,将target/debug/diesel
文件复制到bin
目录下。
(2)配置环境变量
我们需要设置两个环境变量:
- DATABASE_URL——数据库路径
- BACKEND———数据库类型
直接执行如下命令即可:
export DATABASE_URL="examples.db" export BACKEND="sqlite"
(3)创建数据库与建表
执行如下命令:
./bin/diesel database reset
顺利的话,会出现如下返回:
同时根目录下出现examples.db
文件。
6 models.rs
我们在src
目录下创建models.rs
文件,在其中定义结构体Weid
与NewWeid
,定义 schema(模式)weids
。
Scheme,可以简单的理解为我们告诉程序数据库中有哪些字段,这样程序才能顺利对接数据库。
models.rs
:
use chrono::NaiveDateTime; #[cfg(test)] use diesel::debug_query; use diesel::insert_into; use diesel::prelude::*; #[cfg(test)] use diesel::sqlite::Sqlite; use serde_derive::Deserialize; use std::error::Error; pub mod schema { diesel::table! { weids { id -> Integer, chain_id -> Integer, addr -> Text, created_at -> Timestamp, updated_at -> Timestamp, } } } use schema::weids; #[derive(Insertable)] #[table_name = "weids"] pub struct NewWeid { pub chain_id: i32, pub addr: String, } #[derive(Queryable, PartialEq, Debug)] pub struct Weid { pub id: i32, pub chain_id: i32, pub addr: String, pub created_at: NaiveDateTime, pub updated_at: NaiveDateTime, } pub fn insert_default_values(conn: &SqliteConnection) -> QueryResult<usize> { use schema::weids::dsl::*; insert_into(weids).default_values().execute(conn) }
代码解析:
注: 以下内容对《Rust 程序设计语言(第一版)》有所参考。
https://kaisery.gitbooks.io/rust-book-chinese/content/content/Traits.html
trait 是一个告诉 Rust 编译器一个类型必须提供哪些功能语言特性。
例如,我们可以为结构体Circle
实现HasArea
这个trait
:
struct Circle { x: f64, y: f64, radius: f64, } trait HasArea { fn area(&self) -> f64; } impl HasArea for Circle { fn area(&self) -> f64 { std::f64::consts::PI * (self.radius * self.radius) } }
如你所见,trait
块与impl
看起来很像,不过我们没有定义一个函数体,只是函数标记。当我们impl
一个trait时,我们使用impl Trait for Item
,而不是仅仅impl Item
。
重复的实现像Debug
和Default
这样的 trait 会变得很无趣。为此,Rust 提供了一个[属性](https://kaisery.gitbooks.io/rust-book-chinese/content/content/Attributes 属性.md)来允许我们让 Rust 为我们自动实现 trait:
#[derive(Debug)] struct Foo; fn main() { println!("{:?}", Foo); }
Rust 1.15
中引入了自定义derive
特性,从而让derive
有了更多的想象空间。
我们通过#[derive(Insertable)]
与#[derive(Queryable, PartialEq, Debug)]
,让该结构体具备可插入数据库,或从数据库查询的特性。
7 main.rs
main.rs 的内容如下所示:
extern crate pretty_env_logger; pub mod models; use diesel::prelude::*; use std::env; use dotenv::dotenv; use weid_light_client::WeIdRestService; use models::*; use models::schema::weids; use models::schema::weids::dsl::*; #[macro_use] extern crate log; fn main(){ pretty_env_logger::init(); // create data let sqlite_conn = establish_connection(); create_weid(&sqlite_conn, 1, "34be11396f3a91c5Ab5A1220e756C6300FB2b20a"); // query data let results = weids.load::<Weid>(&sqlite_conn) .expect("Error loading weids"); // log weids info!("Displaying {} weids", results.len()); for weid in results{ info!("did:weid:{}:{}", weid.chain_id, weid.addr); } } pub fn establish_connection() -> SqliteConnection { dotenv().ok(); let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); SqliteConnection::establish(&database_url) .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)) } pub fn create_weid(conn: &SqliteConnection, c_id: i32, address: &str) -> usize { let new_weid = NewWeid {chain_id: c_id, addr: address.to_string()}; diesel::insert_into(weids::table) .values(&new_weid) .execute(conn) .expect("Error saving new weid") }
代码解析:
establish_connection
直接拷贝自 Diesel 的Examples,作用是根据环境变量中的DATABASE_URL
连接sqlite
数据库。
create_weid
函数中,我们先创建一个 NewWeid 结构体对象,然后通过diesel::insert_into
函数将新建的结构体对象插入数据库。
通过loads
函数,我们从数据库中加载Weid
结构体。
8 运行
执行RUST_LOG=trace cargo run
。
我们成功向数据库插入一条 weid 数据,并读取 出weid 数据。
关于diesel
的更多更详细的用法,见:
https://diesel.rs/guides/getting-started
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。