Hibernate.org Community Documentation

- Anterior
- Siguiente

## Capítulo 18. SQL Nativo

También puede expresar sus consultas en el dialecto SQL nativo de su base de datos. Esto es útil si quiere utilizar las características especificas de la base de datos tales como hints de consulta o la palabra clave `CONNECT` en Oracle. También proporciona una ruta de migración limpia desde una aplicación basada en SQL/JDBC a Hibernate.

Hibernate3 le permite especificar SQL escrito a mano, incluyendo procedimientos almacenados para todas las operaciones create, update, delete y load.

## 18.1. Uso de una `SQLQuery`

La ejecución de consultas SQL nativas se controla por medio de la interfaz `SQLQuery`, la cual se obtiene llamando a `Session.createSQLQuery()`. Las siguientes secciones describen cómo utilizar esta API para consultas.

### 18.1.1. Consultas escalares

La consulta SQL más básica es para obtener a una lista de escalares (valores).

    sess.createSQLQuery("SELECT * FROM CATS").list();

    sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").list();

Estas retornarán una lista de objetos arrays (Object[]) con valores escalares para cada columna en la tabla CATS. Hibernate utilizará ResultSetMetadata para deducir el orden real y los tipos de los valores escalares retornados.

Para evitar los gastos generales de la utilización de `ResultSetMetadata` o simplemente para ser más explícito en lo que se devuelve se puede utilizar `addScalar()`:

    sess.createSQLQuery("SELECT * FROM CATS")

     .addScalar("ID", Hibernate.LONG)

     .addScalar("NAME", Hibernate.STRING)

     .addScalar("BIRTHDATE", Hibernate.DATE)

Se especifica esta consulta:

- la cadena de consulta SQL

- las columnas y tipos que se devuelven

Esto retornará objetos arrays, pero no utilizará `ResultSetMetdata` sino que obtendrá explícitamente las columnas de IDENTIFICACION, NOMBRE y FECHA DE NACIMIENTO respectivamente como Larga, Cadena y Corta del grupo de resultados subyacente. Esto también significa que sólamente estas tres columnas serán retornadass aunque la consulta este utilizando `*` y pueda devolver más de las tres columnas enumeradas.

Es posible dejar afuera la información de tipo para todos o algunos de los escalares.

    sess.createSQLQuery("SELECT * FROM CATS")

     .addScalar("ID", Hibernate.LONG)

     .addScalar("NAME")

     .addScalar("BIRTHDATE")

Esto es esencialmente la misma consulta que antes, pero ahora se utiliza `ResultSetMetaData` para determinar el tipo de NOMBRE y FECHA DE NACIMIENTO, mientras que el tipo de IDENTIFICACION se especifica explícitamente.

El dialecto controla la manera en que los java.sql.Types retornados de ResultSetMetaData se mapean a los tipos de Hibernate. Si un tipo en especial no se encuentra mapeado o no resulta en el tipo esperado es posible personalizarlo por medio de llamadas a `registerHibernateType` en el dialecto.

### 18.1.2. Consultas de entidades

Todas las consultas anteriores eran sobre los valores escalraes devueltos, basicamente devolviendo los valores "crudos" desde el grupo resultado. Lo siguiente muestra como obtener los objetos entidades desde una consulta sql nativa por medio de `addEntity()`.

    sess.createSQLQuery("SELECT * FROM CATS").addEntity(Cat.class);

    sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE FROM CATS").addEntity(Cat.class);

Se especifica esta consulta:

- la cadena de consulta SQL

- la entidad devuelta por la consulta

Asumiendo que Cat es mapeado como una clase con las columnas IDENTIFICACION, NOMBRE y FECHA DE NACIMIENTO las consultas anteriores devolverán una Lista en donde cada elemento es una entidad Cat.

Si la entidad es mapeada con una `many-to-one` a otra entidad tambien se necesita que devuelva esto cuando realice una consulta nativa, de otra manera, aparecerá un error "no se encontró la columna" específico a la base de datos. Se devolverán automáticamente las columnas adicionales cuando se utiliza la anotación *, pero preferimos ser tan explícitos así como lo muestra el siguiente ejemplo para una `many-to-one` a un `Dog`:

    sess.createSQLQuery("SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS").addEntity(Cat.class);

Esto permitirá que cat.getDog() funcione apropiadamente.

### 18.1.3. Manejo de asociaciones y colecciones

