Introduction to Persistent Objects
This topic presents the concepts that are useful to understand when working with persistent classes.
Some of the examples shown here are from the Samples-Data sample (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.
Persistent Classes
A persistent class is any class that inherits from %PersistentOpens in a new tab. A persistent object is an instance of such a class.
The %PersistentOpens in a new tab class is a subclass of %RegisteredObjectOpens in a new tab and thus is an object class. In addition to providing the object interface methods, the %PersistentOpens in a new tab class defines the persistence interface, a set of methods. Among other things, these methods enable you to save objects to the database, load objects from the database, delete objects, and test for existence.
Introduction to the Default SQL Projection
For any persistent class, the compiler generates an SQL table definition, so that the stored data can be accessed via SQL in addition to via the object interface.
The table contains one record for each saved object, and the table can be queried via InterSystems SQL. The following shows the results of a query of the Sample.Person table:
The following table summarizes the default projection:
From (Object Concept) ... | To (Relational Concept) ... |
---|---|
Package | Schema |
Class | Table |
OID | Identity field |
Data type property | Field |
Reference property | Reference field |
Embedded object | Set of fields |
List property | List field |
Array property | Child table |
Stream property | BLOB |
Index | Index |
Class method | Stored procedure |
Other topics provide details and describe any changes you can make:
-
For information on the table name and the name of the schema to which it belongs, see Defining Persistent Classes.
The same topic also has information on how you can control the projection of subclasses.
-
For information on the projection of literal properties, see Defining and Using Literal Properties.
-
For information on the projection of collection properties, see Storage and SQL Projection of Collection Properties.
-
For information on the projection of stream properties, see Working with Streams.
-
For information on the projection of object-valued properties, see Defining and Using Object-Valued Properties.
-
For information on the projection of relationships, see Defining and Using Relationships.
Identifiers for Saved Objects: ID and OID
When you save an object for the first time, the system creates two permanent identifiers for it, either of which you can later use to access or remove the saved objects. The more commonly used identifier is the object ID. An ID is a simple literal value that is unique within the table. By default, the system generates an integer to use as an ID.
An OID is more general: it also includes the class name and is unique in the database. In general practice, an application never needs to use the OID value; the ID value is usually sufficient.
The %PersistentOpens in a new tab class provides methods that use either the ID or the OID. You specify an ID when you use methods such as %OpenId(), %ExistsId(), and %DeleteId(). You specify the OID as the argument for methods such as %Open(), %Exists(), and %Delete(). That is, the methods that use ID as an argument include Id in their names. The methods that use OID as the argument do not include Id in their names; these methods are used much less often.
When a persistent object is stored in the database, the values of any of its reference attributes (that is, references to other persistent objects) are stored as OID values. For object attributes that do not have OIDs, the literal value of the object is stored along with the rest of the state of the object.
Projection of Object IDs to SQL
The ID of an object is available in the corresponding SQL table. If possible, InterSystems IRIS uses the field name ID. InterSystems IRIS also provides a way to access the ID if you are not sure what field name to use. The system is as follows:
-
An object ID is not a property of the object and is treated differently from the properties.
-
If the class does not contain a property named ID (in any case variation), then the table also contains the field ID, and that field contains the object ID. For an example, see the previous section.
-
If the class contains a property that is projected to SQL with the name ID (in any case variation), then the table also contains the field ID1, and this field holds the value of the object ID.
Similarly, if the class contains properties that are projected as ID and ID1, then the table also contains the field ID2, and this field holds the value of the object ID.
-
In all cases, the table also provides the pseudo-field %ID, which holds the value of the object ID.
The OID is not available in the SQL table.
Object IDs in SQL
InterSystems IRIS enforces uniqueness for the ID field (whatever its actual name might be). InterSystems IRIS also prevents this field from being changed. This means that you cannot perform SQL UPDATE or INSERT operations on this field. For instance, the following shows the SQL needed to add a new record to a table:
INSERT INTO PERSON (FNAME, LNAME)VALUES (:fname, :lname)
Notice that this SQL does not refer to the ID field. InterSystems IRIS generates a value for the ID field and inserts that when it creates the requested record.
Class Members Specific to Persistent Classes
InterSystems IRIS classes can include several kinds of class members that are meaningful only in persistent classes. These are storage definitions, indexes, foreign keys, and triggers.
Storage Definitions
In most cases (as discussed later), each persistent class has a storage definition. The purpose of the storage definition is to describe the global structure that InterSystems IRIS uses when it saves data for the class or reads saved data for the class. Your Integrated Development Environment (IDE) displays the storage definition at the end of the class definition. The following shows a partial example:
<Storage name="Default">
<Data name="PersonDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>SSN</Value>
</Value>
<Value name="4">
<Value>DOB</Value>
</Value>
<Value name="5">
<Value>Home</Value>
</Value>
<Value name="6">
<Value>Office</Value>
</Value>
<Value name="7">
<Value>Spouse</Value>
</Value>
<Value name="8">
<Value>FavoriteColors</Value>
</Value>
</Data>
<DataLocation>^Sample.PersonD</DataLocation>
<DefaultData>PersonDefaultData</DefaultData>
<ExtentSize>200</ExtentSize>
<IdLocation>^Sample.PersonD</IdLocation>
<IndexLocation>^Sample.PersonI</IndexLocation>
<Property name="%%CLASSNAME">
<Selectivity>50.0000%</Selectivity>
</Property>
...
Also in most cases, the compiler generates and updates the storage definition. For more information on the globals used for persistent classes, see Persistent Objects and Globals.
Indices
As with other SQL tables, an InterSystems SQL table can have indexesOpens in a new tab; to define these, you add index definitions to the corresponding class definition.
An index can add a constraint that ensures uniqueness of a given field or combination of fields. For information on such indexes, see Defining Persistent Classes.
Another purpose of an index is to define a specific sorted subset of commonly requested data associated with a class, so that queries can run more quickly. For example, as a general rule, if a query that includes a WHERE clause using a given field, the query runs more rapidly if that field is indexed. In contrast, if there is no index on that field, the engine must perform a full table scan, checking every row to see if it matches the given criteria — an expensive operation if the table is large. See Other Options for Persistent Classes.
Foreign Keys
An InterSystems SQL table can also have foreign keysOpens in a new tab. To define these, you add foreign key definitions to the corresponding class definition.
Foreign keys establish referential integrity constraints between tables that InterSystems IRIS uses when new data is added or when data is changed. If you use relationships, the system automatically treats these as foreign keys. But you can add foreign keys if you do not want to use relationships or if you have other reasons to add them.
For more information on foreign keys, see Other Options for Persistent Classes.
Triggers
An InterSystems SQL table can also have triggersOpens in a new tab. To define these, you add trigger definitions to the corresponding class definition.
Triggers define code to be executed automatically when specific events occur, specifically when a record is inserted, modified, or deleted.
For more information on triggers, see Other Options for Persistent Classes.
Other Class Members
A class method or a class query can be defined so that it can be invoked as a stored procedureOpens in a new tab, which enables you to invoke it from SQL.
For class members not discussed in this topic, there is no projection to SQL. That is, InterSystems IRIS does not provide a direct way to use them from SQL or to make them usable from SQL.