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 matters.
Installs into .claude/skills of the current project.
Are you the author of Mdl Entities?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/mendixlabs-mdl-entities)
---
name: mdl-entities
description: "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 matters."
---
# 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
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
BirthDate: datetime, -- there is no date-only type; `date` is refused
-- 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
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` |
**Note:** Boolean attributes auto-default to `false` when no `default` is specified.
## Generalization (Inheritance)
**CRITICAL: EXTENDS goes BEFORE the opening parenthesis, not after!**
```mdl
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
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):
```mdl
-- EXTENDS after ) = parse error!
create persistent entity Module.Photo (
PhotoCaption: string(200)
) extends System.Image;
```
## Associations
### Reference (Many-to-One)
```mdl
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
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
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
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
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
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
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
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](../choose-edit-mode/SKILL.md)).
An entity, association or enumeration authored in Studio Pro is changed with `alter`,
not by re-running `describe` output:
```mdl
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
```mdl
create persistent entity Module.Name (attributes);
create non-persistent entity Module.Name (attributes);
create view entity Module.Name (attributes) as select ...;
```
### Attribute Syntax
```mdl
attributename: type [(length)] [not null] [unique] [default value]
```
### Association Syntax
```mdl
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
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';
```