#tokio-postgres #orm #postgresql #sql #refinery

氧化剂

氧化剂帮助您在使用 tokio-postgres 和 refinery 时减少编写实体、表和迁移时的样板代码。

4 个版本

0.2.1 2020 年 8 月 30 日
0.2.0 2020 年 8 月 29 日
0.1.1 2020 年 8 月 16 日
0.1.0 2020 年 8 月 9 日

#5#refinery

MIT 许可证

43KB
897

氧化剂

一个简单的基于 tokio-postgresrefinery 的 ORM

#[async_trait]
pub trait Entity: Sized {
    async fn save(&mut self, db: &DB) -> DBResult<bool>;
    async fn delete(&mut self, db: &DB) -> DBResult<bool>;

    fn from_row(row: &Row) -> Self;
    fn create_migration() -> DBResult<Migration>;
    fn get_table_name() -> String;

    async fn find(db: &DB, query: &str, params: &'_ [&'_ (dyn ToSql + Sync)]) -> DBResult<Vec<Self>>;
    async fn first(db: &DB, query: &str, params: &'_ [&'_ (dyn ToSql + Sync)]) -> DBResult<Option<Self>>;
}
use oxidizer::*;
use chrono::{DateTime, Utc};

#[derive(Entity)]
#[derive(Default)]
pub struct MyEntity {
    #[primary_key]
    id: i32,

    name: String,

    #[indexed]
    integer: i32,
    integer64: i64,

    float: f32,
    double: f64,

    boolean: bool,

    datetime: Option<DateTime<Utc>>,
}

#[tokio::test]
async fn test_my_entity() {
    let uri = "postgres://postgres:alkje2lkaj2e@db/postgres";
    let max_open = 50; // mobc
    let ca_file: Option<&str> = None;
    let db = DB::connect(&uri, max_open, ca_file).await.unwrap();

    db.migrate_tables(&[MyEntity::create_migration().unwrap()]).await.unwrap();

    let mut entity = MyEntity::default();
    let creating = entity.save(&db).await.unwrap();
    assert_eq!(creating, true);
}

属性

派生属性可以用来创建索引、更改默认表名和创建反向关系访问器

#[primary_key]

必需字段属性用于将字段标记为主键,这将使字段自动递增

use oxidizer::*;
#[derive(Entity)]
struct Entity {
    #[primary_key]
    id: i32
}

#[indexed]

在数据库中使指定的字段索引化

use oxidizer::*;
#[derive(Entity)]
struct Entity {
    #[primary_key]
    id: i32,
    #[indexed]
    name: String,
}

#[relation]

关系

#[has_many]

关系

#[entity]

实体结构体的通用设置

table_name: String;

允许您更改实体的表名

use oxidizer::*;
#[derive(Entity)]
#[entity(table_name="custom_table_name")]
struct Entity {
    #[primary_key]
    id: i32
}

#[index]

在一个或多个列上创建自定义索引/约束

use oxidizer::*;
#[derive(Default, Entity)]
#[index(name="myindex", columns="name, email", unique)]
struct MyEntity {
    #[primary_key]
    id: i32,

    name: String,
    email: String,
}

#[field_ignore]

忽略指定的字段。字段类型必须实现 Default 特性。

use oxidizer::*;
#[derive(Default, Entity)]
struct MyEntity {
    #[primary_key]
    id: i32,

    name: String,
    #[field_ignore]
    email: String,
}

#[custom_type]

自定义类型属性允许您覆盖氧化剂提供的默认类型。

use oxidizer::*;
pub enum MyEnum {
    Item1,
    Item2,
}

pub enum ConvertError {
    Error
}

impl std::fmt::Display for ConvertError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("Error trying to convert")
    }
}

impl TryFrom<&MyEnum> for i32 {
    type Error = ConvertError;

    fn try_from(v: &MyEnum) -> Result<Self, Self::Error> {
        match v {
            MyEnum::Item1 => Ok(0),
            MyEnum::Item2 => Ok(1),
        }
    }
}

impl TryFrom<i32> for MyEnum {
    type Error = ConvertError;

    fn try_from(v: i32) -> Result<Self, Self::Error> {
        match v {
            0 => Ok(MyEnum::Item1),
            1 => Ok(MyEnum::Item2),
            _ => Err(ConvertError::Error),
        }
    }
}

#[derive(Entity)]
pub struct TestCustomType {
    #[primary_key]
    id: i32,

    #[custom_type(ty = "i32")]
    my_enum: MyEnum,
}

自定义类型需要您显式实现将实际类型和覆盖类型之间进行转换的相关 TryFrom 特性函数。来自 TryFrom 特性的错误类型必须实现 std::fmt::Display 特性

关系

#[relation]

可以使用 relation 属性创建关系,如下例所示

use oxidizer::*;
#[derive(Entity)]
struct Entity {
    #[primary_key]
    id: i32,
}

#[derive(Entity)]
struct TestRelation {
    #[primary_key]
    id: i32,
    device_id: String,

    #[relation(model="Entity", key="id")]
    entity_id: i32,
}

这将实现针对 TestRelation 以下生成的特性

#[oxidizer::async_trait]
pub trait __AccessorTestRelationToEntity {
    async fn get_test_entity(&self, db: &oxidizer::db::DB) -> oxidizer::db::DBResult<Entity>;
    async fn set_test_entity(&mut self, db: &oxidizer::db::DB, v: &Entity) -> oxidizer::db::DBResult<()>;
}

#[has_many] 可以使用 has_many 属性实现 1 对多或多对多关系

基本(1 对多)

use oxidizer::*;

#[derive(Entity)]
#[derive(Default)]
#[has_many(model="TargetEntity", field="entity_id")]
pub struct Entity {
    #[primary_key]
    id: i32,
    name: String
}

#[derive(Default, Entity)]
pub struct TargetEntity {
    #[primary_key]
    id: i32,
    #[relation(model="Entity", key="id")]
    entity_id: i32
}

这将创建访问实体拥有的所有 TargetEntity 的辅助函数。生成的特性和实现如下所示(实现也是生成的)。

#[oxidizer::async_trait]
pub trait __AccessorHasManyTargetEntityToEntity {
    async fn get_all_test_entity(&self, db: &oxidizer::db::DB) -> oxidizer::db::DBResult<Vec<Entity>>;
}

通过表(多对多)

use oxidizer::*;

#[derive(Entity)]
#[derive(Default)]
pub struct Entity {
    #[primary_key]
    id: i32,
    name: String
}

#[derive(Default, Entity)]
#[has_many(model="Entity", field="entity_id", through="TestManyToMany")]
pub struct TargetEntity {
    #[primary_key]
    id: i32,
}

#[derive(Default, Entity)]
pub struct TestManyToMany {
    #[primary_key]
    id: i32,

    #[relation(model="TargetEntity", key="id")]
    target_id: i32,

    #[relation(model="Entity", key="id")]
    entity_id: i32,
}

这将创建访问相关实体的辅助函数。生成的特性如下所示(实现也是生成的)

#[oxidizer::async_trait]
pub trait __AccessorHasManyTargetEntityToEntity {
    async fn get_all_test_entity(&self, db: &oxidizer::db::DB) -> oxidizer::db::DBResult<Vec<TestManyToMany>>;
}

依赖关系

~17-29MB
~506K SLoC