Entendiendo las relaciones de ADA framework (II parte)
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:
- Herencia. ADA puede construir entidades que hereden.
- Relaciones entre entidades: es la forma que tenemos para indicar que entidad va a depender de cuál.
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.
1.
@TableField
(name =
"edificio"
, datatype = DATATYPE_ENTITY)
2.
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.
1.
@TableField
(name =
"alumno"
, datatype = DATATYPE_ENTITY_LINK)
2.
public
List alumnos;
3.
4.
@TableField
(name =
"profesor"
, datatype = DATATYPE_ENTITY_LINK)
5.
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.
UN VISTAZO AL ESQUEMA SQLITE3.
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.
HERENCIA.
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.
01.
@Table
(name =
"profesores"
)
02.
public
class
Profesor
extends
Persona{
03.
04.
@TableField
(name =
"numero_horas_clase"
, datatype = DATATYPE_INTEGER)
05.
private
int
numero_horas_clase;
06.
07.
@TableField
(name =
"sum_numero_horas_clase"
, datatype = DATATYPE_INTEGER, virtual =
true
)
08.
private
int
sum_numero_horas_clase;
09.
10.
public
int
getNumero_horas_clase() {
11.
return
numero_horas_clase;
12.
}
13.
14.
public
void
setNumero_horas_clase(
int
numero_horas_clase) {
15.
this
.numero_horas_clase = numero_horas_clase;
16.
}
17.
18.
public
int
getSum_numero_horas_clase() {
19.
return
sum_numero_horas_clase;
20.
}
21.
22.
public
void
setSum_numero_horas_clase(
int
sum_numero_horas_clase) {
23.
this
.sum_numero_horas_clase = sum_numero_horas_clase;
24.
}
25.
26.
}<br><br>
01.
@Table
(name =
"alumnos"
)
02.
public
class
Alumno
extends
Persona{
03.
04.
@TableField
(name =
"carrera"
, datatype = DATATYPE_STRING)
05.
private
String carrera;
06.
07.
08.
public
String getCarrera() {
09.
return
carrera;
10.
}
11.
12.
public
void
setCarrera(String carrera) {
13.
this
.carrera = carrera;
14.
}
15.
16.
}
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.
01.
public
abstract
class
Persona
extends
Entity{
02.
03.
@TableField
(name =
"nombre"
, datatype = DATATYPE_STRING, required =
true
)
04.
private
String nombre;
05.
@TableField
(name =
"apellido"
, datatype = DATATYPE_STRING, required =
true
)
06.
private
String apellido
07.
@TableField
(name =
"dni"
, datatype = DATATYPE_STRING)
08.
private
String dni;
09.
@TableField
(name =
"fecha_alta"
, datatype = DATATYPE_DATE)
10.
private
Date fechaAlta;
11.
@TableField
(name =
"edad"
, datatype = DATATYPE_INTEGER)
12.
private
int
edad;
13.
@TableField
(name =
"activo"
, datatype = DATATYPE_BOOLEAN)
14.
private
boolean
activo;
15.
@TableField
(name =
"pais"
, datatype = DATATYPE_ENTITY_LINK)
16.
private
Pais pais;
17.
18.
//Se omiten los getters y setters
19.
20.
}
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.
EXTENDER DEL OBJECTSET.
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.
01.
public
class
PaisObjectSet
extends
ObjectSet {
02.
03.
public
PaisObjectSet(ObjectContext pContext)
throws
AdaFrameworkException {
04.
super
(Pais.
class
, pContext);
05.
06.
}
07.
08.
public
Pais getPorNombre(String nombre){
09.
return
getPorNombre(nombre,
false
);
10.
}
11.
12.
public
Pais getPorNombre(String nombre,
boolean
anadirSiNoExiste){
13.
14.
Pais pais =
null
;
15.
16.
try
{
17.
if
(nombre !=
null
&& !nombre.trim().equals(
""
)){
18.
String wherePattern = String.format(
"%s = ?"
,
this
.getDataTableFieldName(
"nombre"
));
19.
20.
String[] whereValores =
new
String[] { nombre };
21.
22.
List resultado = search(
true
, wherePattern, whereValores,
null
,
null
,
null
,
0
,
1
);
23.
if
(resultado !=
null
&& resultado.size() >
0
) {
24.
pais = resultado.get(
0
);
25.
}
else
if
(anadirSiNoExiste) {
26.
pais =
new
Pais(nombre);
27.
28.
save(pais);
29.
30.
add(pais);
31.
}
32.
}
33.
}
catch
(Exception e) {
34.
e.printStackTrace();
35.
}
36.
37.
return
pais;
38.
}
39.
40.
41.
}
* 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().
LLENANDO LAS ENTIDADES.
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.
01.
curso =
new
Curso();
02.
03.
curso.setNombre(
"JAVA"
);
04.
curso.setNumeroMaximoAlumnos(
15
);
05.
curso.setPrecioHora(
50.10
);
06.
curso.setEdificio(
new
Edificio(
"Palencia"
,
"Norte"
,
5
));
07.
curso.setProfesor(ContextoAplicacion.DatosBase.profesorSet.get(
1
));
08.
ContextoAplicacion.DatosBase.alumnoSet.fill(
"nombre = ? or nombre = ?"
,
new
String[]{
"Carlos"
,
"Laura"
},
null
);
09.
curso.setAlumnos(ContextoAplicacion.DatosBase.alumnoSet);
10.
curso.setStatus(Entity.STATUS_NEW);
11.
12.
ContextoAplicacion.DatosBase.cursoSet.add(curso);
13.
ContextoAplicacion.DatosBase.cursoSet.save();
Al dar de alta un curso, damos también de alta el edificio en el que se va a impartir.
1.
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.
1.
curso.setProfesor(ContextoAplicacion.DatosBase.profesorSet.get(
1
));
2.
ContextoAplicacion.DatosBase.alumnoSet.fill(
"nombre = ? or nombre = ?"
,
new
String[]{
"Carlos"
,
"Laura"
},
null
);
3.
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().
1.
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.
1.
ContextoAplicacion.DatosBase.cursoSet.add(curso);
2.
ContextoAplicacion.DatosBase.cursoSet.save();
3.
4.
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.
01.
public
class
ContextoDatos
extends
ObjectContext {
02.
03.
private
final
static
String TAG =
"ContextoDatos"
;
04.
05.
public
ObjectSet profesorSet;
06.
public
ObjectSet alumnoSet;
07.
public
ObjectSet cursoSet;
08.
public
PaisObjectSet paisSet;
09.
10.
11.
public
ContextoDatos(Context pContext){
12.
super
(pContext,
"AdaFramework_test.db"
);
13.
14.
inicializar();
15.
}
16.
17.
private
void
inicializar(){
18.
19.
try
{
20.
if
(profesorSet==
null
)
21.
profesorSet =
new
ObjectSet(Profesor.
class
,
this
);
22.
if
(alumnoSet==
null
)
23.
alumnoSet =
new
ObjectSet(Alumno.
class
,
this
);
24.
if
(cursoSet==
null
)
25.
cursoSet =
new
ObjectSet(Curso.
class
,
this
);
26.
if
(paisSet==
null
)
27.
paisSet =
new
PaisObjectSet(
this
);
28.
}
catch
(Exception e){
29.
Log.e(TAG, e.getMessage());
30.
}
31.
32.
}
33.
34.
35.
}
36.
<br>
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.
01.
public
class
ContextoAplicacion {
02.
public
static
ContextoDatos Contexto;
03.
04.
05.
public
static
void
inicializar(Context pContext) {
06.
if
(Contexto ==
null
) {
07.
Contexto =
new
ContextoDatos(pContext);
08.
}
09.
}
10.
}
Sólo nos falta instanciar el objeto.
1.
ContextoAplicacion.inicializar(
this
);
Reader Comments