mdl-entities

Complete syntax reference for entities, attributes and associations — every entity kind, attribute type, and association form. Use when writing or altering a domain model and the exact spelling matter

SKILL.md

GitHub
<!-- skill-index: generated by scripts/skill-index.py; make check-skill-index -->

Find it fast

sed -n '<from>,<to>p' SKILL.md one section rather than reading the file; ./mxcli syntax <topic> answers how do I write this for a fraction of either.

lines section
32-39 Entity Types
40-77 Persistent Entity
78-94 Non-Persistent Entity
95-109 Attribute Types
110-129 Attribute Modifiers
130-175 Generalization (Inheritance)
176-224 Associations
225-241 Enumerations
242-283 View Entity (OQL)
284-307 Entity with Index
308-369 Complete Domain Model Example
370-387 Changing an Existing Domain Model
388-430 Quick Reference
<!-- /skill-index -->

MDL Entity Syntax Reference

Complete syntax reference for creating entities, attributes, and associations.

Entity Types

Type Keyword Stored in DB Use Case
Persistent create persistent entity Yes Business data
Non-Persistent create non-persistent entity No Temporary/view data
View create view entity No (OQL query) Aggregated/computed data

Persistent Entity

mdl 1;
/**
 * Customer entity for storing customer data
 */
create persistent entity Module.Customer (
  -- String attributes
  Name: string(100) not null,
  Email: string(200),
  Code: string(20) unique,

  -- Numeric attributes
  Age: integer,
  CreditLimit: decimal,

  -- Boolean
  IsActive: boolean default true,

  -- Date/Time
  -- There is no date-only type (`date` is deprecated, MDL-DEPR160). A calendar
  -- date that must not shift with the user's time zone is `not localized`.
  BirthDate: datetime not localized,
  LastLogin: datetime,   -- localized: shown in the user's time zone (default)
  -- Use autocreateddate (not datetime) to record when the object was created.
  -- 'CreatedDate' as a plain datetime triggers lint error MDL020.
  CreatedDate: autocreateddate,

  -- Enumeration
  status: Module.CustomerStatus default Active,

  -- Auto-number (REQUIRES a seed via `default N` — without it the build fails
  -- CE7247 "Value cannot be empty")
  CustomerNumber: autonumber default 1
);

Non-Persistent Entity

Used for temporary data, form parameters, or calculated values.

mdl 1;
/**
 * Search parameters for customer search form
 */
create non-persistent entity Module.CustomerSearchParams (
  SearchName: string(100),
  SearchEmail: string(200),
  MinCreditLimit: decimal,
  IncludeInactive: boolean default false
);

Attribute Types

Type Syntax Example
String Name: string(length) Name: string(100)
Integer Name: integer count: integer
Long Name: long BigNumber: long
Decimal Name: decimal Amount: decimal
Boolean Name: boolean IsActive: boolean
DateTime Name: datetime CreatedAt: datetime
Enumeration Name: Module.EnumName status: Module.Status
AutoNumber Name: autonumber default 1 Code: autonumber default 1 (seed required)
Binary Name: binary FileData: binary
Hashed String Name: hashedstring password: hashedstring

Attribute Modifiers

Modifier Meaning Example
not null Required field Name: string(100) not null
unique Unique constraint Code: string(20) unique
default value Default value IsActive: boolean default true
not localized DateTime only. Studio Pro's "Localize" unticked: no time-zone conversion — use for calendar dates (birth date, due date) BirthDate: datetime not localized
localized DateTime only. The default, stated — switches a not localized attribute back on modify attribute alter entity M.E modify attribute D: datetime localized

DateTime localization on a rewrite: create or modify / modify attribute that does not state localized / not localized keeps what is stored (so restating a Studio Pro-authored non-localized date does not localize it); a stated clause wins. When that keeps a stored not localized, exec prints a Note: naming the attribute — write D: DateTime localized to switch it on. describe prints not localized only when it applies. Using either clause on a non-DateTime attribute is a check error.

Note: Boolean attributes auto-default to false when no default is specified.

Generalization (Inheritance)

CRITICAL: EXTENDS goes BEFORE the opening parenthesis, not after!

mdl 1;
/**
 * Base entity
 */
create persistent entity Module.Person (
  PersonName: string(100) not null,
  Email: string(200)
);

/**
 * Customer extends Person - EXTENDS before (
 */
create persistent entity Module.Customer extends Module.Person (
  CustomerCode: string(20),
  CreditLimit: decimal
);

Common parent entities for file/image storage:

mdl 1;
-- Image entity (inherits Name, Size, Contents, thumbnail)
create persistent entity Module.ProductPhoto extends System.Image (
  PhotoCaption: string(200),
  SortOrder: integer default 0
);

-- File document (inherits Name, Size, Contents)
create persistent entity Module.Attachment extends System.FileDocument (
  AttachmentDescription: string(500)
);

Wrong (parse error):

-- EXTENDS after ) = parse error!
create persistent entity Module.Photo (
  PhotoCaption: string(200)
) extends System.Image;

Associations

Reference (Many-to-One)

mdl 1;
/**
 * Order belongs to one Customer
 */
create association Module.Order_Customer
from Module.Order to Module.Customer
type reference;

Direction: from the entity that holds the foreign key (the "many" / child side) to the entity being referenced (the "one" / parent side). Name convention is Child_Parent.

Reference Set (Many-to-Many)

mdl 1;
/**
 * Product can be in many Categories
 * Category can have many Products
 */
create association Module.Product_Category
from Module.Product to Module.Category
type ReferenceSet
owner both;

Association with Delete Behavior

mdl 1;
/**
 * Delete orders when their customer is deleted
 */
