Hibernate.orgCommunity Documentation
Você também pode expressar consultas no dialeto SQL nativo de seu banco de dados. Isto é bastante útil para usar recursos específicos do banco de dados, assim como dicas de consultas ou a palavra chave em Oracle CONNECT
. Ele também oferece um caminho de migração limpo de uma aplicação baseada em SQL/JDBC direta até o Hibernate.
O Hibernate3 permite que você especifique o SQL escrito à mão, incluindo procedimentos armazenados, para todas as operações de criar, atualizar, deletar e carregar.
A execução de consultas SQL nativa é controlada através da interface SQLQuery
que é obtido, chamando a Session.createSQLQuery()
. As seções abaixo descrevem como usar este API para consultas.
A consulta SQL mais básica é obter uma lista dos escalares (valores).
sess.createSQLQuery("SELECT * FROM CATS").list();
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();
Eles irão retornar uma matriz de Lista de Objeto (Object[]) com valores escalares para cada coluna na tabela CATS. O Hibernate usará o ResultSetMetadata para deduzir a ordem atual e tipos de valores escalares retornados.
Para evitar o uso do ResultSetMetadata
ou simplesmente para ser mais explícito em o quê é retornado, você poderá usar o addScalar()
:
sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME", Hibernate.STRING)
.addScalar("BIRTHDATE", Hibernate.DATE)
Esta consulta especificou:
A string da consulta SQL
as colunas e tipos para retornar
Este ainda irá retornar as matrizes de Objeto, mas desta vez ele não usará o ResultSetMetdata
, ao invés disso, obterá explicitamente a coluna de ID, NOME e DATA DE NASCIMENTO como respectivamente uma Longa, String e Curta a partir do conjunto de resultados adjacentes. Isto também significa que somente estas três colunas irão retornar, embora a consulta esteja utilizando *
e possa retornar mais do que três colunas listadas.
É possível deixar de fora o tipo de informação para todos ou alguns dos escalares.
sess.createSQLQuery("SELECT * FROM CATS")
.addScalar("ID", Hibernate.LONG)
.addScalar("NAME")
.addScalar("BIRTHDATE")
Esta é a mesma consulta de antes, mas desta vez, o ResultSetMetaData
é utilizado para decidir o tipo de NOME e DATA DE NASCIMENTO onde o tipo de ID é explicitamente especificado.
Como o java.sql.Types retornados do ResultSetMetadata é mapeado para os tipos Hibernate, ele é controlado pelo Dialeto. Se um tipo específico não é mapeado ou não resulta no tipo esperado, é possível padronizá-lo através de chamadas para registerHibernateType
no Dialeto.
As consultas acima foram todas sobre o retorno de valores escalares, basicamente retornando os valores "não processados" do conjunto de resultados. A seguir, mostramos como obter objetos de entidade da consulta sql nativa através do addEntity()
.
sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);
Esta consulta especificou:
A string da consulta SQL
A entidade retornada por uma consulta
Considerando que o Cat esteja mapeado como uma classe com colunas ID,NOME e DATA DE NASCIMENTO, as consultas acima irão devolver uma Lista onde cada elemento é uma entidade de Cat.
Se a entidade estiver mapeada com um muitos-para-um
para outra entidade, requer-se que devolva também este ao desempenhar a consulta nativa, senão ocorrerá um erro de banco de dados específico "coluna não encontrada". As colunas adicionais serão automaticamente retornadas ao usar a anotação, mas preferimos ser explícitos como no seguinte exemplo para umamuitos-para-um
para um Dog
:
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);
Isto irá permitir que o cat.getDog() funcione de forma apropriada
É possível realizar a recuperação adiantada no Dog
para evitar uma viagem extra por inicializar o proxy. Isto é feito através do método addJoin()
que permite que você se una à associação ou coleção.
sess.createSQLQuery("SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dog");
Neste exemplo, a devolução do Cat
terá sua propriedade dog
totalmente inicializada sem nenhuma viagem extra ao banco de dados. Note que adicionamos um nome alias ("cat") para poder especificar o caminho da propriedade alvo na união. É possível fazer a mesma união para coleções, ex.: se ao invés disso, o Cat
tivesse um-para-muitos para Dog
.
sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID")
.addEntity("cat", Cat.class)
.addJoin("cat.dogs");
Neste estágio, estamos chegando no limite do que é possível fazer com as consultas nativas sem começar a destacar as colunas sql para torná-las útil no Hibernate. Os problemas começam a surgir quando se retorna entidades múltiplas do mesmo tipo ou quando o padrão de nomes de alias/coluna não são suficientes.
Até aqui, os nomes de colunas do conjunto de resultados são considerados como sendo os mesmos que os nomes de colunas especificados no documento de mapeamento. Isto pode ser problemático para as consultas SQL, que une tabelas múltiplas, uma vez que os mesmos nomes de colunas podem aparecer em mais de uma tabela.
É necessário uma injeção de alias de coluna na seguinte consulta (a qual é bem provável que falhe):
sess.createSQLQuery("SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
A intenção para esta consulta é retornar duas instâncias Cat por linha: um cat e sua mãe. Isto irá falhar pois existe um conflito de nomes, são mapeados aos mesmos nomes de colunas e em alguns bancos de dados os aliases de colunas retornadas estarão, muito provavelmente, na forma de "c.ID", "c.NOME", etc., os quais não são iguais às colunas especificadas no mapeamento ("ID" e "NOME").
A seguinte forma não é vulnerável à duplicação do nome de coluna:
sess.createSQLQuery("SELECT {cat.*}, {mother.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class)
Esta consulta especificou:
a string da consulta SQL, com espaço reservado para Hibernate para injetar aliases de coluna.
as entidades retornadas pela consulta
A anotação {cat.*} e {mãe.*} usada acima, é um atalho para "todas as propriedades". De forma alternativa, você pode listar as colunas explicitamente, mas até neste caso nós deixamos o Hibernate injetar os aliases de coluna SQL para cada propriedade. O espaço reservado para um alias de coluna é simplesmente o nome de propriedade qualificado pelo alias de tabela. No seguinte exemplo, recuperamos os Cats e suas mães de uma tabela diferente (cat_log) para aquele declarado no metadado de mapeamentos. Note que podemos até usar os aliases de propriedade na cláusula where se quisermos.
String sql = "SELECT ID as {c.id}, NAME as {c.name}, " +
"BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " +
"FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID";
List loggedCats = sess.createSQLQuery(sql)
.addEntity("cat", Cat.class)
.addEntity("mother", Cat.class).list()
Para a maioria dos casos, necessita-se da injeção de alias acima. Para consultas relatadas aos mapeamentos mais complexos, como as propriedades compostas, discriminadores de herança, coleções, etc., você pode usar aliases específicos que permitem o Hibernate injetar os aliases apropriados.
As seguintes tabelas mostram as diferentes formas de usar uma injeção de alias. Por favor note que os nomes de alias no resultado são exemplos, cada alias terá um nome único e provavelmente diferente quando usado.
Tabela 17.1. Nomes de injeção de alias
Descrição | Sintáxe | Exemplo |
---|---|---|
Uma propriedade simples | {[aliasname].[propertyname] | A_NAME as {item.name} |
Uma propriedade composta | {[aliasname].[componentname].[propertyname]} | CURRENCY as {item.amount.currency}, VALUE as {item.amount.value} |
Discriminador de uma entidade | {[aliasname].class} | DISC as {item.class} |
Todas as propriedades de uma entidade | {[aliasname].*} | {item.*} |
Uma chave de coleção | {[aliasname].key} | ORGID as {coll.key} |
O id de uma coleção | {[aliasname].id} | EMPID as {coll.id} |
O elemento de uma coleção | {[aliasname].element} | XID as {coll.element} |
propriedade de elemento na coleção | {[aliasname].element.[propertyname]} | NAME as {coll.element.name} |
Todas as propriedades de elemento na coleção | {[aliasname].element.*} | {coll.element.*} |
Todas as propriedades da coleção | {[aliasname].*} | {coll.*} |
É possível aplicar um ResultTransformer para consultas sql nativas, permitindo que o retorno de entidades não gerenciadas.
sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))
Esta consulta especificou:
A string da consulta SQL
um transformador de resultado
A consulta acima irá devolver uma lista de CatDTO
que foi instanciada e injetada com valores dos comandos NAME e BIRTHDATE em suas propriedades correspondentes ou campos.
As consultas sql nativas, as quais consultam entidades mapeadas como parte de uma herança, devem incluir todas as propriedades na classe base e todas as suas subclasses.
Consultas sql Nativas suportam parâmetros posicionais assim como parâmetros nomeados:
Query query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like ?").addEntity(Cat.class);
List pusList = query.setString(0, "Pus%").list();
query = sess.createSQLQuery("SELECT * FROM CATS WHERE NAME like :name").addEntity(Cat.class);
List pusList = query.setString("name", "Pus%").list();
Consultas SQL Nomeadas podem ser definidas no documento de mapeamento e chamadas exatamente da mesma forma que uma consulta HQL nomeada. Neste caso nós não precisamos chamar o addEntity()
.
<sql-query name="persons">
<return alias="person" class="eg.Person"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex}
FROM PERSON person
WHERE person.NAME LIKE :namePattern
</sql-query
>
List people = sess.getNamedQuery("persons")
.setString("namePattern", namePattern)
.setMaxResults(50)
.list();
Os elementos <return-join>
e <load-collection>
são usados para unir associações e definir consultas que inicializam coleções,
<sql-query name="personsWith">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
address.STREET AS {address.street},
address.CITY AS {address.city},
address.STATE AS {address.state},
address.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS address
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query
>
Uma consulta SQL nomeada pode devolver um valor escalar. Você deve declarar um alias de coluna e um tipo Hibernate usando o elemento <return-scalar>
:
<sql-query name="mySqlQuery">
<return-scalar column="name" type="string"/>
<return-scalar column="age" type="long"/>
SELECT p.NAME AS name,
p.AGE AS age,
FROM PERSON p WHERE p.NAME LIKE 'Hiber%'
</sql-query
>
Você pode externar as informações de mapeamento de conjunto de resultado em um elemento <resultset>
tanto para reusá-los em diversas consultas nomeadas quanto através da API setResultSetMapping()
.
<resultset name="personAddress">
<return alias="person" class="eg.Person"/>
<return-join alias="address" property="person.mailingAddress"/>
</resultset>
<sql-query name="personsWith" resultset-ref="personAddress">
SELECT person.NAME AS {person.name},
person.AGE AS {person.age},
person.SEX AS {person.sex},
address.STREET AS {address.street},
address.CITY AS {address.city},
address.STATE AS {address.state},
address.ZIP AS {address.zip}
FROM PERSON person
JOIN ADDRESS address
ON person.ID = address.PERSON_ID AND address.TYPE='MAILING'
WHERE person.NAME LIKE :namePattern
</sql-query
>
Você pode também, como forma alternativa, usar a informação de mapeamento de conjunto de resultado em seus arquivos hbm em código de java.
List cats = sess.createSQLQuery(
"select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"
)
.setResultSetMapping("catAndKitten")
.list();
Com a <return-property>
você pode informar explicitamente, quais aliases de coluna utilizar, ao invés de usar a sintáxe {}
para deixar o Hibernate injetar seus próprios aliases. Por exemplo:
<sql-query name="mySqlQuery">
<return alias="person" class="eg.Person">
<return-property name="name" column="myName"/>
<return-property name="age" column="myAge"/>
<return-property name="sex" column="mySex"/>
</return>
SELECT person.NAME AS myName,
person.AGE AS myAge,
person.SEX AS mySex,
FROM PERSON person WHERE person.NAME LIKE :name
</sql-query>
<return-property>
também funciona com colunas múltiplas. Isto resolve a limitação com a sintáxe {}
que não pode permitir controle granulado fino de muitas propriedades de colunas múltiplas.
<sql-query name="organizationCurrentEmployments">
<return alias="emp" class="Employment">
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
<return-property name="endDate" column="myEndDate"/>
</return>
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
</sql-query
>
Observe que neste exemplo nós usamos <return-property>
combinado à síntáxe {}
para injeção. Permite que os usuários escolham como eles querem se referir à coluna e às propriedades.
Se seu mapeamento possuir um discriminador, você deve usar <return-discriminator>
para especificar a coluna do discriminador.
O Hibernate 3 apresenta o suporte para consultas através de procedimentos e funções armazenadas. A maior parte da documentação a seguir, é equivalente para ambos. Os procedimentos e funções armazenados devem devolver um conjunto de resultados como primeiros parâmetros externos para poder trabalhar com o Hibernate. Um exemplo disto é a função armazenada em Oracle 9 e versões posteriores como se segue:
CREATE OR REPLACE FUNCTION selectAllEmployments
RETURN SYS_REFCURSOR
AS
st_cursor SYS_REFCURSOR;
BEGIN
OPEN st_cursor FOR
SELECT EMPLOYEE, EMPLOYER,
STARTDATE, ENDDATE,
REGIONCODE, EID, VALUE, CURRENCY
FROM EMPLOYMENT;
RETURN st_cursor;
END;
Para usar esta consulta no Hibernate você vai precisar mapeá-lo através de uma consulta nomeada
<sql-query name="selectAllEmployees_SP" callable="true">
<return alias="emp" class="Employment">
<return-property name="employee" column="EMPLOYEE"/>
<return-property name="employer" column="EMPLOYER"/>
<return-property name="startDate" column="STARTDATE"/>
<return-property name="endDate" column="ENDDATE"/>
<return-property name="regionCode" column="REGIONCODE"/>
<return-property name="id" column="EID"/>
<return-property name="salary">
<return-column name="VALUE"/>
<return-column name="CURRENCY"/>
</return-property>
</return>
{ ? = call selectAllEmployments() }
</sql-query
>
Observe que os procedimentos armazenados somente devolvem escalares e entidades. O <return-join>
e <load-collection>
não são suportados.
Para usar procedimentos armazenados com Hibernate, os procedimentos e funções precisam seguir a mesma regra. Caso não sigam estas regras, não poderão ser usados com o Hibernate. Se você ainda desejar usar estes procedimentos, terá que executá-los através da session.connection()
. As regras são diferentes para cada banco de dados, uma vez que os fabricantes possuem procedimentos de semânticas/sintáxe armazenados.
Consultas de procedimento armazenado não podem ser paginados com o setFirstResult()/setMaxResults()
.
O formulário de chamada recomedado é o padrão SQL92: { ? = call functionName(<parameters>) }
or { ? = call procedureName(<parameters>}
. A sintáxe de chamada nativa não é suportada.
As seguintes regras se aplicam para Oracle:
A função deve retornar um conjunto de resultado. O primeiro parâmetro do procedimento deve ser um OUT
que retorne um conjunto de resultado. Isto é feito usando o tipo SYS_REFCURSOR
no Oracle 9 ou 10. No Oracle é necessário definir o tipo de REF CURSOR
, veja a documentação do Oracle.
Para servidores Sybase ou MS SQL aplicam-se as seguintes regras:
O procedimento deve retornar um conjunto de resultados. Observe que, como este servidor pode retornar múltiplos conjuntos de resultados e contas atualizadas, o Hibernate irá inteirar os resultados e pegar o primeiro resultado, o qual é o valor de retorno do conjunto de resultados. O resto será descartado.
Se você habilitar SET NOCOUNT ON
no seu procedimento, ele provavelmente será mais eficiente. Mas, isto não é obrigatório
Hibernate3 can use custom SQL for create, update, and delete operations. The SQL can be overridden at the statement level or inidividual column level. This section describes statement overrides. For columns, see Seção 5.7, “Coluna de expressöes de gravação e leitura”.
A persistência de classe e coleção no Hibernate já contém um conjunto de strings gerados por tempo de configuração (insertsql, deletesql, updatesql etc.). O mapeamento das tags <sql-insert>
, <sql-delete>
, e <sql-update>
sobrescreve essas strings:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert
>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update
>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete
>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class
>
O SQL é executado diretamente no seu banco de dados, então você pode usar qualquer linguagem que quiser. Isto com certeza reduzirá a portabilidade do seu mapeamento se você utilizar um SQL para um banco de dados específico.
Os procedimentos armazenados são suportados se a função callable
estiver ativada:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert callable="true"
>{call createPerson (?, ?)}</sql-insert>
<sql-delete callable="true"
>{? = call deletePerson (?)}</sql-delete>
<sql-update callable="true"
>{? = call updatePerson (?, ?)}</sql-update>
</class
>
A ordem de posições dos parâmetros são vitais, pois eles devem estar na mesma seqüência esperada pelo Hibernate.
Você pode ver a ordem esperada ativando o debug logging no nível org.hibernate.persister.entity
. Com este nível ativado, o Hibernate irá imprimir o SQL estático que foi usado para criar, atualizar, deletar, etc., entidades. Para ver a seqüência esperada, lembre-se de não incluir seu SQL padronizado no arquivo de mapeamento, pois ele irá sobrescrever o SQL estático gerado pelo Hibernate.
Os procedimentos armazenados são na maioria dos casos requeridos para retornar o número de linhas inseridas/atualizadas/deletadas, uma vez que o Hibernate possui algumas verificações em tempo de espera para o sucesso das instruções. O Hibernate sempre registra o primeiro parâmetro da instrução como um parâmetro de saída numérica para as operações CUD:
CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2) RETURN NUMBER IS BEGIN update PERSON set NAME = uname, where ID = uid; return SQL%ROWCOUNT; END updatePerson;
You can also declare your own SQL (or HQL) queries for entity loading. As with inserts, updates, and deletes, this can be done at the individual column level as described in Seção 5.7, “Coluna de expressöes de gravação e leitura” or at the statement level. Here is an example of a statement level override:
<sql-query name="person">
<return alias="pers" class="Person" lock-mode="upgrade"/>
SELECT NAME AS {pers.name}, ID AS {pers.id}
FROM PERSON
WHERE ID=?
FOR UPDATE
</sql-query
>
Este é apenas uma instrução de consulta nomeada, como discutido anteriormente. Você pode referenciar esta consulta nomeada em um mapeamento de classe:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<loader query-ref="person"/>
</class
>
Este também funciona com procedimentos armazenados.
Você pode também definir uma consulta para carregar uma coleção:
<set name="employments" inverse="true">
<key/>
<one-to-many class="Employment"/>
<loader query-ref="employments"/>
</set
>
<sql-query name="employments">
<load-collection alias="emp" role="Person.employments"/>
SELECT {emp.*}
FROM EMPLOYMENT emp
WHERE EMPLOYER = :id
ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query
>
Você pode até definir um carregador de entidade que carregue uma coleção por busca de união:
<sql-query name="person">
<return alias="pers" class="Person"/>
<return-join alias="emp" property="pers.employments"/>
SELECT NAME AS {pers.*}, {emp.*}
FROM PERSON pers
LEFT OUTER JOIN EMPLOYMENT emp
ON pers.ID = emp.PERSON_ID
WHERE ID=?
</sql-query
>
Copyright © 2004 Red Hat, Inc.