0

Persistencia en CRIAX-SDK

1Dic
en CRIAX-SDK, Documentación, Firefox OS, Javascript, SDK

Introducción

En este artículo estaremos viendo de manera simple el trabajo con la persistencia de datos en CRIAX-SDK. El mismo estará dividido en 2 partes, la primera cubrirá un caso sencillo de cursos y estudiantes, realizado en una aplicación para móviles. La segunda parte será una aplicación de escritorio muy sencilla para poder gestionar permisos de usuarios. Ambas aplicaciones no son reales y el código y las prácticas desarrolladas en las mismas son con fines educativos.

La aplicación móvil

La aplicación móvil es muy sencilla un listado de estudiante donde si seleccionamos uno podremos ver los datos del curso donde se encuentra y el listado de sus compañeros de curso.

Crear la base de datos

En este caso no utilizaremos el vendor sqlitemanager para crear la base de datos, sino el programa SQLiteStudio para poder crear las llaves foráneas de manera visual (una simple comodidad).

Sqlitestudio

Sqlitestudio

Creamos una base de datos llamada escuela, que constará de 2 tablas, curso (con el nombre del curso y el id) y estudiante (con el nombre del estudiante, id y el id del curso al que pertenece) una vez terminada el código de la base de datos queda:

-- BD: escuela
-- Table: curso
CREATE TABLE curso (
    id_curso INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
    nombre   VARCHAR NOT NULL
);
-- Datos: curso
INSERT INTO [curso] ([id_curso], [nombre]) VALUES (1, 'curso1');
INSERT INTO [curso] ([id_curso], [nombre]) VALUES (2, 'curso2');
INSERT INTO [curso] ([id_curso], [nombre]) VALUES (3, 'curso3');
INSERT INTO [curso] ([id_curso], [nombre]) VALUES (4, 'curso4');
-- Table: estudiante
CREATE TABLE estudiante (
    id_estudiante INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE,
    nombre        VARCHAR NOT NULL,
    curso_id      INTEGER NOT NULL REFERENCES curso ( id_curso )
);
-- Datos: estudiante
INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (1, 'estudiante1', 1);
INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (2, 'estudiante2', 1);
INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (3, 'estudiante3', 1);
INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (4, 'estudiante4', 2);
INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (5, 'estudiante5', 3);
INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (6, 'estudiante6', 3);

Como se puede ver ya las tablas contienen información, esto para poder agilizar el proceso.

Crear la aplicación

Lo primero que haremos será crear el skeleton de la aplicación llamada escuela. Para ello nos paramos en el directorio raíz de la plataforma y tecleamos por consola:

$ cd Criax
$ create-application (.bat o .sh) -n escuela -o .../CriaxProyect/ -t firefoxos

En Window podemos ejecutar el archivo toolchain.bat que se encuentra en el directorio raíz de la aplicación y simplemente ejecutar los comandos allí.

Una vez creada la aplicación el siguiente paso es generarla para pre-compilar las clases y dejarlas listas para futuro desarrollo de otras aplicaciones, para ello nos vamos hasta el directorio raíz de la aplicación creada y tecleamos en la consola:

$ cd escuela
$ toolchain/generate(.bat o .sh)

Quizás demore un poquito pues esto pre-compila todas las clases de la plataforma. Una vez que termine la generación se nos mostrará una ventana indicándonos que la aplicación esta lista para comenzar a ser desarrollada.

Mapear la bd

La primera acción será mapear la base de datos creada, esto nos facilitará el trabajo, para ello abrimos el vendor CormxDesktop (nuevo en la plataforma), de esta manera obtendremos el esquema y las clases necesarias para la gestión de la información (a las mismas hay que hacerle pequeños ajustes). Primero crearemos un workspace para los proyectos de cormx.

Crear workspace con CormxDesktop

Crear workspace con CormxDesktop

Y luego crearemos el proyecto escuela:

Crear proyecto con CormxDesktop

Crear proyecto con CormxDesktop

