| outline | deep |
|---|
Entities are the schema source of truth in morm.
You describe a table as a normal MoonBit struct, annotate it with #morm.entity, and let mormgen generate table() metadata from it.
///|
#morm.entity
pub(all) struct Student {
#morm.id
#morm.default(autoincrement())
id : Int64
#morm.varchar(length="255")
name : String
age : Int
} derive(ToJson, FromJson)This is enough for mormgen to generate:
impl @morm.Entity for StudentStudent::table() -> @morm.Table
For smooth integration, entity types should usually:
- be plain structs
- derive
ToJson - derive
FromJson
Why:
- write paths convert entities through
ToJson - read paths decode result rows back through
FromJson
Nullability is type-driven.
Tmeans non-nullT?means nullable
Example:
///|
#morm.entity
pub(all) struct Teacher {
#morm.id
id : Int64
name : String
birth_date : @time.ZonedDateTime?
} derive(ToJson, FromJson)birth_date is nullable because the field type is optional.
Use #morm.id to mark the primary key field.
#morm.id
id : Int64Use #morm.default(autoincrement()) when the engine should treat it as an auto-incrementing key.
#morm.id
#morm.default(autoincrement())
id : Int64You can also declare a primary-key generation strategy inline:
#morm.id(strategy="uuid")
id : StringCurrently this annotation is emitted as column engine option pk.strategy=<value>.
mormgen also supports strategy="auto_increment" and strategy="manual".
Use these attributes to force string-related SQL types:
#morm.varchar(length="255")#morm.char(length="1")#morm.text#morm.mediumtext#morm.longtext
Example:
#morm.varchar(length="255")
name : StringCommon numeric attributes:
#morm.tinyint#morm.smallint#morm.int#morm.bigint#morm.float#morm.double#morm.decimal(precision="10", scale="2")
These are useful when you need specific DDL semantics rather than relying on default type mapping.
For document-like or raw storage:
#morm.json#morm.jsonb#morm.binary(length="...")#morm.varbinary(length="...")#morm.blob
Plain MoonBit enums are now mapped directly.
///|
pub(all) enum PostStatus {
Draft
Published
Archived
} derive(ToJson, FromJson, Show)
///|
#morm.entity
pub(all) struct Post {
#morm.id
id : Int
status : PostStatus
} derive(ToJson, FromJson)mormgen will:
- generate
impl @morm/engine.ToParam for PostStatus - generate
impl @morm/engine.FromParam for PostStatus - emit the field as
ColumnType::Enum("PostStatus", ["Draft", ...])
Engine behavior:
- MySQL renders native
ENUM(...) - PostgreSQL creates a native enum type and uses it in the table DDL
- SQLite / SQL Server / Oracle currently fall back to string-like column types
Current limitation:
- only payload-free enums are treated as database enums
- if you override the column type with
#morm.varchar,#morm.text, etc., the explicit annotation wins
Time-related annotations:
#morm.date#morm.time#morm.datetime#morm.timestamp
But in most cases, the field type itself is enough:
@time.PlainDate -> Date@time.PlainTime -> Time@time.PlainDateTime -> DateTime@time.ZonedDateTime -> Timestamp
Use:
PlainDateTimeforcreated_at/updated_atin most appsZonedDateTimewhen offset matters in your domain model
This keeps schema intent aligned with actual application semantics.
Use #morm.foreign_key for explicit foreign key constraints.
#morm.foreign_key(references="teacher.id", on_delete="CASCADE")
teacher_id : IntThis produces:
- a normal scalar column
- matching foreign key metadata in
Table.foreign_keys
morm also recognizes relation-style annotations:
#morm.many_to_one(...)#morm.one_to_many(...)
Example:
#morm.many_to_one(references="student.id", fk="student_id", on_delete="CASCADE")
student : StudentThis can materialize the relation as a foreign-key column in generated metadata.
For one-to-many:
#morm.one_to_many(mapped_by="student")
enrollments : FixedArray[Int]This is treated as a logical relation and is not emitted as a physical table column.
Use #morm.transient when a field should stay in the entity model but not be persisted as a database column.
#morm.transient
display_name : String?Transient fields are excluded from generated table columns and from insert/update/upsert ... from(entity) write paths.
Entity fields can participate in generated timestamp logic.
Convention-based:
created_atupdated_at
Explicit opt-in:
#morm.auto_create_time#morm.auto_update_time
Example:
///|
#morm.entity
pub(all) struct AuditRow {
#morm.id
id : Int64
#morm.auto_create_time
inserted_on : @time.PlainDateTime
#morm.auto_update_time
touched_on : @time.PlainDateTime
} derive(ToJson, FromJson)These annotations do not change the schema by themselves. They affect generated mapper save methods.
Generated table() returns a full @morm.Table value with:
namecolumnsindexesforeign_keys- engine/comment/charset metadata fields
This value is used by:
auto_migrate- tests
- custom tooling that wants to inspect schema shape
A common pattern is to assert on generated metadata in tests:
let table = Student::table()
assert_eq(table.name, "student")
assert_eq(table.columns[0].name, "id")This keeps schema drift visible in CI.