1. Identifica casos de uso para clases de objetos persistentes y registrados
Puntos Clave
- %Persistent: Objetos que se almacenan permanentemente en la base de datos con proyección automática como tabla SQL
- %RegisteredObject: Objetos transitorios que existen solo en memoria durante la vida del proceso
- %SerialObject: Objetos embebibles almacenados en línea dentro de un objeto persistente (sin identidad independiente)
- Usar clases persistentes cuando los datos deben sobrevivir a la terminación del proceso y ser consultables vía SQL
- Usar clases registradas para objetos utilitarios, contenedores de lógica de negocio y estructuras de datos temporales
Notas Detalladas
Descripción general
InterSystems IRIS proporciona tres tipos fundamentales de objetos a través de clases del sistema. Elegir la superclase correcta determina cómo se almacenan y gestionan las instancias de objetos. Esta decisión tiene implicaciones directas en la proyección SQL, el uso de memoria y la persistencia de datos.
Clases Persistentes (%Persistent)
Las clases persistentes almacenan sus datos en globals en disco. Cada clase persistente se proyecta automáticamente como una tabla SQL, y cada instancia de objeto guardada se convierte en una fila. Los objetos persistentes tienen un ID de objeto único asignado al guardar.
Class Sample.Person Extends %Persistent
{
Property Name As %String;
Property DOB As %Date;
}
Casos de uso para clases persistentes:
- Datos de aplicación que deben almacenarse a largo plazo (pacientes, pedidos, transacciones)
- Datos que necesitan ser consultados vía SQL
- Objetos que participan en relaciones con otros objetos persistentes
- Datos compartidos entre múltiples procesos
Clases de Objetos Registrados (%RegisteredObject)
Los objetos registrados viven solo en memoria durante la duración del proceso. Soportan el modelo de objetos completo (propiedades, métodos, herencia) pero no tienen mecanismo de almacenamiento automático.
Class Sample.Calculator Extends %RegisteredObject
{
Property LastResult As %Numeric;
Method Add(a As %Numeric, b As %Numeric) As %Numeric
{
Set ..LastResult = a + b
Return ..LastResult
}
}
Casos de uso para clases registradas:
- Clases utilitarias y auxiliares que realizan cálculos
- Contenedores temporales de datos durante el ciclo de vida de una solicitud
- Clases base abstractas que definen interfaces
- Clases de servicios y adaptadores
Clases de Objetos Seriales (%SerialObject)
Los objetos seriales se embeben dentro de un objeto persistente. No tienen su propio almacenamiento ni ID; en su lugar, sus datos se serializan en el almacenamiento del objeto padre. En SQL, las propiedades seriales se proyectan como múltiples columnas con una convención de nomenclatura (por ejemplo, Home_City, Home_State).
Class Sample.Address Extends %SerialObject
{
Property City As %String;
Property State As %String;
Property Zip As %String;
}
Class Sample.Person Extends %Persistent
{
Property Name As %String;
Property HomeAddress As Sample.Address;
}
Clases Utilitarias (Sin Superclase)
Una clase definida sin ninguna cláusula Extends es una clase utilitaria. Como no hereda de %RegisteredObject, no puede instanciarse con %New(). Solo puede contener ClassMethods y Parameters (sin métodos de instancia ni propiedades).
Class Sample.MathUtils
{
Parameter PI = 3.14159265;
ClassMethod Square(x As %Numeric) As %Numeric
{
Return x * x
}
}
Caso de uso común: agrupar métodos de clase relacionados (funciones utilitarias, constantes) sin necesidad de instanciación de objetos.
Criterios de Decisión
| Criterio | %Persistent | %RegisteredObject | %SerialObject | Sin superclase |
|---|---|---|---|---|
| Almacenado en disco | Sí | No | Embebido en el padre | No |
| Tiene ID único | Sí | No | No | No |
| Proyección SQL | Tabla | Ninguna | Columnas en tabla del padre | Ninguna |
| Existencia independiente | Sí | Solo en memoria | Solo dentro del padre | N/A |
| Instanciable (%New) | Sí | Sí | Sí | No |
Referencias de Documentación
2. Crea y guarda un objeto persistente (%New(), %Save(), %Id())
Puntos Clave
- %New() crea una nueva instancia en memoria de una clase y devuelve una referencia de objeto (oref)
- %Save() persiste el objeto en disco y devuelve un valor %Status que debe verificarse
- %Id() devuelve el ID persistente único asignado al objeto después de guardarlo
- Siempre verificar el estado de retorno de %Save() para el manejo de errores
- %OpenId(id) recupera un objeto previamente guardado del disco por su ID
Notas Detalladas
Descripción general
El ciclo de vida de un objeto persistente involucra la creación en memoria, el guardado en disco y la recuperación posterior. Comprender este ciclo de vida y los métodos involucrados es fundamental para trabajar con InterSystems IRIS.
Creación de un Objeto con %New()
El método %New() se hereda de %RegisteredObject y asigna memoria para una nueva instancia de objeto. Devuelve una referencia de objeto (oref).
Set person = ##class(Sample.Person).%New()
Set person.Name = "John Smith"
Set person.DOB = $HOROLOG
Antes de llamar a %Save(), el objeto existe solo en memoria. Si la variable sale del ámbito sin guardar, el objeto se pierde.
Guardado con %Save()
El método %Save() serializa el objeto en los globals de la base de datos. Devuelve un valor %Status que indica éxito o fallo.
Set sc = person.%Save()
If $$$ISERR(sc) {
// Handle error
Do $System.Status.DisplayError(sc)
}
Comportamientos clave de %Save():
- El primer guardado asigna un nuevo ID al objeto
- Los guardados subsiguientes sobre el mismo oref actualizan el registro existente
- Valida las propiedades requeridas antes de guardar
- Dispara cualquier trigger definido en la tabla SQL
- Propaga los guardados a los objetos seriales embebidos
Recuperación del ID con %Id()
Después de un guardado exitoso, %Id() devuelve el identificador único asignado.
Set person = ##class(Sample.Person).%New()
Set person.Name = "Jane Doe"
Set sc = person.%Save()
If $$$ISOK(sc) {
Write "Saved with ID: ", person.%Id(), !
}
Apertura de un Objeto Existente con %OpenId()
Para recuperar un objeto guardado por su ID:
Set person = ##class(Sample.Person).%OpenId(42)
If person '= "" {
Write person.Name, !
}
El parámetro de concurrencia controla el comportamiento de bloqueo: 0 (sin bloqueo), 1 (lectura atómica, por defecto), 2 (bloqueo compartido), 3 (bloqueo compartido/retenido), 4 (bloqueo exclusivo).
Ejemplo Completo del Ciclo de Vida
// Create
Set person = ##class(Sample.Person).%New()
Set person.Name = "Alice Johnson"
// Save
Set sc = person.%Save()
If $$$ISERR(sc) { Do $System.Status.DisplayError(sc) Quit }
// Capture ID
Set id = person.%Id()
Write "Created person with ID: ", id, !
// Later: open and modify
Set person2 = ##class(Sample.Person).%OpenId(id)
Set person2.Name = "Alice Smith"
Set sc = person2.%Save()
Referencias de Documentación
3. Elimina objetos (%DeleteId(), %DeleteExtent())
Puntos Clave
- %DeleteId(id) elimina un solo objeto por su ID y devuelve un %Status
- %DeleteExtent() elimina todas las instancias de una clase (todas las filas de la tabla SQL)
- %KillExtent() es una alternativa más rápida pero menos segura que elimina directamente los globals de almacenamiento
- La eliminación respeta las restricciones de integridad referencial (claves foráneas)
- Siempre verificar el estado de retorno para errores (por ejemplo, objeto no encontrado, violación de integridad referencial)
Notas Detalladas
Descripción general
InterSystems IRIS proporciona múltiples enfoques para eliminar objetos persistentes. La elección depende de si necesita eliminar una sola instancia, un conjunto filtrado o la extensión completa (todas las instancias) de una clase.
Eliminación de un Solo Objeto con %DeleteId()
El método de clase %DeleteId() elimina un objeto por su ID. Devuelve un valor %Status.
Set sc = ##class(Sample.Person).%DeleteId(42)
If $$$ISERR(sc) {
Do $System.Status.DisplayError(sc)
}
Comportamientos clave:
- Elimina los datos del objeto de los globals
- Elimina las entradas de índice asociadas
- Dispara el callback
%OnDelete()si está definido en la clase - Respeta las restricciones de clave foránea (la eliminación puede fallar si está referenciado por otros objetos)
- Dispara los triggers SQL DELETE si están definidos
Eliminación de Todos los Objetos con %DeleteExtent()
El método de clase %DeleteExtent() elimina todas las instancias de una clase.
Set sc = ##class(Sample.Person).%DeleteExtent()
Este método itera por todos los objetos y los elimina uno a uno, lo que significa:
- Cada eliminación dispara triggers y callbacks
- Las restricciones de clave foránea se verifican para cada objeto
- Puede ser lento para tablas grandes
- Devuelve el conteo de objetos eliminados mediante parámetros de salida
Set sc = ##class(Sample.Person).%DeleteExtent(.deletecount, .instancecount)
Write "Deleted: ", deletecount, " of ", instancecount, !
Uso de %KillExtent() para Eliminación Rápida
Para situaciones donde no se necesitan triggers, callbacks ni verificaciones de integridad referencial, %KillExtent() elimina directamente los globals subyacentes.
Do ##class(Sample.Person).%KillExtent()
Precaución: %KillExtent() omite toda la lógica a nivel de objeto. Úselo solo cuando esté seguro de que no se necesitan efectos secundarios, típicamente durante desarrollo u operaciones de reinicio de datos.
Manejo de Integridad Referencial en la Eliminación
Si una clase tiene referencias de clave foránea desde otras clases, el comportamiento de eliminación depende de la acción OnDelete definida en la clave foránea:
- NoAction (por defecto): La eliminación falla si existen objetos referenciados
- Cascade: Los objetos referenciados también se eliminan
- SetNull: Los campos de clave foránea en los objetos que referencian se establecen en null
- SetDefault: Los campos de clave foránea en los objetos que referencian se establecen en su valor por defecto
El Callback %OnDelete()
Las clases pueden implementar el método callback %OnDelete() para realizar lógica personalizada antes de la eliminación:
ClassMethod %OnDelete(oid As %ObjectIdentity) As %Status
{
// Custom cleanup logic
Write "Deleting object: ", oid, !
Return $$$OK
}
Referencias de Documentación
4. Interpreta definiciones de almacenamiento (DataLocation, IdLocation, IndexLocation, USEEXTENTSET)
Puntos Clave
- La definición de almacenamiento mapea las propiedades de la clase a las estructuras de globals subyacentes
- DataLocation especifica el global donde se almacenan los datos del objeto (por defecto: `^Package.ClassD`)
- IdLocation especifica el global que contiene el contador de ID (por defecto: `^Package.ClassD`)
- IndexLocation especifica el global para los datos de índice (por defecto: `^Package.ClassI`)
- El parámetro USEEXTENTSET cambia el almacenamiento para usar nombres de globals cortos con hash en lugar de globals basados en el nombre de la clase
- Las definiciones de almacenamiento se generan automáticamente pero pueden personalizarse para requisitos especiales
Notas Detalladas
Descripción general
Cada clase persistente tiene una definición de almacenamiento que indica al sistema cómo mapear las propiedades del objeto a globals. Comprender las definiciones de almacenamiento es esencial para la resolución de problemas, el ajuste de rendimiento y la interpretación del código existente.
Disposición de Almacenamiento por Defecto
Por defecto, InterSystems IRIS usa %Storage.Persistent y crea globals con nombres basados en la clase:
Class Sample.Person Extends %Persistent
{
Property Name As %String;
Property Age As %Integer;
}
Globals de almacenamiento por defecto:
- Data:
^Sample.PersonD- almacena los datos del objeto - Index:
^Sample.PersonI- almacena las entradas de índice - Contador de ID:
^Sample.PersonD(0)o un nodo contador separado - rastrea el próximo ID disponible
La estructura del global de datos típicamente se ve así:
^Sample.PersonD = <next ID counter>
^Sample.PersonD(1) = $LB("", "John Smith", 35)
^Sample.PersonD(2) = $LB("", "Jane Doe", 28)
Cada nodo almacena una estructura $LISTBUILD donde los valores de las propiedades se almacenan en un orden definido.
Definición de Almacenamiento en el Código de la Clase
La definición de almacenamiento aparece al final de la clase y es gestionada por el compilador:
Storage Default
{
<Data name="PersonDefaultData">
<Value name="1"><Value>%%CLASSNAME</Value></Value>
<Value name="2"><Value>Name</Value></Value>
<Value name="3"><Value>Age</Value></Value>
</Data>
<DataLocation>^Sample.PersonD</DataLocation>
<DefaultData>PersonDefaultData</DefaultData>
<IdLocation>^Sample.PersonD</IdLocation>
<IndexLocation>^Sample.PersonI</IndexLocation>
<StreamLocation>^Sample.PersonS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
Parámetro USEEXTENTSET
Cuando USEEXTENTSET = 1, el sistema genera nombres de globals cortos con hash en lugar de usar el nombre de la clase:
Class Sample.Person Extends %Persistent [ SqlTableName = Person ]
{
Parameter USEEXTENTSET = 1;
}
Con USEEXTENTSET habilitado:
- Los globals reciben nombres como
^Eprogramming.Hjkl.1(nombres cortos con hash) - Cada grupo de propiedades e índice obtiene su propio global
- Evita el límite de 31 caracteres en nombres de globals para paquetes profundamente anidados
- Recomendado para nuevo desarrollo, especialmente con nombres de paquetes largos
Por Qué Importan las Definiciones de Almacenamiento
- Resolución de problemas: Inspección directa de globals para verificar la integridad de los datos
- Migración: Comprender el almacenamiento es crítico al mover datos entre sistemas
- Rendimiento: Las disposiciones de almacenamiento personalizadas pueden optimizar los patrones de acceso
- Herencia: Las subclases pueden compartir o extender la definición de almacenamiento del padre
Examen del Almacenamiento en Tiempo de Ejecución
Puede inspeccionar programáticamente las definiciones de almacenamiento usando el paquete %Dictionary:
// Open the storage definition for a class
Set storDef = ##class(%Dictionary.StorageDefinition).%OpenId("Sample.Person||Default")
If storDef '= "" {
Write "DataLocation: ", storDef.DataLocation, !
Write "IndexLocation: ", storDef.IndexLocation, !
Write "IdLocation: ", storDef.IdLocation, !
}
// Directly inspect the global
ZWrite ^Sample.PersonD
Referencias de Documentación
5. Implementa herencia múltiple (el orden importa, resolución de métodos)
Puntos Clave
- InterSystems IRIS soporta herencia múltiple (una clase puede extender múltiples superclases)
- El orden de izquierda a derecha determina la prioridad cuando las superclases definen el mismo miembro
- La superclase más a la izquierda en la lista Extends tiene la prioridad más alta
- La herencia de almacenamiento proviene de la superclase persistente primaria (primera)
- Usar la herencia múltiple con cuidado para evitar ambigüedad y complejidad
Notas Detalladas
Descripción general
La herencia múltiple permite que una clase combine comportamientos de varias clases padre. En InterSystems IRIS, una clase puede extender múltiples superclases separadas por comas. El orden en que se listan las superclases es significativo porque determina qué versión de un miembro se hereda cuando ocurren conflictos.
Sintaxis
Class MyApp.Manager Extends (Sample.Person, Sample.Employee, %Populate)
{
// Inherits from all three classes
// Sample.Person has highest priority for conflicts
}
Orden de Resolución de Métodos (MRO)
Cuando dos o más superclases definen un método o propiedad con el mismo nombre, el compilador de clases resuelve el conflicto usando prioridad de izquierda a derecha:
1. Los propios miembros de la clase tienen la prioridad más alta 2. Luego la primera superclase (la más a la izquierda) 3. Luego la segunda superclase 4. Y así sucesivamente
Ejemplo:
Class Base.A Extends %RegisteredObject
{
Method Greet() { Write "Hello from A", ! }
}
Class Base.B Extends %RegisteredObject
{
Method Greet() { Write "Hello from B", ! }
}
Class MyApp.Combined Extends (Base.A, Base.B)
{
// Greet() is inherited from Base.A (leftmost)
}
Llamar a Greet() en una instancia de MyApp.Combined ejecuta la implementación de Base.A.
Almacenamiento y Superclase Primaria
Para clases persistentes, la superclase primaria (la primera clase persistente en la lista Extends) determina la estrategia de almacenamiento. Todas las demás superclases persistentes contribuyen sus propiedades pero no contribuyen una definición de almacenamiento independiente.
Class MyApp.ExtendedPerson Extends (Sample.Person, Sample.Auditable)
{
// Storage comes from Sample.Person (primary)
// Properties from Sample.Auditable are added to Sample.Person's storage
}
Patrones Comunes para Herencia Múltiple
- Patrón mixin: Combinar una clase persistente primaria con clases registradas utilitarias que agregan comportamiento (logging, auditoría, validación)
- Mixin %Populate: Agregar
%Populatepara habilitar la generación de datos de prueba - Patrón adaptador: Combinar almacenamiento persistente con adaptadores de interfaz
Errores a Evitar
- Cambiar el orden de las superclases puede cambiar qué métodos se heredan
- Agregar una nueva superclase en medio de la lista puede romper el comportamiento existente
- La herencia en diamante (donde dos superclases comparten un ancestro común) se maneja automáticamente pero puede crear problemas sutiles
- Las colisiones de nombres de propiedades entre superclases causan errores de compilación si los tipos difieren
Referencias de Documentación
6. Documenta clases (palabra clave Description, comentarios ///)
Puntos Clave
- Los comentarios /// colocados antes de una clase, propiedad o método se convierten en la Description en la referencia de clases
- La palabra clave Description también se puede usar en las definiciones de clase/miembros
- La documentación es accesible a través de la Referencia de Clases en el Portal de Gestión y Studio
- Soporta formato HTML dentro de las descripciones
- Una buena documentación es esencial para la mantenibilidad y la colaboración en equipo
Notas Detalladas
Descripción general
InterSystems IRIS proporciona un sistema de documentación integrado que extrae comentarios con formato especial del código fuente de las clases y los presenta en el navegador de documentación de Referencia de Clases. La documentación adecuada ayuda a otros desarrolladores a entender su código y es una mejor práctica profesional.
Uso de Comentarios ///
Las líneas que comienzan con /// inmediatamente antes de una clase, propiedad, método u otro miembro de clase se tratan como la descripción del miembro. Estos comentarios soportan marcado HTML.
/// This class represents a patient in the healthcare system.
/// <p>It stores demographic information and provides methods
/// for managing patient records.</p>
Class Sample.Patient Extends %Persistent
{
/// The patient's full legal name as it appears on their ID.
Property Name As %String(MAXLEN = 200);
/// The patient's date of birth in $HOROLOG format.
Property DOB As %Date;
/// Calculate the patient's age based on their date of birth.
/// <p>Returns the age in whole years.</p>
/// <example>
/// Set age = patient.GetAge()
/// Write "Patient is ", age, " years old"
/// </example>
Method GetAge() As %Integer
{
// Method implementation
Return $HOROLOG - ..DOB \ 365.25
}
}
Uso de la Palabra Clave Description
Alternativamente, la palabra clave Description se puede usar dentro de las definiciones de miembros:
Property Name As %String [ Description = "The patient's full legal name" ];
Sin embargo, los comentarios /// son generalmente preferidos porque son más fáciles de leer y mantener en el código fuente.
Etiquetas HTML Soportadas en la Documentación
<p>- Párrafos<b>,<i>- Negrita y cursiva<ul>,<ol>,<li>- Listas<table>,<tr>,<td>- Tablas<code>- Código en línea<example>- Bloques de ejemplo de código (etiqueta específica de InterSystems)<class>ClassName</class>- Enlaces a documentación de otras clases<method>MethodName</method>- Enlaces a métodos<property>PropertyName</property>- Enlaces a propiedades
Visualización de la Documentación
La documentación de clases se puede ver a través de:
- Portal de Gestión: System Explorer > Classes > seleccionar clase > pestaña Documentation
- Extensión VS Code: Pasar el cursor sobre nombres de clase/miembro para ver las descripciones
- Referencia de Clases: Documentación HTML generada navegable por paquete
Mejores Prácticas
- Documentar cada clase y método público con comentarios
/// - Incluir descripciones de parámetros y explicaciones de valores de retorno para los métodos
- Usar etiquetas
<example>para mostrar patrones de uso - Documentar comportamientos no obvios, casos límite y suposiciones
- Mantener las descripciones concisas pero informativas
Referencias de Documentación
Resumen de Preparación para el Examen
Conceptos Críticos a Dominar:
- Cuándo usar %Persistent vs %RegisteredObject vs %SerialObject vs clases utilitarias (sin superclase)
- El ciclo de vida completo del objeto: %New() -> asignación de propiedades -> %Save() -> %Id()
- Siempre verificar los valores de retorno %Status de %Save() y %DeleteId()
- Diferencia entre %DeleteExtent() (seguro, dispara callbacks) y %KillExtent() (rápido, sin callbacks)
- Cómo las definiciones de almacenamiento mapean propiedades a globals (DataLocation, IdLocation, IndexLocation)
- Parámetro USEEXTENTSET y cuándo usarlo
- Prioridad de izquierda a derecha en herencia múltiple
- Cómo los comentarios /// generan documentación de referencia de clases
Escenarios Comunes en el Examen:
- Dado un requisito, seleccionar el tipo correcto de clase de objeto (persistente, registrado, serial)
- Identificar la secuencia correcta para crear y guardar un objeto
- Interpretar una definición de almacenamiento para determinar qué global almacena los datos
- Predecir qué método se hereda cuando múltiples superclases definen el mismo método
- Determinar qué sucede cuando se llama a %DeleteId() en un objeto con referencias de clave foránea
Recomendaciones de Práctica:
- Crear clases persistentes y seriales, guardar objetos e inspeccionar los globals subyacentes con ZWrite
- Experimentar con %DeleteId() y %DeleteExtent() y observar los cambios en los globals
- Construir un escenario de herencia múltiple y verificar qué método prevalece
- Agregar documentación /// a una clase y verla en la Referencia de Clases
- Examinar las definiciones de almacenamiento de clases del sistema existentes para entender la estructura