Para escanear la base de datos vamos a Base de Datos -> Escanear, esta acción sacará la meta información de la base de datos (tablas, columnas y características de las mismas). Si se desea inmediatamente después de terminado el escaneo de la base de datos, podemos generar el esquema que brindará información a al aplicación sobre la base de datos. Para generar el esquema vamos a Esquema -> Generar.

Si vamos al directorio donde se creó el proyecto podemos abrir el archivo schema.json que se encuentra dentro del directorio escuela/schema/ y echarle un vistazo al código generado sobre el mapeo de la bd. La mayor parte del tiempo no se utiliza la información tal y como se toma de la base de datos, entonces podemos comenzar por personalizar el nombre de las clases que intervienen en la persistencia, para ello vamos a Entidades -> Gestionar. Seleccionamos la tabla y podremos modificar los nombres de la clase Finder, Fixture, IdentityMap, Mapper y Entidad. Para el presente caso se mantienen tal y como se generaron. Como se puede apreciar en la vida real se puede modificar el namespace completo de cada clase.

Editar namespace de las entidades

Editar namespace de las entidades

Para gestionar la información de loa atributos y métodos de las entidades vamos a Atributos -> Gestionar allí seleccionamos la tabla, el atributo y damos click en la lupa para buscar la información del mismo. Una vez modificado damos click en el botón Actualizar.

Editar atributos, get y set de una entidad

Editar atributos, get y set de una entidad

Para el presente caso, las columnas id se llamarán id y los métodos accesores y mutadores getId y setId respectivamente. En la columna curso_id de la tabla estudiante, el atributo se llamará curso y los métodos getCurso y setCurso, si nos fijamos aquí el tipo de dato es un objeto, este es un elemento importante a tener en cuenta para más adelante en las relaciones.

Para gestionar las relaciones (en este caso de uno a muchos entre curso y estudiante) vamos a Entidades -> Relaciones. La relación se establece siempre en la tabla de muchos, para el presente caso estudiante.

Editar relaciones entre entidades

Editar relaciones entre entidades

En esta aplicación no se crearán eventos, pero para gestionar los mismos en CormxDesktop vamos a Entidades -> Eventos. Los eventos son aquellos métodos que se ejecutarán automáticamente antes y después de insertar, actualizar o eliminar datos en una entidad.

Editar eventos de una entidad

Editar eventos de una entidad

Una vez terminada toda la gestión conforme a lo que se desea generamos el esquema en Esquema -> Generar y las entidades en Entidades -> Generar -> Generar todas las entidades, si queremos generar una por una las entidades Entidades -> Generar -> Generar entidad, donde seleccionamos la tabla de la entidad que deseamos generar.

Ahora solo nos queda exportar el proyecto, esta acción realiza una copia del esquema en el directorio correspondiente de la aplicación escuela/source/resource/persistence/, así como las clases en su namespace escuela/source/class/escuela/entity/, creando automáticamente el directorio entity. Para ello vamos a Proyecto -> Exportar.

La aplicación CormxDesktop tiene otras funcionalidades como: cambiar al idioma inglés, gestionar el workspace y proyecto en que se trabaja, entre otras.

Aunque no lo parezca la aplicación CormxDesktop está creada en su totalidad con HTML, CSS y JS en la plataforma CRIAX-SDK, es uno de los ejemplos de aplicación de escritorio.

Finalmente podremos hacer los arreglos pertinentes desde el mismo proyecto, pues hay que recordar que esta es la primera versión de CormxDesktop. Dichos arreglos son:

  • En los métodos setId de las entidades remplazamos:
this.__id = id;

Por:

if(this.__id == null){
    this.__id = id;
}
  • En las entidades, clases finder, fixture y mapper arreglamos el nombre del constructor constructor por construct.
  • En las clases identity-map podemos eliminar toda la sección de las propiedades y los métodos, quedando:
//clase identity-map de curso
qx.Class.define("escuela.entity.im.CursoIdentityMap",
{
    type : "singleton",
    extend : cormx.opm.im.IdentityMap
});
 
//clase identity-map de estudiante
qx.Class.define("escuela.entity.im.EstudianteIdentityMap",
{
    type : "singleton",
    extend : cormx.opm.im.IdentityMap
});
  • En las clases identity-map y mapper debemos encerrar el nombre del tipo de clase entre comillas type : "singleton",.
  • En el esquema generado escuela/source/resource/persistence/schema.json a los campos id ponerle una longitud de 10 "length" : "10".

