Persistencia de objetos y Java

Durabilidad de objetos, o persistencia , es el término que a menudo se utiliza junto con el problema del almacenamiento de objetos en bases de datos. Se espera que la persistencia opere con integridad transaccional y, como tal, está sujeta a condiciones estrictas. (Consulte la sección Recursos de este artículo para obtener más información sobre el procesamiento de transacciones). Por el contrario, los servicios lingüísticos que se ofrecen a través de bibliotecas y paquetes de idiomas estándar suelen estar libres de restricciones transaccionales.

Como veremos en este artículo, la evidencia sugiere que la persistencia simple de Java probablemente provenga del lenguaje mismo, mientras que los proveedores de bases de datos ofrecerán una funcionalidad de base de datos sofisticada.

Ningún objeto es una isla

En el mundo real, rara vez se encuentra un objeto que carece de relaciones con otros objetos. Los objetos son componentes de modelos de objetos . La cuestión de la durabilidad del objeto trasciende la cuestión de la durabilidad y distribución del modelo de objeto una vez que hacemos la observación de que los objetos están interconectados en virtud de sus relaciones entre sí.

El enfoque relacional del almacenamiento de datos tiende a agregar datos por tipo. Las filas de una tabla representan el agregado físico de objetos del mismo tipo en el disco. Luego, las relaciones entre los objetos se representan mediante claves que se comparten en muchas tablas. Aunque a través de la organización de la base de datos, las bases de datos relacionales a veces permiten que las tablas que probablemente se usen juntas se ubiquen (o agrupen ) en la misma partición lógica, como un segmento de base de datos, no tienen ningún mecanismo para almacenar relaciones de objetos en la base de datos. Por lo tanto, para construir un modelo de objeto, estas relaciones se construyen a partir de las claves existentes en tiempo de ejecución en un proceso denominado combinaciones de tablas . Esta es la misma propiedad conocida de las bases de datos relacionales llamadaindependencia de datos . Casi todas las variantes de bases de datos de objetos ofrecen algún mecanismo para mejorar el rendimiento de un sistema que involucra relaciones complejas de objetos sobre las bases de datos relacionales tradicionales.

¿Consultar o navegar?

Al almacenar objetos en el disco, nos enfrentamos a la opción de ubicar objetos relacionados para acomodar mejor el acceso de navegación, o almacenar objetos en colecciones similares a tablas que agregan objetos por tipo para facilitar el acceso basado en predicados (consultas), o ambos . La ubicación conjunta de objetos en el almacenamiento persistente es un área en la que las bases de datos relacionales y orientadas a objetos difieren ampliamente. La elección del idioma de consulta es otra área a considerar. El lenguaje de consulta estructurado (SQL) y sus extensiones han proporcionado a los sistemas relacionales un mecanismo de acceso basado en predicados. Object Query Language (OQL) es una variante de objeto de SQL, estandarizada por ODMG, pero el soporte para este lenguaje es actualmente escaso. Los métodos polimórficos ofrecen una elegancia sin precedentes al construir una consulta semántica para una colección de objetos. Por ejemplo,imagina un comportamiento polimórfico paraacccountllamado isInGoodStanding. Puede devolver el valor booleano verdadero para todas las cuentas en regla y falso en caso contrario. Ahora imagine la elegancia de consultar la colección de cuentas, donde inGoodStandingse implementa de manera diferente según las reglas comerciales, para todas las cuentas en buen estado. Puede verse algo como:

setOfGoodCustomers = setOfAccounts.query(account.inGoodStanding());

Si bien varias de las bases de datos de objetos existentes son capaces de procesar un estilo de consulta de este tipo en C ++ y Smalltalk, es difícil para ellas hacerlo para colecciones más grandes (por ejemplo, 500+ gigabytes) y expresiones de consulta más complejas. Varias de las empresas de bases de datos relacionales, como Oracle e Informix, pronto ofrecerán otra sintaxis basada en SQL para lograr el mismo resultado.

Persistencia y tipo

Un aficionado al lenguaje orientado a objetos diría que la persistencia y el tipo son propiedades ortogonales de un objeto; es decir, los objetos persistentes y transitorios del mismo tipo pueden ser idénticos porque una propiedad no debería influir en la otra. La visión alternativa sostiene que la persistencia es un comportamiento apoyado solo por objetos persistentes y ciertos comportamientos pueden aplicarse solo a objetos persistentes. El último enfoque requiere métodos que indiquen a los objetos persistentes que se almacenen y recuperen del almacenamiento persistente, mientras que el primero ofrece a la aplicación una vista perfecta de todo el modelo de objetos, a menudo ampliando el sistema de memoria virtual.

