DEV Community

SeaQL
SeaQL

Posted on • Originally published at sea-ql.org

What's new in SeaORM 0.9.0

๐ŸŽ‰ We are pleased to release SeaORM 0.9.0 today! Here are some feature highlights ๐ŸŒŸ:

Dependency Upgrades

[#834] We have upgraded a few major dependencies:

Note that you might need to upgrade the corresponding dependency on your application as well.

Proposed by:

Contributed by:

Cursor Pagination

[#822] Paginate models based on column(s) such as the primary key.

// Create a cursor that order by `cake`.`id`
let mut cursor = cake::Entity::find().cursor_by(cake::Column::Id);

// Filter paginated result by `cake`.`id` > 1 AND `cake`.`id` < 100
cursor.after(1).before(100);

// Get first 10 rows (order by `cake`.`id` ASC)
let rows: Vec<cake::Model> = cursor.first(10).all(db).await?;

// Get last 10 rows (order by `cake`.`id` DESC but rows are returned in ascending order)
let rows: Vec<cake::Model> = cursor.last(10).all(db).await?;
Enter fullscreen mode Exit fullscreen mode

Proposed by:

Contributed by:

Insert On Conflict

[#791] Insert an active model with on conflict behaviour.

let orange = cake::ActiveModel {
    id: ActiveValue::set(2),
    name: ActiveValue::set("Orange".to_owned()),
};

// On conflict do nothing: 
//   - INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO NOTHING
cake::Entity::insert(orange.clone())
    .on_conflict(
        sea_query::OnConflict::column(cake::Column::Name)
            .do_nothing()
            .to_owned()
    )
    .exec(db)
    .await?;

// On conflict do update:
//   - INSERT INTO "cake" ("id", "name") VALUES (2, 'Orange') ON CONFLICT ("name") DO UPDATE SET "name" = "excluded"."name"
cake::Entity::insert(orange)
    .on_conflict(
        sea_query::OnConflict::column(cake::Column::Name)
            .update_column(cake::Column::Name)
            .to_owned()
    )
    .exec(db)
    .await?;
Enter fullscreen mode Exit fullscreen mode

Proposed by:

Contributed by:

Join Table with Custom Conditions and Table Alias

[#793, #852] Click Custom Join Conditions and Custom Joins to learn more.

assert_eq!(
    cake::Entity::find()
        .column_as(
            Expr::tbl(Alias::new("fruit_alias"), fruit::Column::Name).into_simple_expr(),
            "fruit_name"
        )
        .join_as(
            JoinType::LeftJoin,
            cake::Relation::Fruit
                .def()
                .on_condition(|_left, right| {
                    Expr::tbl(right, fruit::Column::Name)
                        .like("%tropical%")
                        .into_condition()
                }),
            Alias::new("fruit_alias")
        )
        .build(DbBackend::MySql)
        .to_string(),
    [
        "SELECT `cake`.`id`, `cake`.`name`, `fruit_alias`.`name` AS `fruit_name` FROM `cake`",
        "LEFT JOIN `fruit` AS `fruit_alias` ON `cake`.`id` = `fruit_alias`.`cake_id` AND `fruit_alias`.`name` LIKE '%tropical%'",
    ]
    .join(" ")
);
Enter fullscreen mode Exit fullscreen mode

Proposed by:

Contributed by:

(de)serialize Custom JSON Type

[#794] JSON stored in the database could be deserialized into custom struct in Rust.

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "json_struct")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    // JSON column defined in `serde_json::Value`
    pub json: Json,
    // JSON column defined in custom struct
    pub json_value: KeyValue,
    pub json_value_opt: Option<KeyValue>,
}

// The custom struct much derive `FromJsonQueryResult`, `Serialize` and `Deserialize`
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, FromJsonQueryResult)]
pub struct KeyValue {
    pub id: i32,
    pub name: String,
    pub price: f32,
    pub notes: Option<String>,
}
Enter fullscreen mode Exit fullscreen mode

Proposed by:

Contributed by:

Derived Migration Name

[#736] Introduce DeriveMigrationName procedural macros to infer migration name from the file name.

use sea_orm_migration::prelude::*;

// Used to be...
pub struct Migration;

impl MigrationName for Migration {
    fn name(&self) -> &str {
        "m20220120_000001_create_post_table"
    }
}

// Now... derive `DeriveMigrationName`,
// no longer have to specify the migration name explicitly
#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table( ... )
            .await
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .drop_table( ... )
            .await
    }
}
Enter fullscreen mode Exit fullscreen mode

Proposed by:

Contributed by:

SeaORM CLI Improvements

  • [#735] Improve logging of generate entity command
  • [#588] Generate enum with numeric like variants
  • [#755] Allow old pending migration to be applied
  • [#837] Skip generating entity for ignored tables
  • [#724] Generate code for time crate
  • [#850] Add various blob column types
  • [#422] Generate entity files with Postgres's schema name
  • [#851] Skip checking connection string for credentials

Proposed & Contributed by:

Miscellaneous Enhancements

  • [#800] Added sqlx_logging_level to ConnectOptions
  • [#768] Added num_items_and_pages to Paginator
  • [#849] Added TryFromU64 for time
  • [#853] Include column name in TryGetError::Null
  • [#778] Refactor stream metrics

Proposed & Contributed by:

Integration Examples

SeaORM plays well with the other crates in the async ecosystem. We maintain an array of example projects for building REST, GraphQL and gRPC services. More examples wanted!

Sponsor

Our GitHub Sponsor profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors ๐Ÿ˜‡:

Community

SeaQL is a community driven project. We welcome you to participate, contribute and together build for Rust's future.

Here is the roadmap for SeaORM 0.10.x.

Top comments (0)