Todos estos pequeños errores seran arreglados en la próxima actualización de CormxDesktop.

Las entidades

Una vez realizados todos estos arreglos las clases de las entidades quedan:



/**
 * Enitdad de la tabla curso
 *
 * @class Curso
 * @public
 * @extends qx.core.Object
 * @author ...
 * @namespace escuela.entity
 * @copyrigth ...
 * @license ...
 * @version ...
 *
 * @use(escuela.entity.mapper.CursoMapper)
 * @use(escuela.entity.im.CursoIdentityMap)
 * @use(escuela.entity.finder.CursoFinder)
 * para relaciones
 */
qx.Class.define("escuela.entity.Curso",
{
    extend : qx.core.Object,
 
    /**
     * @property
     */
    properties :
    {
        /**
         * atributo para la columna de id_curso
         *
         * @name id_curso
         * @private
         * @type {Integer}
         */
        __id : {},
 
        /**
         * atributo para la columna de nombre
         *
         * @name nombre
         * @private
         * @type {String}
         */
        __nombre : {}
    },
 
    /**
     * metodo de inicializacion de la clase
     *
     * @constructor
     * @public
     */
    construct : function()
    {
 
    },
 
    /**
     * @method
     */
    members :
    {
        /**
         * Metodo para devolver ...
         *
         * @method getId
         * @public
         * @return {Integer}
         */
        getId : function(){
            return this.__id;
        },
 
        /**
         * Metodo para establecer ...
         *
         * @method setId
         * @public
         * @return {this}
         */
        setId : function(id){
            if(this.__id == null){
                this.__id = id;
            }
            return this;
        },
 
        /**
         * Metodo para devolver ...
         *
         * @method getNombre
         * @public
         * @return {String}
         */
        getNombre : function(){
            return this.__nombre;
        },
 
        /**
         * Metodo para establecer ...
         *
         * @method setNombre
         * @public
         * @return {this}
         */
        setNombre : function(nombre){
            this.__nombre = nombre;
            return this;
        }
    }
 
});
 
/**
 * Enitdad de la tabla estudiante
 *
 * @class Estudiante
 * @public
 * @extends qx.core.Object
 * @author ...
 * @namespace escuela.entity
 * @copyrigth ...
 * @license ...
 * @version ...
 *
 * @use(escuela.entity.mapper.EstudianteMapper)
 * @use(escuela.entity.im.EstudianteIdentityMap)
 * @use(escuela.entity.finder.EstudianteFinder)
 * para relaciones
 * @use(escuela.entity.Curso)
 */
qx.Class.define("escuela.entity.Estudiante",
{
    extend : qx.core.Object,
 
    /**
     * @property
     */
    properties :
    {
        /**
         * atributo para la columna de id_estudiante
         *
         * @name id_estudiante
         * @private
         * @type {Integer}
         */
        __id : {},
 
        /**
         * atributo para la columna de nombre
         *
         * @name nombre
         * @private
         * @type {String}
         */
        __nombre : {},
 
        /**
         * atributo para la columna de curso_id
         *
         * @name curso_id
         * @private
         * @type {Object}
         */
        __curso : {}
    },
 
    /**
     * metodo de inicializacion de la clase
     *
     * @constructor
     * @public
     */
    construct : function()
    {
 
    },
 
    /**
     * @method
     */
    members :
    {
        /**
         * Metodo para devolver ...
         *
         * @method getId
         * @public
         * @return {Integer}
         */
        getId : function(){
            return this.__id;
        },
 
        /**
         * Metodo para establecer ...
         *
         * @method setId
         * @public
         * @return {this}
         */
        setId : function(id){
            if(this.__id == null){
                this.__id = id;
            }
            return this;
        },
 
        /**
         * Metodo para devolver ...
         *
         * @method getNombre
         * @public
         * @return {String}
         */
        getNombre : function(){
            return this.__nombre;
        },
 
        /**
         * Metodo para establecer ...
         *
         * @method setNombre
         * @public
         * @return {this}
         */
        setNombre : function(nombre){
            this.__nombre = nombre;
            return this;
        },
 
        /**
         * Metodo para devolver ...
         *
         * @method getCurso
         * @public
         * @return {Object}
         */
        getCurso : function(){
            return this.__curso;
        },
 
        /**
         * Metodo para establecer ...
         *
         * @method setCurso
         * @public
         * @return {this}
         */
        setCurso : function(curso){
            this.__curso = curso;
            return this;
        }
    }
 
});

