RELACIONES EN TABLAS EN JPA

La entidad o entidad extendida será la clase con la que realmente trabajemos, ya que tiene las columnas de la clase que hereda y la funcionalidad que añadamos según nuestras necesidades. Al igual que con las super clases, se recomienda crear un paquete dentro del proyecto donde guardar todas nuestras entidades.

La principal diferencia es que esta clase si estará marcada con la anotación @Entity y tendrá que extender de la super clase e implementar Serializable:

@Entity

@Table(name = "TABLA_EJEMPLO")
public class TablaEjemploExtends extends TablaEjemplo implements Serializable {...}

Figura 14. Entidad extendida de ejemplo

El punto más importante que vamos a desarrollar de la lógica que se puede añadir en las clases extendidas son las relaciones Hibernate entre entidades:

@OneToOne

@OneToOne(cascade = CascadeType.ALL, mappedBy = "nombreAtributo",
fetch = FetchType.LAZY)

Figura 15. Relación @OneToOne

La anotación @OneToOne tiene los siguientes atributos:

1.- Cascade: es tarea del programador acertar en el uso de este atributo ya que no hay una regla universal. En este caso se ha usado CascadeType.ALL para darle legibilidad del código y para que se actualice la tabla destino ante cualquier operación. No se recomiendo usar como comodín.

2.- MappedBy: entre comillas se debe insertar el nombre del atributo que deseemos relacionar en la tabla destino.

3.- Fetch: no carga la relación a menos que se invoque el getter. Para realizar lo contrario, marcar como EAGER.

@OneToMay

A diferencia de la anterior, si se marca una relación como @OneToMany debe existir otra que sea @ManyToOne

@OneToMany(cascade = CascadeType.ALL, mappedBy = "test",

FetchType.LAZY)
protected List<CfgTestOpcionExtends> cfgTestOpcionExtendsList;

Figura 16. Relación @OneToMany

Identica a la anterior salvo porque declara una lista como atributo. Es olbigatorio el mappedBy.

@JoinColumn(name = "ID_TEST" referencedColumnName = "ID",

nullable = false, insertable = false, updatable = false)
@ManyToOne(opcional = false, fetch = FetchTye.LAZY)
protected CfgTestExtends test;

Figura 17. Relación @ManyToOne para satisfacer @OneToMany

A destacar, la anotación @JoinColumn. Puede confundirse con @Column, pero esta anotación nos permite definir el nombre exacto de la columna, si debe insertable, actualizable y nulo. En este caso, el atributo name hace referencia al nombre que deseemos que tenga la columna de esa clase, pero el atributo referencedColumnName es el nombre que le hemos puesto en la clase destino.

@ManyToOne

Realmente ya estaría explicada con lo mencionada anteriormente, pero se va a profundizar en el caso de que la relación una dos columnas:

@ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)

