#postgresql #async-orm #orm #async #query-builder

pg-worm

异步、完全类型化且直接的 PostgreSQL 数据库 ORM

7 个版本 (破坏性更新)

0.7.0 2023年8月30日
0.6.2 2023年8月29日
0.5.0 2023年7月25日
0.4.0 2023年6月8日
0.1.0 2023年5月18日

#647数据库接口

Download history 8/week @ 2024-04-01

每月72次下载

MIT/Apache

73KB
1K SLoC

pg-worm

Latest Version GitHub Actions Testing docs license

PostgreSQL 的 Worst ORM

pg-worm 是一个简单的、完全类型化的、异步的 PostgreSQL ORM 和查询构建器。好吧,至少这是目标。

功能/为什么选择 pg-worm

  • 现有的 ORM 不是 async,需要你编写迁移或使用 cli。 pg-worm 的明确目标是 简单,并且只需定义你的类型,无需设置。

  • pg-worm 还具有 内置连接池简洁的语法

  • pg-worm 不会阻碍你 - 在享受其他功能的同时,可以轻松地包含原始查询。

用法

这个库基于 tokio_postgres,并打算与 tokio 一起使用。

幸运的是,使用 pg-worm 非常简单。

只需为你的类型派生 Model 特性,连接到你的数据库,你就可以出发了!

以下是一个快速示例

// Import the prelude to get started quickly
use pg_worm::prelude::*;

#[derive(Model)]
struct Book {
    // An auto-generated primary key
    #[column(primary_key, auto)]
    id: i64,
    title: String,
    author_id: i64
}

#[derive(Model)]
struct Author {
    #[column(primary_key, auto)]
    id: i64,
    name: String
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // First create a connection. This can be only done once.
    Connection::build("postgres://postgres:postgres@localhost:5432").connect()?;

    // Then, create tables for your models. 
    // Use `try_create_table!` if you want to fail if a
    // table with the same name already exists.
    //
    // `force_create_table` drops the old table,
    // which is useful for development.
    //
    // If your tables already exist, skip this part.
    force_create_table!(Author, Book).await?;

    // Next, insert some data.
    // This works by passing values for all
    // fields which aren't autogenerated.
    Author::insert("Stephen King").await?;
    Author::insert("Martin Luther King").await?;
    Author::insert("Karl Marx").await?;
    Book::insert("Foo - Part I", 1).await?;
    Book::insert("Foo - Part II", 2).await?;
    Book::insert("Foo - Part III", 3).await?;

    // Let's start with a simple query for all books:
    let books = Book::select().await?; // Vec<Book>
    assert_eq!(books.len(), 3);

    // You can also search for a specific book.
    // Adding a `WHERE` clause is as simple as
    // calling a method on the respective field:
    let book = Book::select_one()
        .where_(Book::title.eq(&"Foo - Part I".to_string()))
        .await?; // Option<Book>
    assert!(book.is_some());

    // Or update exsisting records:
    let books_updated = Book::update()
        .set(Book::title, &"Foo - Part III".to_string())
        .where_(Book::title.eq(&"Foo - Part II".to_string()))
        .await?; // u64
    assert_eq!(books_updated, 1);

    // Or delete a book, you don't like:
    let books_deleted = Book::delete()
        .where_(Book::title.eq(&"Foo - Part III".to_string()))
        .await?; // u64
    assert_eq!(books_deleted, 2);

    Ok(())
}

如果你想查看更多代码示例,请查看 测试目录

查询构建器

如上所示,pg-worm 允许你通过在所谓的 '构建器' 上链式调用方法来构建查询。对于每种查询类型,pg-worm 都提供相应的构建器(除了 INSERT,它以不同的方式处理)。

这些构建器公开了一组用于构建查询的方法。以下是他们中的几个

