Skip to main content

What's new in SeaORM 0.9.0

Β· 10 min read
SeaQL Team

πŸŽ‰ 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:
Rob Gilson
boraarslan
Contributed by:
Billy Chan

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?;
Proposed by:
Lucas Berezy
Contributed by:
Γ‰mile Fugulin
Billy Chan

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?;
Proposed by:
baoyachi. Aka Rust Hairy crabs
Contributed by:
liberwang1013

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(" ")
);
Proposed by:
Chris Tsang
Tuetuopay
LoΓ―c
Contributed by:
Billy Chan
Matt
liberwang1013

(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 must 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>,
}
Proposed by:
Mara Schulke
Chris Tsang
Contributed by:
Billy Chan

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
}
}
Proposed by:
Chris Tsang
Contributed by:
smonv
Lukas Potthast
Billy Chan

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:
ttys3
kyoto7250
yb3616
Γ‰mile Fugulin
Bastian
Nahua
Mike
Frank Horvath
Maikel Wever

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:
SandaruKasa
Eric
Γ‰mile Fugulin
Renato Dinhani
kyoto7250
Marco Napetti

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!

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

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

Γ‰mile Fugulin
Dean Sheather
Shane Sveller
Sakti Dwi Cahyono
Unnamed Sponsor
Unnamed Sponsor

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.