Defining inheritance
This lesson describes patterns for persisting subclasses in your model. You will learn:
- How to create and edit subclasses using NexJ Studio.
- How to use a single table pattern to implement the persistence of a subclass.
- How to use a class table pattern to implement the persistence of a subclass.
- The differences between an abstract class and a concrete class, and how to express them in your design/model.
Create the subclasses
Object-oriented development allows you to base new classes on existing classes. What does this mean? A subclass (AKA a derived class) inherits the attributes and events of its parent or base class. You can then add additional events and attributes to the subclass without affecting its base class. In this lesson you create four new subclasses. Two use the training:Entity
class as their base, and two use the training:Telcom
class. Later, you use different persistence patterns to store the class instances.
Extend the training:Entity class hierarchy
Here, we extend the training:Entity
model by creating two new classes: training:Person
and training:Company
. training:Company
will have a 0-to-many association with training:Person
: each person can have only one associated company but each company can have any number of associated people as employees.
<picture here>
Create the training:Person class
You need to specify that training:Person
is based on the training:Entity
class, and add the attributes specific to training:Person
. One of the attributes you add is classCode
, that will be used to identify the type of class that a given instance belongs to. Later, you use classCode
to control interactions between some of the class instances.
- Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:Person
- Right mouse click on the class and add the following attributes with "create attribute...".
- Double click on the training:Person class to open the class editor and go to the Overview tab.
- In the Description field, enter
Models instances of people with whom I do business
. - Set the Caption property to
idsc.training.Person.caption
, and set its en value toPerson
. - Set the Base Class property to
training:Entity
. This establishes the relationship between the base class and subclass, makingtraining:Person
a subclass of training:Entity
. In the Attributes tab, update the attributes' properties as follows (in yellow):
Attributes for training:PersonAttribute Property Value Notes classCode Type string Required true Value "PERSON" Include the quotation marks. firstName Type string initials Type string - Save your changes.
You may notice that the training:LOCKING aspect isn't applied to training:Person
. This is because it get's this characteristic from it's base - training:Entity - so it doesn't need to be re-specified.
Create the training:Company Class
You also make the training:Company
class a subclass of training:Entity
and add the attributes that are specific to this class.
- Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:Company
- Right mouse click on the class and add the following attributes with "create attribute...".
- Double click on the training:Company class to open the class editor and go to the Overview tab.
- In the Description field, enter
Models instances of companies with which I do business
. - Set the Caption property to
idsc.training.Company.caption
, and set its en value toCompany
. - Set the Base Class property to
training:Entity
. This makestraining:Company
a subclass of training:Entity
. In the Attributes tab, update the attributes' properties as follows (in yellow):
Attribute descriptions for CompanyAttribute Property Value Notes classCode Type string Required true Value "COMPANY" Include the quotation marks. businessNumber Type string incorporationDate Type timestamp - Save your changes.
Edit the training:Entity class
To be able to identify when you are using the base class rather than one of the subclasses, you need to add the classCode
attribute to it as well.
Edit the training:Entity
class in the training:Fundamentals diagram or in the class editor and add the classCode attribute as follows:
New attribute description for Entity
Attribute | Property | Value | Notes |
---|---|---|---|
classCode | Type | string | |
Required | true | ||
Value | "ENTITY" | Include the quotation marks. |
Save your changes.
Add the employees association and fix up the diagram
- Open the training:Fundamentals diagram.
- Select the training:Person and training:Company classes, one at a time, and "unhide association > (inheritance)".
- Right mouse click on the training:Person class and select "create association..." and complete the association as follows
- Press OK and save
Your diagram should now look something like...
Extend the training:Telcom class
You extend the training:Telcom
class by creating two new subclasses: training:TelephoneNumber
and training:EmailAddress
.
Create the training:TelephoneNumber class
- Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:TelephoneNumber
- Right mouse click on the class and add the following attributes with "create attribute...".
- Double click on the training:TelephoneNumber class to open the class editor and go to the Overview tab.
- In the Description field, enter
Captures the phone numbers of the entities with which I do business
. - Set the Caption property to
idsc.training.TelephoneNumber.caption
, and set its en value toTelephone Number
. - Set the Base Class property to
training:Telcom
. This makestraining:TelephoneNumber
a subclass of training:Telcom
. In the Attributes tab, update the attributes' properties as follows (in yellow):
Attributes for training:TelephoneNumber
Attribute Property Value Notes classCode Type string Required true Value "TELEPHONE" Include the quotation marks. extension Type string - Save your changes.
Create the training:EmailAddress class
- Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:EmailAddress
- Right mouse click on the class and add the following attributes with "create attribute...".
- Double click on the training:EmailAddress class to open the class editor and go to the Overview tab.
- In the Description field, enter
Captures the email addresses of the entities with which I do business
. - Set the Caption property to
idsc.training.EmailAddress.caption
, and set its en value toEmail Address
. - Set the Base Class property to
training:Telcom
. This makestraining:EmailAddress
a subclass of training:Telcom
. In the Attributes tab, update the attributes' properties as follows (in yellow):
Attributes for training:EmailAddress
Attribute Property Value Notes classCode Type string Required true Value "EMAIL" Include the quotation marks. displayName Type string - Save your changes.
Update the training:Telcom class
Defining the classCode
attribute of the training:Telcom
class as required, but omitting a value for it, ensures that no instance of training:Telcom
can be instantiated. This turns training:Telcom
into an abstract class, a class that exists only to be a base class for derived classes.
To update the training:Telcom
class:
Open the
training:Telcom
class and add aclassCode
attribute to it as well.
New attribute for training:TelcomAttribute Property Value Notes classCode Type string Required true Value Leave the classCode value empty - Save your changes.
Update the class diagram
View the model changes that you have made by expanding the training:Fundamentals
diagram that you created in the previous module.
To modify the class diagram:
- Open the
training:Fundamentals
diagram (Business Model → Diagrams). - Right mouse click on training:Telcom and Unhide attribute → classCode
- Right mouse click on the training:TelephoneNumber and training:EmailAddress classes and select Unhide association → (inheritance) and also uncheck "Hide description".
- Rearrange the diagram so that you can see all the elements clearly. It should resemble the following:
- Save your changes.
Define persistence for the subclasses
Enterprise data is typically persisted to relational databases, which do not naturally support inheritance. When mapping classes to a relational database you need to consider how to store their inherited structure.
The platform supports three persistence patterns, or strategies, for storing your class instance data:
Concrete Table
Each class and subclass stores all its data in a separate table, one per class. This pattern is simplest to define a mapping for, but results in a large number of tables storing related data.
Single Table
One table stores all the data for the base class and its subclasses. This pattern minimizes the complexity of table joins when retrieving class data. If the classes are refactored in such a way that fields move up or down the class hierarchy, then the table schema remains unaffected and the database structure does not need to be modified.
Class Table
One table stores the data that is common to the base class and its subclasses. The columns that are unique to each subclass are stored in a subclass specific table. This pattern minimizes unused database space as all columns are relevant for every row filled by an instance. It results in a database structure that mimics the class structure, creating a direct relationship between the object oriented model and the relational data model.
In this model you use the Single Table pattern to persist the derived classes of the training:Entity
class and the Class Table pattern to persist derived classes of the training:Telcom
class. The Concrete Table pattern is not used in this course.
Create the class persistence mapping
Use the Persistence Mapping tab for each class to map the persistence of the class attributes to columns in the data sources that you have defined.
Define persistence for the training:Entity subclasses using the single table pattern
In the Single Table pattern the data from the base and the derived classes is all stored in a single table. You use the TrainingEntity table to persist the data for the training:Person
and training:Company
classes. For all three classes, you set the Class Code Attribute to the classCode
attribute of each class. This specifies that the value of the classCode
column determines which class a persisted instance belongs to. All the information for any instance of the training:Entity
, training:Person
, or training:Company
classes will be contained in this table. For any given row of the table, there will be columns, from other subclasses, that are unused.
Edit the persistence for the training:Entity class
To edit the persistence for the training:Entity
class:
- Open the
training:Entity
class for editing. You can either access it through the Classes tab in the Business Model layer, or by double-clicking it in theClass
model diagram. - Click the Persistence Mapping tab.
- Set the Class Code Attribute value to
classCode
. - Click the Select Attributes Mapping button and add the
classCode
attribute to the list and press OK. - Click the Update Data Source button, view the changes to be made, and click OK.
- In the Columns tab of the
training:DB
data source, apply the following changes to the Entity tables'sclassCode
column- Precision: 10
- Case Insensitive: false
Add the
companyId
attribute to theEntity table
. This attribute is used to create an index that keeps track of the relationship betweentraining:
Person
andtraining:
Company
instances.Column Name Type Allocation Allow Nulls Precision Case insensitive companyId binary fixed true 16 false In the Indexes tab, add the following two indexes:
Indexes to add to the Entity tableName Type Unique Index Columns Entity.OK_ClassCode btree false classCode, lastName Entity.FK_Company btree false companyId Info
The
Entity.OK_ClassCode
index is used to optimize searches on thelastName
field for a given subclass. TheEntity.FK
index is used with the already-existingTraining
Entity.PK
index to persist the associations between thetraining:
Person
andtraining:
Company
class instances.Save your changes.
Define the persistence for the training:Person class
To define the persistence for the training:Person
class:
- Open the
training:Person
class and select the Persistence Mapping tab. - Set the Data Source property to
training:DB
. - Set the Class Code Attribute value to
classCode
. - Set the Primary Table to
Entity
. When you create the persistence mapping for a derived class, the Primary Table property should always refer to the table associated with the most-base class. That is the most primary table in the hierarchy of classes. In a single table pattern, the base class table and the derived class table are the same. - In the Key Generator field, select
KeyGenerator.GUIDGen (GUID key generator)
. - Click the Select Attributes Mapping button and add the following attributes to the list, then press OK:
firstName
initials
company
Set the Attribute, Source Key, and Destination Key values for the company attribute according to the following table.
Association attribute mapping for the Person classAttribute Source Key Destination Key company Entity.FK_Company company
is a complex attribute and points to one or more instances of thetraining:Company
class, whose instances are identified through theEntity.PK
index in the training:DB datasource.
When you set up the persistence for associated classes, the Source Key points to the index associated with the current class, and the Destination Key points to an index on the associated class. The values of the columns indexed by the two keys is coordinated to track the association between the instances.- Click the Update Data Source button, review the changes that will be made, and click OK.
- The default values of the table's firstName and initials columns are fine.
Save your changes.
Info
The company
attribute of the training:
Person
class is not persisted in the database. The relationship between training:
Company
and training:
Person
is maintained by the model.
Create the class persistence mapping for the training:Company class
Create the class persistence mapping for the training:Company
class:
- Open the
training:Company
class and select the Persistence Mapping tab. - Set the Data Source property to
training:DB
. - Set the Class Code Attribute value to
classCode
. - Set the Primary Table to
Entity
. - Click the Select Attributes Mapping button and add the following attributes to the list, then press OK:
businessNumber
incorporationDate
employees
Set the Name, Source Key, and Destination Key values for the employees attribute according to the following table.
Association attribute mapping for the Company className Source Key Destination Key employees Entity.FK_Company The employees attribute is complex, and points to one or more instances of the training:
Company
class, whose related instances are identified through the Entity.FK_Company index.- Click the Update Data Source button, view the changes to be made, and click OK.
- Save your changes.
Persistence mapping for the training:Telcom subclasses - class table pattern
When using the class table pattern of persistence mapping, you map the common attribute set of the base class to one table. You then map unique attribute sets of each derived class to its own extension table.
Edit the class persistence mapping for the training:Telcom class
- Open the
training:Telcom
class and select the Persistence Mapping tab. Set the Class Code Attribute value to
classCode
.Info
This property must be set to ensure that the class is abstract, as it was defined. Setting this value allows the class to properly identity the subclass that its derived instances belong to.
- Click the Select Attributes Mapping button and add the
classCode
attribute to the list. - Click the Update Data Source button, view the changes to be made, and click OK.
- In the Columns tab of the training:DB data source, apply the following changes to the Telcom table's
classCode
column:- Precision: 10
- Case Insensitive: false
- Save your changes.
Define persistence for the training:TelephoneNumber class
- Open the
training:TelephoneNumber
class and select the Persistence Mapping tab. - Set the Data Source property to
training:DB
. - Set the Class Code Attribute value to
classCode
. - Set the Primary Table to
Telcom
. Notice that the Primary Table is specified to be base class' table, not a subclass table. This will allow us to associate the two tables (base and subclass) through the base table's primary key, and allows the persistence data to be split properly across the tables. - In the Key Generator field, select KeyGenerator.GUIDGen (GUID key generator).
- Click the Select Attributes Mapping button and add the
extension
attribute and press OK. - Set the Extension Table property for the
extension
attribute toTelephoneNumber
.
You specify the table in which the attribute is stored so it will be different from the primary table. Similar to the single-table pattern, you need only specify a mapping for the attributes that are specific to the derived class. When an instance oftraining:TelephoneNumber
is persisted, its data is split across a row of the Telcom table and the TelephoneNumber table. These two rows are related through the primary keys of the two tables. - Click the Update Data Source button, view the changes to be made, and click OK.
- In the properties of the TelephoneNumber table enter the Description:
Stores telephone extension information for a Telephone Telcom instance
. - In the Columns tab of the
DefaultRelationalDatabase
data source, apply the following change to the TelephoneNumber talble'sid
column:- Case Insensitive: false
- Make the following changes to the
extension
column:- Precision: 25
- Save your changes.
Define persistence for the training:EmailAddress class
- Open the
training:EmailAddress
class and select the Persistence Mapping tab. - Set the Data Source property to
training:DB
. - Set the Class Code Attribute value to
classCode
. - Set the Primary Table to
Telcom
. Remember, when creating an extension table, the Primary Table value should be the name of the table that you are extending. - In the Key Generator field, select KeyGenerator.GUIDGen (GUID key generator).
- In the Attributes Mappings section, click the Select button and add the
displayName
attribute. - Set the Extension Table value for the
displayName
attribute toEmailAddress
. - Click the Update Data Source button, view the changes to be made, and click OK.
- In the properties of the EmailAddress table enter the Description:
Stores the email address of an EmailAddress Telcom instance.
- In the Columns tab of the
DefaultRelationalDatabase
data source, apply the following change to the EmailAddress table'sid
column:- Case Insensitive: false
- Click the Save button in the toolbar to save your work.
Edit the upgrade file
To update the upgrade file:
- In the Persistence layer on the Data Sources tab right-click the training:DB and select Generate Upgrade Steps.
- Leave the default values for Upgrade and Current Data Source.
- Select Old Model as the Old Data Source. You are going to use a published model as the old model.
- Select Local History and select the timestamp before you started this lesson. You can verify the time be inspecting the Text Compare for your selected Revision Time. click Next.
- Whenever you upgrade a database, you must increment the version number by a whole number. e.g. If the version retrieved from the published JAR file is 4.1092.1307.167.53; change the Version to 5.1092.1307.167.53.
- Enter the following as the Description:
Added support for EmailAddress, TelephoneNumber, Person, and Company.
- On the left side is a list of the upgrade steps that will be applied to the database:
- Two CreateTable steps: one for the
TelephoneNumber
table and one for theEmailAddress
table. - Several CreateColumn steps in which columns will be added to existing tables.
- Two CreateIndex steps for the new indexes that need to be added to the
Entity
table.
- Two CreateTable steps: one for the
- The
classCode
attribute requires a value, so you need to provide a value for any existing instances of classes that now use the newclassCode
attribute:- Select the step that adds the
classCode
column to theTrainingEntity
table (this should be the third step) and click the SQL Scripts tab. - In the SQL Scripts list, click the Add button and select SQL. This adds an SQL script, which is run after the column is created.
In the scripting area to the right of the SQL scripts list, enter the following SQL statement.
SQLupdate ${table:Entity} set classCode = 'ENTITY'
This statement updates any existing rows of the
Entity
table to give them aclassCode
value ofENTITY
.Info
It is also possible to use the command update
${table} set classCode = 'ENTITY'
as the SQL statement. Using${table}
without an argument when carrying out a column SQL step causes the value to default to the table on which the column update is taking place.- Select the step that adds the
classCode
column to theTelcom
table (this should be the ninth step) and click the SQL Scripts tab. - In the SQL Scripts list, click the Add button and select SQL. This adds an SQL script, which is run after the column is created.
In the scripting area to the right of the SQL scripts list, enter the following SQL statement.
SQLupdate ${table:Telcom} set classCode = 'TELEPHONE'
This statement updates any existing rows of the
Telcom
table to give them a classCode value ofTELEPHONE
.
- Select the step that adds the
- Click Finish to create the upgrade steps.
- Your upgrade is added to the bottom of the list of upgrades.
- You must ensure that your current model's version matches the upgrade version. Click the Set current model button in the toolbar to open the Model Library.
- With the current model selected, click Edit. Update the Model Version field to match the version associated with the upgrade you just created. For example, if your new upgrade step is 5.1092.1307.167.53, then change the Model version to 5.1092.1307.167.53. This enables NexJ Studio to recognize that the model has been changed from the previous version so that your update can be applied to the supporting data stores.
- Click OK. Note that the new version is now reflected in the table in the Models tab.
- Save your work.
Publish the model (optional and takes time)
Publish the updated model from the Model Library. You might want to just validate your model instead with the validate button . (this is optional too if you are super confident in your work).
- Click the Set current modelbutton to open the Model Library.
- Select the
training
model. - Click Publish. The Publish Model window opens.
- Select the folder that you want to publish. This should already be correct.
- Click Save. Click Close.
The Console view lists the actions taking place as NexJ Studio publishes the model. If the publish is successful, you will see the message
BUILD SUCCESSFUL
.Info
If the model contains any warnings or errors, you are prompted to confirm whether you want to continue publishing. For the purposes of this tutorial, it is OK to have warnings, but not errors, when you publish.
Upgrade the database
Use the Data Load Tool to apply the changes from the Upgrade file to the physical database.
- Launch the Data Load Tool by clicking the drop-down arrow next to the Run tool button in the toolbar.
- In the Model section, select Current.
- In the Server and Connection field, ensure that the Test option is selected.
- In the Server field, select Development(development.properties).
- In the Command field, select upgrade.
- In the Data Source field, select *.
- Select Ignore Upgradable Flag.
- Click Run. Confirm that you want to perform the upgrade action against a non-test connection.
To verify that the upgrade worked:
- Run your database's management tool or query client.
- Verify that the table
TRNTelcom
exists with the proper column definitions in the training database:- For Microsoft SQL Server use the
exec sp_help TRNTelcom
statement.
- For Microsoft SQL Server use the
Test the model
To test your model, you create new instances of training:Entity
, training:Person
, and training:Company
and set the persons employer to the company instance. Then you create new instances of telecoms.
To test your model:
Use and existing or create a new scratchpad and enter the following code
CODE;The following code tests the training:Person and training:Company classes (define e (training:Entity'new (: lastName "Thing2"))) (define p (training:Person'new (: lastName "Cole") (: firstName "Nat"))) (define c (training:Company'new (: lastName "Food Corp") (: businessNumber "BN-1234"))) ; Set p's employer to c (p'company c) ; Persist the changes (commit) ; Display c's employees (logger'info "Company [" (c'lastName) "] has " ((c'employees)'size) " employee(s)")
Javascript
JS;The following code tests the training:Person and training:Company classes var e = new #"training:Entity"({lastName: "Thing2"}); var p = new #"training:Person"({lastName: "Cole", firstName: "Nat"}); var c = new #"training:Company"({lastName: "Food Corp", businessNumber: "BN-1234"}); ; Set p's employer to c p.company = c; ; Persist the changes commit(); ; Display c's employees logger.info("Company [" + c.lastName + "] has " + c.employees.size + " employee(s)");
- Start the Server Console with DEBUG log level.
Select the code and press Ctrl+U. Inspect the SQL statements in the console & review the output of the last Scheme statement, which should read:
CODE; 15:35:55,039 INFO[GlobalEnvironment] Company [Food Corp] has 1 employee(s) ; #t
To get an understanding of how these instances have been persisted in the database, query the training database with the following queries:
Microsoft SQL ServerSQLselect id, classCode, firstName, lastName, companyId, businessNumber from TRNEntity;
In particular notice the following features, which result from using the single table pattern for persistence:
All the data pertaining to a class instance is contained in a single row.
A number of columns store NULL values because they map to attributes that do not apply to the class instance persisted in the row.
The companyId value of the
Nat Cole
row is the same as the id value of theFood Corp
row. This persists the association between the two instances.
Now add the following code to the scratchpad and run it.
CODE;The following code tests the training:Telcom and training:EmailAddress classes ;Create a new training:Entity with a lastName of Doe (training:Entity'new (: lastName "Doe")) (commit) (training:Entity'read '(lastName) '(= lastName "Doe") '() '() '() '()) (define e2 (training:Person'new (: firstName "Ned") (: lastName "Shawn"))) (define tp (training:TelcomType'new (: name "Business"))) (define telcom (training:EmailAddress'new (: entity e2) (: type tp) (: name (tp'name)) (: address "ned@nexj.com") (: displayName "Ned Shawn"))) (define phone (training:TelephoneNumber'new (: entity e2) (: type tp) (: name (tp'name)) (: address "555-5555") (: extension "123"))) (commit) (for-each (lambda (x) (if (instance? x training:TelephoneNumber) (logger'info (format "TelephoneNumber is {0} x{1}" (x'address) (x'extension))) ;else (logger'info (format "EmailAddress is {0} ({1})" (x'address) (x'displayName))) ) ) (training:Telcom'read '(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName)) '() '() '() '() '()) )
Javascript
JS;The following code tests the training:Telcom and training:EmailAddress classes ;Create a new training:Entity with a lastName of Doe new #"training:Entity"({lastName: "Doe"}); commit(); #"training:Entity".read(scm("'(lastName)"), scm("'(= lastName \"Doe\")"), null, null, null, null); var e2 = new #"training:Person"({firstName: "Ned", lastName: "Shawn"}); var tp = new #"training:TelcomType"({name: "Business"}); var telcom = new #"training:EmailAddress"({entity: e2, type: tp, name: tp.name, address: "ned@nexj.com", displayName: "Ned Shawn"}); var phone = new #"training:TelephoneNumber"({entity: e2, type: tp, name: tp.name, address: "555-5555", extension: "123"}); commit(); #"for-each"( function (x) { if (#"instance?"(x, #"training:TelephoneNumber")) { logger.info(format("TelephoneNumber is {0} x{1}", x.address, x.extension)); } else { logger.info(format("EmailAddress is {0} ({1})", x.address, x.displayName)); } }, #"training:Telcom".read(scm("'(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName))"), null, null, null, null, null) );
You should receive a result similar to the following:
CODE; 11:24:06,562 INFO [GlobalEnvironment] (NexJ Main) TelephoneNumber is (416) 555-1234 xnull ; 11:24:06,562 INFO [GlobalEnvironment] (NexJ Main) EmailAddress is ned@nexj.com (Ned Shawn) ; 11:24:06,562 INFO [GlobalEnvironment] (NexJ Main) TelephoneNumber is 555-5555 x123
To get an understanding of how these instances have been persisted in the database, query the training database with the following queries. Run each query separately.
Microsoft SQL ServerSQLselect id, name, address, telcomTypeId, entityID, isPrimary, classCode FROM TRNTelcom select id, displayName FROM TRNEmailAddress select id, extension FROM TRNTelephoneNumber
- In particular notice the following features, which result from using the Class Table pattern of persistence:
• The data for each instance is spread across multiple tables.
• There is no NULL data stored in the tables.
• The rows that define values for the same class instance share the same id value. For example, the id of the EMAIL row in theTRNTelcom
table is the same as the id of the Ned Shawn row in theTRNEmailAddress
table.
Optional - more on the class code attribute
In the section Set the persistence mapping for the training:Telcom subclasses using the class table pattern, you read about the importance of setting the Class Code Attribute. This step is easily overlooked, so to help you with future debugging, this section will let you see what happens if this step is overlooked. These steps assume that you have already run the test code described in Test the model. If this code has not been run, or if you have carried out an action that has reset the database, then run the code again before proceeding.
Start the Server Console with DEBUG logging on. Once the Console is started, add the following line to your scratchpad and run it:
CODE(training:Telcom'read '(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName)) '() '() '() '() '())
Javascript
JS#"training:Telcom".read(scm("'(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName))"), null, null, null, null, null);
Notice the following line in the output:
CODE; #<Instance<training:EmailAddress, OID:1:V32:A2833125621845559D5076FC5C0D6318, CLEAN>(classCode="EMAIL", address="ned@nexj.com", locking=0, displayName="Ned Shawn")>
Even though you gave the instruction to read a training:
Telcom
instance, the Class Code Attribute of that instance was read, correctly identifying it as an instance of the derivedtraining:EmailAddress
class.- Click the Terminate button to stop the Scheme Console.
- Open the training:
Telcom
class in the editor and click the Persistence Mapping tab. - Delete the value of the Class Code Attribute field.
- Click the Save button in the toolbar to save your changes.
- Restart the Server Console.
- Go to your scratchpad, and rerun the code that you ran in step 1.
Notice the following line in the output:
CODE#<Instance<training:Telcom, OID:1:V32:A2833125621845559D5076FC5C0D6318, CLEAN>(address="ned@nexj.com", locking=0)>
In this case, the value of the Class Code Attribute was not set, so there was never an indication provided to the read command that the instance was anything other than a
training:Telcom
instance.Now, try to run the for-each statement from the Test the model section:
CODE(for-each (lambda (x) (if (instance? x training:TelephoneNumber) (logger'debug (format "TelephoneNumber is {0} x{1}" (x'address) (x'extension))) ;else (logger'debug (format "EmailAddress is {0} ({1})" (x'address) (x'displayName))) ) ) (Telcom'read '(address (@@ TelephoneNumber extension) (@@ EmailAddress displayName)) '() '() '() '() '()) )
Javascript
JS#"for-each"( function (x) { if (#"instance?"(x, #"training:TelephoneNumber")) { logger.info(format("TelephoneNumber is {0} x{1}", x.address, x.extension)); } else { logger.info(format("EmailAddress is {0} ({1})", x.address, x.displayName)); } }, #"training:Telcom".read(scm("'(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName))"), null, null, null, null, null) );
You should receive an error reading:
CODEnexj.core.meta.MetadataLookupException: Unknown event or attribute "displayName" in class "Telcom". (err.meta.selectorLookup)
The Class Code Attribute for
training:Telcom
is not set, so all of the instances found by the read command are treated as training:Telcom instances. The error occurs because thetraining:Telcom
class does not have adisplayName
attribute.Click the Terminate button to stop the Console.
Open the
training:Telcom
class in the editor and click the Persistence Mapping tab.Restore the value of the Class Code Attribute field to
classCode
.Save your work.