mdl-entities · git:20260821.ff81a24 · 2026-08-21 · sha256 1a640a0aa99f4c83

mdl-entities git:20260821.ff81a24A

Immutable. This exact content is served forever at /api/v1/blob/1a640a0aa99f4c83.

---
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
/**
 * 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: date,
  -- 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
/**
 * 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` |
| Date | `Name: date` | `BirthDate: date` |
| 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
/**
 * 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
-- 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
/**
 * 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
/**
 * Product can be in many Categories
 * Category can have many Products
 */
create association Module.Product_Category
from Module.Product to Module.Category
type reference_set
owner both;
/
```

### Association with Delete Behavior

```mdl
/**
 * Delete orders when their customer is deleted
 */
create association Module.Order_Customer
from Module.Order to Module.Customer
type reference
delete_behavior DELETE_AND_REFERENCES;
/
```

Delete behaviors (applied to the referenced `to` entity):
- `delete_behavior DELETE_AND_REFERENCES` - delete the referencing objects too (cascade)
- `delete_behavior DELETE_BUT_KEEP_REFERENCES` - delete, nullify the reference (default)
- `delete_behavior DELETE_IF_NO_REFERENCES` - only delete when nothing references it

## Enumerations

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

## View Entity (OQL)

```mdl
/**
 * 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
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
/**
 * 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 idx_product_code (Code)
index idx_product_category (Category);
/
```

> The `on` keyword is optional and reads SQL-like: `index idx_product_code on (Code)`
> is equivalent to `index idx_product_code (Code)`. Multi-column indexes list the
> columns in order: `index idx_pos on (Row, Col)`.

## Complete Domain Model Example

```mdl
-- 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
delete_behavior DELETE_AND_REFERENCES;
/

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

## 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 | reference_set]
[owner default | both]
[storage column | table]
[delete_behavior DELETE_AND_REFERENCES | DELETE_BUT_KEEP_REFERENCES | DELETE_IF_NO_REFERENCES];
```

### Enumeration Syntax
```mdl
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';
```