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 <literal>Query</literal> 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=? ]]>