---
name: matlab/matlab-map-database-objects
source: https://app.decimal.ai/s/matlab-matlab-map-database-objects@1/SKILL.md
source_sha256: 3ff04692990c
---

# MATLAB Object Relational Mapper

Use when mapping MATLAB classes to relational database tables using the Object Relational Mapping (ORM) layer in Database Toolbox. Defines Mappable classes with property-to-column mappings and uses ormread, ormwrite, and ormupdate for CRUD operations on objects. Available since R2023b.

## When to Use This Skill

- Mapping MATLAB classes to database tables
- Reading database rows as MATLAB objects
- Writing MATLAB objects to database tables
- Updating database rows from modified objects
- Generating SQL CREATE TABLE from a class definition
- Object-oriented database workflows
- User mentions keywords: ORM, object relational mapping, Mappable, ormread, ormwrite, ormupdate, orm2sql, class mapping, property mapping, object database

## When NOT to Use

- Ad-hoc queries or data exploration — use `sqlread`/`fetch` with `RowFilter` instead
- Bulk imports/exports of thousands of rows — ORM is slower than `sqlwrite`/`sqlread` for bulk operations
- MongoDB, Cassandra, or Neo4j — ORM only works with relational databases (JDBC, ODBC, native MySQL/PostgreSQL/SQLite)
- MATLAB releases before R2023b — ORM is not available

## Critical Rules

### ORM Requirements
- **ALWAYS** inherit from `database.orm.mixin.Mappable` — this is required for ORM functionality.
- **ALWAYS** define at least one `PrimaryKey` property — ORM requires it for identity.
- **ALWAYS** use class-level attribute `TableName` to specify the database table name.
- **ALWAYS** include a constructor that handles `nargin == 0` (allows preallocation by ORM).
- ORM requires **R2023b or later**.

### Supported Connections
- ORM works with: JDBC, ODBC, PostgreSQL native, MySQL native, SQLite native, DuckDB native.
- ORM does **NOT** work with: MongoDB (mongoc), Cassandra (apacheCassandra), or Neo4j.

## Decision Framework

| Scenario | Use ORM | Use sqlread/sqlwrite |
|----------|---------|---------------------|
| Object identity and business logic needed | Yes | No |
| Class-based type safety required | Yes | No |
| Domain validation on read/write | Yes | No |
| Ad-hoc queries or exploration | No | Yes |
| Bulk operations (thousands of rows) | No | Yes (faster) |
| No object mapping needed | No | Yes |

## Core Concepts

### Defining a Mappable Class

A Mappable class maps MATLAB properties to database columns:

```matlab
classdef (TableName = "employees") Employee < database.orm.mixin.Mappable

    properties (PrimaryKey, ColumnName = "EmployeeID")
        ID int32
    end

    properties
        Name string
        Department string
        Salary double
    end

    properties (ColumnName = "HireDate", ColumnType = "date")
        StartDate datetime
    end

    methods
        function obj = Employee(id, name, dept, salary, startDate)
            if nargin ~= 0
                obj.ID = id;
                obj.Name = name;
                obj.Department = dept;
                obj.Salary = salary;
                obj.StartDate = startDate;
            end
        end

        function obj = promote(obj, raise)
            obj.Salary = obj.Salary + raise;
        end
    end
end
```

### Property Attributes Reference

| Attribute | Purpose | Example |
|-----------|---------|---------|
| `PrimaryKey` | Marks property as the primary key (required) | `properties (PrimaryKey)` |
| `ColumnName` | Maps property to a differently-named column | `properties (ColumnName = "EmpID")` |
| `ColumnType` | Specifies the database column type | `properties (ColumnType = "date")` |
| `TableName` | Class-level attribute for the target table name | `classdef (TableName = "employees")` |

### Common Mistakes

