NexJ Logo

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:Companytraining: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.

  1. Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:Person
  2. Right mouse click on the class and add the following attributes with "create attribute...".
  3. Double click on the training:Person class to open the class editor and go to the Overview tab.
  4. In the Description field, enter Models instances of people with whom I do business.
  5. Set the Caption property to idsc.training.Person.caption, and set its en value to Person.
  6. Set the Base Class property to training:Entity. This establishes the relationship between the base class and subclass, making training:Person a subclass of training:Entity.
  7. In the Attributes tab, update the attributes' properties as follows (in yellow):

    Attributes for training:Person

    AttributePropertyValueNotes
    classCodeTypestring
    Requiredtrue
    Value"PERSON"Include the quotation marks.
    firstNameTypestring
    initialsTypestring
  8. 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.

  1. Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:Company
  2. Right mouse click on the class and add the following attributes with "create attribute...".
  3. Double click on the training:Company class to open the class editor and go to the Overview tab.
  4. In the Description field, enter Models instances of companies with which I do business.
  5. Set the Caption property to idsc.training.Company.caption, and set its en value to Company.
  6. Set the Base Class property to training:Entity. This makes training:Company a subclass of training:Entity.
  7. In the Attributes tab, update the attributes' properties as follows (in yellow):

    Attribute descriptions for Company

    AttributePropertyValueNotes
    classCodeTypestring
    Requiredtrue
    Value"COMPANY"Include the quotation marks.
    businessNumberTypestring
    incorporationDateTypetimestamp
  8. 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

AttributePropertyValueNotes
classCodeTypestring
Requiredtrue
Value"ENTITY"Include the quotation marks.
  1. Save your changes.

Add the employees association and fix up the diagram

  1. Open the training:Fundamentals diagram.
  2. Select the training:Person and training:Company classes, one at a time, and "unhide association > (inheritance)".
  3. Right mouse click on the training:Person class and select "create association..." and complete the association as follows
  4. 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

  1. Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:TelephoneNumber
  2. Right mouse click on the class and add the following attributes with "create attribute...".
  3. Double click on the training:TelephoneNumber class to open the class editor and go to the Overview tab.
  4. In the Description field, enter Captures the phone numbers of the entities with which I do business.
  5. Set the Caption property to idsc.training.TelephoneNumber.caption, and set its en value to Telephone Number.
  6. Set the Base Class property to training:Telcom. This makes training:TelephoneNumber a subclass of training:Telcom.
  7. In the Attributes tab, update the attributes' properties as follows (in yellow):

    Attributes for training:TelephoneNumber

    AttributePropertyValueNotes
    classCodeTypestring
    Requiredtrue
    Value"TELEPHONE"Include the quotation marks.
    extensionTypestring
  8. Save your changes.

Create the training:EmailAddress class

  1. Right mouse click in the training:Fundamentals diagram and select New/Class... and name the class training:EmailAddress
  2. Right mouse click on the class and add the following attributes with "create attribute...".
  3. Double click on the training:EmailAddress class to open the class editor and go to the Overview tab.
  4. In the Description field, enter Captures the email addresses of the entities with which I do business.
  5. Set the Caption property to idsc.training.EmailAddress.caption, and set its en value to Email Address.
  6. Set the Base Class property to training:Telcom. This makes training:EmailAddress a subclass of training:Telcom.
  7. In the Attributes tab, update the attributes' properties as follows (in yellow):

    Attributes for training:EmailAddress

    AttributePropertyValueNotes
    classCodeTypestring
    Requiredtrue
    Value"EMAIL"Include the quotation marks.
    displayNameTypestring
  8. 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:

  1. Open the training:Telcom class and add a classCode attribute to it as well.

    New attribute for training:Telcom

    AttributePropertyValueNotes
    classCodeTypestring
    Requiredtrue
    Value
    Leave the classCode value empty
  2. 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:

  1. Open the training:Fundamentals diagram (Business Model → Diagrams).
  2. Right mouse click on training:Telcom and Unhide attribute → classCode
  3. Right mouse click on the training:TelephoneNumber and training:EmailAddress classes and select Unhide association → (inheritance) and also uncheck "Hide description". 
  4. Rearrange the diagram so that you can see all the elements clearly. It should resemble the following:
  5. 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:

  1. 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 the Class model diagram.
  2. Click the Persistence Mapping tab.
  3. Set the Class Code Attribute value to classCode.
  4. Click the Select Attributes Mapping button and add the classCode attribute to the list and press OK.
  5. Click the Update Data Source button, view the changes to be made, and click OK.
  6. In the Columns tab of the training:DB data source, apply the following changes to the Entity tables's classCode column
    • Precision: 10
    • Case Insensitive: false
  7. Add the companyId attribute to the Entity table. This attribute is used to create an index that keeps track of the relationship between training:Person and training:Company instances.

    Column NameTypeAllocationAllow NullsPrecisionCase insensitive
    companyIdbinaryfixedtrue16false
  8. In the Indexes tab, add the following two indexes:

    Indexes to add to the Entity table

    NameTypeUniqueIndex Columns
    Entity.OK_ClassCodebtreefalseclassCode, lastName
    Entity.FK_CompanybtreefalsecompanyId

    Info

    The Entity.OK_ClassCode index is used to optimize searches on the lastName field for a given subclass. The Entity.FK index is used with the already-existing TrainingEntity.PK index to persist the associations between the training:Person and training:Company class instances.

  9. Save your changes.