Es posible unir de manera temprana en el `Dog` para evitar el posible viaje de ida y vuelta para iniciar el proxy. Esto se hace por medio del método `addJoin()`, el cual le permite unirse en una asociación o colección.

    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");

En este ejemplo los `Cat`s retornados tendrán su propiedad `dog` completamente iniciada sin ningún viaje extra de ida y vuelta a la base de datos. Observe que agregó un nombre alias ("cat") para poder especificar la ruta de la propiedad de destino de la unión. Es posible hacer la misma unión temprana para colecciones, por ejemplo, si el `Cat` tuviese en lugar un `Dog` uno-a-muchos.

    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");

En este punto estamos alcanzando los límites de lo que es posible con las consultas nativas sin empezar a mejorar las consultas sql para hacerlas utilizables en Hibernate. Los problemas empiezan a surgir cuando las entidades múltiples retornadas son del mismo tipo o cuando no son suficientes los nombres de las columnas/alias predeterminados.

### 18.1.4. Devolución de entidades múltiples

Hasta ahora se ha asumido que los nombres de las columnas del grupo de resultados son las mismas que los nombres de columnas especificados en el documento de mapeo. Esto puede llegar a ser problemático para las consultas SQL que unen múltiples tablas ya que los mismos nombres de columnas pueden aparecer en más de una tabla.

Se necesita una inyección de alias en las columnas en la siguiente consulta (que con mucha probabilidad fallará):

    sess.createSQLQuery("SELECT c.*, m.*  FROM CATS c, CATS m WHERE c.MOTHER_ID = m.ID")

     .addEntity("cat", Cat.class)

     .addEntity("mother", Cat.class)

La intención de esta consulta es retornar dos instancias Cat por fila: un gato y su mamá. Sin embargo, esto fallará debido a que hay un conflicto de nombres;las instancias se encuentran mapeadas a los mismos nombres de columna. También en algunas bases de datos los alias de las columnas retornadas serán con mucha probabilidad de la forma "c.IDENTIFICACION", "c.NOMBRE", etc, los cuales no son iguales a las columnas especificadas en los mapeos ("IDENTIFICACION" y "NOMBRE").

La siguiente forma no es vulnerable a la duplicación de nombres de columnas:

    sess.createSQLQuery("SELECT {cat.*}, {m.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = m.ID")

     .addEntity("cat", Cat.class)

     .addEntity("mother", Cat.class)

Se especifica esta consulta:

- la cadena de consultas SQL, con un espacio reservado para que Hibernate inserte alias de columnas

- las entidades devueltas por la consulta

La anotación {cat.*} y {mother.*} que se utilizó anteriormente es la abreviatura para "todas las propiedades". Opcionalmente puede enumerar las columnas explícitamente, pero inclusive en este caso Hibernate inyecta los alias de columnas SQL para cada propiedad. El espacio para un alias de columna es sólamente el nombre calificado de la propiedad del alias de la tabla. En el siguiente ejemplo, recuperamos Cats y sus madres desde una tabla diferente (cat_log) a la declarada en los meta datos de mapeo. Inclusive puede utilizar los alias de propiedad en la cláusula where.

    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()

#### 18.1.4.1. Referencias de propiedad y alias

Para la mayoría de los casos, se necesita la inyección del alias anterior. Para las consultas relacionadas con mapeos más complejos como propiedades compuestas, discriminadores de herencia, colecciones, etc, existen alias especificos a utilizar para permitir que Hibernate inyecte los alias apropiados.

La siguiente tabla muestra las diferentes maneras de utilizar la inyección de alias. Note que los nombres alias en el resultado son simplemente ejemplos; cada alias tendrá un nombre único y probablemente diferente cuando se utilice.

Tabla 18.1. Nombres con inyección alias

Descripción | Sintaxis | Ejemplo
--- | --- | ---
Una propiedad simple | `{[aliasname].[propertyname]` | `A_NAME as {item.name}`
Una propiedad compuesta | `{[aliasname].[componentname].[propertyname]}` | `CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}`
Discriminador de una entidad | `{[aliasname].class}` | `DISC as {item.class}`
Todas las propiedades de una entidad | `{[aliasname].*}` | `{item.*}`
Una clave de colección | `{[aliasname].key}` | `ORGID as {coll.key}`
La identificación -id- de una colección | `{[aliasname].id}` | `EMPID as {coll.id}`
El elemento de una colección | `{[aliasname].element}` | `XID as {coll.element}`
propiedad del elemento en la colección | `{[aliasname].element.[propertyname]}` | `NAME as {coll.element.name}`
Todas las propiedades del elemeto en la colección | `{[aliasname].element.*}` | `{coll.element.*}`
Todas las propiedades de la colección | `{[aliasname].*}` | `{coll.*}`

