You use OCLps (OCL Persistent Storage) when you need to find or calculate over large persisted data sets without loading every object into memory; it is for MDriven developers building searches, ViewModel search nestings, and targeted database queries.
What OCLps does
OCL is normally evaluated in memory. This is appropriate after MDriven has loaded the objects that a ViewModel needs.
OCLps is the persistent-storage subset of OCL. MDriven translates a supported OCLps expression to SQL and lets the database evaluate it. The database query returns object identities, and MDriven then resolves those identities to objects through its normal fetch operations.
For example, this expression finds the Person objects whose first name is Peter:
Person.allInstances->select(p | p.FirstName = 'Peter')
In an OCLps context, MDriven can translate this filtering expression to SQL. In normal in-memory OCL, evaluating the same expression may require loading all Person objects before filtering them. The difference matters when the database contains hundreds, thousands, or millions of objects.
Choose the right expression type
Use the evaluator that matches the work you need to perform.
| Type | Where it runs | Use it for | Example |
|---|---|---|---|
| OCL | In memory | Working with objects already loaded for a ViewModel, including normal UI logic and navigation. | A ViewModel evaluates presentation logic on its current objects. |
| OCLps | Database, after translation to SQL | Finding a limited set of relevant objects or calculating over a large persisted data set. | Find persons named Peter before loading the matching persons. |
| EAL | MDriven action evaluation | Changing data using OCL-like syntax. | An action updates an attribute. |
OCL and OCLps are query languages and must have no side effects. Use EAL when an action must change data. Do not use OCLps as a replacement for ordinary OCL in ViewModel logic: use it to establish the relevant object set, then use normal OCL once those objects are loaded.
Write an OCLps filter
An OCLps query commonly begins with all instances of a class and filters that collection with ->select.
- Start with the class whose persisted objects you want to find:
Person.allInstances. - Add
->select(...)to filter the collection. - Give each object an iterator name before the vertical bar (
|). - Write the condition using that iterator.
Person.allInstances->select(p | p.FirstName = 'Peter')
Here, p is the iterator: it represents one Person while the condition is evaluated. The result is still a collection, even when it contains one matching Person.
The OCL select operation corresponds most closely to a SQL WHERE condition. OCLps is normally used to select objects, not to shape a database result into all of the fields required by a user interface. After MDriven has obtained the matching object identities, build the ViewModel using normal OCL.
For a worked example, see HowTos:OCLps Example. For the ViewModel search mechanism that uses persistent-storage filtering, see Documentation:Searching.
Where MDriven uses OCLps
MDriven uses OCLps in the following places:
- Search-expression nestings in ViewModels.
- PSEval when an OCL or EAL expression must retrieve objects directly from the database.
- PSEvalValue when the database should return a scalar value, such as a count.
- PSEvalTuples when the database query returns tuples.
- ViewModel columns whose names begin with
PSExpression_. This is the older approach; use PSEval, PSEvalValue, or PSEvalTuples for new work.
Query objects with PSEval
Use PSEval to place an OCLps object query inside an OCL or EAL expression.
SysUser.PSEval(SysUser.allinstances->select(su | su.Username = userName), 2, 0, '')
This query finds up to two SysUser objects whose Username equals userName. The offset is 0-based; use 0 unless you are deliberately retrieving a later page of results.
Query one value with PSEvalValue
Use PSEvalValue for a database-calculated value rather than an object set.
SysAsyncTicket.PSEvalValue(
SysAsyncTicket.allInstances->select(at | at.Error.notNull)->size,
Calendar.Now
).asString
This evaluates a count of tickets with a non-null Error. In this example, Calendar.Now is the dependency expression that causes reevaluation as its value changes.
Query tuples with PSEvalTuples
Base OCLps is intentionally restricted and is primarily used to return object identities. When you need tuple results, use PSEvalTuples rather than assuming that tuple-producing OCL operations are supported in every OCLps context.
Validate a new tuple query carefully. In particular, multiple aggregations over different sets in the same tuple may not translate to the expected SQL result; split such calculations into separate expressions when necessary.
OCLps restrictions
OCLps is a subset of OCL because every expression must be translated into SQL. Keep expressions focused on persisted-data filtering and supported database calculations.
- OCLps has no side effects. It cannot change data.
- You cannot call your own methods in OCLps, even when a method is marked
IsQuery. - Do not expect general OCL tuple operations such as
collectandgroupByto work in a basic OCLps expression. Use PSEvalTuples where tuple output is required and test the translated calculation. - An expression that is valid in in-memory OCL is not necessarily translatable to SQL.
- OCLps retrieves matching identities first. Once the corresponding objects are loaded, continue with normal OCL for object navigation and UI behavior.
Keep database queries efficient
Use OCLps when the cost of loading the candidate object set would be significant, not merely because a condition exists.
| Situation | Recommended approach |
|---|---|
| A ViewModel already has a small set of loaded objects. | Use normal OCL. |
| You must locate one object in a table containing millions of rows. | Use OCLps through search nesting or PSEval. |
| You need a count, sum, minimum, maximum, or average over a large data set. | Use PSEvalValue or the appropriate dedicated database-access operator. |
| You need objects and calculated tuple values. | Use PSEvalTuples, then verify the results. |
PSEval, PSEvalValue, and PSEvalTuples do not subscribe to changes in database result sets. Provide a meaningful dependon expression when the value or result set must be reevaluated, for example a timer or an input value used by the query. Without such a dependency, changed database content is not fetched automatically.
Database performance also depends on the SQL server. Add appropriate indexes and verify the query plan and response time for expressions that run over large tables.
Validate expressions in the model
MDriven dynamically type-checks OCL, EAL, and OCLps when the model is loaded or saved. You can also initiate a manual model check by clicking ModelCheck. The model check cross-references the model, helping you identify where model elements are used.
Type checking confirms that the expression is valid against the model. It does not remove the need to test whether an OCLps expression translates correctly and performs well against realistic database data.
See also
- Documentation:OCL-PS
- Documentation:Learn OCL
- Documentation:Searching
- HowTos:OCLps Example
- Documentation:OCLOperators PSEval
- Documentation:PSExpression , or how to do things in the DB from MDriven
Relationship to Standard OCL
Relationship to Standard OCL
You use OCL for declarative queries and rules over your model; use OCLps when the expression must be translated to SQL, and use EAL when the expression must change data.
OCL is the Object Constraint Language: a declarative language for constraints and object-query expressions on UML and other MOF models. MDriven uses OCL expressions throughout the model and ViewModels. For example, Person.allInstances->select(p | p.FirstName = 'Peter') is an OCL query that returns the Person objects whose FirstName is Peter.
MDriven uses the same OCL-like syntax in three expression types:
| Expression type | Definition | Side effects | Example |
|---|---|---|---|
| OCL | An in-memory query and constraint expression over model objects. | No. OCL expressions must not change data. | a.Active) |
| OCLps | The Persistent Storage (PS) subset of OCL that MDriven translates to SQL for evaluation in the database. | No. OCLps expressions must not change data. | p.FirstName = 'Peter') |
| EAL | Extended Action Language: OCL-like syntax with action capabilities for changing data. | Yes. EAL supports actions such as assignment, object creation, deletion, and collection mutation. | self.SomeInt := 27
|
PSEval operators
PSEval operators let an OCL or EAL expression evaluate an OCLps expression against persistent storage.
| Operator | Returns | Use it when | Example |
|---|---|---|---|
| PSEval | Objects | You need a limited object set from the database. | su.Username = userName), 2, 0, ) |
| PSEvalValue | A scalar value | You need a database-calculated value such as a count. | at.Error.notNull)->size, Calendar.Now) |
| PSEvalTuples | Tuples | You need a database query to return tuple results. | Consultant.PSEvalTuples(<ps-expression>, 100, 0, vTheStartDate)
|
The dependon expression in these operators controls reevaluation. Database result-set changes are not fetched automatically; provide a dependency value that changes when you need the expression to run again. For example, Calendar.Now can cause a PSEvalValue expression to reevaluate as that value changes.
OCLps support boundaries
OCLps is not general in-memory OCL. It is limited to expressions that MDriven can translate to SQL, and its primary use is to return object identities from persisted data. MDriven resolves those identities to objects through normal fetch operations; then you can continue with normal OCL.
| Use in OCLps | Status | Example or consequence |
|---|---|---|
| Persisted-data filtering | Supported use | Use ->select to find matching objects before loading them.
|
| Database calculations | Supported where the expression translates to SQL | Use PSEvalValue for a value such as ->size over a filtered set.
|
Your own methods, including methods marked IsQuery
|
Not supported | Move the method-dependent work to normal OCL after the matching objects are loaded. |
| Side effects | Not supported | Use EAL for an assignment such as self.SomeInt := 27.
|
General tuple-producing operations, including collect and groupby
|
Not supported in base OCLps | Use PSEvalTuples when tuple results are required, and validate the translated result. |
| Multiple aggregations over different data sets in one tuple | Restricted | Split separate ->sum, ->minvalue, ->maxvalue, ->average, or ->size calculations into separate expressions.
|
Validate each new PSEvalTuples expression against expected results. SQL translation can produce unexpected results when one tuple contains multiple aggregations over different sets. Also ensure that the database has the indexes and performance settings required by the query.
