# SeaORM 2.0.0 SeaORM 2.0 is the first stable release of the 2.x line. It reworks how entities, relations, and ActiveModels are defined and used, adds an entity-first workflow, introduces role-based access control, and brings the library onto SeaQuery 1.0 and SQLx 0.9. The full, itemized changelog for the 2.0 series (all release candidates included) is in [CHANGELOG.md](../CHANGELOG.md#200---2026-07-19). ## Highlights ### New entity format Relations are now declared directly on the `Model` struct with `#[sea_orm::model]`, replacing the separate `Relation` enum and `Related` impls. ```rust #[sea_orm::model] #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] #[sea_orm(table_name = "user")] pub struct Model { #[sea_orm(primary_key)] pub id: i32, pub name: String, #[sea_orm(has_one)] pub profile: HasOne, #[sea_orm(has_many)] pub posts: HasMany, } ``` See the [new entity format walk-through](https://www.sea-ql.org/blog/2025-10-20-sea-orm-2.0/). ### `BelongsTo` relation type with compile-time cardinality A `belongs_to` relation can be typed `BelongsTo` (required) or `BelongsTo>` (optional), encoding the foreign-key cardinality in the type and paired with the write-side `ActiveBelongsTo`. The macro validates the type against the nullability of the `from` columns at compile time. `BelongsTo` is the recommended type for `belongs_to`; the legacy `HasOne` field type remains supported for backward compatibility. (https://github.com/SeaQL/sea-orm/pull/3118) ### Strongly-typed columns Filter with the typed `COLUMN` constant for compile-time type safety, alongside the existing `Column` enum. ```rust user::Entity::find().filter(user::COLUMN.name.contains("Bob")) ``` See [strongly-typed columns](https://www.sea-ql.org/blog/2025-11-11-sea-orm-2.0/). ### Nested ActiveModel Build and persist an entity together with its related rows in one expression, and push has-many children onto a loaded model. ```rust let bob = user::ActiveModel::builder() .set_name("Bob") .set_email("bob@sea-ql.org") .set_profile(profile::ActiveModel::builder().set_picture("Tennis")) .insert(db) .await?; ``` See [nested ActiveModel](https://www.sea-ql.org/blog/2025-11-25-sea-orm-2.0/). ### Entity Loader Load an entity together with its relations, including nested relations, in a single call. ```rust let user = user::Entity::load() .filter_by_id(12) .with(profile::Entity) .with((post::Entity, comment::Entity)) .one(db) .await?; ``` ### Entity-first workflow Create tables directly from entity definitions via the schema registry, without writing a migration first. ```rust db.get_schema_registry("my_crate::*").sync(db).await?; ``` See the [entity-first workflow](https://www.sea-ql.org/blog/2025-10-30-sea-orm-2.0/). ### Role-Based Access Control A table-scoped, hierarchical RBAC engine with a query auditor and a `RestrictedConnection` that implements `ConnectionTrait` and enforces permissions on all Entity operations (including complex joins, insert-select, and CTE queries). (https://github.com/SeaQL/sea-orm/pull/2683) ### Overhauled `insert_many` `insert_many` no longer shares a helper struct with single insert. Panic-prone APIs were removed, empty input returns `None` / `vec![]` on exec, and the new `InsertMany` helper exposes `last_insert_id: Option`. (https://github.com/SeaQL/sea-orm/pull/2628) ### Synchronous SeaORM The `sea-orm-sync` crate provides a synchronous SeaORM backed by `rusqlite`, mirroring the async API with async/await stripped away. ## Upgrading from 1.x Follow the [1.0 to 2.0 migration guide](https://www.sea-ql.org/blog/2026-01-12-sea-orm-2.0/) and the [2.0 walk-through](https://www.sea-ql.org/blog/2025-12-05-sea-orm-2.0/). Notable breaking changes to be aware of: - Expression methods like `.eq()`, `.like()`, `.contains()` now require `use sea_orm::ExprTrait;` in scope. Also read [SeaQuery's breaking changes](https://github.com/SeaQL/sea-query/blob/master/CHANGELOG.md#breaking-changes). - `execute` / `query_one` / `query_all` / `stream` now take a SeaQuery statement; the raw-SQL variants are `execute_raw` / `query_one_raw` / `query_all_raw` / `stream_raw`. - PostgreSQL auto-increment columns now use `GENERATED BY DEFAULT AS IDENTITY` instead of `serial`; opt back in with `option-postgres-use-serial` if needed. - SQLite maps both `Integer` and `BigInteger` to `integer`. - `DeriveValueType` now also derives `NotU8`, `IntoActiveValue`, and `TryFromU64`; remove any manual implementations to avoid conflicts. - Removed the `runtime-actix` feature alias (use `runtime-tokio`); removed `DeriveCustomColumn` and `default_as_str`. ## Dependencies - SeaQuery 1.0 - SQLx 0.9 - sea-schema 0.18