Schema Definition

Ballerina ORM uses annotated record types as the schema source of truth. You do not need a separate schema DSL.

Why annotated records

  • schema and runtime model stay in one type
  • annotations make mapping intent explicit
  • compiler-time tooling can validate constraints

Entity and fields

ballerina
import thambaru/bal_orm.orm;
import ballerina/time;

@orm:Entity {tableName: "users"}
@orm:Index {columns: ["email"], unique: true}
public type User record {|
    @orm:Id @orm:AutoIncrement
    int id;

    @orm:Column {length: 255, nullable: false}
    string email;

    @orm:Column {nullable: false}
    string name;

    @orm:CreatedAt
    time:Utc createdAt;

    @orm:UpdatedAt
    time:Utc updatedAt;

    @orm:Relation {'type: orm:ONE_TO_MANY}
    Post[]? posts;
|};

Supported field annotations

AnnotationPurpose
@orm:IdMarks primary key fields
@orm:AutoIncrementMarks auto-increment behavior for key fields
@orm:Column { ... }Column options (name, 'type, length, nullable, unique, 'default)
@orm:Relation { ... }Relationship metadata ('type, references, foreignKey, joinTable)
@orm:CreatedAtORM-managed creation timestamp
@orm:UpdatedAtORM-managed update timestamp
@orm:IgnoreExcludes field from persistence mapping

Entity-level annotations

AnnotationPurpose
@orm:Entity {tableName, schema, engine}Maps record to table/schema/engine
@orm:Index {columns, unique, name}Defines single or composite indexes

@orm:Index is repeatable, so you can attach multiple indexes to one entity.

Relation example

ballerina
@orm:Entity {tableName: "posts"}
public type Post record {|
    @orm:Id @orm:AutoIncrement
    int id;

    string title;

    @orm:Column {nullable: false}
    int authorId;

    @orm:Relation {
        'type: orm:MANY_TO_ONE,
        references: ["id"],
        foreignKey: ["authorId"]
    }
    User? author;
|};

Next step

Read ORM Client to connect these models to a live database.