Você está na página 1de 68

Tutorial de EJB avanzado

Introducción
„ Este apartado se da un tutorial sobre
„ Relaciones entre entidades
„ Herencia de entidades
„ EJB-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
Análisis: objetos del dominio
Project

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

0..n
0..n

Employee Dirigido por Department


- employeeIdentifier : Long 1 0..1
- departmentIdentifier : String
- firstName : String
- name : String
- surname : String
0..n 1 - creationDate : Calendar
- positionIdentifier : String
- salary : int

DistinguishedEmployee

- mentionDate : Calendar
- comment : String
Estructura de paquetes
es.udc.fbellas.j2ee.advancedejbtutorial.psa
model
department
entity
vo

employee
entity
vo

project
entity
vo

psafacade
ejb
exceptions
vo

util

testclient
Fachada del modelo y cliente de prueba

„ Fachada del modelo


„ es.udc.fbellas.j2ee.advancedejbtutorial.psa.
model.psafacade.ejb.PSAFacade
„ Fachada remota
„ Define operaciones para
„ Crear departamentos, empleados y proyectos
„ Hacer asignaciones (director de un departamento y empleado a
un proyecto)
„ Realizar consultas
„ Realizar borrados en masa
„ La clase de implementación (PSAFacadeEJB) es un SLSB
„ Cliente de prueba
„ es.udc.fbellas.j2ee.advancedejbtutorial.psa.
testclient.TestClient
„ Invoca las operaciones de la fachada
Entidades (1)
Project

0..n

0..n

Employee
Department
Dirigido por
- employeeIdentifier : Long
- firstName : String - departmentIdentifier : String
1 0..1
- surname : String - name : String
- positionIdentifier : String - creationDate : Calendar
- salary : int 0..n 1 - employees : List<Employee>
- department : Department - director : Employee
- projects : List<Project> - version : long
- version : long + Constructores
+ Constructores + Métodos get/set
+ Métodos get/set

DistinguishedEmployee
Entidades (y 2)
Project

- projectIdentifier : String
- name : String
- startDate : Calendar
- endDate : Calendar
- employees : List<Employee>
- version : long
+ Constructores
+ Métodos get/set

0..n

0..n

Employee

DistinguishedEmployee

- mentionDate : Calendar
- comment : String
+ Constructores
+ Métodos get/set
Tablas
(PK) (FK)

depId dirId name creationDate version


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

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

empId depId firstName surname posId salary version type Tabla = Employee

Herencia entre Employee y DistinguishedEmployee

empId mentionDate comment Tabla = DSTEmployee

(PK, FK)
(PK, FK)
empId prjId Tabla = EmpPrj
Relación “Employee(0..N)<-->Project(0..N)”
(PK, FK)

(PK)

prjId name startDate endDate version


Tabla = Project
Relaciones (1)
„ Tipos de relación
„ Uno-a-Uno
„ E.g. Department(0..1)--[Dirigido por]-->Employee(1)
„ Uno-a-Muchos / Muchos-a-Uno
„ E.g. Department(1)<-->Employee(0..N)
„ Muchos-a-Muchos
„ E.g. Employee(0..N)<-->Project(0..N)
„ NOTA:
„ Uno significa 0..1 o 1
„ Muchos significa 0..N o N
„ Atributos/propiedades que representan relaciones
„ Tipos: 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
Relaciones (2)
„ Direccionalidad
„ Unidireccionales
„ Sólo se puede navegar desde una entidad a la otra
„ E.g. Department(0..1)--[Dirigido por]-->Employee(1)
„ Employee director = department.getDirector();
„ Bidireccionales
„ Desde cualquiera de las dos entidades es posible navegar hacia la
otra
„ E.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();
Relaciones (y 3)
„ Lado propietario (“owning side”) y lado inverso
(“inverse side”) en una relación
„ Intuición
„ El 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 propietario
„ El 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 Muchos
„ Si es Uno-a-Uno, la entidad cuya tabla contiene la clave
foránea es el lado propietario
„ Si es Muchos-a-Muchos, cualquier lado puede ser el lado
propietario
Relación “Department(0..1)--[Dirigido por]-->Employee(1)” (1)