### 18.1.5. Devolución de entidades no-administradas

Es posible aplicar un ResultTransformer para consultas SQL nativas, permitiéndole retornar entidades no-administradas.

    sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")

            .setResultTransformer(Transformers.aliasToBean(CatDTO.class))

Se especifica esta consulta:

- la cadena de consulta SQL

- un transformador de resultado

La consulta anterior devolverá una lista de `CatDTO` a la cual se ha instanciado e inyectado los valores de NOMBRE y FECHA DE NACIMIENTO en su propiedades o campos correspondientes.

### 18.1.6. Manejo de herencias

Las consultas SQL nativas, las cuales consultan por entidades que son mapeadas como parte de una herencia tienen que incluir todas las propiedades para la clase base y todas sus subclases.

### 18.1.7. Parámetros

Las consultas SQL nativas soportan parámetros nombrados así como posicionales:

    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();

## 18.2. Consultas SQL nombradas

Named SQL queries can also be defined in the mapping document and called in exactly the same way as a named HQL query (see Sección 11.4.1.7, “Externalización de consultas con nombre”). In this case, you do not need to call `addEntity()`.

Ejemplo 18.1. Named sql query using the <sql-query> maping element

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

Ejemplo 18.2. Execution of a named query

    List people = sess.getNamedQuery("persons")

        .setString("namePattern", namePattern)

        .setMaxResults(50)

        .list();

El elemento `<return-join>` se utiliza para unir asociaciones y el elemento `<load-collection>` se usa para definir consultas, las cuales dan inicio a colecciones.

Ejemplo 18.3. Named sql query with association

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

Una consulta SQL nombrada puede devolver un valor escalar. Tiene que declarar el alias de la columna y el tipo de Hibernate utilizando el elemento `<return-scalar>`:

Ejemplo 18.4. Named query returning a 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>

Puede externalizar el grupo de resultados mapeando información en un elemento `<resultset>`, el cual le permitirá reutilizarlos a través de consultas nombradas o por medio de la API `setResultSetMapping()`.

Ejemplo 18.5. <resultset> mapping used to externalize mapping information

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

Opcionalmente, puede utilizar el grupo de resultados mapeando la información en sus archivos hbm directamente en código java.

Ejemplo 18.6. Programmatically specifying the result mapping information

    List cats = sess.createSQLQuery(

            "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id"

        )

        .setResultSetMapping("catAndKitten")

        .list();

So far we have only looked at externalizing SQL queries using Hibernate mapping files. The same concept is also available with anntations and is called named native queries. You can use `@NamedNativeQuery` (`@NamedNativeQueries`) in conjunction with `@SqlResultSetMapping` (`@SqlResultSetMappings`). Like `@NamedQuery`, `@NamedNativeQuery` and `@SqlResultSetMapping` can be defined at class level, but their scope is global to the application. Lets look at a view examples.

Ejemplo 18.7, “Named SQL query using @NamedNativeQuery together with @SqlResultSetMapping” shows how a `resultSetMapping` parameter is defined in `@NamedNativeQuery`. It represents the name of a defined `@SqlResultSetMapping`. The resultset mapping declares the entities retrieved by this native query. Each field of the entity is bound to an SQL alias (or column name). All fields of the entity including the ones of subclasses and the foreign key columns of related entities have to be present in the SQL query. Field definitions are optional provided that they map to the same column name as the one declared on the class property. In the example 2 entities, `Night` and `Area`, are returned and each property is declared and associated to a column name, actually the column name retrieved by the query.

In Ejemplo 18.8, “Implicit result set mapping” the result set mapping is implicit. We only describe the entity class of the result set mapping. The property / column mappings is done using the entity mapping values. In this case the model property is bound to the model_txt column.

Finally, if the association to a related entity involve a composite primary key, a `@FieldResult` element should be used for each foreign key column. The `@FieldResult` name is composed of the property name for the relationship, followed by a dot ("."), followed by the name or the field or property of the primary key. This can be seen in Ejemplo 18.9, “Using dot notation in @FieldResult for specifying associations ”.