```matlab
% INCORRECT — class without PrimaryKey (ormwrite will error)
classdef (TableName = "employees") Employee < database.orm.mixin.Mappable
    properties
        Name string
        Dept string
    end
end
% ormwrite(conn, emp) → Error: No PrimaryKey defined

% CORRECT — PrimaryKey is required for ORM operations
classdef (TableName = "employees") Employee < database.orm.mixin.Mappable
    properties(PrimaryKey)
        EmployeeID int32
    end
    properties
        Name string
        Dept string
    end
end

% INCORRECT — class not inheriting Mappable
classdef Employee
    properties(PrimaryKey)
        EmployeeID int32
    end
end
% ormread(conn, "Employee") → Error! Not a Mappable class.

% CORRECT — must inherit from database.orm.mixin.Mappable
classdef Employee < database.orm.mixin.Mappable
    properties(PrimaryKey)
        EmployeeID int32
    end
end

% INCORRECT — constructor without nargin==0 guard
classdef (TableName = "emp") Employee < database.orm.mixin.Mappable
    methods
        function obj = Employee(id, name)
            obj.EmployeeID = id;
            obj.Name = name;
        end
    end
end
% ormread will fail because ORM needs to construct empty objects

% CORRECT — nargin==0 guard allows ORM to construct empty objects
classdef (TableName = "emp") Employee < database.orm.mixin.Mappable
    methods
        function obj = Employee(id, name)
            if nargin == 0
                return;
            end
            obj.EmployeeID = id;
            obj.Name = name;
        end
    end
end
```

### Writing Objects with `ormwrite`

```matlab
% Create and insert a single object
emp = Employee(1, "Alice", "Engineering", 95000, datetime(2023,3,15));
ormwrite(conn, emp);

% Create and insert an array of objects
emps = [Employee(2, "Bob", "Sales", 72000, datetime(2023,6,1)), ...
        Employee(3, "Carol", "Engineering", 105000, datetime(2022,1,10))];
ormwrite(conn, emps);
```

### Reading Objects with `ormread`

```matlab
% Read all objects
allEmployees = ormread(conn, "Employee");

% Read with a row filter
rf = rowfilter("Salary");
highEarners = ormread(conn, "Employee", RowFilter=rf.Salary > 90000);

% Refresh an existing object from database
emp = ormread(conn, emp);
```

### Updating Objects with `ormupdate`

```matlab
% Modify object in MATLAB
emp = promote(emp, 5000);

% Push changes to database
ormupdate(conn, emp);
```

### Deleting Records

ORM does not provide an `ormdelete` function. Use `execute` for deletion:

```matlab
execute(conn, "DELETE FROM employees WHERE EmployeeID = 42");
```

### Error Handling

```matlab
try
    ormwrite(conn, employeeObj);
catch ME
    if contains(ME.message, "UNIQUE") || contains(ME.message, "primary key")
        warning("Duplicate primary key. Use ormupdate instead.");
        ormupdate(conn, employeeObj);
    else
        rethrow(ME);
    end
end
```

### Generating SQL from Class Definition

```matlab
% View the CREATE TABLE SQL that corresponds to the class
sql = orm2sql(conn, "Employee");
disp(sql);
% Output: "CREATE TABLE employees (EmployeeID integer, Name text, ...)"
```

## Complete Examples

### Example 1: Full ORM Workflow — Define, Write, Read, Update

**Step 1: Define the class (save as `Product.m`):**

```matlab
classdef (TableName = "products") Product < database.orm.mixin.Mappable

    properties (PrimaryKey, ColumnName = "ProductNumber")
        ID int32
    end

    properties
        Name string
        Description string
        Quantity int32
    end

    properties (ColumnName = "UnitCost")
        CostPerItem double
    end

    properties (ColumnType = "date")
        InventoryDate datetime
    end

    methods
        function obj = Product(id, name, desc, cost, qty, invDate)
            if nargin ~= 0
                obj.ID = id;
                obj.Name = name;
                obj.Description = desc;
                obj.CostPerItem = cost;
                obj.Quantity = qty;
                obj.InventoryDate = invDate;
            end
        end

        function obj = restock(obj, amount)
            obj.Quantity = obj.Quantity + amount;
            obj.InventoryDate = datetime("today");
        end
    end
end
```

**Step 2: Use ORM operations (save as `ormWorkflow.m`):**

(Uses the `Product` class defined above.)