Canonicalización e independencia lingüística

Los objetos del mismo tipo en un idioma deben almacenarse en almacenamiento persistente con el mismo diseño, independientemente del orden en que aparezcan sus interfaces. Los procesos de transformación de un diseño de objeto a este formato común se conocen colectivamente como canonicalización de la representación de objetos. En lenguajes compilados con escritura estática (no Java), los objetos escritos en el mismo lenguaje, pero compilados en diferentes sistemas, deben representarse de forma idéntica en el almacenamiento persistente.

Una extensión de la canonicalización aborda la representación de objetos independiente del lenguaje. Si los objetos se pueden representar de forma independiente del lenguaje, será posible que diferentes representaciones del mismo objeto compartan el mismo almacenamiento persistente.

Un mecanismo para lograr esta tarea es introducir un nivel adicional de direccionamiento indirecto a través de un lenguaje de definición de interfaz (IDL). Las interfaces de la base de datos de objetos se pueden hacer a través del IDL y las estructuras de datos correspondientes. La desventaja de los enlaces de estilo IDL es doble: primero, el nivel adicional de direccionamiento indirecto siempre requiere un nivel adicional de traducción, lo que afecta el rendimiento general del sistema; en segundo lugar, limita el uso de servicios de base de datos que son exclusivos de proveedores particulares y que pueden ser valiosos para los desarrolladores de aplicaciones.

Un mecanismo similar es admitir servicios de objetos a través de una extensión de SQL. Los proveedores de bases de datos relacionales y los proveedores de objetos / relacionales más pequeños son partidarios de este enfoque; sin embargo, aún está por verse el éxito que tendrán estas empresas en la configuración del marco para el almacenamiento de objetos.

Pero la pregunta sigue siendo: ¿La persistencia del objeto es parte del comportamiento del objeto o es un servicio externo que se ofrece a los objetos a través de interfaces separadas? ¿Qué hay de las colecciones de objetos y métodos para consultarlos? Los enfoques relacional, relacional extendido y objeto / relacional tienden a abogar por una separación entre el lenguaje, mientras que las bases de datos de objetos, y el lenguaje Java en sí, ven la persistencia como algo intrínseco al lenguaje.

Persistencia nativa de Java mediante serialización

La serialización de objetos es el mecanismo específico del lenguaje Java para el almacenamiento y recuperación de primitivas y objetos Java en secuencias. Vale la pena señalar que, aunque las bibliotecas comerciales de terceros para serializar objetos C ++ existen desde hace algún tiempo, C ++ nunca ha ofrecido un mecanismo nativo para la serialización de objetos. A continuación, se explica cómo utilizar la serialización de Java:

// Escribiendo "foo" en una secuencia (por ejemplo, un archivo)

// Paso 1. Crea un flujo de salida

// es decir, crea un depósito para recibir los bytes

FileOutputStream out = new FileOutputStream ("fooFile");

// Paso 2. Crear ObjectOutputStream

// es decir, crea una manguera y mete su cabeza en el cubo

ObjectOutputStream os = nuevo ObjectOutputStream (fuera)

// Paso 3. Escribe una cadena y un objeto en la secuencia

// es decir, deja que la corriente fluya hacia el cubo

os.writeObject ("foo");

os.writeObject (nuevo Foo ());

// Paso 4. Vaciar los datos a su destino

os.flush ();

El Writeobjectmétodo serializa foo y su cierre transitivo, es decir, todos los objetos a los que se puede hacer referencia desde foo dentro del gráfico. Dentro de la secuencia solo existe una copia del objeto serializado. Otras referencias a los objetos se almacenan como identificadores de objetos para ahorrar espacio y evitar referencias circulares. El objeto serializado comienza con la clase seguida por los campos de cada clase en la jerarquía de herencia.

// Leer un objeto de una secuencia

// Paso 1. Crea un flujo de entrada

FileInputStream in = new FileInputStream ("fooFile");

// Paso 2. Crea un flujo de entrada de objeto

ObjectInputStream ins = new ObjectInputStream (en);

// Paso 3. Debes saber lo que estás leyendo