Desarrollo de la aplicación

Ahora pasemos a lo importante, la funcionalidad de nuestra aplicación, para ello abrimos el vendor Criax-IDE …/Criax/vendors/Criax-IDE/spket (.exe o .bin) y creamos un nuevo proyecto, a diferencia de lo que se piensa la ruta del proyecto en el IDE no será el directorio raíz de la aplicación sino escuela/source/class/escuela/. Seguidamente lo que haremos será crear un modelo que se encargará de trabajar la lógica de negocio de la aplicación. Para ello en el IDE seleccionamos el directorio model y allí dentro creamos un archivo llamado Escuela.js. Con la ayuda de los snippets creamos una clase modelo.

Lo primero que haremos será mostrar un listado de los estudiantes y cuando seleccionemos el estudiante la información del curso en el que se encuentra y debajo todos los demás estudiantes de ese mismo curso. De esta manera lo primero que haremos sera un método que devuelva todos los estudiantes.

getEstudiantes : function(){
    var em = this.container('entityManager');
    return em.all(escuela.entity.Estudiante);
}

Ahora en el controlador de la página 1, preparamos el entorno para que se carguen los datos de la aplicación si es la primera vez o si ya estan cargados previamente, para ello en el método index ponemos:

var self = this;
var em = this.container("entityManager");
em.loadData(function(){
 
},function(){
 
});

Como las aplicaciones móviles deben crear toda la estructura de la base de datos desde el inicio, crearemos una clase en el namespace vendor cuyos métodos tendrán el código sql para crear la estructura de la base de datos.

qx.Class.define("escuela.vendors.SqlDb",
{
    type : "static",
 
    statics :
    {
        tablaCurso : function(){
            return "CREATE TABLE curso (" +
                    "id_curso INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, " +
                    "nombre   VARCHAR NOT NULL" +
                    ");";
        },
 
        datoCurso : function(){
            return "INSERT INTO [curso] ([id_curso], [nombre]) VALUES (1, 'curso1');" +
                   "INSERT INTO [curso] ([id_curso], [nombre]) VALUES (2, 'curso2');" +
                   "INSERT INTO [curso] ([id_curso], [nombre]) VALUES (3, 'curso3');" +
                   "INSERT INTO [curso] ([id_curso], [nombre]) VALUES (4, 'curso4');";
        },
 
        tablaEstudiante : function(){
            return "CREATE TABLE estudiante (" +
                    "id_estudiante INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE, " +
                    "nombre        VARCHAR NOT NULL, " +
                    "curso_id      INTEGER NOT NULL REFERENCES curso ( id_curso )" +
                    ");";
        },
 
        datoEstudiante : function(){
            return "INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (1,'estudiante1',1);"+
                   "INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (2,'estudiante2',1);"+
                   "INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (3,'estudiante3',1);"+
                   "INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (4,'estudiante4',2);"+
                   "INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (5,'estudiante5',3);"+
                   "INSERT INTO [estudiante] ([id_estudiante], [nombre], [curso_id]) VALUES (6,'estudiante6',3);";
        },
 
        crearBd : function(){
            var db = [
            escuela.vendors.SqlDb.tablaCurso(),
            escuela.vendors.SqlDb.datoCurso(),
            escuela.vendors.SqlDb.tablaEstudiante(),
            escuela.vendors.SqlDb.datoEstudiante()
            ];
            return db.join(" ");
        }
    }
});

Ahora dentro de la segunda función anónima de load en el controlador de la página 1, ponemos el código correspondiente a la creación de la base de datos:

var sql = escuela.vendors.SqlDb.createDb();
self.container('dbal').simple(sql);