Define the persistence for the training:Person class

To define the persistence for the training:Person class:

  1. Open the training:Person class and select the Persistence Mapping tab.
  2. Set the Data Source property to training:DB.
  3. Set the Class Code Attribute value to classCode.
  4. 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.
  5. In the Key Generator field, select KeyGenerator.GUIDGen (GUID key generator).
  6. Click the Select Attributes Mapping button and add the following attributes to the list, then press OK:
    • firstName
    • initials
    • company
  7. Set the Attribute, Source Key, and Destination Key values for the company attribute according to the following table.

    Association attribute mapping for the Person class

    AttributeSource KeyDestination Key
    companyEntity.FK_Company


    company is a complex attribute and points to one or more instances of the training:Company class, whose instances are identified through the Entity.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.

  8. Click the Update Data Source button, review the changes that will be made, and click OK.
  9. The default values of the table's firstName and initials columns are fine.
  10. 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:

  1. Open the training:Company class and select the Persistence Mapping tab.
  2. Set the Data Source property to training:DB.
  3. Set the Class Code Attribute value to classCode.
  4. Set the Primary Table to Entity.
  5. Click the Select Attributes Mapping button and add the following attributes to the list, then press OK:
    • businessNumber
    • incorporationDate
    • employees
  6. Set the Name, Source Key, and Destination Key values for the employees attribute according to the following table.

    Association attribute mapping for the Company class

    NameSource KeyDestination 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.

  7. Click the Update Data Source button, view the changes to be made, and click OK.
  8. 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

  1. Open the training:Telcom class and select the Persistence Mapping tab.
  2. 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. 

  3. Click the Select Attributes Mapping button and add the classCode attribute to the list.
  4. Click the Update Data Source button, view the changes to be made, and click OK.
  5. 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
  6. Save your changes.

Define persistence for the training:TelephoneNumber class

  1. Open the training:TelephoneNumber class and select the Persistence Mapping tab.
  2. Set the Data Source property to training:DB.
  3. Set the Class Code Attribute value to classCode.
  4. 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.
  5. In the Key Generator field, select KeyGenerator.GUIDGen (GUID key generator).
  6. Click the Select Attributes Mapping button and add the extension attribute and press OK.
  7. Set the Extension Table property for the extension attribute to TelephoneNumber.
    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 of training: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.
  8. Click the Update Data Source button, view the changes to be made, and click OK.
  9. In the properties of the TelephoneNumber table enter the Description:
    Stores telephone extension information for a Telephone Telcom instance.
  10. In the Columns tab of the DefaultRelationalDatabase data source, apply the following change to the TelephoneNumber talble's id column:
    • Case Insensitive: false
  11. Make the following changes to the extension column:
    • Precision: 25
  12. Save your changes.

Define persistence for the training:EmailAddress class

  1. Open the training:EmailAddress class and select the Persistence Mapping tab.
  2. Set the Data Source property to training:DB.
  3. Set the Class Code Attribute value to classCode.
  4. 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.
  5. In the Key Generator field, select KeyGenerator.GUIDGen (GUID key generator).
  6. In the Attributes Mappings section, click the Select button and add the displayName attribute.
  7. Set the Extension Table value for the displayName attribute to EmailAddress.
  8. Click the Update Data Source button, view the changes to be made, and click OK.
  9. In the properties of the EmailAddress table enter the Description:
    Stores the email address of an EmailAddress Telcom instance.
  10. In the Columns tab of the DefaultRelationalDatabase data source, apply the following change to the EmailAddress table's id column:
    • Case Insensitive: false
  11. Click the Save button in the toolbar to save your work.

Edit the upgrade file

