The framework model layer serves as the bridge between the domain layer’s entity definitions and the UI layer’s visual components. It provides a comprehensive set of model classes that handle data retrieval, manipulation, and state management while maintaining strict separation of concerns.
1. Overview
The model layer implements a reactive MVC architecture where:
-
Models manage data and business logic through observable state
-
Views (UI layer) observe models for state changes
-
Controllers are the reactive bindings (like ComponentValue) that automatically synchronize models and views
All model components are built on Codion’s observable foundation, ensuring that any data or state change automatically propagates to interested observers.
2. Core Components
2.1. EntityModel
The EntityModel serves as the central coordinator, managing both edit and table models along with any detail models in master-detail relationships.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
// Establish master-detail relationship
customerModel.detailModels().add(invoiceModel);
2.2. EntityEditModel
The EntityEditModel handles single entity CRUD operations and maintains the current entity state.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityEditModel editModel = customerModel.editModel();
// Access entity values
Value<String> nameValue = editModel.editor().value(Customer.FIRSTNAME);
// Perform operations
editModel.insert();
editModel.update();
editModel.delete();
2.3. EntityTableModel
The EntityTableModel manages collections of entities, providing sorting, filtering, and selection capabilities.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityTableModel tableModel = customerModel.tableModel();
// Refresh data
tableModel.items().refresh();
// Access selection
Collection<Entity> selected = tableModel.selection().items().get();
2.4. EntityQueryModel
The EntityQueryModel controls how data is fetched from the database, including conditions, limits, and ordering.
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityTableModel tableModel = customerModel.tableModel();
EntityQueryModel queryModel = tableModel.queryModel();
// Configure query behavior
queryModel.limit().set(200);
queryModel.conditionRequired().set(true);
queryModel.orderBy().set(OrderBy.ascending(Customer.LASTNAME));
3. Observable Architecture
Every model component extends Codion’s observable foundation:
3.1. State Management
Models expose their state through observable values:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
EntityEditModel editModel = customerModel.editModel();
EntityTableModel tableModel = customerModel.tableModel();
// Edit model states
State updateEnabled = editModel.updateEnabled();
State updateMultipleEnabled = editModel.updateMultipleEnabled();
ObservableState modified = editModel.editor().modified();
// Table model states
ObservableState refreshing = tableModel.items().refresher().active();
ObservableState hasSelection = tableModel.selection().empty().not();
// Combine states
ObservableState canDelete = State.and(hasSelection, refreshing.not());
3.2. Event System
Models emit events for all significant operations:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityEditModel editModel = customerModel.editModel();
SwingEntityTableModel tableModel = customerModel.tableModel();
// Listen for entity changes
editModel.afterInsert().addConsumer(entities -> {
System.out.println("Inserted: " + entities);
});
// Listen for selection changes
tableModel.selection().item().addConsumer(selectedEntity -> {
if (selectedEntity != null) {
loadDetails(selectedEntity);
}
});
3.3. Value Observers
Entity values are observable and can be bound across models:
SwingEntityModel trackModel = new SwingEntityModel(Track.TYPE, connectionProvider);
EntityEditModel editModel = trackModel.editModel();
// Bind edit model value to UI state
EditorValue<BigDecimal> priceValue = editModel.editor().value(Track.UNITPRICE);
ObservableState priceValid = editModel.editor().value(Track.UNITPRICE).valid();
// React to value changes
priceValue.addConsumer(newPrice -> updateTotalPrice(newPrice));
// React to value edits
priceValue.edited().addConsumer(newPrice -> System.out.println("Price: " + newPrice));
4. Model Relationships
4.1. Master-Detail Pattern
The framework supports arbitrarily deep master-detail hierarchies:
// Three-level hierarchy
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityModel invoiceModel = new SwingEntityModel(Invoice.TYPE, connectionProvider);
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
customerModel.detailModels().add(invoiceModel);
invoiceModel.detailModels().add(invoiceLineModel);
// Selection cascades down the hierarchy
Entity customer = getCustomer(connectionProvider);
customerModel.tableModel().selection().item().set(customer);
// Invoices for selected customer are loaded
Entity invoice = invoiceModel.tableModel().items().visible().get(0);
invoiceModel.tableModel().selection().item().set(invoice);
// Invoice lines for selected invoice are loaded
5. Best Practices
5.1. Query Optimization
Always configure appropriate limits to prevent loading excessive data:
class CustomerTableModel extends SwingEntityTableModel {
public CustomerTableModel(EntityConnectionProvider connectionProvider) {
super(new SwingEntityEditModel(Customer.TYPE, connectionProvider));
// Prevent loading entire customer base
queryModel().limit().set(100);
queryModel().conditionRequired().set(true);
}
}
5.2. Event Handling
Use the event system to maintain consistency across models:
SwingEntityModel invoiceLineModel = new SwingEntityModel(InvoiceLine.TYPE, connectionProvider);
// Update summary when details change
invoiceLineModel.editModel().afterInsert().addConsumer(entities -> updateInvoiceTotal());
invoiceLineModel.editModel().afterUpdate().addConsumer(entities -> updateInvoiceTotal());
invoiceLineModel.editModel().afterDelete().addConsumer(entities -> updateInvoiceTotal());
5.3. Custom Data Sources
For specialized queries, consider custom data sources:
SwingEntityModel customerModel = new SwingEntityModel(Customer.TYPE, connectionProvider);
SwingEntityTableModel tableModel = customerModel.tableModel();
// Fetch only Customers with emails by default
tableModel.queryModel().dataSource().set(queryModel -> {
EntityConnection connection = queryModel.connectionProvider().connection();
return connection.select(and(
Customer.EMAIL.isNotNull(),
queryModel.condition().where(Conjunction.AND))
);
});
6. See Also
-
EntityModel - Detailed EntityModel documentation
-
EntityEditModel - Edit model specifics
-
EntityTableModel - Table model features
-
Domain Model - Entity definition layer