--- name: matlab-map-database-objects description: "Generates MATLAB Object Relational Mapping (ORM) code using Database Toolbox. Use when mapping MATLAB classes to database tables, reading/writing objects with ormread/ormwrite/ormupdate, defining Mappable classes, converting classes to SQL with orm2sql, or using object-oriented database workflows." license: MathWorks BSD-3-Clause metadata: author: MathWorks version: "1.0" --- # 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. ----