What's new in SeaORM 0.9.0
Tags: rust, githunt, opensource, github
Published: 2022-07-18 07:40:23

🎉 We are pleased to release SeaORM [`0.9.0`](https://github.com/SeaQL/sea-orm/releases/tag/0.9.0) today! Here are some feature highlights 🌟:

## Dependency Upgrades

[[#834](https://github.com/SeaQL/sea-orm/pull/834)] We have upgraded a few major dependencies:
- Upgrade [`sqlx`](https://github.com/launchbadge/sqlx) to 0.6
- Upgrade [`time`](https://github.com/time-rs/time) to 0.3
- Upgrade [`uuid`](https://github.com/uuid-rs/uuid) to 1.0
- Upgrade [`sea-query`](https://github.com/SeaQL/sea-query) to 0.26
- Upgrade [`sea-schema`](https://github.com/SeaQL/sea-schema) to 0.9

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

Proposed by:
- [Rob Gilson](https://github.com/D1plo1d)
- [boraarslan](https://github.com/boraarslan)

Contributed by:
- [Billy Chan](https://github.com/billy1624)

## Cursor Pagination

[[#822](https://github.com/SeaQL/sea-orm/pull/822)] Paginate models based on column(s) such as the primary key.

```rust
// 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](https://github.com/lberezy)

Contributed by:
- [Émile Fugulin](https://github.com/Sytten)
- [Billy Chan](https://github.com/billy1624)

## Insert On Conflict

[[#791](https://github.com/SeaQL/sea-orm/pull/791)] Insert an active model with on conflict behaviour.

```rust
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](https://github.com/baoyachi)

Contributed by:
- [liberwang1013](https://github.com/liberwang1013)

## Join Table with Custom Conditions and Table Alias

[[#793](https://github.com/SeaQL/sea-orm/pull/793), [#852](https://github.com/SeaQL/sea-orm/pull/852)] Click [Custom Join Conditions](/SeaORM/docs/next/relation/custom-join-condition) and [Custom Joins](/SeaORM/docs/next/advanced-query/custom-joins) to learn more.

```rust
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](https://github.com/tyt2y3)
- [Tuetuopay](https://github.com/Tuetuopay)
- [Loïc](https://github.com/nag763)

Contributed by:
- [Billy Chan](https://github.com/billy1624)
- [Matt](https://github.com/MattGson)
- [liberwang1013](https://github.com/liberwang1013)

## (de)serialize Custom JSON Type

[[#794](https://github.com/SeaQL/sea-orm/pull/794)] JSON stored in the database could be deserialized into custom struct in Rust.

```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>,
}
```

Proposed by:
- [Mara Schulke](https://github.com/mara214)
- [Chris Tsang](https://github.com/tyt2y3)

Contributed by:
- [Billy Chan](https://github.com/billy1624)

## Derived Migration Name

[[#736](https://github.com/SeaQL/sea-orm/pull/736)] Introduce `DeriveMigrationName` procedural macros to infer migration name from the file name.

```rust
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](https://github.com/tyt2y3)

Contributed by:
- [smonv](https://github.com/smonv)
- [Lukas Potthast](https://github.com/inf0rm4tik3r)
- [Billy Chan](https://github.com/billy1624)

## SeaORM CLI Improvements

- [[#735](https://github.com/SeaQL/sea-orm/pull/735)] Improve logging of generate entity command
- [[#588](https://github.com/SeaQL/sea-orm/pull/588)] Generate enum with numeric like variants
- [[#755](https://github.com/SeaQL/sea-orm/pull/755)] Allow old pending migration to be applied
- [[#837](https://github.com/SeaQL/sea-orm/pull/837)] Skip generating entity for ignored tables
- [[#724](https://github.com/SeaQL/sea-orm/pull/724)] Generate code for `time` crate
- [[#850](https://github.com/SeaQL/sea-orm/pull/850)] Add various blob column types
- [[#422](https://github.com/SeaQL/sea-orm/pull/422)] Generate entity files with Postgres's schema name
- [[#851](https://github.com/SeaQL/sea-orm/pull/851)] Skip checking connection string for credentials

Proposed & Contributed by:
- [ttys3](https://github.com/ttys3)
- [kyoto7250](https://github.com/kyoto7250)
- [yb3616](https://github.com/yb3616)
- [Émile Fugulin](https://github.com/Sytten)
- [Bastian](https://github.com/cemoktra)
- [Nahua](https://github.com/nahuakang)
- [Mike](https://github.com/hunjixin)
- [Frank Horvath](https://github.com/frankhorv)
- [Maikel Wever](https://github.com/maikelwever)

## Miscellaneous Enhancements

- [[#800](https://github.com/SeaQL/sea-orm/pull/800)] Added `sqlx_logging_level` to `ConnectOptions`
- [[#768](https://github.com/SeaQL/sea-orm/pull/768)] Added `num_items_and_pages` to `Paginator`
- [[#849](https://github.com/SeaQL/sea-orm/pull/849)] Added `TryFromU64` for `time`
- [[#853](https://github.com/SeaQL/sea-orm/pull/853)] Include column name in `TryGetError::Null`
- [[#778](https://github.com/SeaQL/sea-orm/pull/778)] Refactor stream metrics

Proposed & Contributed by:
- [SandaruKasa](https://github.com/SandaruKasa)
- [Eric](https://github.com/fistons)
- [Émile Fugulin](https://github.com/Sytten)
- [Renato Dinhani](https://github.com/dinhani)
- [kyoto7250](https://github.com/kyoto7250)
- [Marco Napetti](https://github.com/nappa85)

## 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](https://github.com/SeaQL/sea-orm/issues/269)!

- [Rocket Example](https://github.com/SeaQL/sea-orm/tree/master/examples/rocket_example)
- [Actix Example](https://github.com/SeaQL/sea-orm/tree/master/examples/actix_example)
- [Axum Example](https://github.com/SeaQL/sea-orm/tree/master/examples/axum_example)
- [Poem Example](https://github.com/SeaQL/sea-orm/tree/master/examples/poem_example)
- [GraphQL Example](https://github.com/SeaQL/sea-orm/tree/master/examples/graphql_example)
- [jsonrpsee Example](https://github.com/SeaQL/sea-orm/tree/master/examples/jsonrpsee_example)
- [Tonic Example](https://github.com/SeaQL/sea-orm/tree/master/examples/tonic_example)

## Sponsor

Our [GitHub Sponsor](https://github.com/sponsors/SeaQL) profile is up! If you feel generous, a small donation will be greatly appreciated.

A big shout out to our sponsors 😇:

- [Émile Fugulin](https://github.com/Sytten)
- [Dean Sheather](https://github.com/deansheather)
- [Shane Sveller](https://github.com/shanesveller)
- [Sakti Dwi Cahyono](https://github.com/sakti)
- 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`](https://github.com/SeaQL/sea-orm/milestone/10).