Mejoras en la Versión 2.1 de la Especificación EJB

En esta página:


Nuevas Funcionalidades en EJB QL

Nuevas Funciones

EJB 2.1 han añadido cinco nuevas funciones agregadas para la cláusula SELECT así como una nueva función MOD para la cláusula WHERE.

EJB 2.0 definió seis funciones que podemos usar en la cláusula WHERE de una sentencia EJB QL: CONCAT(), LENGTH(), LOCATE(), SUBSTRING(), ABS(), y SQRT(). EJB 2.1 presenta una nueva expresión modular funcional, MOD() toma dos valores enteros como parámetros, divide el primero por el segundo, y devuelve el resto. Por ejemplo:

MOD( 7, 3 ) = 1.

Sin embargo, un cambio mucho más importante es la definición de cinco nuevas funciones agregadas para su utilización en la cláusula SELECT de un sentencia EJB QL incluyendo: COUNT(), MAX( ), MIN( ), AVG( ) y SUM( ). Las funciones agregadas se utilizan con consultas que devuelven una colección de valores. Son bastante simples de entender y pueden ser útiles, especialmente la función COUNT(). Es importante entender que las funciones agregadas sólo se pueden utilizar con métodos select, no con métodos find. Los métodos find podrían devolver sólo unas referencias a un objeto EJB de entidad (local o remota) bien una sola referencia o una Collection de ellas.

Veamos cada una de las nuevas funciones agregadas en más detalle. Los ejemplo usan un EJB Customer que tiene una relación uno-a-uno con un EJB Address y una relación uno-a-muchos con un EJB Job. Un Job es un servicio que se compró. En otras palabras, no hay items de línea.

COUNT(identificador o expresión de path)

Esta función devuelve el número items en el resultset final de la consulta. Por ejemplo, la siguiente consulta proporciona un contador de todos los clientes que viven en Wisconsin:

SELECT COUNT( c )
FROM Customers AS c
WHERE c.address.state = 'WI'

La función COUNT() se puede utilizar con identificadores, en cuyo caso siempre cuenta entidades, o con expresiones de path, en cuyo caso cuenta campos CMR o CMP. Por ejemplo, esto proporciona una contador para todos los códigos postales que empiezan con los caracteres "554."

SELECT COUNT(c.address.zip)
FROM Customers AS c
WHERE c.address.zip LIKE '554%'

MAX(expresión de path), MIN(expresión de path)

MAX() y MIN() se pueden utilizar para encontrar los valores máximo y mínimo en una colección de cualquier tipo de campo CMP. No se puede utilizar con identificadores o paths que terminen en un ampo CMR. El tipo del resultado será el tipo del campo CMP que se está evaluando. Por ejemplo, esta consulta devuelve la mayor cantidad pagada por run Job:

SELECT MAX( job.totalSale )
FROM Customer AS c, IN (c.jobs) AS job

Las funciones MAX() y MIN() se puede aplicar a cualquier valor CMP válido, incluyendo tipos primitivos, strings, e incluso objetos serializables. Como podríamos recordar, un c ampo CMP puede ser un objeto Serializable que se convierte a un campo binario almacenado en una base de datos. El resultado de aplicar estas funciones a un objeto serializable no está especificado, porque no hay una forma estandarizada de determinar qué objeto se serializable es mayor que otro.

El resultado de aplicar MAX() y MIN() a un String depende del tipo de datos almacenados. Esta incertidumbre resulta de los problemas heredadados por las comparaciones de String.

AVG(numerico ), SUM(numerico)

Las funciones AVG() y SUM() sólo se pueden aplicar a expresiones de path que terminen en un tipo numérico primitivo (byte, short, int, long, double, float) o una de sus envolturas (Byte, Short, Integer, etc.)

Por ejemplo, la siguiente consulta usa la función SUM() para obtener la cantidad total que un cliente (especificado por el parámetro de entrada) ha pagado por todos sus Job:

SELECT SUM( job.totalSale )
FROM Customer AS c, IN( c.jobs ) AS job
WHERE c = ?1

DISTINCT, nulls, y argumentos vacíos

Podemos usar el operador DISTINCT para eliminar valores duplicados antes de que la consulte aplique cualquier función agregada. La siguiente consulta modifica una mostrad anteriormente. Emplea el operador DISTINCT para cambiar la salida de la consulta. En lugar de contar todos los códigos postales que correspondan con el patrón, cuenta el número de diferentes códigos postales que correspondan con el patrón de búsqueda:

SELECT DISTINCT COUNT(c.address.zip)
FROM Customers AS c
WHERE c.address.zip LIKE '554%'

En este caso, el operador DISTINCT primero elimina los códigos postales duplicados, por eso si varios clientes viven en un área con el mismo código postal, este código sólo se cuenta una vez. Luego la función COUNT() cuenta el número de códigos postales únicos. El resultado de esta consulta es totalmente diferente de la que hicimos anteriormente. Sin el operador DISTINCT, 100 clientes que vivieran en el mismo código postal incrementaría el resultado total en 100, en vez de en 1.

AVG(), SUM(), MAX(), y MIN() devuelven null cuando el argumento que evalúan es una colección vacía. Por ejemplo, la siguiente sentencia intenta obtener el precio medio pagado por un Cliente específico por todos sus trabajos:

SELECT AVG( job.totalSale )
FROM Customer AS c, IN( c.jobs ) AS job
WHERE c = ?1

Si cliente especificado por el parámetro de entrada no tiene trabajos, la colección sobre la que se supone que tiene operar la función AVG() estará vacía (no hay trabajos ni ventas totales), y la consulta devolverá null. Al contrario que el resto de las funciones agregadas COUNT() devuelve 0 (cero) cuando el argumento que evalúa es una colección vacía.

La nueva cláusula ORDER BY

La nueva cláusula ORDER BY no permite ordenar las colección resultante de una consulta de la forma que queramos; sin ella el ordena de la salida es impredecible. La razón porque la que EJB 2.0 no incluyó una cláusula ORDER BY era que tenía muchos problemas con la comparación de strings. En esta sección primero explicaremos este problema y luego veremos como usar la cláusula ORDER BY. El problema con los strings

De acuerdo con Sun, la cláusula ORDER BY no se incluyó en [ EJB 2.0 ] debido a los problemas del comportamiento de ordenación entre el lenguaje Java y las bases de datos. El ejemplo que daban tenía que ver con los valores string. La semántica de ordenación de strings en una base de datos podría ser diferente de la del lenguaje Java. Por ejemplo, Java ordena los tipos Strings de acuerdo a la secuencia de caracteres y el caso (minúsculas contra mayúsculas). Diferentes bases de datos podrían considerar o no el caso en su ordenación, y también podrían considerar o no los espacios en blanco al principio o al final. A la luz de estas posibles diferencias, parece ser que Sun tiene un argumento razonable, pero sólo para limitar la portabilidad de ORDER BY, no para eliminar totalmente su utilización. Los desarrolladores de EJB puede vivir sin la portabilidad perfecta de la cláusula ORDER BY, pero no pueden vivir sin la cláusula ORDER BY.

Los problemas con la comparación de strings no han cambiado desde entonces, y probablemente nunca lo harán. La diferencia es que en EJB 2.1 Sun ha decidido finalmente aceptar una portabilidad menos-que-perfecta de la comparación de strings para soportar la cláusula ORDER BY que tanto necesitamos. De hecho, EJB 2.1 también permite la comparación de strings usando los símbolos de mayor-que y menor-que ( > , < ) en sentencias WHERE.

Para que EJB QL mantenga su estatus como una abstracción para los lenguajes de consultas nativos (SQL-92, JDOQL, OQL, etc.) no puede dictar ordenación String, porque los lenguajes nativos podrían tener reglas de ordenación muy diferentes. De hecho, los vendedores de bases de datos relacionales difieren en la ordenación de String, lo que hace casi imposible estandarizar la ordenación incluso para las bases de datos compatibles con SQL. Utilizar la Cláusula ORDER BY

Ahora que ya tenemos una apreciación de los problemas asociados con las comparaciones de strings, estamos más preparados para utilizar la cláusula ORDER BY.

La cláusula ORDER BY será familiar para la gente con experiencia en SQL porque la semántica es básicamente la misma. Como ejemplo, podemos construir una simple consulta que use la cláusula ORDER BY para que devuelve una lista alfabética de todos los Customers:

SELECT OBJECT( c )
FROM Customers AS c
ORDER BY c.lastName