Luego obtenemos el listado de todos los estudiantes, para ello utilizamos un método aparte que también se llamará en la primera función anónima del método load, esto con el objetivo de poder obtener el listado tanto si es la primera vez que carga la aplicación o no.

self.__listadoEstudiantes();

Dicho método quedaría:

__listadoEstudiantes : function(){
    var modelo = new escuela.model.Escuela();
    var estudiantes = modelo.getEstudiantes();
 
    var list = this.getView().listadoCursos;
    list.setArrayModel(estudiantes);
}

Dentro del mismo se le dice a un componente de lista de la vista cual será su contenido para ello en la vista Page1, debemos crear un componente de lista para mostrar los estudiantes, ello lo haremos en una propiedad llamada listadoCursos : {} y:

//en el metodo components
this.listadoCursos = new criax.mobile.list.List(null,criax.mobile.list.List.BLACK);
 
//en el metodo initialize
this.addContent(new criax.mobile.basic.Header('Listado de estudiantes:'));
this.listadoCursos.addListener("changeSelection",this._controller.goPage2,this._controller);
this.listadoCursos.setConfigureItem(this.__createItemList);
this.listadoCursos.setArrayModel([]);
this.addContent(this.listadoCursos);

El método que gestionará la información de la lista, en el título mostramos el nombre del estudiante y en el subtítulo el nombre del curso en el que se encuentra:

__createItemList : function(item, data, row){
    item.setName(data.getId().toString());
    item.setTitle(data.getNombre());
    item.subTitle(data.getCurso()().getNombre());
    item.setShowArrow(true);
}

Fijémonos particularmente en la línea que contiene:

item.subTitle(data.getCurso()().getNombre());

El método getCurso, devuelve una función anónima, la cual será la encargada de buscar el curso asociado a este estudiante. De esta manera la búsqueda del objeto asiciado mediante el id del mismo se hace automáticamente (tener cuidado con esto). El detalle radica en que cada vez que deseemos obtener el curso de ese estudiando se ejecutará la funcion anónima y buscará el objeto de la entidad del curso en al base de datos. Para que ello no ocurra lo mejor seria que una vez obtenido el objeto del curso del estudiante, no se procese más la función anónima, para ello vamos a la entidad Estudiante y en el método getCurso agregamos antes de devolver el atributo __curso:

if((this.__curso instanceof escuela.entity.Curso) == false){
    this.__curso = this.__curso();
}

De esta manera verificamos antes, que el valor del atributo no sea un objeto de la entidad escuela.entity.Curso, si no es así guardamos el resultado de la función anónima en el atributo y devolvemos el mismo. Así cada vez que pidamos el curso del estudiante, si ya se encuentra simplemente lo devolverá y sino lo buscará y almacenará para futuros usos. Entonces en la vista podemos obtener el curso del estudiante de manera normal:

item.subTitle(data.getCurso().getNombre());

A pesar de todo ello recordar que los datos se cachean directamente en el identity map de la entidad, a partir del valor del id.

Listado de estudiantes

Listado de estudiantes

La información del curso y el resto de los estudiantes

En el momento que seleccionemos un estudiante de la lista, iremos a otra página y veremos la información del curso del mismo, así como un listado del resto de los estudiantes del curso. Esto será en la vista Page2. Para ello anteriormente habíamos escrito la línea:

this.listadoCursos.addListener("changeSelection",this._controller.goPage2,this._controller);

Esta llamaría el método del controlador que iría a la segunda página, pasándole por parámetro el item del estudiante seleccionado por lo tanto este método quedaría:

goPage2 : function(objEvent){
    var view = this.getView();
    var pos = objEvent.getData();
    var listadoCursos = view.listadoCursos.getModel();
    var item = listadoCursos.getItem(pos);
    this.assambler(Page2Controller,Page2,"index",[item]);
}

A partir de la entidad del estudiante podemos obtener la entidad del curso, pero como obtener el listado de estudiantes de un curso, bueno para ello vamos a la entidad Curso y agregamos un atributo privado llamado estudiantes, el mismo inicialmente será un arreglo vacio.

