最近在折腾rust与数据库集成,选了tidb cloud serverless tier 作为数据源。tidb 无疑是近五年来最优秀的国产开源分布式数据库,tidb cloud serverless tier作为pingcap旗下的云产品方便又经济,这次使用还有一些小惊喜,这个后文再说。硬广时间到这里,咱们说正事儿。
tidb cloud serverless tier 的使用文档还是很全面的,详细情况请参考:使用 tidb cloud (serverless tier) 构建 tidb 集群(https://docs.pingcap.com/zh/tidb/stable/dev-guide-build-cluster-in-cloud#%e7%ac%ac-1-%e6%ad%a5%e5%88%9b%e5%bb%ba%e5%85%8d%e8%b4%b9%e9%9b%86%e7%be%a4)
集群建立完成后,tidb cloud serverless tier 有个小功能是可以显示主流客户端以及流行编程语言的连接代码。包括: mysqlcli、mycli、jdbc、python、golang以及nodejs。
嗯?rust 的代码在哪儿?很遗憾没有rust的代码。而且为了安全起见,tidb cloud serverless tier 貌似只支持安全连接。在查找文档过程中rust 的 数据库驱动和很多orm文档中也没有关于安全详细的描述,不少思路是在issues里面给出的。索性把rust 连接 mysql 主流方式的安全连接代码都记录下来,一来给自己留个备忘,二来给需要的同学做个提示。
以下实例所使用的的标的建表语句如下
create table if not exists sample ( id bigint not null , name varchar(128) not null, gender tinyint not null, mobile varchar(11) not null, create_time datetime not null, update_time datetime not null, primary key(id) ) engine=innodb default charset=utf8;mysql rust driver rust-mysql-simple(https://github.com/blackbeam/rust-mysql-simple),纯 rust 实现的 mysql 驱动。
依赖
[dependencies]# mysql originmysql = *代码use chrono::local;use mysql::prelude::*;use mysql::*;use rbatis::snowflake::new_snowflake_id;use serde::deserialize;use serde::serialize;pub const table_name: &str = sample;#[derive(clone, debug, serialize, deserialize)]pub struct bizorigin { pub id: i64, pub name: string, pub gender: u8, pub mobile: string, pub create_time: option, pub update_time: option,}fn main() -> std::result { let fmt = %y-%m-%d %h:%m:%s; // 原生方式连接 let cert_path = std::new(/etc/ssl/cert.pem); let ssl_opts = sslopts::default().with_root_cert_path(some(cert_path)); let opts = optsbuilder::new() .ip_or_hostname(some(gateway01.us-east-19.prod.aws.tidbcloud.com)) .tcp_port(4000) .user(some(tidbcloudtier.root)) .pass(some(xxxxxxxxxxxx)) .ssl_opts(ssl_opts) .db_name(some(test)); let mut conn_origin = conn::new(opts)?; let (_, cipher_origin): (value, string) = show status like 'ssl_cipher' .first(&mut conn_origin)? .unwrap(); println!(>>>>> cipher in use from origin: {}, cipher_origin); let create_statment = format!( create table if not exists {} ( id bigint not null , name varchar(128) not null, gender tinyint not null, mobile varchar(11) not null, create_time datetime not null, update_time datetime not null, primary key(id) ) engine=innodb default charset=utf8;, table_name ); conn_origin.query_drop(create_statment)?; let bizes = vec![ bizorigin { id: new_snowflake_id(), name: bob.to_string(), gender: 1, mobile: 13037777876.to_string(), create_time: some(local::now().format(fmt).to_string()), update_time: some(local::now().format(fmt).to_string()), }, bizorigin { id: new_snowflake_id(), name: jecika.to_string(), gender: 0, mobile: 13033457876.to_string(), create_time: some(local::now().format(fmt).to_string()), update_time: some(local::now().format(fmt).to_string()), }, ]; conn_origin.exec_batch( rinsert into sample (id,name,gender,mobile,create_time,update_time) values (:id,:name,:gender,:mobile,:create,:update), bizes.iter().map(|p| -> params { params! { id=>p.id, name=>p.name.to_owned(), gender=>p.gender.to_owned(), mobile=>p.mobile.to_owned(), create=>p.create_time.as_ref(), update=>p.update_time.as_ref() } }), )?; // let's select payments from database. type inference should do the trick here. let selected_bizs = conn_origin.query_map( select id,name,gender,mobile,create_time,update_time from sample, |(id, name, gender, mobile, create_time, update_time)| bizorigin { id, name, gender, mobile, create_time, update_time, }, )?; println!(selected result {:?}, selected_bizs); ok(())}代码并不复杂,首先创建sslopts,指定ca文件的位置;然后使用optsbuilder 生成链接配置信息;最后创建connection。后面是执行表创建以及验证链接,最后是对标的 insert 和 select 操作。 sqlx
sqlx(https://github.com/launchbadge/sqlx)是纯 rust 编写的异步 sql crate。
依赖
[dependencies]# sqlxsqlx = 0.6.2代码use futures::trystreamext;use sqlx::mysql::mysqlpooloptions;#[tokio::main]async fn main() { let sqlx_opts = sqlx::new() .host(gateway01.us-east-19.prod.aws.tidbcloud.com) .port(4000) .database(test) .username(tidbcloudtier.root) .password(xxxxxxxxxxxx) .ssl_ca(/etc/ssl/cert.pem); let pool = mysqlpooloptions::new() .connect_with(sqlx_opts) .await .unwrap(); let mut rows = sqlx::query(select * from sample).fetch(&pool); while let some(row) = rows.try_next().await.unwrap() { println!(row is {:?}, row); }}seaorm seaorm(https://github.com/seaql/sea-orm)是在 sqlx 之上构建的 orm 框架。
依赖
[dependencies]# seaormsqlx = 0.6.2sea-orm = { version = 0.10.6, features = [ sqlx-mysql, runtime-async-std-native-tls, macros ] }代码use sea_orm::connectiontrait;use sea_orm::dbbackend;use sea_orm::sqlxmysqlconnector;use sea_orm::{fromqueryresult, statement as sea_statment};use sqlx::mysqlpool;#[derive(debug, fromqueryresult)]pub struct seaormbiz { pub id: i64, pub name: string, pub gender: option, pub mobile: string, pub create_time: chrono::naivedatetime, pub update_time: chrono::naivedatetime,}#[tokio::main]async fn main() { let sqlx_opts = sqlx::new() .host(gateway01.us-east-19.prod.aws.tidbcloud.com) .port(4000) .database(test) .username(tidbcloudtier.root) .password(xxxxxxxxx) .ssl_ca(/etc/ssl/cert.pem); let pool = mysqlpool::connect_with(sqlx_opts).await.unwrap(); let db = sqlxmysqlconnector::from_sqlx_mysql_pool(pool); let rs = db .execute(sea_statment::from_string( db.get_database_backend(), select 1 from dual;.to_string(), )) .await; println!(>>>>> cipher in use from sea_orm:{:?}, rs); let biz: vec = seaormbiz::from_sql_and_values( dbbackend::mysql, r#select * from sample;#, vec![], )) .all(&db) .await .unwrap(); println!(>>>>> selet rs is {:?}, biz);}seaorm 依赖 sqlx。首先构建 sqlx::mysqlconnectoptions 然后根据 mysqlconnectoptions 构建 sqlx::mysqlpool 最后构建 sea_orm::sqlxmysqlconnector 用于与 mysql 通信。 rbatis
rbatis:https://github.com/rbatis/rbatis
依赖
[dependencies]# rbatis integrationrbs = 0.1.13rbatis = 4.0.44rbdc-mysql = 0.1.18代码use rbatis::fastdatetime;use rbatis::rbatis;use rbdc_mysql::mysqlconnectoptions;use rbdc_mysql::{driver::mysqldriver, options::mysqlsslmode as rbdc_mysqlsslmode};use rbs::to_value;use serde::{deserialize, serialize};use std::hashmap;pub const table_name: &str = sample;#[derive(clone, debug, serialize, deserialize)]pub struct bizrbatis { pub id: option, pub name: option, pub gender: option, pub mobile: option, pub create_time: option, pub update_time: option,}rbatis::crud!(bizrbatis {}, table_name);#[tokio::main]async fn main() -> std::result { // rbatis 连接 let rb = rbatis::new(); let opt = mysqlconnectoptions::new() .host(gateway01.us-east-19.prod.aws.tidbcloud.com) .port(4000) .database(test) .username(tidbcloudtier.root) .password(xxxxxxxxxx) .ssl_mode(rbdc_mysqlsslmode::verifyidentity) .ssl_ca(/etc/ssl/cert.pem); rb.init_opt(mysqldriver {}, opt).unwrap(); rb.get_pool().unwrap().resize(3); let sql_show_ssl_cipher = show status like 'ssl_cipher'; let cipher_rbatis = rb .fetch_decode::>>(sql_show_ssl_cipher, vec![]) .await; println!(>>>>> cipher in use from rbatis: {:?}, cipher_rbatis); let sql_select_one = format!(select * from {} limit ?;, table_name); let row = rb .fetch_decode::(&sql_select_one, vec![to_value!(1)]) .await; println!(>>>>> rbatsis select result={:?}, row); ok(())}首先,新建一个rbatis struct;构建 rbdc_mysql::mysqlconnectoptions (rbdc 相当于java体系里的jdbc,是rbatis的衍生项目);最后通过配置好的 rbdc_mysql::mysqlconnectoptions 初始化 rbatis。 后记
在这次实验中笔者也试图使用 diesel(https://github.com/diesel-rs/diesel) 建立 mysql 安全连接,不过在编译的时候失败,未入门先放弃。diesel 由于开发时间久远,彼时各个数据库的 rust 原生驱动缺失,所以大量采用 c/c++ driver进行构建,这次编译失败也是因为在macos上找不到 mysqlclient 导致。有对 diesel 强依赖的同学可以继续探索。
再来说说对 seaorm 和 rbatis 的直观感受。seaorm 构建实体比较麻烦,如果不是通过工具手工构建实体比较烧脑;实体中包含各种与其他实体的关系;动态sql 可以通过 sea_query 工具包来构建。rbatis 构建实体心智负担就小很多,一张表一个实体;动态 sql 可以通过 htmlsql 和 pysql 实现,sql 与代码充分解耦。rbdc 作为 rbatis 的衍生项目,显然是要做 rust 生态的jdbc。从感觉上来讲 seaorm 更像 hibernate;而 rbatis 是复刻 mybatis。
数据库是应用程序打交道最多的外部资源,相关话题也很多,有机会再和大家聊聊 rust 与 数据库打交道的更多细节。
LED显示屏用底部填充胶应用案例分析
如何实现模块与模块之间点对点的通信
IOTE 2023深圳国际物联网展圆满结束!落幕不散场,IoT再启数智新希望
深海泰坦X7Ti-S怎么样?深海泰坦X7Ti-S评测:极佳体验的4K屏与机械键盘
如何选择接触器的大小
安全连接 TiDB/Mysql编程案例分析
pid调节器中系数是什么有什么用
对电流互感器安装的要求是什么?
新款雪铁龙e-C4电动汽车将于本月正式到货
适用于PCB的最佳脚印焊盘布局指南
一栋楼和它见证的互联网创业时代
为什么现在智能手机使用Type-C接口的越来越多
电子膨胀阀坏了会怎样_电子膨胀阀怎么测好坏
单片机能代替PLC吗?
戴姆勒汽车共享服务品牌Car2Go将与宝马的DriveNow共享服务
传微软Windows Creators Update 4月推出
通过智能化妆镜将美妆黑科技融入到我们的生活中
iPad2内部(手机)器件大曝光
5G+AI如何引领下一个时代的详细资料说明
基于解码芯片AD2S1205的旋变位置解码系统方案