Rust数据库ORM库diesel-derive-newtype的使用:简化Diesel中对新类型(Newtype)模式的支持与操作
Rust数据库ORM库diesel-derive-newtype的使用:简化Diesel中对新类型(Newtype)模式的支持与操作
diesel-derive-newtype
是一个简化在Diesel中使用Newtype模式的库。Newtype模式是Rust中一种常见的模式,通过将基础类型包装在元组结构体中来创建新类型。
安装
根据你使用的Diesel版本选择对应的diesel-derive-newtype
版本:
对于Diesel 2.1.x:
[dependencies]
diesel-derive-newtype = "2.1.0"
对于Diesel 2.0.x:
[dependencies]
diesel-derive-newtype = "~ 2.0.0"
对于Diesel 1.x:
[dependencies]
diesel-derive-newtype = "1.0"
#[derive(DieselNewType)]
这个库提供了一个自定义派生宏DieselNewType
,为单字段元组结构体(NewType)实现了ToSql
、FromSql
、FromSqlRow
、Queryable
、AsExpression
和QueryId
等trait。
示例
以下是内容中提供的示例代码:
#[macro_use]
extern crate diesel_derive_newtype;
#[derive(DieselNewType)] // 不需要单独一行
#[derive(Debug, Hash, PartialEq, Eq)] // Diesel要求的
struct MyId(i64);
这允许你在实体中使用MyId
结构体,就像使用基础类型一样:
table! {
my_items {
id -> Integer,
val -> Integer,
}
}
#[derive(Debug, PartialEq, Identifiable, Queryable)]
struct MyItem {
id: MyId,
val: u8,
}
完整示例Demo
下面是一个更完整的示例,展示如何在Diesel中使用diesel-derive-newtype
:
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_derive_newtype;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
// 定义Newtype
#[derive(DieselNewType, Debug, Hash, PartialEq, Eq, Clone)]
pub struct UserId(i32);
#[derive(DieselNewType, Debug, Hash, PartialEq, Eq, Clone)]
pub struct Email(String);
// 定义表结构
table! {
users {
id -> Integer,
email -> Text,
name -> Text,
}
}
// 定义User结构体
#[derive(Debug, PartialEq, Identifiable, Queryable, Insertable)]
#[diesel(table_name = users)]
pub struct User {
pub id: UserId,
pub email: Email,
pub name: String,
}
// 插入新用户
pub fn create_user(conn: &mut SqliteConnection, email: Email, name: &str) -> QueryResult<User> {
use self::users::dsl::*;
let new_user = User {
id: UserId(0), // 自增ID将由数据库生成
email,
name: name.to_string(),
};
diesel::insert_into(users)
.values(&new_user)
.execute(conn)?;
Ok(users.order(id.desc()).first(conn)?)
}
// 根据ID查询用户
pub fn find_user_by_id(conn: &mut SqliteConnection, user_id: UserId) -> QueryResult<User> {
use self::users::dsl::*;
users.filter(id.eq(user_id)).first(conn)
}
// 根据Email查询用户
pub fn find_user_by_email(conn: &mut SqliteConnection, user_email: Email) -> QueryResult<User> {
use self::users::dsl::*;
users.filter(email.eq(user_email)).first(conn)
}
局限性
DieselNewtype
派生不会创建新的数据库类型或Diesel序列化类型。也就是说,如果你有MyId(i64)
,这将使用Diesel的底层BigInt
类型,这意味着虽然你的Newtype可以在任何使用基础类型的地方使用,但基础类型或其他相同基础类型的Newtype也可以使用。
例如:
#[derive(Debug, Hash, PartialEq, Eq, DieselNewType)]
struct OneId(i64);
#[derive(Debug, Hash, PartialEq, Eq, DieselNewType)]
struct OtherId(i64);
#[derive(Debug, Clone, PartialEq, Identifiable, Insertable, Queryable)]
#[diesel(table_name = my_entities)]
pub struct MyEntity {
id: OneId,
val: i32,
}
fn darn(conn: &Connection) {
// 不应该允许构造错误类型,但确实允许
let OtherId: Vec<OtherId> = my_entities
.select(id)
.filter(id.eq(OtherId(1))) // 不应该允许用错误类型过滤
.execute(conn).unwrap();
}
许可证
diesel-derive-newtype
采用以下任一许可证:
- Apache License, Version 2.0
- MIT license
欢迎提交补丁和错误报告!
Rust数据库ORM库diesel-derive-newtype使用指南
diesel-derive-newtype
是一个简化Diesel ORM中新类型(Newtype)模式支持的Rust库。它允许你轻松地为自定义包装类型实现Diesel所需的trait,从而在数据库操作中使用这些类型。
什么是Newtype模式
Newtype模式是Rust中一种常见的模式,通过创建一个新的结构体来包装已有类型,提供类型安全和更清晰的语义:
struct UserId(i32);
struct Email(String);
安装
在Cargo.toml
中添加依赖:
[dependencies]
diesel-derive-newtype = "2.1.0"
diesel = { version = "2.1.0", features = ["postgres"] } # 根据你的数据库选择
基本用法
1. 为Newtype派生Diesel支持
use diesel::sql_types::Integer;
use diesel_derive_newtype::DieselNewType;
#[derive(DieselNewType)]
struct UserId(i32);
2. 在模型中使用
use diesel::prelude::*;
use diesel::dsl::*;
#[derive(Queryable, Insertable)]
#[diesel(table_name = users)]
struct User {
id: UserId,
name: String,
}
// 假设有对应的schema
table! {
users (id) {
id -> Integer,
name -> Text,
}
}
3. 数据库操作示例
// 插入
let new_user = User {
id: UserId(1),
name: "Alice".to_string(),
};
diesel::insert_into(users::table)
.values(&new_user)
.execute(conn)?;
// 查询
let user = users::table
.filter(users::id.eq(UserId(1)))
.first::<User>(conn)?;
高级特性
自定义SQL类型
如果你的Newtype对应特定的SQL类型:
#[derive(DieselNewType)]
#[diesel(pg_type = "Text")] // 对于PostgreSQL的Text类型
struct Email(String);
多个字段的Newtype
#[derive(DieselNewType)]
struct Point {
x: i32,
y: i32,
}
实现其他trait
#[derive(DieselNewType, Debug, Clone, PartialEq)]
struct UserId(i32);
实际示例
完整用户系统示例
use diesel::prelude::*;
use diesel_derive_newtype::DieselNewType;
#[derive(DieselNewType, Debug, Clone, PartialEq)]
struct UserId(i32);
#[derive(DieselNewType, Debug, Clone)]
#[diesel(pg_type = "Text")]
struct Email(String);
table! {
users (id) {
id -> Integer,
email -> Text,
name -> Text,
}
}
#[derive(Queryable, Insertable, Debug)]
#[diesel(table_name = users)]
struct User {
id: UserId,
email: Email,
name: String,
}
// 使用示例
fn create_user(conn: &mut PgConnection) -> QueryResult<User> {
let new_user = User {
id: UserId(1),
email: Email("alice@example.com".to_string()),
name: "Alice".to_string(),
};
diesel::insert_into(users::table)
.values(&new_user)
.get_result(conn)
}
fn find_user(conn: &mut PgConnection, user_id: UserId) -> QueryResult<User> {
users::table
.filter(users::id.eq(user_id))
.first(conn)
}
完整示例代码
下面是一个更完整的示例,展示了如何使用diesel-derive-newtype构建一个简单的用户管理系统:
// 导入必要的库
use diesel::{prelude::*, PgConnection};
use diesel_derive_newtype::DieselNewType;
use serde::{Serialize, Deserialize};
// 定义Newtype包装类型
#[derive(DieselNewType, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UserId(i32);
#[derive(DieselNewType, Debug, Clone, Serialize, Deserialize)]
#[diesel(pg_type = "Text")]
pub struct Email(String);
// 定义数据库表结构
table! {
users (id) {
id -> Integer,
email -> Text,
name -> Text,
age -> Nullable<Integer>,
}
}
// 定义用户模型
#[derive(Queryable, Insertable, AsChangeset, Debug, Serialize, Deserialize)]
#[diesel(table_name = users)]
pub struct User {
pub id: UserId,
pub email: Email,
pub name: String,
pub age: Option<i32>,
}
// 定义新建用户结构体
#[derive(Insertable, Debug, Serialize, Deserialize)]
#[diesel(table_name = users)]
pub struct NewUser {
pub email: Email,
pub name: String,
pub age: Option<i32>,
}
// 用户操作实现
impl User {
// 创建新用户
pub fn create(conn: &mut PgConnection, new_user: NewUser) -> QueryResult<User> {
diesel::insert_into(users::table)
.values(&new_user)
.get_result(conn)
}
// 根据ID查找用户
pub fn find_by_id(conn: &mut PgConnection, user_id: UserId) -> QueryResult<User> {
users::table
.filter(users::id.eq(user_id))
.first(conn)
}
// 更新用户信息
pub fn update(&self, conn: &mut PgConnection) -> QueryResult<User> {
diesel::update(users::table.find(self.id))
.set(self)
.get_result(conn)
}
// 删除用户
pub fn delete(conn: &mut PgConnection, user_id: UserId) -> QueryResult<usize> {
diesel::delete(users::table.find(user_id))
.execute(conn)
}
}
// 示例使用
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 假设已经建立了数据库连接
let database_url = "postgres://username:password@localhost/mydatabase";
let mut conn = PgConnection::establish(database_url)?;
// 创建新用户
let new_user = NewUser {
email: Email("bob@example.com".to_string()),
name: "Bob".to_string(),
age: Some(30),
};
let created_user = User::create(&mut conn, new_user)?;
println!("Created user: {:?}", created_user);
// 查询用户
let user = User::find_by_id(&mut conn, created_user.id)?;
println!("Found user: {:?}", user);
// 更新用户
let mut user_to_update = user;
user_to_update.name = "Robert".to_string();
let updated_user = user_to_update.update(&mut conn)?;
println!("Updated user: {:?}", updated_user);
// 删除用户
let deleted_count = User::delete(&mut conn, updated_user.id)?;
println!("Deleted {} users", deleted_count);
Ok(())
}
注意事项
- 确保你的Newtype内部类型与数据库列类型兼容
- 对于复杂的自定义类型,可能需要手动实现一些trait
- 在使用前确保你的Diesel版本与diesel-derive-newtype兼容
这个库极大地简化了在Diesel中使用Newtype模式的工作,使得代码更类型安全且语义清晰,同时减少了大量样板代码。