๐ We are pleased to release SeaORM 1.0 today! This is an special occasion for us, so this blog post will be a little more than a release notes.
Our Journeyโ
It's nearly been three years since our SeaORM 0.2 release. At that time, we set out to build a SQL ORM for the async Rust ecosystem, bringing together the best crates to allow developers to build high-performance and robust web services.
We would like to thank all early-adoptors, contributors and sponsors of SeaORM. Thank you to all our users for your trust and for being a part of our journey.
Today, many startups and companies are building applications in Rust, with SeaORM being an integral part of the stack. We are particularly pleased to see application frameworks, such as Loco, that provide tight integration with SeaORM, thereby offering a streamlined developer experience.
I think we've achieved our initial goal! We understand that maturity and stability are important considerations for teams when making technology choices. Therefore, we believe it's now time to stabilize SeaORM.
New Featuresโ
Here are the highlights of some new features and enhancements introduced in SeaORM 1.0.
Refreshed migration schema definitionโ
#2099 Thanks to the clever design made by Loco, we've refreshed the schema definition syntax.
An old migration script looks like this:
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Users::Table)
.if_not_exists()
.col(
ColumnDef::new(Users::Id)
.integer()
.not_null()
.auto_increment()
.primary_key(),
)
.col(ColumnDef::new(Users::Pid).uuid().not_null())
.col(ColumnDef::new(Users::Email).string().not_null().unique_key())
// ...
}
}
Now, using the new schema helpers, you can define the schema with a simplified syntax!
// Remember to import `sea_orm_migration::schema::*`
use sea_orm_migration::{prelude::*, schema::*};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Users::Table)
.if_not_exists()
.col(pk_auto(Users::Id)) // Primary key with auto-increment
.col(uuid(Users::Pid)) // UUID column
.col(string_uniq(Users::Email)) // String column with unique and not null constraint
.col(string(Users::Password)) // String column
.col(string(Users::ApiKey).unique_key())
.col(string(Users::Name))
.col(string_null(Users::ResetToken)) // Nullable string column
.col(timestamp_null(Users::ResetSentAt)) // Nullable timestamp column
.col(string_null(Users::EmailVerificationToken))
.col(timestamp_null(Users::EmailVerificationSentAt))
.col(timestamp_null(Users::EmailVerifiedAt))
.to_owned(),
)
.await
}
// ...
}
There are three variants for each commonly used column type:
<COLUMN_TYPE>()
helper function, e.g.string()
, define a non-null string column<COLUMN_TYPE>_null()
helper function, e.g.string_null()
, define a nullable string column<COLUMN_TYPE>_uniq()
helper function, e.g.string_uniq()
, define a non-null and unique string column
The new schema helpers can be used by importing sea_orm_migration::schema::*
. The migration library is fully backward compatible, so there is no rush to migrate old scripts. The new syntax is recommended for new scripts, and all examples in the SeaORM repository have been updated for demonstration. For advanced use cases, the old SeaQuery syntax can still be used.
Reworked SQLite Type Mappingsโ
sea-orm#2077 sea-query#735 sea-schema#117 We've reworked the type mappings for SQLite across the SeaQL ecosystem, such that SeaQuery and SeaSchema are now reciprocal to each other. Migrations written with SeaQuery can be rediscovered by sea-orm-cli
and generate compatible entities! In other words, the roundtrip is complete.
Data types will be mapped to SQLite types with a custom naming scheme following SQLite's affinity rule:
INTEGER
:integer
,tiny_integer
,small_integer
,big_integer
andboolean
are stored asinteger
REAL
:float
,double
,decimal
andmoney
are stored asreal
BLOB
:blob
andvarbinary_blob
are stored asblob
TEXT
: all other data types are stored astext
, includingstring
,char
,text
,json
,uuid
,date
,time
,datetime
,timestamp
, etc.
The full type mapping table is documented here. For more information, please refer to our previous blog post.
Alternative casing support for EntityModel
& ActiveEnum
โ
#2170 Added rename_all
attribute to DeriveEntityModel
& DeriveActiveEnum
#[derive(DeriveEntityModel)]
#[sea_orm(table_name = "user", rename_all = "camelCase")]
pub struct Model {
#[sea_orm(primary_key)]
id: i32,
first_name: String, // firstName
#[sea_orm(column_name = "lAsTnAmE")]
last_name: String, // lAsTnAmE
}
#[derive(EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)", rename_all = "camelCase")]
pub enum TestEnum {
DefaultVariant, // defaultVariant
#[sea_orm(rename = "kebab-case")]
VariantKebabCase, // variant-kebab-case
#[sea_orm(rename = "snake_case")]
VariantSnakeCase, // variant_snake_case
#[sea_orm(string_value = "CuStOmStRiNgVaLuE")]
CustomStringValue, // CuStOmStRiNgVaLuE
}
Other Enhancementsโ
- #2185
PrimaryKeyArity
trait withARITY
Constant
fn get_arity_of<E: EntityTrait>() -> usize {
E::PrimaryKey::iter().count() // before; runtime
<<E::PrimaryKey as PrimaryKeyTrait>::ValueType as PrimaryKeyArity>::ARITY // now; compile-time
}
- #2137
DerivePartialModel
macro attributeentity
now supportssyn::Type
#[derive(DerivePartialModel)]
#[sea_orm(entity = "<entity::Model as ModelTrait>::Entity")]
struct EntityNameNotAIdent {
#[sea_orm(from_col = "foo2")]
_foo: i32,
#[sea_orm(from_col = "bar2")]
_bar: String,
}
- #2146 Added
RelationDef::from_alias()
#[derive(DeriveIden)]
pub struct Cf;
assert_eq!(
cake::Entity::find()
.join_as(
JoinType::LeftJoin,
cake_filling::Relation::Cake.def().rev(),
Cf
)
.join(
JoinType::LeftJoin,
cake_filling::Relation::Filling.def().from_alias(Cf)
)
.build(DbBackend::MySql)
.to_string(),
[
"SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
"LEFT JOIN `cake_filling` AS `cf` ON `cake`.`id` = `cf`.`cake_id`",
"LEFT JOIN `filling` ON `cf`.`filling_id` = `filling`.`id`",
]
.join(" ")
);
- #2256 Added non-TLS runtime
- #2244 Added
Insert::on_conflict_do_nothing
- #2255 Migration schema nullable column set NULL explicitly
- #2194 Added
ActiveValue::set_if_not_equals()
- #2197 Added
ActiveValue::try_as_ref()
- #2228 Added
QuerySelect::order_by_with_nulls
- #2233 Expose
get_xxx_connection_pool
by default - #2148 Added
QueryResult::column_names
- #2199 [sea-orm-macro] Add
@generated
in generated code - #1665 [sea-orm-macro] Qualify traits in
DeriveActiveModel
macro - #2064 [sea-orm-cli] Fix
migrate generate
on emptymod.rs
files
Release Planningโ
SQLx 0.8 is recently released! We want to upgrade from 0.7
as soon as possible. However, since sea-orm
1.0
has been in release candidate status for a while, and considering the breaking changes in sqlx
0.8
, we decided to keep sea-orm
1.0
on sqlx
0.7
.
We plan to release sea-orm
1.1
on sqlx
0.8
soon. To avoid unplanned build failures, we recommend all users to specify SeaORM dependency with tilde
requirement:
sea-orm = { version = "~1.0" }
According to the Cargo Book, this will prevent automatic upgrade to 1.1
, so you can perform the upgrade at a convenient time.
If you've been depending on sea-orm
0.12
, you're recommended to upgrade to 1.0
today. As they're both using sqlx
0.7
, this upgrade focuses on SeaORM. And then upgrade to 1.1
down the line, which will then focus on SQLx, as we won't introduce any breaking changes. We recommend taking it one step at a time!
SeaORM 1.x will be maintained for at least 1 year. By then, we'll decide whether we want to release 2.0 and/or extend the lifecycle of 1.x.
Our Futureโ
You may ask, does this mean SeaORM is "done"? No, not at all! SeaORM 1.0 provides us and the community a solid foundation to build more ambitious features around SeaORM. Here are some facets of our vision:
GraphQL support via Seaography. We want to further develop Seaography to allow developers to turn a set of SeaORM entities into a fully-fledged GraphQL server!
Admin dashboard. We want to build a first-class frontend framework for SeaORM. It will be a breeze to develop admin / client portals with SeaORM!
Data science / analytics. SeaORM's been focusing on OLTP for now, and we're aware that people also have been using SeaORM for OLAP workloads. We want to provide better integration with dataframe libraries and develop more features tailored for data science and engineering.
Scale-out features. We want to develop features to help with scaling applications, e.g. sharding, caching and multi-tenancy.
SQL Server Supportโ
We've been planning SQL Server for SeaORM for a while, and SQL Server is finally coming to SeaORM 1.0! It will first be offered as a closed beta to our partners. If you are interested, please join our waiting list.
Sponsorโ
A small donation will be greatly appreciated, and goes a long way towards sustaining the organization.
A big shout out to our sponsors ๐:
Gold Sponsorsโ
osmos offers a data ingestion platform to streamline data ingestion, transformation and workflow management, and they're using SeaQL libraries under the hood!
Contact us if you also want to become a company sponsor and be featured here.
GitHub Sponsorsโ
Rustacean Sticker Pack ๐ฆโ
The Rustacean Sticker Pack is the perfect way to express your passion for Rust. Our stickers are made with a premium water-resistant vinyl with a unique matte finish. Stick them on your laptop, notebook, or any gadget to show off your love for Rust!
Moreover, all proceeds contributes directly to the ongoing development of SeaQL projects.
Sticker Pack Contents:
- Logo of SeaQL projects: SeaQL, SeaORM, SeaQuery, Seaography, FireDBG
- Mascot of SeaQL: Terres the Hermit Crab
- Mascot of Rust: Ferris the Crab
- The Rustacean word
Support SeaQL and get a Sticker Pack!