El código y las explicaciones están basadas en la versión 2.3 de ADA framework. ADA framework es una librería creada por Txus Ballesteros de MobAndMe.
En este apartado veremos las siguientes características:
En la primera parte del tutorial se planteó el ejemplo que se iba a desarrollar. El ejemplo simulaba la gestión de una academia. Nos interesa poder gestionar los cursos que se imparten en la misma. Cada curso, sólo puede estar impartido por un profesor y un profesor puede impartir varios cursos. Un curso, está compuesto de varios alumnos, puediendo los alumnos realizar a la vez varios cursos. Además, introducimos dos entidades más, el edificio y el país.
En el edificio indicamos, en qué aula de qué edificio se va a impartir el curso. Un aula sólo puede estar ocupada por un curso y el curso entero se realiza en ese aula. El país nos vale para saber la nacionalidad de los alumnos y los profesores.
ADA dispone de dos atributos para indicar el tipo de relación que se establece entre entidades: DATATYPE_ENTITY y DATATYPE_ENTITY_LINK. Es muy importante entender qué es lo que supone cada una de ellas para poder implementar modelos de datos.
El atributo DATATYPE_ENTITY indica que estamos estableciendo una relación con una entidad que no se va a compartir, y que por lo tanto, va a formar parte exclusivamente de la entidad maestra. En una relación, entre la cabecera de un pedido y sus líneas, las líneas pertenecerían exclusivamente a la cabecera, no se compartiría con ninguna otra entidad.
El atributo DATATYPE_ENTITY_LINK establece relaciones con entidades que a su vez también son maestras, y que pueden relacionarse con cualquier otra entidad. Este sería el caso, de una tabla de países, que va a utilizarse con distintas entidades.
La forma de aplicar las entidades varía según como entendamos el modelo. En nuestro ejemplo, la entidad Edificio, es una entidad, que sólo va a poder relacionarse con un curso. Ni profesores, ni alumnos van a tener ninguna relación con los edificios. Por ello, utilizaremos DATATYPE_ENTITY para relacionar el curso con su edificio.
@TableField(name = "edificio", datatype = DATATYPE_ENTITY) public Edificio edificio;
En el caso de los profesores, queremos que también sean una entidad maestra, pues podríamos relacionarlos además de con los cursos con distintas academias, con lo que estamos compartiéndolos. Por ello, relacionamos el curso con los profesores mediante DATATYPE_ENTITY_LINK. Igualmente
haremos con los alumnos.
@TableField(name = "alumno", datatype = DATATYPE_ENTITY_LINK) public List alumnos; @TableField(name = "profesor", datatype = DATATYPE_ENTITY_LINK) public Profesor profesor;
Como podéis ver, también podríamos haber utilizado DATATYPE_ENTITY con alumnos y profesores si tuviésemos claro que no van a compartirse con nadie más y dejar como única entidad maestra el curso.
Con el atributo DATATYPE_ENTITY podremos crear relaciones 1:N mientras que la DATATYPE_ENTITY_LINK nos permite tener relaciones N:M.
Cada entidad maestra crea su propia tabla. Cuando utilizamos una relación DATATYPE_ENTITY se crea una tabla con los datos de la entidad no maestra junto con la clave foránea de la entidad master. La relación DATATYPE_ENTITY_LINK crea una tabla "LINK" que contiene la clave de la referencia maestra
y la de la referencia hija.
* Ejemplo de una tabla creada mediante el tipo de datos DATATYPE_ENTITY.
*Ejemplo de una tabla LINK. Contiene las claves de los alumnos que pertenecen a un curso.
Profesor y alumno heredan de la clase abstracta persona, pues comparten con ella parte de los atributos. Gracias a Ada, no vamos a tener que prescindir de la herencia o crearnos una entidad adicional para la entidad Persona. Ada localiza todas las clases heredadas y compone la clase entidad con los atributos que hereda.
@Table(name = "profesores") public class Profesor extends Persona{ @TableField(name = "numero_horas_clase", datatype = DATATYPE_INTEGER) private int numero_horas_clase; @TableField(name = "sum_numero_horas_clase", datatype = DATATYPE_INTEGER, virtual = true) private int sum_numero_horas_clase; public int getNumero_horas_clase() { return numero_horas_clase; } public void setNumero_horas_clase(int numero_horas_clase) { this.numero_horas_clase = numero_horas_clase; } public int getSum_numero_horas_clase() { return sum_numero_horas_clase; } public void setSum_numero_horas_clase(int sum_numero_horas_clase) { this.sum_numero_horas_clase = sum_numero_horas_clase; } }
@Table(name = "alumnos") public class Alumno extends Persona{ @TableField(name = "carrera", datatype = DATATYPE_STRING) private String carrera; public String getCarrera() { return carrera; } public void setCarrera(String carrera) { this.carrera = carrera; } }
Profesor y Alumno extienden de la clase Persona. Como las entidades que van a crear las tablas son estas, indicamos en ellas el nombre de la tabla con la anotación @Table. Si no le indicamos el nombre, ADA llamará a la tabla como la clase.
public abstract class Persona extends Entity{ @TableField(name = "nombre", datatype = DATATYPE_STRING, required = true) private String nombre; @TableField(name = "apellido", datatype = DATATYPE_STRING, required = true) private String apellido @TableField(name = "dni", datatype = DATATYPE_STRING) private String dni; @TableField(name = "fecha_alta", datatype = DATATYPE_DATE) private Date fechaAlta; @TableField(name = "edad", datatype = DATATYPE_INTEGER) private int edad; @TableField(name = "activo", datatype = DATATYPE_BOOLEAN) private boolean activo; @TableField(name = "pais", datatype = DATATYPE_ENTITY_LINK) private Pais pais; //Se omiten los getters y setters }
Además de todos los atributos también se van a heredar las relaciones. Una persona tiene un país, y esta relación se extiende a los profesores y los alumnos. Cada profesor y alumno tendrá su relación país.
A partir de esta última versión (2.3) se permite extender del objectSet para poder crear métodos específicos que satisfagan ciertos problemas.
Por ejemplo, para añadir un país a un profesor, primero tendríamos que dar de alta todos los países, y después recuperar el país específico para relacionarle con el profesor. Sería mucho más cómodo, el disponer de un método, que indicándole el nombre del país, cuando se está
creando la relación con el profesor, comprube si este ya existe y recupere su entidad o en el caso de no existir lo de de alta devolviéndonos la nueva entidad.
public class PaisObjectSet extends ObjectSet { public PaisObjectSet(ObjectContext pContext) throws AdaFrameworkException { super(Pais.class, pContext); } public Pais getPorNombre(String nombre){ return getPorNombre(nombre, false); } public Pais getPorNombre(String nombre, boolean anadirSiNoExiste){ Pais pais = null; try{ if (nombre != null && !nombre.trim().equals("")){ String wherePattern = String.format("%s = ?", this.getDataTableFieldName("nombre")); String[] whereValores = new String[] { nombre }; List resultado = search(true, wherePattern, whereValores, null, null, null, 0, 1); if (resultado != null && resultado.size() > 0) { pais = resultado.get(0); } else if (anadirSiNoExiste) { pais = new Pais(nombre); save(pais); add(pais); } } } catch (Exception e) { e.printStackTrace(); } return pais; } }
* Este ejemplo es una copia del código que podeís encontrar en el github de MobAndMe. Se trata del ejemplo que se utilizó para generar el código del primer tournamente de ADA.
En esta clase, se extiende del ObjectSet para poder implementar nuestro método getPorNombre. El método recibe el nombre del país, comprueba si existe en la base de datos mediante el método search. Si existe simplemente devuelve el resultado y sino existe lo da de alta mediante el método save().
A la hora de grabar los datos de las entidades, debemos diferenciar también entre los dos tipos de relaciones. Si se trata de una relación DATATYPE_ENTITY los datos de esta entidad se graban cuando grabemos los datos de la entidad maestra.
curso = new Curso(); curso.setNombre("JAVA"); curso.setNumeroMaximoAlumnos(15); curso.setPrecioHora(50.10); curso.setEdificio(new Edificio("Palencia","Norte",5)); curso.setProfesor(ContextoAplicacion.DatosBase.profesorSet.get(1)); ContextoAplicacion.DatosBase.alumnoSet.fill("nombre = ? or nombre = ?", new String[]{"Carlos", "Laura"}, null); curso.setAlumnos(ContextoAplicacion.DatosBase.alumnoSet); curso.setStatus(Entity.STATUS_NEW); ContextoAplicacion.DatosBase.cursoSet.add(curso); ContextoAplicacion.DatosBase.cursoSet.save();
Al dar de alta un curso, damos también de alta el edificio en el que se va a impartir.
curso.setEdificio(new Edificio("Palencia","Norte",5));
Para las relaciones DATATYPE_ENTITY_LINK como van a se van a poder compartir, primero tenemos que darlas de alta y después una vez que hemos recuperado la entidad podemos añadirle a la entidad maestra.
curso.setProfesor(ContextoAplicacion.DatosBase.profesorSet.get(1)); ContextoAplicacion.DatosBase.alumnoSet.fill("nombre = ? or nombre = ?", new String[]{"Carlos", "Laura"}, null); curso.setAlumnos(ContextoAplicacion.DatosBase.alumnoSet);
En este caso, recuperamos el segundo profesor de nuestra lista y lo añadimos al curso. Mediante una búsqueda recuperamos los alumnos que se llamen Carlos y Laura y les añadimos a ese curso.
No debemos olvidar en cada entidad que vayamos a dar de alta el indicar que es una entidad nueva mediante el método setStatus().
curso.setStatus(Entity.STATUS_NEW);
Hay dos formas de realizar las altas, añadiendo el objeto al objectSet y después salvándole o pasando el objeto directamente al método.
ContextoAplicacion.DatosBase.cursoSet.add(curso); ContextoAplicacion.DatosBase.cursoSet.save(); ContextoAplicacion.DatosBase.cursoSet.save(curso);
Usar una u otra forma depende de la actividad que estés realizando. Si salvamos directamente nuestro objeto no queda reflejado en el objectSet con lo cual tendremos que realizar alguna búsqueda para recuperarlo cuando queramos usarlo. Si vamos a grabar de golpe muchos objetos, el rendimiento es mucho mayor anadiéndoles primero al objectSet.
Finalmente, vamos a echar un vistazo a nuestra clase ObjectSet.
public class ContextoDatos extends ObjectContext { private final static String TAG = "ContextoDatos"; public ObjectSet profesorSet; public ObjectSet alumnoSet; public ObjectSet cursoSet; public PaisObjectSet paisSet; public ContextoDatos(Context pContext){ super(pContext, "AdaFramework_test.db"); inicializar(); } private void inicializar(){ try{ if (profesorSet==null) profesorSet = new ObjectSet(Profesor.class, this); if (alumnoSet==null) alumnoSet = new ObjectSet(Alumno.class, this); if (cursoSet==null) cursoSet = new ObjectSet(Curso.class, this); if (paisSet==null) paisSet = new PaisObjectSet(this); }catch(Exception e){ Log.e(TAG, e.getMessage()); } } }
En esta clase creamos nuestros objetos Set que van a ser los encargados de almacenar los datos de las entidades y realizar toda la gestión de los datos: búsquedas, insercciones, eliminaciones y actualizaciones. La única diferencia es la declaración y la instanciación del set de países, en donde utilizamos la clase que nos hemos creado (PaisObjectSet) que extiende el ObjectSet.
Para ahorrar en recursos, algo que nunca sobra en los dispositivos móviles, podemos utilizar el patrón singleton como nos recomienda Txus.
public class ContextoAplicacion { public static ContextoDatos Contexto; public static void inicializar(Context pContext) { if (Contexto == null) { Contexto = new ContextoDatos(pContext); } } }
Sólo nos falta instanciar el objeto.
ContextoAplicacion.inicializar(this);