@JoinColumns({@JoinColumn(name = "COLUMNA_UNO",

@JoinColumn(name = "COLUMNA_DOS")})
private ClaseRelacionada nombreClaseRelacionada;

Figura 18. Múltiples columnas en una unión

Gracias a la anotación @JoinColumns podremos crear múltiples columnas de la unión.

@ManyToMany

Para entender este caso, vamos a plantear la situación en la que un usuario puede tener diversos roles y los roles pueden estar asignados a más de un usuario. La forma en la que podemos relacionar ambas entidades es la siguiente:

En la clase Usuarios:

@JoinTable(name = "ROL_USUARIOS", joinColumns = {

@JoinColumn(name = "USUARIO", referencedColumnName = "USUARIO",
nullable = false) },
inverseJoinColumns = {
@JoinColumn(name = "ID_ROL", referencedColumnName = "ID",
nullable = false})
@ManyToMany(fetch = FetchType.LAZY)
private List<RolesExtends> rolesList;

Figura 19. Relación @ManyToMany en usuarios

La forma en la que hemos orientado la solución es la de crear una tercera tabla adicional que reúna una columna de cada tabla. La anotación @JoinTable realiza dicha acción en la nueva tabla ROL_USUARIOS uniendo el identificador del usuario y el identificador del rol. Se usa inverseJoinColumns para establecer la columna que no pertenece a la clase donde se implementa.

En este caso, se llama a la ajena Roles desde la clase Usuarios.

En la clase Roles:

@ManyToMany(mappedBy = "rolesList", fetch = FetchType.LAZY)
private List<UsuariosExtends> usuariosList;

Dado que se ha realizado el proceso principal en la clase Usuarios, en ésta solo necesitamos indicar en el mappedBy el nombre del atributo al cual hace referencia. 

TDD 1

Documentación de pruebas

Tomando como referencia el estándar IEEE 829-1983, la documentación mínima que se debe generar durante la fase de pruebas de un ciclo de vida del software es:

1.- Plan de pruebas.

2.- Especificación de los requerimientos para el diseño de los casos de prueba.

3.- Caso de prueba, que incluye, además, la descripción del procedimiento de prueba y al descripción de ítem a probar (véase Capítulo 8).

4.- Reporte de incidentes de prueba.

5.- Resumen de pruebas.

Basándose nuevamente en al definición del IEEE Standard Glossary of Software Engineering Technology, un plan de pruebas es un documento que describe el alcance, enfoque, recursos y planificación de las actividades de prueba deseadas. Identifica los elementos y características a probar, las tareas de prueba, quién realizará cada tarea y cualquier riesgo que requiera planes de contingencia. Cabe destacar que, a pesar de ser un documento que se utiliza en la fase de pruebas, el plan de pruebas debería escribirse en la etapa de diseño, que es cuando se especifíca la arquiectura del sistema y se establecen y detallan los módulos/objetos que van a integrar el sistema. En este momento es mucho más fácil realizar un diseño de los casos de prueba y definir el plan de pruebas.

Seguidamente se lista y se explica un ejemplo estándar del índice de un plan de pruebas:

1.- Introducción al documento

2.- Alcance de la fase de pruebas

3.- Requisitos del entorno de pruebas (hardware y software)

4.- Ítems a probar

5.- Planificación de las pruebas

5.1.- Calendario

5.2.- Equipo de pruebas

5.3.- Responsabilidades (para cada una de las tareas previstas en el plan)

5.4.- Manejo de riesgos (identificación de riesgos y planes de contigencia)

5.5.- Técnicas

5.6.- Estrategía de integración utilizada

6.- Para cada paso de integración

6.1.- Orden de integración

6.2.- Módulos a ser probados

6.3.- Pruebas de unidad para los módulos a ser probados

6.4.- Resultados esperados

6.5.- Entorno de pruebas

7.- Resultados de las pruebas de verificación

7.1.- Datos

7.2.- Condiciones

8.- Resultados de las pruebas de validación

9.- Pruebas de estrés

10.- Monitorización de las pruebas

11.- Referencias y apéndices 

OOP - COHESION Y ACOPLAMIENTO

Acoplamiento y cohesión

El acoplamiento es una medida de la interconexión entre los módulos de un programa. El diseño es la fase en la que se puede y debe tener en cuenta esta medida. Así, hay que tender a un bajo acoplamiento, ya que, entre otras cosas, se minimiza el efecto onda (propagación de errores), se minimiza el riesgo al coste de cambiar un módulo por otro y se facilita el entendimiento del programa.

La cohesión mide la relación, principalmente funcional pero no sólo, de los elementos de un módulo. Hay que conseguir un alto grado de cohesión ya que conlleva un menor coste de programación y consecuentemente una mayor calidad del producto.

En definitiva, hay que diseñar haciendo que los módulos sean tan independientes como sea posible (bajo acoplamiento) y que cada módulo haga (idealmente) una sola función (alta cohesión).

Ambas medidas tienen una gran repercusión en las pruebas, ya que facilitan las mismas tanto a la hora de detección de un error, como a la hora de corregirlo y realizar las correspondientes pruebas de regresión.


 

SPRING JPA - HERENCIA

 

Herencia


En el caso de herencia, se puede utilizar una serie de anotaciones para indicar cómo se quiere tratar. Se utilizará como ejemplo una serie de clases: Step y sus clases hijas SimpleStep y MultipleStep.

Cuando una serie de objetos comparten herencia, se puede apreciar que, normalmente, no contienen los mismos datos. Es por esto que no se pueden almacenar todas las clases en una misma tabla. Sabiendo esto, hay tres distintas opciones que se pueden utilizar:

1.- Utilizar una tabla única para todas las clases, dejando vacíos los campos que no son propios de la clase de la que se trata. Esto puede producir una gran cantidad de espacio reservado innecesario en muchos casos, pero es viable cuando los atributos de las clases prácticamente no varían. Debe utilizar un discriminador para distinguir la clase de la que se trata en cada registro. El discriminador almacena el nombre de la clase para poder generar un objeto de la clase adecuada cuando se requiera.

2.- Utilizar una tabla para cada clase. En este caso, el espacio reservado siempre será adecuado y se puede distinguir la clase sin necesidad de un discriminador. Por otra parte, puede complicar peticiones como búsquedas conjuntas.

3.- Utilizar una tabla únia para datos comunes y una tabla específica de cada clase para el resto. Es el caso en el que los atributos de la clase padre se comparten y los datos específicos se almacenan en distintas tablas. También requiere un discriminador.

Con estos tres casos se cubren todas las posibilidades, la decisión depende de la naturaleza de los datos de cada clase. Una vez tomada la decisión, la implementación es simple ya que sólo debe añadirse la anotación @Inheritance indicanod la estrategía (por defecto SINGLE_TABLE) y en caso de que sea necesario indicar un nombre para la columna del discriminador mediante la anotación @DiscriminatorColumn.


@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "disc")
@Table(name = "step")public abstract class Step {

...

}

Para las clases hijas no es necesario realizar ningún cambio, basta con que estén anotadas como entidades tambén y que hereden de la clase padre como es debido.






Contenido desarrollo de software - Arquitectura Software

ENUM en JAVA