//en las propiedades
/**
 * columna de ...
 *
 * @name __$$
 * @private
 * @type {Integer}
 */
__estudiantes : {}
 
//en el constructor
this.__estudiantes = [];

Después agregamos un método para devolver el listado de estudiantes:

getEstudiantes : function(){
    if(this.__estudiantes.length == 0){
        var em = criax.dic.DiContainer.getInstance().get('entityManager');
        var finderEstudiante = em.getFinder(escuela.entity.Estudiante);
        finderEstudiante.extend('findByCurso');
        this.__estudiantes = finderEstudiante.findByCurso(this.__id);
    }
    return this.__estudiantes;
}

Lo primero es verificar que el listado de estudiantes este vacío, para entonces realizar la búsqueda (cuidado con esto), obtenemos el servicio del entity-manager y con él, el finder de la entidad Estudiante. Ahora viene lo novedoso, extenderemos el finder de la entidad Estudiante, mediante la llamada del método extend, pasándole por parámetro el nombre del nuevo método que queramos que tenga.

Solamente se puede extender con métodos de tipo findBy o findOneBy, y seguido el nombre del atributo por el cual buscamos.

Por último llamamos al método extendido, pasándole el valor del id del curso. O sea buscaremos en todos los estudiantes donde el id de su curso sea igual al id pasado al método extendido. Como es un método del tipo findBy, el resultado será un arreglo de entidades de tipo Estudiante.

De esta manera en el controlador de la segunda vista en el método index, obtenemos el curso y del mismo el listado de estudiantes:

//en el metodo index del controlador de la segunda vista
var curso = item.getCurso();
var estudiantes = curso.getEstudiantes();
var vars = {
    'curso' : curso,
    'estudiantes' : estudiantes
};
this.setVar(vars);

En la vista, creamos un label para el nombre del curso y una lista para los estudiantes de ese curso:

//en las propiedades
/**
 * label de contenido
 *
 * @name label
 * @public
 * @type {}
 */
label : {},
 
/**
 * propiedad para ...
 *
 * @name $$
 * @public
 * @type {}
 */
listadoEstudiantes : {}
 
//en el metodo components
this.label = new criax.mobile.basic.Header(this.vars['curso'].getNombre(),3);
this.listadoEstudiantes = new criax.mobile.list.List();
 
//en el metodo initialize
this.base(arguments);
this.addListener("back",this._controller.goPage1,this._controller);
this.addContent(this.label);
this.listadoEstudiantes.setConfigureItem(this.__createItemList);
this.listadoEstudiantes.setArrayModel(this.vars['estudiantes']);
this.addContent(this.listadoEstudiantes);

Por supuesto siempre queda el método que se encargará de procesar y mostrar cada uno de los elementos de la lista:

__createItemList : function(item, data, row){
    item.setName(data.getId().toString());
    item.setTitle(data.getNombre());
}
Listado de estudiantes del curso

Listado de estudiantes del curso

En este caso la lista es simple con fondo blanco. Para que no salga el nombre del estudiante seleccionado que por supuesto estará en el listado de ese curso, podemos simplemente filtrar a los estudiantes en el controlador:

var curso = item.getCurso();
var estudiantes = curso.getEstudiantes();
var estudiantesFiltrados = [];
for(var i=0;i<estudiantes.length;i++){
    if(item.getId() !== estudiantes[i].getId()){
        estudiantesFiltrados.push(estudiantes[i]);
    }
}
var vars = {
    'curso' : curso,
    'estudiantes' : estudiantesFiltrados
};
this.setVar(vars);
Listado de estudiantes filtrado

Listado de estudiantes filtrado

Claro está la mejor opción sería una consulta que devolviera los estudiantes ya filtrados, esto simplemente fue un ejemplo didáctico.

Todas las consultas llevadas a cabo en la aplicación se muestran en la consola, así podremos tener referencias de las mismas y cuantas veces se ejecutan.

Consultas en la consola

Consultas en la consola

Hasta aquí un ejemplo muy simple de como se usa la persistencia para el desarrollo de aplicaciones con CRIAX, este caso en particular para aplicaciones móviles, pero funciona exactamente igual para las aplicaciones de escritorio.

Dejar un comentario