create association Module.Order_Customer
from Module.Order to Module.Customer
type reference
on delete cascade;

Delete behaviors (applied to the referenced to entity):

  • on delete cascade - delete the referencing objects too (cascade)
  • on delete set null - delete, nullify the reference (default)
  • on delete restrict - only delete when nothing references it

Enumerations

mdl 1;
/**
 * Order status values
 */
create enumeration Module.OrderStatus (
  Draft 'Draft',
  Pending 'Pending',
  Approved 'Approved',
  Shipped 'Shipped',
  Delivered 'Delivered',
  Cancelled 'Cancelled'
);

View Entity (OQL)

mdl 1;
/**
 * Monthly sales summary by customer
 */
create view entity Module.CustomerSalesSummary (
  CustomerName: string(100),
  TotalOrders: integer,
  TotalAmount: decimal,
  LastOrderDate: datetime
)
as
  select
    c.Name as CustomerName,
    count(o.OrderID) as TotalOrders,
    sum(o.Amount) as TotalAmount,
    max(o.OrderDate) as LastOrderDate
  from Module.Customer c
  left join c/Module.Order_Customer/Module.Order o
  GROUP by c.Name;

Derived string columns must be string(200). A plain pass-through column (c.Name as CustomerName) inherits its source attribute's length, so declaring string(100) above is fine. But a derived string column — cast(x as string), a string-returning CASE, or a string expression — is normalized by Mendix to the platform default length string(200). Declaring any other length (string(30), unlimited, …) passes mxcli check's parser but fails the MxBuild consistency check with CE6770 "View Entity is out of sync with the OQL Query." mxcli check catches this pre-build as MDL031 with a suggested fix:

mdl 1;
create view entity Module.TicketLabel (
  StatusLabel: string(200)          -- derived → must be 200, not string(30)
) as
  select cast(t.Status as string) as StatusLabel from Module.Ticket t;

Entity with Index

mdl 1;
/**
 * Product with search index
 */
create persistent entity Module.Product (
  Code: string(20) not null,
  Name: string(100) not null,
  Category: string(50),
  Price: decimal
)
index (Code)
index (Category);

A Mendix index has no name — its columns, in order and direction, are its identity. A name is accepted (index idx_code on (Code)) but not stored, so check warns (MDL-IDX01), describe prints the index back as index (Code), and drop index idx_code cannot find it. Write indexes anonymously; drop one by its columns: alter entity Module.Product drop index (Code). Multi-column indexes list the columns in order: index (Row, Col desc).

Complete Domain Model Example

mdl 1;
-- Enumeration
create enumeration Shop.OrderStatus (
  Draft 'Draft',
  Confirmed 'Confirmed',
  Shipped 'Shipped',
  Delivered 'Delivered'
);

-- Customer entity
create persistent entity Shop.Customer (
  Name: string(100) not null,
  Email: string(200) not null unique,
  Phone: string(20),
  IsActive: boolean default true,
  CreatedDate: autocreateddate
);

-- Product entity
create persistent entity Shop.Product (
  Code: string(20) not null unique,
  Name: string(100) not null,
  description: string(500),
  Price: decimal not null,
  Stock: integer default 0,
  IsAvailable: boolean default true
);

-- Order entity
create persistent entity Shop.Order (
  OrderNumber: autonumber default 1,
  OrderDate: datetime not null,
  status: Shop.OrderStatus default Draft,
  TotalAmount: decimal,
  Notes: string(500)
);

-- Order line entity
create persistent entity Shop.OrderLine (
  Quantity: integer not null,
  UnitPrice: decimal not null,
  LineTotal: decimal
);

-- Associations
create association Shop.Order_Customer
from Shop.Order to Shop.Customer
type reference;

create association Shop.OrderLine_Order
from Shop.OrderLine to Shop.Order
type reference
on delete cascade;

create association Shop.OrderLine_Product
from Shop.OrderLine to Shop.Product
type reference;

Changing an Existing Domain Model

Choose the mode by who owns the entity (choose-edit-mode). An entity, association or enumeration authored in Studio Pro is changed with alter, not by re-running describe output:

mdl 1;
alter entity Shop.Order add attribute Note: string(200);
alter association Shop.Order_Customer set on delete set null;
alter enumeration Shop.OrderStatus add value Cancelled caption 'Cancelled';

Re-running create or modify from describe on a Studio Pro association has flipped its storage from table to column, which is a schema change. Run mxcli diff first: it runs the script on a scratch copy and lists every unit exec would write. Never drop and re-create an entity to change it: the new entity has a new identity, and the runtime drops the old table and its rows.

Quick Reference

Entity Creation

create persistent entity Module.Name (attributes);
create non-persistent entity Module.Name (attributes);
create view entity Module.Name (attributes) as select ...;

Attribute Syntax

attributename: type [(length)] [not localized | localized] [not null] [unique] [default value]

Association Syntax

create association Module.Child_Parent
from Module.ChildEntity to Module.ParentEntity
[type reference | ReferenceSet]
[owner default | both]
[storage column | table]
[on delete cascade | restrict | set null [error message '...']];

Every clause is optional; unstated means type Reference owner Default storage column on delete set null. describe prints only the clauses that differ, so a table association always shows storage table.

Enumeration Syntax

mdl 1;
create enumeration Module.Name (
  Value1 'Caption1',
  Value2 'Caption2'
);

-- Optionally place the enumeration in a module folder:
create enumeration Module.Currency (
  USD 'US Dollar',
  EUR 'Euro'
) FOLDER 'Shared';
-- Or move an existing one:  move enumeration Module.Currency to folder 'Shared';