EJB-Apartado5 - Tutorial de EJB avanzado

68
Tutorial de EJB avanzado

Transcript of EJB-Apartado5 - Tutorial de EJB avanzado

Page 1: EJB-Apartado5 - Tutorial de EJB avanzado

Tutorial de EJB avanzado

Page 2: EJB-Apartado5 - Tutorial de EJB avanzado

Introducción

Este apartado se da un tutorial sobreRelaciones entre entidadesHerencia de entidadesEJB-QL

El tutorial gira entorno a la capa modelo de una aplicación PSA (Professional Service Automation) muy simplificada

Gestión de los recursos humanos de una empresa

Page 3: EJB-Apartado5 - Tutorial de EJB avanzado

Análisis: objetos del dominio

Employee

- employeeIdentifier : Long- firstName : String- surname : String- positionIdentifier : String- salary : int

Department

- departmentIdentifier : String- name : String- creationDate : Calendar10..n

1 0..1

DistinguishedEmployee

- mentionDate : Calendar- comment : String

Project

- projectIdentifier : String- name : String- startDate : Calendar- endDate : Calendar

0..n

0..n

Dirigido por

Page 4: EJB-Apartado5 - Tutorial de EJB avanzado

Estructura de paqueteses.udc.fbellas.j2ee.advancedejbtutorial.psa

model

department

entity

testclient

employee

entity

vo

psafacade

util

vo

ejb

exceptions

vo

entity

vo

project

Page 5: EJB-Apartado5 - Tutorial de EJB avanzado

Fachada del modelo y cliente de prueba

Fachada del modeloes.udc.fbellas.j2ee.advancedejbtutorial.psa.model.psafacade.ejb.PSAFacade

Fachada remotaDefine operaciones para

Crear departamentos, empleados y proyectosHacer asignaciones (director de un departamento y empleado a un proyecto)Realizar consultasRealizar borrados en masa

La clase de implementación (PSAFacadeEJB) es un SLSB

Cliente de pruebaes.udc.fbellas.j2ee.advancedejbtutorial.psa.testclient.TestClient

Invoca las operaciones de la fachada

Page 6: EJB-Apartado5 - Tutorial de EJB avanzado

Entidades (1)

Employee

- employeeIdentifier : Long- firstName : String- surname : String- positionIdentifier : String- salary : int- department : Department- projects : List<Project>- version : long

Department

- departmentIdentifier : String- name : String- creationDate : Calendar- employees : List<Employee>- director : Employee- version : long

DistinguishedEmployee

Project

10..n

1 0..1

0..n

0..n

+ Constructores+ Métodos get/set+ Constructores

+ Métodos get/set

Dirigido por

Page 7: EJB-Apartado5 - Tutorial de EJB avanzado

Entidades (y 2)

Employee

Project

- projectIdentifier : String- name : String- startDate : Calendar- endDate : Calendar- employees : List<Employee>- version : long

DistinguishedEmployee

- mentionDate : Calendar- comment : String

0..n

0..n

+ Constructores+ Métodos get/set

+ Constructores+ Métodos get/set

Page 8: EJB-Apartado5 - Tutorial de EJB avanzado

Tablas

versioncreationDatenamedirIddepIdTabla = Department

(PK)

posId salary version typesurnamefirstNamedepIdempId Tabla = Employee

(PK) (FK)

(FK)

Relación “Department(0..1)--[Dirigido por]-->Employee(1)”

Relación “Department(1)<-->Employee(0..N)”

commentmentionDateempId Tabla = DSTEmployee

(PK, FK)

Herencia entre Employee y DistinguishedEmployee

prjIdempId

(PK, FK)

(PK, FK)

Tabla = EmpPrj

versionendDatestartDatenameprjIdTabla = Project

(PK)

Relación “Employee(0..N)<-->Project(0..N)”

Page 9: EJB-Apartado5 - Tutorial de EJB avanzado

Relaciones (1)

Tipos de relaciónUno-a-Uno

E.g. Department(0..1)--[Dirigido por]-->Employee(1)

Uno-a-Muchos / Muchos-a-UnoE.g. Department(1)<-->Employee(0..N)

Muchos-a-Muchos E.g. Employee(0..N)<-->Project(0..N)

NOTA: Uno significa 0..1 o 1Muchos significa 0..N o N

Atributos/propiedades que representan relacionesTipos: clases entidad o colecciones de clases entidad (Collection, List, Set y Map)Se usan anotaciones para especificar cómo mapear las relaciones a columnas/tablas

Page 10: EJB-Apartado5 - Tutorial de EJB avanzado

Relaciones (2)

DireccionalidadUnidireccionales

Sólo se puede navegar desde una entidad a la otraE.g. Department(0..1)--[Dirigido por]-->Employee(1)

Employee director = department.getDirector();

BidireccionalesDesde cualquiera de las dos entidades es posible navegar hacia la otraE.g. Department(1)<-->Employee(0..N)

Department department = employee.getDepartment();List<Employee> employees = department.getEmployees();

E.g. Employee(0..N)<-->Project(0..N)List<Project> projects = employee.getProjects();List<Employee> employees = project.getEmployees();

Page 11: EJB-Apartado5 - Tutorial de EJB avanzado

Relaciones (y 3)