```matlab
% Connect to SQLite (no driver needed)
conn = sqlite("inventory.db", "create");

% Create and insert products
p1 = Product(1, "Widget", "Small widget", 9.99, 100, datetime(2024,1,1));
p2 = Product(2, "Gadget", "Large gadget", 29.99, 50, datetime(2024,1,1));
ormwrite(conn, [p1, p2]);

% Read all products back as objects
allProducts = ormread(conn, "Product");
disp(allProducts(1));

% Filter: find products under $15
rf = rowfilter("CostPerItem");
cheapProducts = ormread(conn, "Product", RowFilter=rf.CostPerItem < 15);

% Update: restock a product
cheapProducts(1) = restock(cheapProducts(1), 200);
ormupdate(conn, cheapProducts(1));

% Verify
refreshed = ormread(conn, cheapProducts(1));
disp(refreshed.Quantity); % Should be 300

close(conn);
```

## Best Practices

- **ALWAYS** define Mappable classes in their own `.m` file — one class per file (MATLAB requirement).
- **ALWAYS** include a constructor that handles `nargin == 0` (allows preallocation).
- **ALWAYS** mark exactly one property block with `PrimaryKey`.
- Use `ColumnName` when the MATLAB property name differs from the database column name.
- Use `ColumnType` for types that need explicit mapping (e.g., `"date"` for datetime).
- Use `ormread` with `RowFilter` to import only the objects you need — pushes filter to database.
- Use `orm2sql` to verify your class mapping matches the expected database schema before writing.
- Modify objects in MATLAB using class methods, then push changes with `ormupdate`.

## Common Patterns

### Pattern 1: Define-Write-Read-Update

```matlab
% Define class → Product.m
p = Product(1, "Item", "Desc", 9.99, 10, datetime("today"));
ormwrite(conn, p);
p = ormread(conn, p);        % Refresh from DB
p = restock(p, 50);          % Modify in MATLAB
ormupdate(conn, p);          % Push to DB
```

### Pattern 2: Filtered Read with Business Logic

```matlab
rf = rowfilter("Quantity");
lowStock = ormread(conn, "Product", RowFilter=rf.Quantity < 10);
for i = 1:numel(lowStock)
    lowStock(i) = restock(lowStock(i), 100);
    ormupdate(conn, lowStock(i));
end
```

### Pattern 3: Schema Verification

```matlab
sql = orm2sql(conn, "MyClass");
disp(sql); % Verify CREATE TABLE matches expectations
```

## Checklist

Before finalizing, verify:
- [ ] Mappable class inherits from `database.orm.mixin.Mappable`
- [ ] Class has `TableName` attribute on `classdef`
- [ ] At least one property block has `PrimaryKey` attribute
- [ ] Constructor handles `nargin == 0` for preallocation
- [ ] Class saved in its own `.m` file
- [ ] Connection established and verified (`isopen(conn)`)
- [ ] Connection type is supported (JDBC, ODBC, MySQL/PostgreSQL/SQLite/DuckDB native)
- [ ] R2023b or later (ORM not available in earlier releases)

## Troubleshooting

**Issue**: `ormwrite` fails with "class is not Mappable"
- **Solution**: Ensure the class inherits from `database.orm.mixin.Mappable`. The class definition must include `< database.orm.mixin.Mappable`.

**Issue**: `ormread` returns empty array
- **Solution**: Verify the table exists in the database using `sqlfind(conn, tableName)`. Verify the class `TableName` attribute matches the actual table name.

**Issue**: `ormupdate` doesn't change database values
- **Solution**: `ormupdate` matches rows by primary key. Verify the object's primary key property value exists in the database. Use `ormread` to refresh and check.

**Issue**: Property-to-column mapping is incorrect
- **Solution**: Use `orm2sql(conn, "ClassName")` to inspect the generated SQL. Verify `ColumnName` and `ColumnType` attributes match the database schema.

**Issue**: ORM functions not found ("Undefined function")
- **Solution**: ORM requires R2023b or later. Check your release with `ver('database')`. For older releases, use `sqlread`/`sqlwrite` instead.


----

Copyright 2026 The MathWorks, Inc.

----