„ En Department
@Entity
public class Department {

// ...

@OneToOne
@JoinColumn(name="dirId")
public Employee getDirector() {
return director;
}

public void setDirector(Employee director) {


this.director = director;
}

// ...

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

„ Relaciones Uno-a-Uno
„ Se 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 Department
„ Especifica la columna que actúa como clave foránea para
mantener la relación
„ Se 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
Relación “Department(1)<-->Employee(0..N)” (1)

„ En Employee
@Entity
public class Employee {

// ...

@ManyToOne(optional=false)
@JoinColumn(name="depId")
public Department getDepartment() {
return department;
}

public void setDepartment(Department department) {


this.department = department;
}

// ...

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

„ En Department
@Entity
public 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>();

}
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;
}

// ...

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

„ Relaciones Uno-a-Muchos / Muchos-a-Uno


„ Se 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, @OneToMany
y @ManyToMany
„ No se puede usar en @ManyToOne porque en una relación
bidireccional el lado Muchos siempre es el lado propietario
„ Especifica el nombre del atributo/propiedad del otro lado (lado
propietario) 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
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
„ NOTAS
„ optional=false
„ En las anotaciones @OneToOne, @OneToMany, @ManyToOne y
@ManyToMany, por defecto optional es true
„ En getDepartment de Employee se ha usado @ManyToOne
con optional=false para especificar que getDepartment
siempre devuelve un departamento, es decir, todo empleado
está asignado necesariamente a un departamento
„ Ninguna fila en la tabla Employee puede contener un NULL en
la clave foránea depId
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 @OneToMany
con 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ón
„ Las propiedades/atributos de tipo clase entidad (relaciones
Uno/Muchos-a-Uno) devuelven null cuando no hay asociación
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 @OneToMany
y @ManyToMany
„ targetEntity permite especificar el tipo de la clase entidad
relacionada
„ Cuando se usan generics, el tipo de la clase entidad
relacionada está implícito en el tipo colección
Relación “Employee(0..N)<-->Project(0..N)” (1)

„ En Employee
@Entity
public 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;
}

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

„ En Project
@Entity
public class Project {

// ...

@ManyToMany(mappedBy="projects")
public List<Employee> getEmployees() {
return employees;
}

public void setEmployees(List<Employee> employees) {


this.employees = employees;
}

// ...

}
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 otro
„ En 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ón
„ joinColumns: claves foráneas (normalmente una) que
referencian las claves primarias de la tabla en la que se mapea
la entidad del lado propietario
„ inverseJoinColumns: claves foráneas (normalmente una)
que referencian las claves primarias de la tabla en la que se
mapea la entidad del lado inverso
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);

}
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
Integridad referencial: ejemplos
Tipo de relación Antes Operación Después
Department-Director (1:1) depA-emp1 depA.setDirector( depA-emp24
depB-emp24 depB.getDirector()); depB-NULL
Department-Employee (1:N) depA-emp1 depA.setEmployees( null-emp1
depA-emp2 depB.getEmployees()); null-emp2
depB-emp3 depA-emp3
depB-emp4 depA-emp4
depB-colección vacía
Department-Employee (1:N) depA-emp1 emp1.setDepartment( depB-emp1
depA-emp2 emp3.getDepartment()); depA-emp2
depB-emp3 depB-emp3
depB-emp4 depB-emp4
Department-Employee (1:N) depA-emp1 depB.getEmployees(). depB-emp1
depA-emp2 add(emp1); depA-emp2
depB-emp3 depB-emp3
depB-emp4 depB-emp4
Department-Employee (1:N) depA-emp1 depA.getEmployees(). null-emp1
depA-emp2 remove(emp1); depA-emp2
depB-emp3 depB-emp3
depB-emp4 depB-emp4
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-Muchos
„ En realidad, podrían mapearse de manera similar a las
relaciones bidireccionales Uno-a-Muchos, es decir, con una
clave foránea en la tabla destino
„ Puede 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
Carga de instancias relacionadas (1)
„ E.g. Cuando se carga una instancia de Department
en 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áticamente
„ FetchType.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 empleados
„ Para 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)
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)
Estrategias de mapeo de herencia (1)
„ El tipo enumerado InheritanceType define tres
estrategias para mapear una relación de herencia
„ InheritanceType.SINGLE_TABLE
„ Utiliza una única tabla que contiene una columna por cada
atributo/propiedad presente en las clases entidad
„ Se necesita incluir una columna que actúe como
discriminador
„ Permite saber a qué entidad corresponde una fila
„ Ventaja: ejecución eficiente de consultas polimórficas
„ E.g. encontrar todos los empleados (del tipo que sean) que
ocupen un determinado cargo
„ Se 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
Estrategias de mapeo de herencia (y 2)
„ InheritanceType.TABLE_PER_CLASS
„ Utiliza una tabla por cada entidad, que contiene una
columna por cada atributo/propiedad, propio o heredado, de
la entidad
„ Desventaja: ejecución ineficiente de consultas polimórficas
„ Requiere lanzar consultas sobre cada tabla
„ El soporte para este tipo de persistencia es opcional
„ InheritanceType.JOINED
„ Utiliza una tabla por cada clase entidad, que contiene una
columna por cada atributo/propiedad específico a esa clase
„ La clave primaria de las tablas no raíz actúa como clave
foránea de la clave primaria de la tabla raíz
„ Ventaja: ahorro de espacio y ejecución razonablemente
eficiente de consultas polimórficas
„ Desventaja: requiere uno o varios JOINs para resolver las
consultas polimórficas (prohibitivo en jerarquías profundas)
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 {
// ...
}
Herencia entre Employee y DistinguishedEmployee (y 2)