Lado propietario (“owning side”) y lado inverso(“inverse side”) en una relación

IntuiciónEl lado propietario es la entidad cuya tabla asociada tiene la clave foránea que mantiene la relación

Una relación unidireccional sólo tiene lado propietarioEl lado propietario es la entidad que permite navegar hacia la otra

Una relación bidireccional tiene un lado propietario y un lado inverso

Si es Uno-a-Muchos o Muchos-a-Uno, el lado propietario es el lado MuchosSi es Uno-a-Uno, la entidad cuya tabla contiene la clave foránea es el lado propietarioSi es Muchos-a-Muchos, cualquier lado puede ser el lado propietario

Page 12: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(0..1)--[Dirigido por]-->Employee(1)” (1)

En Department

@Entitypublic class Department {

// ...

@OneToOne@JoinColumn(name="dirId")public Employee getDirector() {

return director;}

public void setDirector(Employee director) {this.director = director;

}

// ...

}

Page 13: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(0..1)--[Dirigido por]-->Employee(1)” (y 2)

Relaciones Uno-a-UnoSe utiliza @OneToOne sobre los atributos/propiedades que definen la relación

En el ejemplo, dado que la relación es unidireccional, sólo se aplica sobre el método getDirector de la entidad Department (en otro caso, se aplicaría en ambas entidades)

Se utiliza @JoinColumn sobre el atributo/propiedad que define la relación en el lado propietario

En el ejemplo, dado que la relación es unidireccional, el lado propietario es DepartmentEspecifica la columna que actúa como clave foránea para mantener la relaciónSe puede usar el elemento referencedColumnName para especificar el nombre de la columna a la que hace referencia la clave foránea

Por defecto se asume que es la clave primaria de la tabla de la otra entidad

Page 14: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (1)

En Employee

@Entitypublic class Employee {

// ...

@ManyToOne(optional=false)@JoinColumn(name="depId")public Department getDepartment() {

return department;}

public void setDepartment(Department department) {this.department = department;

}

// ...

}

Page 15: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (2)

En Department

@Entitypublic class Department {

public Department() {employees = new ArrayList<Employee>();

}

public Department(String departmentIdentifier, String name,Calendar creationDate) {

this.departmentIdentifier = departmentIdentifier;this.name = name;this.creationDate = creationDate;employees = new ArrayList<Employee>();

}

Page 16: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (3)

En Department (cont)

// ...

@OneToMany(mappedBy="department", cascade=CascadeType.REMOVE)public List<Employee> getEmployees() {

return employees;}

public void setEmployees(List<Employee> employees) {this.employees = employees;

}

// ...

}

Page 17: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (4)

Relaciones Uno-a-Muchos / Muchos-a-UnoSe utiliza @OneToMany (lado Uno) o @ManyToOne (lado Muchos) sobre los atributos/propiedades que definen la relación

Si la relación es unidireccional, sólo se anota el lado que permite navegar hacia el otro

En las relaciones bidireccionales, el lado inverso tiene que usar el elemento mappedBy en @OneToOne, @OneToManyy @ManyToMany

No se puede usar en @ManyToOne porque en una relación bidireccional el lado Muchos siempre es el lado propietarioEspecifica el nombre del atributo/propiedad del otro lado (ladopropietario) de la relación

En el ejemplo, el elemento mappedBy está diciendo que la propiedad employees en Department conjuntamente con la propiedad department en Employee forman conjuntamente una relación bidireccional

Page 18: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (5)

Relaciones Uno-a-Muchos / Muchos-a-Uno (cont)Al igual que en las relaciones Uno-a-Uno, se utiliza @JoinColumn sobre el atributo/propiedad que define la relación en el lado propietario

Especifica la columna que actúa como clave foránea para mantener la relación

NOTASoptional=false

En las anotaciones @OneToOne, @OneToMany, @ManyToOne y @ManyToMany, por defecto optional es trueEn getDepartment de Employee se ha usado @ManyToOnecon optional=false para especificar que getDepartmentsiempre devuelve un departamento, es decir, todo empleado está asignado necesariamente a un departamentoNinguna fila en la tabla Employee puede contener un NULL en la clave foránea depId

Page 19: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (6)

NOTAS (cont)cascade=CascadeType.REMOVE

Sólo se puede aplicar portablemente en @OneToOne y @OneToMany (por defecto no se realiza ninguna operación en cascada)En getEmployees de Department se ha usado @OneToManycon cascade=CascadeType.REMOVE para especificar que cuando se elimine un departamento se eliminen sus empleados

Los dos constructores de Department inicializan employees a una lista vacía (y no a null)

Las propiedades/atributos de tipo colección (relaciones Uno/Muchos-a-Muchos) devuelven una colección vacía cuando no hay asociaciónLas propiedades/atributos de tipo clase entidad (relaciones Uno/Muchos-a-Uno) devuelven null cuando no hay asociación

Page 20: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Department(1)<-->Employee(0..N)” (y 7)

NOTAS (cont)El uso de generics en las colecciones (e.g. List<Employee>) hace que no sea necesario emplear el elemento targetEntity en las anotaciones @OneToManyy @ManyToMany

targetEntity permite especificar el tipo de la clase entidad relacionadaCuando se usan generics, el tipo de la clase entidad relacionada está implícito en el tipo colección

