Skip to main content

Defining Persistent Classes

A persistent class is a class that defines persistent objects. This topic describes how to create such classes.

The samples shown on this page are from Samples-Data (https://github.com/intersystems/Samples-DataOpens in a new tab). InterSystems recommends that you create a dedicated namespace called SAMPLES (for example) and load samples into that namespace. For the general process, see Downloading Samples for Use with InterSystems IRIS® data platform.

Defining a Persistent Class

To define a class that defines persistent objects, ensure that the primary (first) superclass of your class is either %PersistentOpens in a new tab or some other persistent class.

For example:

Class MyApp.MyClass Extends %Persistent
{
}

Projection of Packages to Schemas

For persistent classes, the package is represented in SQL as an SQL schema. For instance, if a class is called Team.Player (the Player class in the Team package), the corresponding table is Team.Player (the Player table in the Team schema).

The default package is User, which is represented in SQL as the SQLUser schema. Hence, a class called User.Person corresponds to a table called SQLUser.Person.

If a package name contains periods, the corresponding table name uses an underscore in the place of each. For example, the class MyTest.Test.MyClass (the MyClass class in the MyTest.Test package) becomes the table MyTest_Test.MyClass (the MyClass table in the MyTest_Test schema).

If an SQL table name is referenced without the schema name, the default schema name (SQLUser) is used. For instance, the command:

Select ID, Name from Person

is the same as:

Select ID, Name from SQLUser.Person

Specifying the Table Name for a Persistent Class

For a persistent class, by default, the short class name (the part of the name after the last period) becomes the table name.

To specify a different table name, use the SqlTableName class keyword. For example:

Class App.Products Extends %Persistent [ SqlTableName = NewTableName ]

Although InterSystems IRIS places no restrictions on class names, SQL tables cannot have names that are SQL reserved words. Thus if you create a persistent class with a name that is an SQL reserved word, you will not be able to query the associated table. In this case, you must either rename the class or specify a table name for the projection that differs from the class name, using the technique described here.

Controlling How IDs Are Generated

When you save an object for the first time, the system generates an ID for the object. IDs are permanent.

By default, InterSystems IRIS uses an integer for the ID, incremented by 1 from the last saved object.

You can define a given persistent class so that it generates IDs in either of the following ways:

  • The ID can be based on a specific property of the class, if that property is unique per instance. For example, you could use a drug code as the ID. To define a class this way, add an index like the following to the class:

    Index IndexName On PropertyName [ IdKey ];
    

    Or (equivalently):

    Index IndexName On PropertyName [ IdKey, Unique ];
    

    Where IndexName is the name of the index, and PropertyName is the name of the property.

    If you define a class this way, when InterSystems IRIS saves an object for the first time, it uses the value of that property as the ID. Furthermore, InterSystems IRIS requires a value for the property and enforces uniqueness of that property. If you create another object with the same value for the designated property and then attempt to save the new object, InterSystems IRIS issues this error:

    ERROR #5805: ID key not unique for extent 
    

    Also, InterSystems IRIS prevents you from changing that property in the future. That is, if you open a saved object, change the property value, and try attempt to save the changed object, InterSystems IRIS issues this error:

    ERROR #5814: Oid previously assigned
    

    This message refers to the OID rather than the ID, because the underlying logic prevents the OID from being changed; the OID is based on the ID.

  • The ID can be based on multiple properties. To define a class this way, add an index like the following to the class:

    Index IndexName On (PropertyName1,PropertyName2,...) [ IdKey, Unique ];
    

    Or (equivalently):

    Index IndexName On (PropertyName1,PropertyName2,...) [ IdKey ];
    

    Where IndexName is the name of the index, and PropertyName1, PropertyName2, and so on are the property names.

    If you define a class this way, when InterSystems IRIS saves an object for the first time, it generates an ID as follows:

    PropertyName1||PropertyName2||...
    

    Furthermore, InterSystems IRIS requires values for the properties and enforces uniqueness of the given combination of properties. It also prevents you from changing any of those properties.

Important:

If a literal property (that is, an attribute) contains a sequential pair of vertical bars (||), do not add an IdKey index that uses that property. This restriction is imposed by the way in which the InterSystems SQL mechanism works. The use of || in IdKey properties can result in unpredictable behavior.

The system generates an OID as well. In all cases, the OID has the following form:

$LISTBUILD(ID,Classname)

Where ID is the generated ID, and Classname is the name of the class.

Controlling the SQL Projection of Subclasses

When several persistent classes are in superclass/subclass hierarchy, there are two ways in which InterSystems IRIS can store their data. The default scenario is by far the most common.

Default SQL Projection of Subclasses

The class compiler projects a flattened representation of a persistent class, such that the projected table contains all the appropriate fields for the class, including those that are inherited. Hence, for a subclass, the SQL projection is a table composed of:

  • All the columns in the extent of the superclass

  • Additional columns based on properties only in the subclass

  • Rows that represent the saved instances of the subclass

Furthermore, in the default scenario, the extent of the superclass contains one record for each saved object of the superclass and all its subclasses. The extent of each subclass is a subset of the extent of the superclass.

For example, consider the persistent classes Sample.Person and Sample.Employee in SAMPLES. The Sample.Employee class inherits from Sample.Person and adds some additional properties. In the SAMPLES, both classes have saved data.

  • The SQL projection of Sample.Person is a table that contains all the suitable properties of the Sample.Person class. The Sample.Person table contains one record for each saved instance of the Sample.Person class and each saved instance of the Sample.Employee class.

  • The Sample.Employee table includes the same columns as Sample.Person and also includes columns that are specific to the Sample.Employee class. The Sample.Employee table contains one record for each saved instance of the Sample.Employee class.

To see this, use the following SQL queries. The first lists all instances of Sample.Person and shows their properties:

SELECT * FROM Sample.Person

The second query lists all instances of Sample.Employee and their properties:

SELECT * FROM Sample.Employee

Notice that the Sample.Person table contains records with IDs in the range 1 to 200. The records with IDs in the range 101 to 200 are employees, and the Sample.Employee table shows the same employees (with the same IDs and with additional columns). The Sample.Person table is arranged in two apparent groups only because of the artificial way that the SAMPLES database is built. The Sample.Person table is populated and then the Sample.Employee table is populated.

Typically, the table of a subclass has more columns and fewer rows than its parent. There are more columns in the subclass because it usually adds additional properties when it extends the parent class; there are often fewer rows because there are often fewer instances of the subclass than the parent.

Alternative SQL Projection of Subclasses

The default projection is the most convenient, but on occasion, you might find it necessary to use the alternative SQL projection. In this scenario, each class has its own extent. To cause this form of projection, include the following in the definition of the superclass:

[ NoExtent ]

For example:

Class MyApp.MyNoExtentClass [ NoExtent ] 
{
//class implementation
}

Each subclass of this class then receives its own extent.

If you create classes in this way and use them as properties of other classes, see Variation: CLASSNAME Parameter.

See Also

FeedbackOpens in a new tab