„ @Inheritance
„ Permite especificar la estrategia de herencia
„ Se usa en la clase padre
„ @DiscriminatorColumn
„ Permite 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 entidad
„ Se usa en cada entidad concreta de la jerarquía
EJB-QL
„ Lenguaje de consultas de búsqueda y
borrados/actualizaciones en masa
„ Sintaxis 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
destino
„ Las 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
Conceptos básicos (1)
„ Ejemplo: obtener todos los departamentos
SELECT d FROM Department d

„ Devuelve una lista de objetos Department


„ Department d
„ Representa la declaración de la variable d
„ En las palabras clave (e.g. SELECT, FROM, etc.) no se
distingue entre mayúsculas y minúsculas
„ Ejecución
List<Department> departments = entityManager.createQuery(
"SELECT d FROM Department d").getResultList();

„ El método createQuery devuelve un objeto Query


„ El 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
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
resultado
„ Siempre 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 Department
tiene el atributo/propiedad departmentIdentifier
Conceptos básicos (3)
„ Literales
„ Numé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 dos
„ Ejemplo: d.name = 'uno''dos'
„ Literales booleanos: TRUE y FALSE
Conceptos básicos (4)
„ Parámetros nombrados (“named parameters”)
„ Tienen la forma :nombeParámetro
„ Ejemplo: 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
Conceptos básicos (5)
„ Consultas polimórficas
„ En 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 relaciones
„ Ejemplo: obtener todos los empleados de un departamento
SELECT e FROM Employee e WHERE
e.department.departmentIdentifier = :depId

„ e.department navega a la entidad Department


Conceptos básicos (6)
„ Expresiones constructor
„ Obtener 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 completo
„ La 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 Custom
Value 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
Conceptos básicos (7)
„ Si se desea, no es necesario emplear expresiones
constructor cuando cada resultado de la consulta
consta de varios items
„ Ejemplo
SELECT 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)
Conceptos básicos (y 8)
„ ORDER BY
„ Ejemplo: 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

„ Subconsultas
„ Es posible anidar subconsultas en las cláusulas WHERE y
HAVING
„ Iremos viendo ejemplos ...
Expresiones condicionales (1)
„ Operadores matemáticos (+, -, *, /), de
comparación (=, >, <, >=, <=, <>) y lógicos (AND,
OR, NOT)
„ Ejemplo
SELECT e FROM Employee e WHERE
e.positionIdentifier = 'atp' AND e.salary >= 1000

„ Explicación
„ Obtener todos los empleados que ocupan el cargo de atp y su
salario es >= 1000
„ [NOT] BETWEEN
„ Ejemplo
SELECT e FROM Employee e WHERE e.salary BETWEEN 1000 AND 2000