To update the upgrade file:

  1. In the Persistence layer on the Data Sources tab right-click the training:DB and select Generate Upgrade Steps.
  2. Leave the default values for Upgrade and Current Data Source.
  3. Select Old Model as the Old Data Source. You are going to use a published model as the old model.
  4. 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.
  5. 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.
  6. Enter the following as the Description:
    Added support for EmailAddress, TelephoneNumber, Person, and Company.
  7. 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 the EmailAddress 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.
  8. The classCode attribute requires a value, so you need to provide a value for any existing instances of classes that now use the new classCode attribute:
    1. Select the step that adds the classCode column to the TrainingEntity table (this should be the third step) and click the SQL Scripts tab.
    2. 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.
    3. In the scripting area to the right of the SQL scripts list, enter the following SQL statement.

      update ${table:Entity} set classCode = 'ENTITY'

      This statement updates any existing rows of the Entity table to give them a classCode value of ENTITY.

      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.

    4. Select the step that adds the classCode column to the Telcom table (this should be the ninth step) and click the SQL Scripts tab.
    5. 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.
    6. In the scripting area to the right of the SQL scripts list, enter the following SQL statement.

      update ${table:Telcom} set classCode = 'TELEPHONE'

      This statement updates any existing rows of the Telcom table to give them a classCode value of TELEPHONE.

  9. Click Finish to create the upgrade steps.
  10. Your upgrade is added to the bottom of the list of upgrades.
  11. 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.
  12. 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.
  13. Click OK. Note that the new version is now reflected in the table in the Models tab.
  14. 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).

  1. Click the Set current modelbutton to open the Model Library.
  2. Select the training model.
  3. Click Publish. The Publish Model window opens.
  4. Select the folder that you want to publish.  This should already be correct.
  5. Click Save. Click Close.
  6. 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.

  1. Launch the Data Load Tool by clicking the drop-down arrow next to the Run tool button  in the toolbar.
  2. In the Model section, select Current.
  3. In the Server and Connection field, ensure that the Test option is selected.
  4. In the Server field, select Development(development.properties).
  5. In the Command field, select upgrade.
  6. In the Data Source field, select *.
  7. Select Ignore Upgradable Flag.
  8. Click Run. Confirm that you want to perform the upgrade action against a non-test connection.

To verify that the upgrade worked:

  1. Run your database's management tool or query client.
  2. 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.

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:

  1. Use and existing or create a new scratchpad and enter the following 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
    ;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)");
  2. Start the Server Console with DEBUG log level.
  3. 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:

    ; 15:35:55,039 INFO[GlobalEnvironment] Company [Food Corp] has 1 employee(s)
    
    ; #t
  4. To get an understanding of how these instances have been persisted in the database, query the training database with the following queries:
    Microsoft SQL Server

    select 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 the Food Corp row. This persists the association between the two instances.

  5. Now add the following code to the scratchpad and run it.

    ;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
    ;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)
    );
  6. You should receive a result similar to the following:

    ; 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
  7. 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 Server

    select id, name, address, telcomTypeId, entityID, isPrimary, classCode FROM TRNTelcom
    select id, displayName FROM TRNEmailAddress
    select id, extension FROM TRNTelephoneNumber
  8. 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 the TRNTelcom table is the same as the id of the Ned Shawn row in the TRNEmailAddress 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.

  1. Start the Server Console with DEBUG logging on. Once the Console is started, add the following line to your scratchpad and run it:

    (training:Telcom'read '(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName)) '() '() '() '() '())
    Javascript
    #"training:Telcom".read(scm("'(address (@@ training:TelephoneNumber extension) (@@ training:EmailAddress displayName))"), null, null, null, null, null);

    Notice the following line in the output:

    ; #<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 derived training:EmailAddress class.

  2. Click the Terminate button to stop the Scheme Console.
  3. Open the training:Telcom class in the editor and click the Persistence Mapping tab.
  4. Delete the value of the Class Code Attribute field.
  5. Click the Save button  in the toolbar to save your changes.
  6. Restart the Server Console.
  7. Go to your scratchpad, and rerun the code that you ran in step 1.
  8. Notice the following line in the output:

    #<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.

  9. Now, try to run the for-each statement from the Test the model section:

    (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
    #"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:

    nexj.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 the training:Telcom class does not have a displayName attribute.

  10. Click the Terminate button to stop the Console.

  11. Open the training:Telcom class in the editor and click the Persistence Mapping tab.

  12. Restore the value of the Class Code Attribute field to classCode.

  13. Save your work.