Page 21: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Employee(0..N)<-->Project(0..N)” (1)

En Employee

@Entitypublic class Employee {

// ...

@ManyToMany@JoinTable(

name="EmpPrj",joinColumns=@JoinColumn(name="empId"),inverseJoinColumns=@JoinColumn(name="prjId"))

public List<Project> getProjects() {return projects;

}

public void setProjects(List<Project> projects) {this.projects = projects;

}

// ...}

Page 22: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Employee(0..N)<-->Project(0..N)” (2)

En Project

@Entitypublic class Project {

// ...

@ManyToMany(mappedBy="projects")public List<Employee> getEmployees() {

return employees;}

public void setEmployees(List<Employee> employees) {this.employees = employees;

}

// ...

}

Page 23: EJB-Apartado5 - Tutorial de EJB avanzado

Relación “Employee(0..N)<-->Project(0..N)” (y 3)

Relaciones Muchos-a-Muchos Se utiliza @ManyToMany sobre los atributos/propiedades que definen la relación

Si la relación es unidireccional, sólo se anota el lado que permite navegar hacia el otroEn el ejemplo se ha elegido Project como el lado inverso de la relación

@ManyToMany(mappedBy="projects") sobre getEmployees en Project

Se utiliza @JoinTable sobre el atributo/propiedad que define la relación en el lado propietario

name: nombre de la tabla en la que se mapea la relaciónjoinColumns: claves foráneas (normalmente una) que referencian las claves primarias de la tabla en la que se mapea la entidad del lado propietarioinverseJoinColumns: claves foráneas (normalmente una) que referencian las claves primarias de la tabla en la que se mapea la entidad del lado inverso

Page 24: EJB-Apartado5 - Tutorial de EJB avanzado

Establecimiento de relaciones (1)

Ejemplo: en PSAFacadeEJB

public void setDepartmentDirector(String departmentIdentifier,Long employeeIdentifier) throws InstanceNotFoundException {

Department department = PSAFacadeHelper.findDepartment(entityManager, departmentIdentifier);

Employee employee = PSAFacadeHelper.findEmployee(entityManager,employeeIdentifier);

department.setDirector(employee);

}

Page 25: EJB-Apartado5 - Tutorial de EJB avanzado

Establecimiento de relaciones (y 2)

Ejemplo: en PSAFacadeEJB

public void assignEmployeeToProject(Long employeeIdentifier,String projectIdentifier) throws InstanceNotFoundException {

/* Find employee and project. */Employee employee = PSAFacadeHelper.findEmployee(entityManager,

employeeIdentifier);Project project = PSAFacadeHelper.findProject(entityManager,

projectIdentifier);

/* Assign employee to project. */employee.getProjects().add(project);

}

NOTA: en los ejemplos, el código es un poco más complejo porque se comprueba si el empleado ya está asignado a ese proyecto, y en caso afirmativo, se lanza una excepción

Page 26: EJB-Apartado5 - Tutorial de EJB avanzado

Integridad referencial: ejemplos

null-emp1depA-emp2depB-emp3depB-emp4

depA.getEmployees().remove(emp1);

depA-emp1depA-emp2depB-emp3depB-emp4

Department-Employee (1:N)

depB-emp1depA-emp2depB-emp3depB-emp4

depB.getEmployees().add(emp1);

depA-emp1depA-emp2depB-emp3depB-emp4

Department-Employee (1:N)

depB-emp1depA-emp2depB-emp3depB-emp4

emp1.setDepartment(emp3.getDepartment());

depA-emp1depA-emp2depB-emp3depB-emp4

Department-Employee (1:N)

null-emp1null-emp2depA-emp3depA-emp4depB-colección vacía

depA.setEmployees(depB.getEmployees());

depA-emp1depA-emp2depB-emp3depB-emp4

Department-Employee (1:N)

depA-emp24depB-NULL

depA.setDirector(depB.getDirector());

depA-emp1depB-emp24

Department-Director (1:1)

DespuésOperaciónAntesTipo de relación

Page 27: EJB-Apartado5 - Tutorial de EJB avanzado

Relaciones unidireccionales Uno-a-Muchos

El mapping del API de Persistencia asume el uso de una tabla intermedia

Similar a la tabla usada para el mapping de un relación Muchos-a-MuchosEn realidad, podrían mapearse de manera similar a las relaciones bidireccionales Uno-a-Muchos, es decir, con una clave foránea en la tabla destinoPuede que algunas implementaciones permitan que se especifique que se desea el mismo mapping que las relaciones bidireccionales Uno-a-Muchos, pero eso no es portable

Si se desea utilizar ese mapping, es mejor modelarlas como relaciones bidireccionales Uno-a-Muchos

NOTA: las relaciones unidireccionales Muchos-a-Uno utilizan el mapping normal

Page 28: EJB-Apartado5 - Tutorial de EJB avanzado

Carga de instancias relacionadas (1)

E.g. Cuando se carga una instancia de Departmenten memoria, ¿se cargan en memoria también sus empleados (instancias de Employee)?El enumerado FetchType define dos políticas de carga

FetchType.EAGER: la instancia o instancias relacionadas se cargan automáticamenteFetchType.LAZY: actúa como una indicación (la implementación puede aplicar FetchType.EAGER) para especificar que la instancia o instancia relacionadas no se carguen hasta el primer momento en que se precisen

Cuando se invoca department.getEmployees(), la implementación del API de Persistencia, carga el los empleadosPara lograrlo, la implementación del API de Persistencia puede generar subclases o usar frameworks de Programación Orientada a Aspectos (interceptando la invocación a getEmployees)

Page 29: EJB-Apartado5 - Tutorial de EJB avanzado

Carga de instancias relacionadas (y 2)

La política de carga se puede especificar con el elemento fetch de las anotaciones

@OneToOne (por defecto FetchType.EAGER)@ManyToOne (por defecto FetchType.EAGER)@OneToMany (por defecto FetchType.LAZY)@ManyToMany (por defecto FetchType.LAZY)

Page 30: EJB-Apartado5 - Tutorial de EJB avanzado

Estrategias de mapeo de herencia (1)

El tipo enumerado InheritanceType define tres estrategias para mapear una relación de herenciaInheritanceType.SINGLE_TABLE

Utiliza una única tabla que contiene una columna por cada atributo/propiedad presente en las clases entidadSe necesita incluir una columna que actúe como discriminador

Permite saber a qué entidad corresponde una filaVentaja: ejecución eficiente de consultas polimórficas

E.g. encontrar todos los empleados (del tipo que sean) que ocupen un determinado cargoSe pueden implementar con una sola consulta

Desventaja: las columnas correspondientes a los atributos/propiedades de las clases que extienden (directa o indirectamente) de la clase raíz tienen que admitir NULL

Quizás haya muchas filas con valor NULL para algunas de esas columnas

Page 31: EJB-Apartado5 - Tutorial de EJB avanzado

Estrategias de mapeo de herencia (y 2)

InheritanceType.TABLE_PER_CLASSUtiliza una tabla por cada entidad, que contiene una columna por cada atributo/propiedad, propio o heredado, de la entidadDesventaja: ejecución ineficiente de consultas polimórficas

Requiere lanzar consultas sobre cada tablaEl soporte para este tipo de persistencia es opcional

InheritanceType.JOINEDUtiliza una tabla por cada clase entidad, que contiene una columna por cada atributo/propiedad específico a esa claseLa clave primaria de las tablas no raíz actúa como clave foránea de la clave primaria de la tabla raízVentaja: ahorro de espacio y ejecución razonablemente eficiente de consultas polimórficasDesventaja: requiere uno o varios JOINs para resolver las consultas polimórficas (prohibitivo en jerarquías profundas)

Page 32: EJB-Apartado5 - Tutorial de EJB avanzado

Herencia entre Employee y DistinguishedEmployee (1)

En Employee

@Entity@Inheritance(strategy=InheritanceType.JOINED)@DiscriminatorColumn(name="type",

discriminatorType=DiscriminatorType.STRING)@DiscriminatorValue("STD") // "STD" stands for "standard employee".public class Employee {

// ...}

En DistinguishedEmployee

@Entity@Table(name="DSTEmployee")@DiscriminatorValue("DST") // "DST" stands for "distinguished employee".public class DistinguishedEmployee extends Employee {

// ...}

Page 33: EJB-Apartado5 - Tutorial de EJB avanzado

Herencia entre Employee y DistinguishedEmployee (y 2)

@InheritancePermite especificar la estrategia de herenciaSe usa en la clase padre

@DiscriminatorColumnPermite especificar una columna que actúe como discriminador

Especifica el nombre de la columna y el tipo de discriminador (DiscriminatorType.STRING, DiscriminatorType.CHAR o DiscriminatorType.INTEGER)

Obligatoria con InheritanceType.SINGLE_TABLE y opcional (aunque típicamente usada) en InheritanceType.JOINED

NOTA: con la estrategia InheritanceType.JOINED, JBoss no da valor a la columna discriminador

Se usa en la clase padre@DiscriminatorValue

Especifica el valor que tomará la columna discriminador para las filas que correspondan a instancias de esta entidadSe usa en cada entidad concreta de la jerarquía

Page 34: EJB-Apartado5 - Tutorial de EJB avanzado

EJB-QL

Lenguaje de consultas de búsqueda y borrados/actualizaciones en masaSintaxis parecida a SQL (para facilitar el aprendizaje)Las consultas EJB-QL usan los nombre de las entidades y los atributos/propiedades (y no los nombres de las tablas y columnas)La implementación del API de Persistencia traduce las consultas EJB-QL al SQL de la BD relacional destinoLas consultas EJB-QL se ejecutan con el objeto Query

EntityManager dispone del método createQuery, que crea un objeto Query a partir de un String que contiene la consulta EJB-QL

Page 35: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (1)

Ejemplo: obtener todos los departamentosSELECT d FROM Department d

Devuelve una lista de objetos DepartmentDepartment d

Representa la declaración de la variable dEn las palabras clave (e.g. SELECT, FROM, etc.) no se distingue entre mayúsculas y minúsculas

EjecuciónList<Department> departments = entityManager.createQuery(

"SELECT d FROM Department d").getResultList();

El método createQuery devuelve un objeto QueryEl método getResultList permite ejecutar una consulta de lectura y devuelve una lista con los resultados

Si no hay resultados, devuelve una lista vacía

Page 36: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (2)

Ejecución (cont)Query también dispone del método getSingleResult, que permite ejecutar un consulta de lectura que sólo devuelva un resultado (o ninguno)

Lanza NoResultException (excepción de runtime) si no hay ningún resultadoSiempre se puede usar getResultList, pero getSingleResult es más cómodo cuando es seguro que la consulta sólo devuelve un resultado (o ninguno)Ejemplo: obtener los datos del departamento tic

try { Department department =

(Deparment) entityManager.createQuery("SELECT d FROM Department d " +"WHERE d.departmentIdentifier = 'tic'").getSingleResult();

// ...catch (NoResultException e) {

// ...}Es posible usar d.departmentIdentifier porque Departmenttiene el atributo/propiedad departmentIdentifier

Page 37: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (3)

LiteralesNuméricos (enteros o reales)

Ejemplo: e.salary >= 1000

Los literales de tipo cadena de caracteres se entrecomillan con comillas simples

Ejemplo: d.departmentIdentifier = 'tic'

Si el literal incluye una comilla simple, se sustituye por dosEjemplo: d.name = 'uno''dos'

Literales booleanos: TRUE y FALSE

Page 38: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (4)

Parámetros nombrados (“named parameters”)Tienen la forma :nombeParámetroEjemplo: obtener los empleados que ocupan un determinado cargo

SELECT e FROM Employee e WHERE e.positionIdentifier = :posId

Ejecución:

List<Employee> employees = entityManager.createQuery("SELECT e FROM Employee e " +"WHERE e.positionIdentifier = :posId").setParameter("posId", positionIdentifier).getResultList();

Query dispone del método setParameter, que da valor a un parámetro nombrado y devuelve otra vez el objeto Query

Permite escribir de manera “compacta” la construcción de la consulta y su ejecución

Page 39: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (5)

Consultas polimórficasEn la consulta ...

SELECT e FROM Employee e WHERE e.positionIdentifier = :posId

... los empleados devueltos pueden ser empleados normales (instancias de Employee) o empleados distinguidos (instancias de DistinguishedEmployee)

Es posible navegar a través de las relacionesEjemplo: obtener todos los empleados de un departamento

SELECT e FROM Employee e WHERE e.department.departmentIdentifier = :depId

e.department navega a la entidad Department

Page 40: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (6)

Expresiones constructorObtener los datos de todos los departamentos

SELECT NEW es.udc...DepartmentVO(d.departmentIdentifier, d.name, d.creationDate) FROM Department d

Después de la cláusula NEW se especifica un constructor de una clase

Se tiene que usar su nombre completoLa anterior consulta devuelve una lista con instancias de DeparmentVO (una por cada departamento)Cada elemento devuelto por Query.getResultList, o el único elemento devuelto por Query.getSingleResult(aunque en este ejemplo no es aplicable), es una instancia de DepartmentVO

Especialmente útil cuando se quiere devolver un CustomValue Object (CVO) o una lista de CVOs

Alternativamente se podría lanzar una consulta que devolviese una entidad o una lista de entidades, y finalmente crear los objetos CVOs a partir de las entidades

Page 41: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (7)

Si se desea, no es necesario emplear expresiones constructor cuando cada resultado de la consulta consta de varios items

EjemploSELECT d.departmentIdentifier, d.name, d.creationDate

FROM Department d

Cada elemento devuelto por Query.getResultList, o el único elemento devuelto por Query.getSingleResult(aunque en este ejemplo no es aplicable), es una instancia de Object[]Cada elemento del vector corresponde a un item (en el mismo orden que figuran en la consulta)En el ejemplo

Cada elemento de la lista devuelta por getResultList es una instancia de Object[]Cada Object[] tiene tres elementos: el identificador, el nombre y la fecha (en este orden)

Page 42: EJB-Apartado5 - Tutorial de EJB avanzado

Conceptos básicos (y 8)

ORDER BYEjemplo: recuperar todos los departamentos ordenados por identificador

SELECT d FROM Department d ORDER BY d.departmentIdentifier

Al igual que en SQL, se puede ordenar por varios criterios simultáneamente, especificando opcionalmente ASC (por defecto) o DESC

Ejemplos

SELECT e FROM Employee ORDER BY e.surname, e.firstName

SELECT e FROM Employee ORDER BY e.surname DESC,e.firstName DESC

SubconsultasEs posible anidar subconsultas en las cláusulas WHERE y HAVINGIremos viendo ejemplos ...

Page 43: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (1)

Operadores matemáticos (+, -, *, /), de comparación (=, >, <, >=, <=, <>) y lógicos (AND, OR, NOT)

EjemploSELECT e FROM Employee e WHERE

e.positionIdentifier = 'atp' AND e.salary >= 1000

ExplicaciónObtener todos los empleados que ocupan el cargo de atp y su salario es >= 1000

[NOT] BETWEENEjemploSELECT e FROM Employee e WHERE e.salary BETWEEN 1000 AND 2000

ExplicaciónSELECT e FROM Employee e WHERE

e.salary >= 1000 AND e.salary <= 2000

Page 44: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (2)

[NOT] INEjemploSELECT d FROM Department d WHERE

d.departmentIdentifier IN ('tic', 'dc')

ExplicaciónSELECT d FROM Department d WHERE

d.departmentIdentifier = 'tic' OR d.departmentIdentifier = 'dc'

[NOT] LIKEEjemploSELECT e FROM Employee e WHERE e.firstName LIKE 'F%o'

ExplicaciónDevuelve todos los empleados cuyo nombre empieza por F y termina en oMetacaracteres

% (secuencia de 0 o más caracteres), _ (cualquier carácter)Se puede utilizar la cláusula ESCAPE para indicar un carácter de escapeEjemplo: e.firstName LIKE '%\_%' ESCAPE '\' devuelve TRUE para cualquier nombre que incluya un subrayado (_)

Page 45: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (3)

IS [NOT] NULLEjemploSELECT d FROM Department d WHERE d.name IS NULL

ExplicaciónDevuelve todos los departamentos para los que no se ha especificado un valor para el atributo nameIS [NOT] NULL permite comprobar si un campo no colección es NULL

IS [NOT] EMPTYEjemploSELECT d FROM Department d WHERE d.employees IS NOT EMPTY

ExplicaciónDevuelve todos los departamentos que tienen empleadosIS [NOT] EMPTY permite comprobar si un campo colección es vacío

Page 46: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (4)

[NOT] EXISTSEjemplo

SELECT d FROM Department d WHERE EXISTS (SELECT e FROM Employee e WHERE

e.positionIdentifier = :posId AND e.department = d)

ExplicaciónDevuelve todos los departamentos que tengan al menos un empleado desempeñando un determinado cargoEXISTS devuelve TRUE si la subconsulta devuelve uno o más resultados

Page 47: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (5)

ALL y ANY/SOMEEjemploSELECT e FROM Employee e WHERE e.salary >= ALL

(SELECT e.salary FROM Employee e)

ExplicaciónDevuelve todos los empleados que tengan el salario más altoALL

TRUE si la comparación es TRUE para todos los valores devueltos por la subconsulta, o si la subconsulta no devuelven ningún resultado

ANY/SOMETRUE si la comparación es TRUE para alguno de los valores devueltos por la subconsulta (si la subconsulta no devuelve ningún resultado, la expresión es FALSE)ANY y SOME son sinónimos

Page 48: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (6)

Funciones de cadenasCONCAT(String, String)

Devuelve un String que es una concatenación de los dos pasados como parámetro

LENGTH(String)Devuelve el número (int) de caracteres del String

LOCATE(String, String, [start])Busca el segundo String en el primeroEl tercer parámetro (opcional) indica la posición (de 1 en adelante) desde la que comenzar la búsqueda (por defecto, desde el primer carácter)Devuelve la posición (int) en la que lo encontró (0 si no lo encontró)

SUBSTRING(String, start, length) devuelve un String

Devuelve el subcadena que comienza en la posición start y tiene longitud length

Page 49: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (7)

Funciones de cadenas (cont)TRIM

Por defecto, TRIM(String) devuelve el String sin los blancos iniciales y finales

LOWER(String)Devuelve el String en minúsculas

UPPER(String)Devuelve el String en mayúsculasEjemplo: obtener los empleados cuyo nombre empieza por F/fy termina en O/oSELECT e FROM Employee e WHERE UPPER(e.firstName) LIKE 'F%O'

Page 50: EJB-Apartado5 - Tutorial de EJB avanzado

Expresiones condicionales (y 8)

Funciones aritméticasABS(number)

Valor absoluto de un int, float o double

SQRT(number)Raíz cuadradaRecibe un argumento numérico y devuelve un double

MOD(number, base)Módulo Recibe dos int y devuelve un int

SIZE(collection)Tamaño de una colecciónDevuelve un intEjemplo: obtener todos los departamentos que tienen empleadosSELECT d FROM Department d WHERE SIZE(d.employees) > 0

Page 51: EJB-Apartado5 - Tutorial de EJB avanzado

Funciones agregadas (1)

Son funciones que se pueden usar como resultado de una consultaTodas aceptan como argumento una expresión que haga referencia a un atributo/propiedad no relaciónAdicionalmente, COUNT acepta como argumento una variable o una expresión que haga referencia a un atributo/propiedad relaciónAVG

Calcula la mediaRecibe un argumento numérico y devuelve un DoubleEjemplo: calcular el salario medio de los empleados

SELECT AVG(e.salary) FROM Employee e

Page 52: EJB-Apartado5 - Tutorial de EJB avanzado

Funciones agregadas (2)

AVG (cont)Ejemplo: ejecución de la anterior consulta

public int getAverageSalary() {

try {

Double averageSalary = (Double) entityManager.createQuery("SELECT AVG(e.salary) FROM Employee e").getSingleResult();

return averageSalary.intValue();

} catch (NoResultException e) {return 0;

}

}

Page 53: EJB-Apartado5 - Tutorial de EJB avanzado

Funciones agregadas (3)

COUNTDevuelve el número (Long) de resultadosEjemplo: calcular el número de departamentos

SELECT COUNT(d) FROM Department d

MAX/MINCalcula el valor máximo/mínimoRequiere un argumento de tipo numérico, String, char o fechasEjemplo: obtener todos los empleados que tengan el salario más alto

SELECT e FROM Employee e WHERE e.salary >=(SELECT MAX(e.salary) FROM Employee e)

Page 54: EJB-Apartado5 - Tutorial de EJB avanzado

Funciones agregadas (y 4)

SUMCalcula la sumaDevuelve Long si se aplica a enteros, Double si se aplica a reales y BigInteger/BigDecimal cuando se aplica a argumentos BigInteger/BigDecimalEjemplo: calcula el salario total de los empleados

SELECT SUM(e.salary) FROM Employee e

Page 55: EJB-Apartado5 - Tutorial de EJB avanzado

JOIN (1)

INNER JOIN implícitoProducto cartesiano en la cláusula FROM + condición en la cláusula WHERE

Ejemplo: obtener todos los departamentos que tengan al menos un empleado desempeñando un determinado cargo

SELECT DISTINCT d FROM Department d, Employee e WHEREe.department = d AND e.positionIdentifier = :posId

NOTAS:Equivalente a la consulta SQLSELECT DISTINCT d.* FROM Department d, Employee e

WHERE e.depId = d.depId AND e.posId = 'XXX'

e.department = d es equivalente a e.department.departmentIdentifier = d.departmentIdentifier

DISTINCT: mismo significado que en SQL (evita que se repitan departamentos cuando hay más de un empleado en un mismo departamento desempeñando el cargo especificado)

Page 56: EJB-Apartado5 - Tutorial de EJB avanzado

JOIN (2)

INNER JOIN explícitoUsa la cláusula [INNER] JOIN

INNER JOIN y JOIN son sinónimosEjemplo: el anterior

SELECT DISTINCT dFROM Department d JOIN d.employees e WHERE e.positionIdentifier = :posId

Equivalente a la consulta SQL

SELECT DISTINCT d.* FROM Department d JOIN Employee e ON e.depId = d.depId WHERE e.posId = 'XXX'

Con respecto a la consulta SQL, la sintaxis EJB-QL evita la condición sobre las claves (entre otras cosas)Con respecto a un INNER JOIN implícito, el uso de la cláusula JOIN evita la condición e.department = d(equivalente a la condición sobre las claves)

Page 57: EJB-Apartado5 - Tutorial de EJB avanzado

JOIN (y 3)

Operador INEjemplo: el anteriorSELECT DISTINCT d FROM Department d, IN(d.employees) e

WHERE e.positionIdentifier = :posId

Es otra manera de hacer un INNER JOINLa sintaxis del INNER JOIN explícito es más parecida a la de SQL

Otros ejemplosObtener todos los proyectos en los que trabaja un empleado

SELECT p FROM Project p JOIN p.employees e WHEREe.employeeIdentifier = :empId

Obtener todos los proyectos en los que trabaja un departamento

SELECT DISTINCT p FROM Project p JOIN p.employees e

JOIN e.department d WHERE d.departmentIdentifier = :depId

Page 58: EJB-Apartado5 - Tutorial de EJB avanzado

GROUP BY, HAVING (1)

Similares a las cláusulas SQLGROUP BY forma grupos en función de uno o varios atributos/propiedadesHAVING (opcional) permite especificar una condición (usando atributos/propiedades especificados en GROUP BY) para filtrar los elementos que irán dentro de cada grupo

EjemploSELECT e.department.departmentIdentifier, AVG(e.salary)

FROM Employee e GROUP BY e.department.departmentIdentifier

HAVING e.department.departmentIdentifier IN ('tic', 'dc')

ExplicaciónDevuelve el salario medio para los departamentos tic y dc

Page 59: EJB-Apartado5 - Tutorial de EJB avanzado

GROUP BY, HAVING (y 2)

Otro ejemploSELECT NEW es.udc...DepartmentStatisticsVO(

d.departmentIdentifier, COUNT(e), AVG(e.salary),MIN(e.salary), MAX(e.salary), SUM(e.salary))FROM Department d JOIN d.employees eGROUP BY d.departmentIdentifier

ExplicaciónDevuelve estadísticas para cada departamentoLos datos estadísticos de cada departamento incluyen: identificador del departamento, número de empleados, salario medio, salario mínimo, salario máximo y salario total

Page 60: EJB-Apartado5 - Tutorial de EJB avanzado

Borrados y actualizaciones en masa (1)

Para borrados o actualizaciones individualesBorrado de una entidad

EntityManager.remove

Actualización de una entidadModificar los atributos/propiedades Antes de terminar la ejecución del caso de uso, la implementación del API de Persistencia actualiza en BD

¿Y si queremos eliminar/actualizar un conjunto (potencialmente) grande de entidades?

Opción 1Localizar las entidades y eliminar/actualizar cada unaIneficiente

Opción 2Utilizar el soporte de EJB-QL para borrados y actualizaciones en masaSentencias DELETE y UPDATE

Page 61: EJB-Apartado5 - Tutorial de EJB avanzado

Borrados y actualizaciones en masa (2)

EjemplosEliminar todos los departamentosDELETE FROM Department

También provoca que se eliminen los empleadosRecordar que en Department

@OneToMany(mappedBy="department", cascade=CascadeType.REMOVE)

public List<Employee> getEmployees()

Eliminar todos los empleados con cargo atp

DELETE FROM Employee e WHERE e.positionIdentifier = 'atp'

Subirle el sueldo (en 100) a todos los empleados atp

UPDATE Employee e SET e.salary = e.salary + 100 WHERE e.positionIdentifier = 'atp'

Page 62: EJB-Apartado5 - Tutorial de EJB avanzado

Borrados y actualizaciones en masa (3)

Las sentencias UPDATE y DELETE trabajan directamente contra la BD

La sentencia UPDATE no actualiza automáticamente los atributos/propiedades anotados con @Version

¡No se aplica la estrategia Optimistic Locking!Conclusión: no usar la sentencia UPDATE cuando puede haber actualizaciones concurrentes

Al ejecutar las sentencias UPDATE y DELETE dentro de un caso de uso, las entidades que puedan estar cargadas en memoria (durante la ejecución del caso de uso) no se sincronizan con el nuevo estado en BD

Conclusión: las sentencias UPDATE y DELETE se deben usar en una transacción (caso de uso) independiente o al principio de la ejecución de una transacción (antes de que se haya recuperado alguna mediante EntityManager.find o Query.get{ResultList/SingleResult})

Page 63: EJB-Apartado5 - Tutorial de EJB avanzado

Borrados y actualizaciones en masa (y 4)

Ejecución de sentencias UPDATE y DELETEMediante Query.executeUpdate()

Devuelve el número de entidades actualizadas/borradasEjemplo: en PSAFacadeEJB ...

public void removeAllDepartments() {entityManager.createQuery("DELETE FROM Department").

executeUpdate();}

public void removeAllProjects() {entityManager.createQuery("DELETE FROM Project").

executeUpdate();}

Page 64: EJB-Apartado5 - Tutorial de EJB avanzado

Notas sobre los ejemplos (1)

Subsistema AdvancedEJBTutorialMuchas de las consultas que incluyen estas transparencias aparecen en la implementación de la fachada (PSAFacadeEJB)Simplificaciones

Por sencillez, las consultas ilustradas en las transparencias se han simplificado ligeramente con respecto a las de los ejemplosEn los ejemplos, la mayor parte de las consultas utilizan la cláusula ORDER BY (para ordenar los resultados)En los ejemplos, las consultas que devuelven los datos de un departamento utilizan una expresión constructor

public List<DepartmentVO> findAllDepartments() {

return entityManager.createQuery("SELECT NEW " + DepartmentVO.class.getName() + "(d.departmentIdentifier, d.name, " +"d.creationDate) FROM Department d ORDER BY " +"d.departmentIdentifier").getResultList();

}

Page 65: EJB-Apartado5 - Tutorial de EJB avanzado

Notas sobre los ejemplos (2)

Subsistema AdvancedEJBTutorialSimplificaciones (cont)

También se podría haber lanzado ...

SELECT d FROM Department d ORDER BY d.departmentIdentifier

... y luego crear una lista de DepartmentVO a partir de la lista de DepartmentSin embargo, las consultas que devuelven empleados no usan expresiones constructor porque los empleados pueden ser de tipo Employee o DistinguishedEmployee

public List<EmployeeVO> findEmployeesInPosition(String positionIdentifier) {

List<Employee> employees = entityManager.createQuery("SELECT e FROM Employee e WHERE " +"e.positionIdentifier = :posId ORDER BY e.surname").setParameter("posId", positionIdentifier).getResultList();

return PSAFacadeHelper.toEmployeeVOs(employees); }

Page 66: EJB-Apartado5 - Tutorial de EJB avanzado

Notas sobre los ejemplos (3)

Subsistema AdvancedEJBTutorialSimplificaciones (cont)

En PSAFacadeHelper

public final static List<EmployeeVO> toEmployeeVOs(List<Employee> employees) {

List<EmployeeVO> employeeVOs = new ArrayList<EmployeeVO>();

for (Employee e : employees) {employeeVOs.add(e.toEmployeeVO());

} return employeeVOs;

}

DistinguishedEmployee redefine toEmployeeVO para devolver un DistinguishedEmployeeVO (que extiende a EmployeeVO)

Page 67: EJB-Apartado5 - Tutorial de EJB avanzado

Notas sobre los ejemplos (4)

MiniBankNo se ha usado el soporte de relaciones para modelar la relación Uno-a-Muchos que conceptualmente existe entre Account y AccountOperation

Account no dispone de getAcountOperations()El número de operaciones devueltas podría ser excesivamente grandeEs más lógico utilizar el patrón Page-by-Page Iterator para recuperar las operaciones bancarias asociadas a una cuenta (apartado 5.4)

Page 68: EJB-Apartado5 - Tutorial de EJB avanzado

Notas sobre los ejemplos (y 5)

MiniBank (cont)Se podría haber modelado la relación (unidireccional) Muchos-a-Uno entre AccountOperation y Account

AccountOperation dispondría del método getAccount()AccountOperation no tendría la propiedad/atributo accountIdentifier

La consulta lanzada en la implementación del patrón Page-by-PageIterator quedaría como

SELECT o FROM AccountOperation oWHERE o.account.accountIdentifier = :accountIdentifier ANDo.date >= :startDate AND o.date <= :endDateORDER BY o.date

o (si antes se recupera la cuenta)

SELECT o FROM AccountOperation oWHERE o.account = :account ANDo.date >= :startDate AND o.date <= :endDateORDER BY o.date