„ Explicación
SELECT e FROM Employee e WHERE
e.salary >= 1000 AND e.salary <= 2000
Expresiones condicionales (2)
„ [NOT] IN
„ Ejemplo
SELECT d FROM Department d WHERE
d.departmentIdentifier IN ('tic', 'dc')
„ Explicación
SELECT d FROM Department d WHERE
d.departmentIdentifier = 'tic' OR
d.departmentIdentifier = 'dc'
„ [NOT] LIKE
„ Ejemplo
SELECT e FROM Employee e WHERE e.firstName LIKE 'F%o'
„ Explicación
„ Devuelve todos los empleados cuyo nombre empieza por F y
termina en o
„ Metacaracteres
„ % (secuencia de 0 o más caracteres), _ (cualquier carácter)
„ Se puede utilizar la cláusula ESCAPE para indicar un carácter de
escape
„ Ejemplo: e.firstName LIKE '%\_%' ESCAPE '\' devuelve
TRUE para cualquier nombre que incluya un subrayado (_)
Expresiones condicionales (3)
„ IS [NOT] NULL
„ Ejemplo
SELECT d FROM Department d WHERE d.name IS NULL

„ Explicación
„ Devuelve todos los departamentos para los que no se ha
especificado un valor para el atributo name
„ IS [NOT] NULL permite comprobar si un campo no colección
es NULL
„ IS [NOT] EMPTY
„ Ejemplo
SELECT d FROM Department d WHERE d.employees IS NOT EMPTY

„ Explicación
„ Devuelve todos los departamentos que tienen empleados
„ IS [NOT] EMPTY permite comprobar si un campo colección
es vacío
Expresiones condicionales (4)
„ [NOT] EXISTS
„ Ejemplo
SELECT d FROM Department d WHERE
EXISTS (SELECT e FROM Employee e WHERE
e.positionIdentifier = :posId AND e.department = d)

„ Explicación
„ Devuelve todos los departamentos que tengan al menos un
empleado desempeñando un determinado cargo
„ EXISTS devuelve TRUE si la subconsulta devuelve uno o más
resultados
Expresiones condicionales (5)
„ ALL y ANY/SOME
„ Ejemplo
SELECT e FROM Employee e WHERE e.salary >= ALL
(SELECT e.salary FROM Employee e)

„ Explicación
„ Devuelve todos los empleados que tengan el salario más alto
„ ALL
„ 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/SOME
„ TRUE 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
Expresiones condicionales (6)
„ Funciones de cadenas
„ CONCAT(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 primero
„ El 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
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úsculas
„ Ejemplo: obtener los empleados cuyo nombre empieza por F/f
y termina en O/o
SELECT e FROM Employee e WHERE UPPER(e.firstName) LIKE 'F%O'
Expresiones condicionales (y 8)
„ Funciones aritméticas
„ ABS(number)
„ Valor absoluto de un int, float o double
„ SQRT(number)
„ Raíz cuadrada
„ Recibe 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ón
„ Devuelve un int
„ Ejemplo: obtener todos los departamentos que tienen
empleados
SELECT d FROM Department d WHERE SIZE(d.employees) > 0
Funciones agregadas (1)
„ Son funciones que se pueden usar como resultado de
una consulta
„ Todas aceptan como argumento una expresión que
haga referencia a un atributo/propiedad no relación
„ Adicionalmente, COUNT acepta como argumento una
variable o una expresión que haga referencia a un
atributo/propiedad relación
„ AVG
„ Calcula la media
„ Recibe un argumento numérico y devuelve un Double
„ Ejemplo: calcular el salario medio de los empleados
SELECT AVG(e.salary) FROM Employee e
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;
}

}
Funciones agregadas (3)
„ COUNT
„ Devuelve el número (Long) de resultados
„ Ejemplo: calcular el número de departamentos
SELECT COUNT(d) FROM Department d

„ MAX/MIN
„ Calcula el valor máximo/mínimo
„ Requiere un argumento de tipo numérico, String, char o
fechas
„ Ejemplo: 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)
Funciones agregadas (y 4)
„ SUM
„ Calcula la suma
„ Devuelve Long si se aplica a enteros, Double si se aplica a
reales y BigInteger/BigDecimal cuando se aplica a
argumentos BigInteger/BigDecimal
„ Ejemplo: calcula el salario total de los empleados
SELECT SUM(e.salary) FROM Employee e
JOIN (1)
„ INNER JOIN implícito
„ Producto 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 WHERE
e.department = d AND e.positionIdentifier = :posId