方法 描述 可用性
where_ WHERE 子句附加到查询。 所有构建器(SelectUpdateDelete
where_raw where_ 相同,但你可以传递原始 SQL。 所有构建器(SelectUpdateDelete
set SET 列的值。注意:必须在执行查询之前至少调用此方法一次。 Update
limitoffset LIMITOFFSET 附加到查询。 Select

使用 WHERE 进行过滤

.where_() 可用于轻松地将 WHERE 子句包含在查询中。

这是通过传递一个由调用相应列的方法构建的 Where 对象来完成的。 pg-worm 会自动为你的 Model 的每个字段构造一个常量。

一个实际例子可能看起来像这样

let where_: Where<'_> = MyModel::my_field.eq(&5);

可用方法

目前,已实现了以下方法

函数 描述 可用性
eq 检查相等。 任何类型
gtgteltlte 检查此列的值是否大于或等于某个其他值。 实现 PartialOrd 的任何类型。注意:并不能保证Postgres支持类型为 PartialOrd 的操作符。在使用之前,请务必检查Postgres文档。
nullnot_null 检查列是否为 NULL 任何 Option<T>。所有其他类型都不是可空类型,因此保证不为 NULL
containscontains_notcontains_allconatains_nonecontains_any 数组操作。检查此列的数组是否包含一个值、一个 包含的值或另一个数组的任意/所有/无值。 任何 Vec<T>

布尔逻辑

您还可以使用标准布尔逻辑链接/修改这些过滤器

Book::select()
    .where_(!Book::id.eq(&1) & Book::id.gt(&3))
    .await?;
操作符/方法 描述
!.not() 使用逻辑 NOT 取消一个过滤器的过滤。
&.and() 使用逻辑 AND 结合两个过滤器。
||.or() 使用逻辑 OR 结合两个过滤器。

执行查询

构建完查询后,您只需调用 .await。这将把构建器转换为 Query 对象,然后异步执行。

执行查询总是会返回一个 Result

原始查询

虽然这些功能很棒,但它们并不适用于所有应用程序。这就是为什么您可以轻松地执行自定义查询,同时仍然利用自动解析等优势。

// NOTE: You have to pass the exact type that PostgreSQL is 
// expecting. Doing otherwise will result in a runtime error.
let king_books = Book::query(r#"
        SELECT * FROM book 
        JOIN author ON author.id = book.author_id
        WHERE POSITION(? in author.name) > 0 
    "#, 
    vec![&"King".to_string()]
).await?;
assert_eq!(king_books.len(), 2);

有关查询构建器上的 .where_raw,请参阅相关内容,您可以通过它传递一个原始条件,而无需自己编写整个查询。

事务

pg-worm 也支持事务。您可以在 Transaction 中轻松执行任何查询,并且只有在您满意时才提交。

当事务被丢弃时,事务会自动回滚,除非它们之前已被提交。

以下是一个示例

use pg_worm::prelude::*;

#[derive(Model)]
struct Foo {
    bar: i64
}

async fn foo() -> Result<(), Box<dyn std::error::Error>> {
    // Easily create a new transaction
    let transaction = Transaction::begin().await?;

    // Execute any query inside the transaction
    let all_foo = transaction.execute(
        Foo::select()
    ).await?;   

    // Commit the transaction when done.
    // If not committed, transaction are rolled back
    // when dropped.
    transaction.commit().await?;
}

支持类型

以下是一个支持(Rust)类型及其映射到PostgreSQL类型的列表。

Rust类型 PostgreSQL类型
bool BOOL
i16 INT2
i32 INT4
i64 INT8
f32 FLOAT4
f64 FLOAT8
String TEXT
Option<T>* T(但列变为 NULLABLE
Vec<T>* T[]

*T必须是另一个支持类型。嵌套和混合 Option/Vec 目前不支持。

JSON、时间戳等

也是支持的。要使用它们,请激活相应的功能,如下所示

# Cargo.toml
[dependencies]
pg-worm = { version = "latest-version", features = ["foo"] }

以下是一个支持的功能/类型及其相应的PostgreSQL类型的列表

  • "serde-json" 用于 serde_json v1.0

    Rust类型 PostgreSQL类型
    Value JSONB
  • "time" 用于 time v3.0

    Rust类型 PostgreSQL类型
    Date DATE
    Time TIME
    PrimitiveDateTime TIMESTAMP
    OffsetDateTime TIMESTAMP WITH TIME ZONE
  • "uuid" 用于 uuid v1.0

    Rust类型 PostgreSQL类型
    Uuid UUID

derive 选项

您可以为您 Model 配置一些选项。这是通过使用 pg-worm 暴露的两个属性之一来完成的。

属性 #[table]

属性 #[table] 可以用来向 Model 传递配置,该配置影响相应的表本身。

use pg_worm::prelude::*;

#[derive(Model)]
#[table(table_name = "book_list")]
struct Book {
    id: i64
}
Option 含义 用法 默认
table_name 设置表的名称 table_name= "new_table_name" 使用 这个包 将结构体名称转换为蛇形命名法。

属性 #[column]

属性 #[column] 可以用来向 Model 的字段传递配置,该配置影响相应的列。

use pg_worm::prelude::*;

#[derive(Model)]
struct Book {
    #[column(primary_key, auto)]
    id: i64
}
Option 含义 用法 默认
column_name 设置此列的名称。 #[column(column_name= "new_column_name")] 使用 这个包 将字段名称转换为蛇形命名法。
primary_key 将此列设置为主键。每个 Model 只能使用一次。如果希望此列自动生成,请使用 auto #[column(primary_key)] false
auto 使此列自动生成。仅适用于 i16i32i64,以及 Uuid 如果 已启用 "uuid" 功能 并且 您使用 PostgreSQL 版本 13 或更高版本。 #[column(auto)] false

MSRV

最低支持的 Rust 版本是 1.70,因为此 crate 使用了最近引入的 OnceLock

许可协议

此项目在 MIT 和 Apache 2.0 许可协议下双授权。

依赖项

~8–18MB
~256K SLoC