Ejemplo 18.7. Named SQL query using `@NamedNativeQuery` together with `@SqlResultSetMapping`

    @NamedNativeQuery(name="night&area", query="select night.id nid, night.night_duration, "

        + " night.night_date, area.id aid, night.area_id, area.name "

        + "from Night night, Area area where night.area_id = area.id",

                      resultSetMapping="joinMapping")

    @SqlResultSetMapping(name="joinMapping", entities={

        @EntityResult(entityClass=Night.class, fields = {

            @FieldResult(name="id", column="nid"),

            @FieldResult(name="duration", column="night_duration"),

            @FieldResult(name="date", column="night_date"),

            @FieldResult(name="area", column="area_id"),

            discriminatorColumn="disc"

        }),

        @EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {

            @FieldResult(name="id", column="aid"),

            @FieldResult(name="name", column="name")

        })

        }

    )

Ejemplo 18.8. Implicit result set mapping

    @Entity

    @SqlResultSetMapping(name="implicit",

                         entities=@EntityResult(entityClass=SpaceShip.class))

    @NamedNativeQuery(name="implicitSample",

                      query="select * from SpaceShip",

                      resultSetMapping="implicit")

    public class SpaceShip {

        private String name;

        private String model;

        private double speed;

        @Id

        public String getName() {

            return name;

        }

        public void setName(String name) {

            this.name = name;

        }

        @Column(name="model_txt")

        public String getModel() {

            return model;

        }

        public void setModel(String model) {

            this.model = model;

        }

        public double getSpeed() {

            return speed;

        }

        public void setSpeed(double speed) {

            this.speed = speed;

        }

    }

Ejemplo 18.9. Using dot notation in @FieldResult for specifying associations

    @Entity

    @SqlResultSetMapping(name="compositekey",

            entities=@EntityResult(entityClass=SpaceShip.class,

                fields = {

                        @FieldResult(name="name", column = "name"),

                        @FieldResult(name="model", column = "model"),

                        @FieldResult(name="speed", column = "speed"),

                        @FieldResult(name="captain.firstname", column = "firstn"),

                        @FieldResult(name="captain.lastname", column = "lastn"),

                        @FieldResult(name="dimensions.length", column = "length"),

                        @FieldResult(name="dimensions.width", column = "width")

                        }),

            columns = { @ColumnResult(name = "surface"),

                        @ColumnResult(name = "volume") } )

    @NamedNativeQuery(name="compositekey",

        query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip",

        resultSetMapping="compositekey")

    } )

    public class SpaceShip {

        private String name;

        private String model;

        private double speed;

        private Captain captain;

        private Dimensions dimensions;

        @Id

        public String getName() {

            return name;

        }

        public void setName(String name) {

            this.name = name;

        }

        @ManyToOne(fetch= FetchType.LAZY)

        @JoinColumns( {

                @JoinColumn(name="fname", referencedColumnName = "firstname"),

                @JoinColumn(name="lname", referencedColumnName = "lastname")

                } )

        public Captain getCaptain() {

            return captain;

        }

        public void setCaptain(Captain captain) {

            this.captain = captain;

        }

        public String getModel() {

            return model;

        }

        public void setModel(String model) {

            this.model = model;

        }

        public double getSpeed() {

            return speed;

        }

        public void setSpeed(double speed) {

            this.speed = speed;

        }

        public Dimensions getDimensions() {

            return dimensions;

        }

        public void setDimensions(Dimensions dimensions) {

            this.dimensions = dimensions;

        }

    }

    @Entity

    @IdClass(Identity.class)

    public class Captain implements Serializable {

        private String firstname;

        private String lastname;

        @Id

        public String getFirstname() {

            return firstname;

        }

        public void setFirstname(String firstname) {

            this.firstname = firstname;

        }

        @Id

        public String getLastname() {

            return lastname;

        }

        public void setLastname(String lastname) {

            this.lastname = lastname;

        }

    }

## Sugerencia

If you retrieve a single entity using the default mapping, you can specify the `resultClass` attribute instead of `resultSetMapping`:

    @NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultClass=SpaceShip.class)

    public class SpaceShip {

In some of your native queries, you'll have to return scalar values, for example when building report queries. You can map them in the `@SqlResultsetMapping` through `@ColumnResult`. You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though).

Ejemplo 18.10. Scalar values via `@ColumnResult`

    @SqlResultSetMapping(name="scalar", columns=@ColumnResult(name="dimension"))

    @NamedNativeQuery(name="scalar", query="select length*width as dimension from SpaceShip", resultSetMapping="scalar")

An other query hint specific to native queries has been introduced: `org.hibernate.callable` which can be true or false depending on whether the query is a stored procedure or not.

### 18.2.1. Utilización de la propiedad return para especificar explícitamente los nombres de columnas/alias

Con `<return-property>` usted puede decirle a Hibernate explícitamente qué alias de columnas se deben utilizar, en vez de utilizar la sintaxis `{}` para dejar que Hibernate inyecte sus propios alias. Por ejemplo:

    <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>` también funciona con columnas múltiples. Esto resuelve una limitación con la sintaxis `{}`, la cual no puede permitir control muy detallado de propiedades multi-columnas.

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

En este ejemplo utilizamos `<return-property>` en combinación junto con la sintaxis `{}` para inyección. Esto le permite a los usuarios escoger cómo quieren referirse a la columna y a las propiedades.

Si su mapeo tiene un discriminador usted tiene que utilizar `<return-discriminator>` para especificar la columna discriminadora.

### 18.2.2. Utilización de procedimientos para consultas

Hibernate 3 brinda soporte para consultas por medio de procedimientos almacenados y funciones. La mayoría de la siguiente documentación es igual para ambos. La función/procedimiento almacenado tiene que retornar un grupo de resultados como el primer parámetro de salida para poder trabajar con Hibernate. A continuación hay un ejemplo de tal función almacenada en Oracle 9 y posteriores:

    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 utilizar esta consulta en Hibernate u.d necesita mapearla por medio de una consulta nombrada.

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

Los procedimientos almacenados actualmente sólo retornan escalares y entidades. No se soporta `<return-join>` ni `<load-collection>` .

#### 18.2.2.1. Reglas/limitaciones para utilizar procedimientos almacenados

Para utilizar procedimientos almacenados con Hibernate, debe seguir ciertas reglas de funciones/procedimientos. Si no siguen esas reglas entonces no se pueden utilizar con Hibernate. Si todavía quiere utilizar estos procedimientos tiene que ejecutarlos por medio de `session.connection()`. Las reglas son diferentes para cada base de datos debido a que los vendedores de la base de datos tienen diferentes sintaxis/semántica de procedimientos almacenados.

Las consultas de procedimientos almacenados no se pueden llamar con `setFirstResult()/setMaxResults()`.

La forma de la llamada recomendada es SQL92 estándar: `{ ? = call functionName(<parameters>) }` o `{ ? = call procedureName(<parameters>}`. No se soporta la sintaxis de llamadas nativas.

Para Oracle aplican las siguientes reglas:

- Una función tiene que retornar un grupo de resultados. El primer parámetro de un procedimiento tiene que ser un `OUT` que retorna un grupo de resultados. Esto se hace utilizando un tipo `SYS_REFCURSOR` en Oracle 9 o 10. En Oracle necesita definir un tipo `REF CURSOR`. Consulte la documentación de Oracle para obtener mayor información.

Para Sybase o el servidor MS SQL aplican las siguientes reglas:

- El procedimiento tiene que retornar un grupo de resultados. Observe que debido a que estos servidores pueden retornar grupos de resultados múltiples y cuentas actualizadas, Hibernate iterará los resultados y tomará el primer resultado que sea un grupo resultados como su valor retornado. Todo lo demás será descartado.

- Si puede habilitar `SET NOCOUNT ON` en su procedimiento probablemente será más eficiente, pero no es un requerimiento.

## 18.3. Personalice SQL para crear, actualizar y borrar

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 Sección 5.6, “Column transformers: read and write expressions”. Ejemplo 18.11, “Custom CRUD via annotations” shows how to define custom SQL operatons using annotations.

Ejemplo 18.11. Custom CRUD via annotations

    @Entity

    @Table(name="CHAOS")

    @SQLInsert( sql="INSERT INTO CHAOS(size, name, nickname, id) VALUES(?,upper(?),?,?)")

    @SQLUpdate( sql="UPDATE CHAOS SET size = ?, name = upper(?), nickname = ? WHERE id = ?")

    @SQLDelete( sql="DELETE CHAOS WHERE id = ?")

    @SQLDeleteAll( sql="DELETE CHAOS")

    @Loader(namedQuery = "chaos")

    @NamedNativeQuery(name="chaos", query="select id, size, name, lower( nickname ) as nickname from CHAOS where id= ?", resultClass = Chaos.class)

    public class Chaos {

        @Id

        private Long id;

        private Long size;

        private String name;

        private String nickname;

`@SQLInsert`, `@SQLUpdate`, `@SQLDelete`, `@SQLDeleteAll` respectively override the INSERT, UPDATE, DELETE, and DELETE all statement. The same can be achieved using Hibernate mapping files and the `<sql-insert>`, `<sql-update>` and `<sql-delete>` nodes. This can be seen in Ejemplo 18.12, “Custom CRUD XML”.

Ejemplo 18.12. Custom CRUD XML

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

If you expect to call a store procedure, be sure to set the `callable` attribute to `true`. In annotations as well as in xml.

To check that the execution happens correctly, Hibernate allows you to define one of those three strategies:

- none: no check is performed: the store procedure is expected to fail upon issues

- count: use of rowcount to check that the update is successful

- param: like COUNT but using an output parameter rather that the standard mechanism

To define the result check style, use the `check` parameter which is again available in annoations as well as in xml.

You can use the exact same set of annotations respectively xml nodes to override the collection related statements -see Ejemplo 18.13, “Overriding SQL statements for collections using annotations”.

Ejemplo 18.13. Overriding SQL statements for collections using annotations

    @OneToMany

    @JoinColumn(name="chaos_fk")

    @SQLInsert( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = ? where id = ?")

    @SQLDelete( sql="UPDATE CASIMIR_PARTICULE SET chaos_fk = null where id = ?")

    private Set<CasimirParticle> particles = new HashSet<CasimirParticle>();

## Sugerencia

The parameter order is important and is defined by the order Hibernate handles properties. You can see the expected order by enabling debug logging for the `org.hibernate.persister.entity` level. With this level enabled Hibernate will print out the static SQL that is used to create, update, delete etc. entities. (To see the expected sequence, remember to not include your custom SQL through annotations or mapping files as that will override the Hibernate generated static sql)

Overriding SQL statements for secondary tables is also possible using `@org.hibernate.annotations.Table` and either (or all) attributes `sqlInsert`, `sqlUpdate`, `sqlDelete`:

Ejemplo 18.14. Overriding SQL statements for secondary tables

    @Entity

    @SecondaryTables({

        @SecondaryTable(name = "`Cat nbr1`"),

        @SecondaryTable(name = "Cat2"})

    @org.hibernate.annotations.Tables( {

        @Table(appliesTo = "Cat", comment = "My cat table" ),

        @Table(appliesTo = "Cat2", foreignKey = @ForeignKey(name="FK_CAT2_CAT"), fetch = FetchMode.SELECT,

            sqlInsert=@SQLInsert(sql="insert into Cat2(storyPart2, id) values(upper(?), ?)") )

    } )

    public class Cat implements Serializable {

The previous example also shows that you can give a comment to a given table (primary or secondary): This comment will be used for DDL generation.

## Sugerencia

The SQL is directly executed in your database, so you can use any dialect you like. This will, however, reduce the portability of your mapping if you use database specific SQL.

Last but not least, stored procedures are in most cases required to return the number of rows inserted, updated and deleted. Hibernate always registers the first statement parameter as a numeric output parameter for the CUD operations:

Ejemplo 18.15. Stored procedures and their return value

    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;

## 18.4. Personalice SQL para cargar

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 Sección 5.6, “Column transformers: read and write expressions” 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>

Esta es tan sólo una declaración de consulta nombrada, como se discutió anteriormente. Puede referenciar esta consulta nombrada en un mapeo de clase:

    <class name="Person">

        <id name="id">

            <generator class="increment"/>

        </id>

        <property name="name" not-null="true"/>

        <loader query-ref="person"/>

    </class>

Esto funciona inclusive con procedimientos almacenados.

Puede incluso definir una consulta para la carga de colección:

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

También puede definir un cargador de entidad que cargue una colección con una unión temprana:

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

The annotation equivalent `<loader>` is the @Loader annotation as seen in Ejemplo 18.11, “Custom CRUD via annotations”.

Copyright © 2004 Red Hat, Inc.

- AnteriorCapítulo 17. Consultas por criterios
- Subir
- Inicio
- SiguienteCapítulo 19. Filtración de datos