„ NOTAS:
„ Equivalente a la consulta SQL
SELECT 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)
JOIN (2)
„ INNER JOIN explícito
„ Usa la cláusula [INNER] JOIN
„ INNER JOIN y JOIN son sinónimos
„ Ejemplo: el anterior
SELECT DISTINCT d
FROM 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)
JOIN (y 3)
„ Operador IN
„ Ejemplo: el anterior
SELECT DISTINCT d FROM Department d, IN(d.employees) e
WHERE e.positionIdentifier = :posId

„ Es otra manera de hacer un INNER JOIN


„ La sintaxis del INNER JOIN explícito es más parecida a la de
SQL
„ Otros ejemplos
„ Obtener todos los proyectos en los que trabaja un empleado
SELECT p FROM Project p JOIN p.employees e WHERE
e.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
GROUP BY, HAVING (1)
„ Similares a las cláusulas SQL
„ GROUP BY forma grupos en función de uno o varios
atributos/propiedades
„ HAVING (opcional) permite especificar una condición
(usando atributos/propiedades especificados en GROUP BY)
para filtrar los elementos que irán dentro de cada grupo
„ Ejemplo
SELECT e.department.departmentIdentifier, AVG(e.salary)
FROM Employee e
GROUP BY e.department.departmentIdentifier
HAVING e.department.departmentIdentifier IN ('tic', 'dc')

„ Explicación
„ Devuelve el salario medio para los departamentos tic y dc
GROUP BY, HAVING (y 2)
„ Otro ejemplo
SELECT 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 e
GROUP BY d.departmentIdentifier

„ Explicación
„ Devuelve estadísticas para cada departamento
„ Los 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
Borrados y actualizaciones en masa (1)
„ Para borrados o actualizaciones individuales
„ Borrado de una entidad
„ EntityManager.remove
„ Actualización de una entidad
„ Modificar 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 1
„ Localizar las entidades y eliminar/actualizar cada una
„ Ineficiente
„ Opción 2
„ Utilizar el soporte de EJB-QL para borrados y actualizaciones en
masa
„ Sentencias DELETE y UPDATE
Borrados y actualizaciones en masa (2)

„ Ejemplos
„ Eliminar todos los departamentos
DELETE FROM Department

„ También provoca que se eliminen los empleados


„ Recordar 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'
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})
Borrados y actualizaciones en masa (y 4)
„ Ejecución de sentencias UPDATE y DELETE
„ Mediante Query.executeUpdate()
„ Devuelve el número de entidades actualizadas/borradas
„ Ejemplo: en PSAFacadeEJB ...

public void removeAllDepartments() {


entityManager.createQuery("DELETE FROM Department").
executeUpdate();
}

public void removeAllProjects() {


entityManager.createQuery("DELETE FROM Project").
executeUpdate();
}
Notas sobre los ejemplos (1)
„ Subsistema AdvancedEJBTutorial
„ Muchas 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 ejemplos
„ En 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();

}
Notas sobre los ejemplos (2)
„ Subsistema AdvancedEJBTutorial
„ Simplificaciones (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 Department


„ Sin 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);
}
Notas sobre los ejemplos (3)
„ Subsistema AdvancedEJBTutorial
„ Simplificaciones (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)
Notas sobre los ejemplos (4)
„ MiniBank
„ No 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
grande
„ Es más lógico utilizar el patrón Page-by-Page Iterator para
recuperar las operaciones bancarias asociadas a una cuenta
(apartado 5.4)
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-Page
Iterator quedaría como

SELECT o FROM AccountOperation o


WHERE o.account.accountIdentifier = :accountIdentifier AND
o.date >= :startDate AND o.date <= :endDate
ORDER BY o.date

„ o (si antes se recupera la cuenta)

SELECT o FROM AccountOperation o


WHERE o.account = :account AND
o.date >= :startDate AND o.date <= :endDate
ORDER BY o.date

Você também pode gostar