String fooString = (String) ins.readObject ();

Foo foo = (Foo) s.readObject ();

Seguridad y serialización de objetos

De forma predeterminada, la serialización escribe y lee campos no estáticos y no transitorios de la secuencia. Esta característica se puede utilizar como un mecanismo de seguridad al declarar campos que no pueden serializarse como transitorios privados. Si una clase no puede ser serializado en absoluto, writeObjecty readObjectmétodos debe aplicarse a tirar NoAccessException.

Persistencia con integridad transaccional: Presentación de JDBC

Siguiendo el modelo de la CLI (interfaz de nivel de cliente) SQL de X / Open y las abstracciones ODBC de Microsoft, la conectividad de base de datos Java (JDBC) tiene como objetivo proporcionar un mecanismo de conectividad de base de datos que es independiente del sistema de gestión de base de datos subyacente (DBMS). necesita admitir al menos la API de nivel de entrada ANSI SQL-2, que brinda a los proveedores de herramientas y aplicaciones de terceros suficiente flexibilidad para el acceso a la base de datos.

JDBC está diseñado para ser coherente con el resto del sistema Java. Se recomienda a los proveedores que escriban una API con un tipo más estricto que ODBC, lo que permite una mayor verificación de tipos estática en el momento de la compilación.

A continuación, se muestra una descripción de las interfaces JDBC más importantes:

  • java.sql.Driver.Manager maneja la carga de controladores y brinda soporte para nuevas conexiones de bases de datos.

  • java.sql.Connection representa una conexión a una base de datos en particular.

  • java.sql.Statement actúa como un contenedor para ejecutar una instrucción SQL en una conexión determinada.

  • java.sql.ResultSet controla el acceso al conjunto de resultados.

Puede implementar un controlador JDBC de varias formas. La más simple sería construir el controlador como un puente a ODBC. Este enfoque es más adecuado para herramientas y aplicaciones que no requieren un alto rendimiento. Un diseño más extensible introduciría un nivel adicional de direccionamiento indirecto al servidor DBMS al proporcionar un controlador de red JDBC que accede al servidor DBMS a través de un protocolo publicado. Sin embargo, el controlador más eficiente accedería directamente a la API propietaria de DBMS.

Bases de datos de objetos y persistencia de Java

Varios proyectos en curso en la industria ofrecen persistencia de Java a nivel de objeto. Sin embargo, al momento de escribir este artículo, PSE (Persistent Storage Engine) y PSE Pro de Object Design son los únicos paquetes de base de datos orientados a objetos completamente basados ​​en Java disponibles (al menos, que yo sepa). Consulte la sección Recursos para obtener más información sobre PSE y PSE Pro.

El desarrollo de Java ha llevado a una desviación del paradigma de desarrollo tradicional para los proveedores de software, sobre todo en la línea de tiempo del proceso de desarrollo. Por ejemplo, PSE y PSE Pro se desarrollan en un entorno heterogéneo. Y debido a que no hay un paso de vinculación en el proceso de desarrollo, los desarrolladores han podido crear varios componentes funcionales independientes entre sí, lo que da como resultado un código orientado a objetos mejor y más confiable.

PSE Pro tiene la capacidad de recuperar una base de datos dañada de una transacción abortada causada por una falla del sistema. Las clases que son responsables de esta funcionalidad adicional no están presentes en la versión de PSE. No existen otras diferencias entre los dos productos. Estos productos son lo que llamamos "dribbleware": versiones de software que mejoran su funcionalidad al conectar nuevos componentes. En un futuro no muy lejano, el concepto de comprar software monolítico grande se convertiría en cosa del pasado. El nuevo entorno empresarial en el ciberespacio, junto con la informática Java, permite a los usuarios comprar solo aquellas partes del modelo de objetos (gráfico de objetos) que necesitan, lo que da como resultado productos finales más compactos.

PSE funciona mediante el posprocesamiento y la anotación de archivos de clase después de que hayan sido creados por el desarrollador. Desde el punto de vista de PSE, las clases en un gráfico de objetos son capaces de persistir o tienen capacidad de persistencia. Las clases con capacidad persistente pueden persistir por sí mismas, mientras que las clases con capacidad persistente pueden operar en objetos persistentes. Esta distinción es necesaria porque la persistencia puede no ser un comportamiento deseado para ciertas clases. El postprocesador de archivos de clases realiza las siguientes modificaciones a las clases: