SQL Nativo
Puedes también expresar consultas en el dialecto SQL nativo de tu base de datos. Esto es útil si quieres
utilizar aspectos específicos de base de datos tal como consejos (hints) de consulta o la palabra clave
CONNECT en Oracle. Provee además una clara ruta de migración desde una aplicación
basada en SQL/JDBC directo a Hibernate.
Hibernate3 te permite especificar SQL escrito a mano (incluyendo procedimientos almacenados) para todas
las operaciones de creación, actualización, borrado y carga.
Creando una Query de SQL nativo
Las consultas SQL se controlan por medio de la interface SQLQuery, que se obtiene
llamando a Session.createSQLQuery().
Esta consulta especificada:
la cadena de consulta SQL, con un lugar para que Hibernate inyecte los alias de columnas
la entidad devuelta por la consulta, y sus alias de tablas SQL
El método addEntity() asocia alias de tablas SQL con clases de entidad,
y determina la forma del conjunto resultado de la consulta.
El método addJoin() puede ser usado para cargar asociaciones a otras entidades y
colecciones.
Una consulta SQL nativa podría devolver un valor escalar simple o una combinación de escalares y entidades.
Alias y referencias de propiedad
La notación {cat.*} usada arriba es un atajo para "todas las propiedades".
Alternativamente, puedes listar las columnas explícitamente, pero incluso en este caso dejamos
que Hibernate inyecte los alias de columnas SQL para cada propiedad. El lugar para un alias de columna
es sólo el nombre de propiedad cualificado por el alias de la tabla. En el siguiente ejemplo,
recuperamos Cats de una tabla diferente (cat_log) a una
declarada en los metadatos de mapeo. Nota que podríamos incluso usar los alias de propiedad en la
cláusula where si quisieramos.
La sintáxis {} no es requerida para consultas con nombre.
Ver
Nota: si listas cada propiedad explícitamente, ¡debes incluir todas las
propiedades de la clase y sus subclases!
Consultas SQL con nombre
Las consultas SQL con nombre pueden definirse en el documento de mapeo y llamadas exactamente
en la misma forma en que a una consulta HQL con nombre. En este caso, no
necesitamos llamar a addEntity().
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
]]>
Los elementos <return-join> y <load-collection>
se usan para unir asociaciones y definir consultas que inicialicen colecciones, respectivamente.
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
adddress.STREET AS {address.street},
adddress.CITY AS {address.city},
adddress.STATE AS {address.state},
adddress.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS adddress
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
]]>
Una consulta SQL con nombre puede devolver un valor escalar. Debes especificar el alias de columna y
tipo Hibernate usando el elementp <return-scalar>:
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
]]>
Usando return-property para especificar explícitamente nombres de columna/alias
Con <return-property> puedes decirle explícitamente a Hibernate qué
alias de columna usar, en vez de usar la sintáxis {} para dejar que Hibernate
inyecte sus propios alias.
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
]]>
<return-property> también trabaja con múltiples columnas. Esto resuelve una
limitación de la sintáxis {}, la cual no puede permitir un control fino de propiedades
multi-columna.
SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY
FROM EMPLOYMENT
WHERE EMPLOYER = :id AND ENDDATE IS NULL
ORDER BY STARTDATE ASC
]]>
Nota que en este ejemplo hemos usado <return-property> en combinación con
la sintáxis {} para inyección, permitiendo a los usuarios elejir cómo quieren
referirse a las columnas y propiedades.
Si tu mapeo tiene un discriminador debes usar <return-discriminator>
para especificar la columna discriminadora.
Usando procedimientos almacenados para consultar
Hibernate3 introduce soporte para consultas vía procedimientos almacenados. Los procedimientos
almacenados deben devolver un conjunto resultado como el primer parámetro de salida para ser
capaces de funcionar con Hibernate. Un ejemplo de uno procedimiento almacenado en Oracle 9
o superior es así:
Para usar esta consulta en Hibernate necesitas mapearla por medio de una consulta con nombre.
{ ? = call selectAllEmployments() }
]]>
Nota que los procedimientos almacenados sólo devuelven escalares y entidades.
No están soportados <return-join> y <load-collection>.
Reglas/limitaciones para usar procedimientos almacenados
Para usar procedimientos almacenados con Hibernate los procedimientos tienen que seguir algunas reglas.
Si no siguen esas reglas no son usables por Hibernate. Si aún quisieras usar estos procedimientos
tendrías que ejecutarlos por medio de session.connection(). Las reglas son
diferentes para cada base de datos, ya que los vendedores de base de datos tienen diferentes
semánticas/sintáxis de procedimientos almacenados.
Las consultas de procedimientos almacenados no pueden ser paginadas con
setFirstResult()/setMaxResults().
Para Oracle se aplican las siguientes reglas:
El procedimiento debe devolver un conjunto resultado. Esto se hace devolviendo un
SYS_REFCURSOR en Oracle 9 o 10. En Oracle necesitas definir un
tipo REF CURSOR.
La forma recomendada es { ? = call procName(<parameters>) } o
{ ? = call procName } (esto es más una regla de Oracle que una regla de Hibernate).
Para Sybase o MS SQL server se aplican las siguientes reglas:
El procedimiento debe devolver un conjunto resultado. Nota que ya que estos servidores pueden
y devolverán múltiples conjuntos resultados y cuentas de actualización, Hibernate iterará
los resultados y tomará el primer resultado que sea un conjunto resultado como su valor
a devolver. Todo lo demás será descartado.
Si habilitas SET NOCOUNT ON en tu procedimiento será probablemente más
eficiente, pero esto no es un requerimiento.
SQL personalizado para crear, actualizar y borrar
Hibernate3 puede usar sentencias SQL personalizadas para las operaciones de
crear, actualizar y borrar. Los persistidores de clases y colecciones en Hibernate
ya contienen un conjunto de cadenas generadas en tiempo de configuración (insertsql,
deletesql, updatesql, etc.). Las etiquetas de mapeo <sql-insert>,
<sql-delete>, y <sql-update> sobrescriben
estas cadenas:
INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )
UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?
DELETE FROM PERSON WHERE ID=?
]]>
El SQL se ejecuta directamente en tu base de datos, de modo que eres libre de usar cualquier
dialecto que quieras. Esto reducirá, por supuesto, la portabilidad de tu mapeo si usas SQL
específico de la base de datos.
Los procedimientos almacenados son soportados si está establecido el atributo
callable:
{call createPerson (?, ?)}
{? = call deletePerson (?)}
{? = call updatePerson (?, ?)}
]]>
El orden de los parámetros posicionales son actualmente vitales, ya que deben estar en la
misma secuencia en que las espera Hibernate.
Puedes ver el orden esperado habilitando el registro de depuración para el nivel
org.hibernate.persister.entity. Con este nivel habilitado, Hibernate
imprimirá el SQL estático que se usa para crear, actualizar, borrar, etc. las entidades.
(Para ver la secuencia esperada, recuerda no incluir tu SQL personalizado en los ficheros
de mapeo ya que sobrescribirán el sql estático generado por Hibernate.)
Los procedimientos almacenados son, en la mayoría de los casos (léase, mejor hacerlo que no hacerlo),
obligados a devolver el número de filas insertadas/actualizadas/borradas, ya que Hibernate tiene algunas
comprobaciones en tiempo de ejecución del éxito de la sentencia. Hibernate siempre registra el primer
parámetro de la sentencia como un parámetro de salida numérico para las operaciones CUD:
SQL personalizado para carga
Puedes también declarar tu propias consultas SQL (o HQL) para cargar entidades:
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
]]>
Esto es sólo una declaración de consulta con nombrem como se ha discutido anteriormente.
Puedes hacer referencia a esta consulta con nombre en un mapeo de clase:
]]>
Esto incluso funciona con procedimientos almacenados.
Puedes incluso definit una consulta para la carga de colecciones:
]]>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
]]>
Podrías incluso definir un cargador de entidades que cargue una colección por
recuperación por unión (join fetching):
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
]]>