Esto podría devolver una Collection de EJB's Customer EJB's en el siguiente orden (asumimos que también se imprime el nombre en la salida):

Aares, John
Astro, Linda
Brooks, Hank
.
.
Xerces, Karen
Zastro, William

Podemos usar la cláusula ORDER BY con o sin la cláusula WHERE. Por ejemplo, podemos refinar la consulta anterior para que sólo liste los clientes que residen en Boston, Massachusetts:

SELECT OBJECT( c )
FROM Customers AS c
WHERE c.address.city = 'Boston' AND c.address.state = 'MA'
ORDER BY c.lastName

Podemos especificar el orden de listado como ascendente o descendente usando las palabras clave ASC y DESC. El orden por defecto es siempre ascendente, lo que significa que los valores más pequeños se listan primero. Podemos listar los EJB's Customer en orden descendente especificando DESC, como en este ejemplo:

SELECT OBJECT( c )
FROM Customers AS c
ORDER BY c.lastName DESC

que resulta en:

Zastro, William
Xerces, Karen
.
.
Brooks, Hank
Astro, Linda
Aares, John

Podemos especificar la ordenación por varios campos. Por ejemplo, podemos ordenar los EJB Customer por lastName de forma ascendente y por firstName en orden descendente de esta forma:

SELECT OBJECT( c )
FROM Customers AS c
ORDER BY c.lastName ASC, c.firstName DESC

Si tuviéramos cinco clientes con el lastName de "Brooks", esta consulta ordenaría el resultado de esta forma:

...
Bond, James
Brooks, William
Brooks, Henry
Brooks, Hank
Brooks, Ben
Brooks, Andy
Brundage, Avery
...

Los campos usado en la cláusula ORDER BY deben ser campos CMP, no pueden identificadores de entidad o campos CMR. Además, debemos ser cuidados en los campos CMP que especificamos en la cláusula ORDER BY. Si la consulta selecciona una colección de entidades, la cláusula ORDER BY sólo se puede utilizar con campos CMP del tipo entidad seleccionado. Por ejemplo, la siguiente consulta es ilegal, porque el campo CMP usando en la cláusula ORDER BY no es un campo del tipo de entidad seleccionado:

SELECT OBJECT( c )
FROM Customer AS c
ORDER BY c.address.city

Como el campo CMP city no es un campo CMP directo del EJB Customer, no podemos usarlo en la cláusula ORDER BY. Los únicos campos CMP que se pueden usar en la cláusula ORDER BY son aquellos que son campos CMP directos del tipo de entidad que se está seleccionando. Esto es un restricción poco razonable.

Se aplica una restricción similar a los resultados CMP. El campo CMP usando en la cláusula ORDER BY debe ser el mismo que el campo CMP identificado en la cláusula SELECT. Por ejemplo, la siguiente consulta es ilegal, porque el CMP identificado en la cláusula SELECT es diferente del utilizado en la cláusula ORDER BY:

SELECT c.address.city
FROM Customer AS c
ORDER BY c.address.state

En la consulta anterior, queríamos listar todas las ciudades, ordenadas por estado. Desafortunadamente, esto es ilegal. No podemos ordenamos por el campo CMP state estado si estamos seleccionando usando el campo CMP city.

Problemas con EJB QL

Aunque las mejora realizadas a EJB QL (especialmente la adicción de la cláusula ORDER BY y las nuevas funciones) mejoran nuestra apreciación, quedan varias características que se deberían añadir a este lenguaje de consulta y al menos una que se debería quedar obsoleta:

  • Dejar obsoleto el uso del operador OBJECT( ). Ya no es necesario simplemente embadurna la sintaxis.
  • Añadir soporte para java.util.Date y java.util.Calendar como tipos para identificadores y parámetros. También hay la necesidad de soportar literales de fechas.
  • Añadir soporte para más funciones como UPPER( ), LOWER( ), y CAST( ), y funciones de fechas (DOW(), MONTH(), etc.).
  • Añadir soporte para expresiones múltiples en la sentencia SELECT, para que una consulta EJB QL pueda devolver más de una columna de datos en el resulset.
  • Añadir soporte para las cláusulas GROUP BY y HAVING.
  • Añadir soporte para sub-consultas.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR