Hibernate.org Community Documentation

- Zurück
- Weiter

## Kapitel 18. Native SQL

You can also express queries in the native SQL dialect of your database. This is useful if you want to utilize database-specific features such as query hints or the `CONNECT` keyword in Oracle. It also provides a clean migration path from a direct SQL/JDBC based application to Hibernate.

Hibernate3 allows you to specify handwritten SQL, including stored procedures, for all create, update, delete, and load operations.

## 18.1. Using a `SQLQuery`

Execution of native SQL queries is controlled via the `SQLQuery` interface, which is obtained by calling `Session.createSQLQuery()`. The following sections describe how to use this API for querying.

### 18.1.1. Skalare Anfragen

Die grundlegendste SQL-Anfrage erfolgt durch eine Liste von Skalaren (Werten).

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

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

These will return a List of Object arrays (Object[]) with scalar values for each column in the CATS table. Hibernate will use ResultSetMetadata to deduce the actual order and types of the returned scalar values.

To avoid the overhead of using `ResultSetMetadata`, or simply to be more explicit in what is returned, one can use `addScalar()`:

    sess.createSQLQuery("SELECT * FROM CATS")

     .addScalar("ID", Hibernate.LONG)

     .addScalar("NAME", Hibernate.STRING)

     .addScalar("BIRTHDATE", Hibernate.DATE)

Diese Anfrage spezifiziert:

- den SQL-Anfragen-String

- die wiederzugebenden Spalten und Typen

This will return Object arrays, but now it will not use `ResultSetMetadata` but will instead explicitly get the ID, NAME and BIRTHDATE column as respectively a Long, String and a Short from the underlying resultset. This also means that only these three columns will be returned, even though the query is using `*` and could return more than the three listed columns.

Es ist möglich, die Typeninformationen für alle oder einige der Skalare auszulassen.

    sess.createSQLQuery("SELECT * FROM CATS")

     .addScalar("ID", Hibernate.LONG)

     .addScalar("NAME")

     .addScalar("BIRTHDATE")

This is essentially the same query as before, but now `ResultSetMetaData` is used to determine the type of NAME and BIRTHDATE, where as the type of ID is explicitly specified.

How the java.sql.Types returned from ResultSetMetaData is mapped to Hibernate types is controlled by the Dialect. If a specific type is not mapped, or does not result in the expected type, it is possible to customize it via calls to `registerHibernateType` in the Dialect.

### 18.1.2. Entity-Anfragen

Die Anfragen oben behandeln die erhaltenen Skalarwerte, wobei es sich dabei um die "unbearbeiteten" Werte von resultset handelt. Nachfolgend sehen Sie, wie Sie Entity-Objekte von einer nativen SQL-Anfrage mittels `addEntity()` erhalten.

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

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

Diese Anfrage spezifiziert:

- den SQL-Anfragen-String

- die von der Anfrage wiedergegebene Entity

Geht man davon aus, dass Cat als Klasse mit den Spalten ID, NAME und BIRTHDATE gemappt ist, werden die Anfragen oben beide mit einer Liste antworten, in der jedes Element eine Entity von Cat ist.

Falls die Entity mit `many-to-one` zu einer anderen Entity gemappt ist, so ist dies ebenfalls erforderlich wenn die native Anfrage durchgeführt wird, da sonst eine Datenbank-spezifische "Spalte nicht gefunden"-Fehlermeldung ("column not found") erscheint. Die zusätzlichen Spalten werden bei Verwendung der * Notation automatisch wiedergegeben, aber wie im folgenden Beispiel für eine `many-to-one` zu `Dog` wollen wir lieber explizit sein:

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

Dies ermöglicht die ordnungsgemäße Funktion von cat.getDog().

### 18.1.3. Umgang mit Assoziationen und Collections

Es ist möglich "eager Join" in `Dog` anzuwenden, um den Extra-Weg zur Datenbank zur Initialisierung des Proxy zu vermeiden. Dies geschieht mittels der `addJoin()`-Methode, die es Ihnen ermöglicht, eine Assoziation oder Collection zu verbinden.

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

In this example, the returned `Cat`'s will have their `dog` property fully initialized without any extra roundtrip to the database. Notice that you added an alias name ("cat") to be able to specify the target property path of the join. It is possible to do the same eager joining for collections, e.g. if the `Cat` had a one-to-many to `Dog` instead.

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

At this stage you are reaching the limits of what is possible with native queries, without starting to enhance the sql queries to make them usable in Hibernate. Problems can arise when returning multiple entities of the same type or when the default alias/column names are not enough.

### 18.1.4. Wiedergabe mehrerer Entities

Until now, the result set column names are assumed to be the same as the column names specified in the mapping document. This can be problematic for SQL queries that join multiple tables, since the same column names can appear in more than one table.

Spalten Alias-Einspeisung wird bei der folgenden Anfrage benötigt (die aller Wahrscheinlichkeit nach fehlschlagen wird):

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

     .addEntity("cat", Cat.class)

     .addEntity("mother", Cat.class)

The query was intended to return two Cat instances per row: a cat and its mother. The query will, however, fail because there is a conflict of names; the instances are mapped to the same column names. Also, on some databases the returned column aliases will most likely be on the form "c.ID", "c.NAME", etc. which are not equal to the columns specified in the mappings ("ID" and "NAME").

Die folgende Form ist nicht anfällig für die Duplizierung von Spaltennamen:

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

     .addEntity("cat", Cat.class)

     .addEntity("mother", Cat.class)

Diese Anfrage spezifiziert:

- der SQL Anfragen-String mit Platzhaltern für die durch Hibernate eingespeisten Aliasse

- die von der Anfrage erhaltenen Entities

The {cat.*} and {mother.*} notation used above is a shorthand for "all properties". Alternatively, you can list the columns explicitly, but even in this case Hibernate injects the SQL column aliases for each property. The placeholder for a column alias is just the property name qualified by the table alias. In the following example, you retrieve Cats and their mothers from a different table (cat_log) to the one declared in the mapping metadata. You can even use the property aliases in the where clause.

    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. Alias- und Property-Referenzen

In most cases the above alias injection is needed. For queries relating to more complex mappings, like composite properties, inheritance discriminators, collections etc., you can use specific aliases that allow Hibernate to inject the proper aliases.

The following table shows the different ways you can use the alias injection. Please note that the alias names in the result are simply examples; each alias will have a unique and probably different name when used.

Tabelle 18.1. Alias-Einspeisungsnamen

Beschreibung | Syntax | Beispiel
--- | --- | ---
Eine einfache Property | `{[aliasname].[propertyname]` | `A_NAME as {item.name}`
Eine zusammengesetzte Property | `{[aliasname].[componentname].[propertyname]}` | `CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}`
Diskriminator einer Entity | `{[aliasname].class}` | `DISC as {item.class}`
Alle Properties einer Entity | `{[aliasname].*}` | `{item.*}`
Ein Collection-Schlüssel | `{[aliasname].key}` | `ORGID as {coll.key}`
Die id einer Collection | `{[aliasname].id}` | `EMPID as {coll.id}`
Das Element einer Collection | `{[aliasname].element}` | `XID as {coll.element}`
property of the element in the collection | `{[aliasname].element.[propertyname]}` | `NAME as {coll.element.name}`
Alle Properties des Elements in der Collection | `{[aliasname].element.*}` | `{coll.element.*}`
All properties of the collection | `{[aliasname].*}` | `{coll.*}`

### 18.1.5. Wiedergabe nicht gemanagter Entities

It is possible to apply a ResultTransformer to native SQL queries, allowing it to return non-managed entities.

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

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

Diese Anfrage spezifiziert:

- den SQL-Anfragen-String

- ein Ergebnistransformer

Die Anfrage oben wird eine Liste von `CatDTO` wiedergeben, die instantiiert wurde und die Werte für NAME und BIRTHNAME in die entsprechenden Properties oder Felder eingespeist hat.

### 18.1.6. Umgang mit Vererbung

Native SQL queries which query for entities that are mapped as part of an inheritance must include all properties for the baseclass and all its subclasses.

### 18.1.7. Parameter

Native SQL queries support positional as well as named parameters:

    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. Benannte SQL-Anfragen

Named SQL queries can also be defined in the mapping document and called in exactly the same way as a named HQL query (see Abschnitt 11.4.1.7, „Externalisierung benannter Anfragen“). In this case, you do not need to call `addEntity()`.

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

Beispiel 18.2. Execution of a named query

    List people = sess.getNamedQuery("persons")

        .setString("namePattern", namePattern)

        .setMaxResults(50)

        .list();

The `<return-join>` element is use to join associations and the `<load-collection>` element is used to define queries which initialize collections,

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

Eine benannte SQL-Anfrage kann einen Skalarwert wiedergeben. Sie müssen unter Verwendung des `<return-scalar>`-Elements den Spalten-Alias und den Hibernate-Typ deklarieren:

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

You can externalize the resultset mapping information in a `<resultset>` element which will allow you to either reuse them across several named queries or through the `setResultSetMapping()` API.

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

You can, alternatively, use the resultset mapping information in your hbm files directly in java code.

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

Beispiel 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 Beispiel 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 Beispiel 18.9, „Using dot notation in @FieldResult for specifying associations “.

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

        })

        }

    )

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

        }

    }

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

        }

    }

## Tipp

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

Beispiel 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. Die Verwendung der Return-Property zur expliziten Spezifizierung von Spalten-/Aliasnamen

You can explicitly tell Hibernate what column aliases to use with `<return-property>`, instead of using the `{}`-syntax to let Hibernate inject its own aliases.For example:

    <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>` also works with multiple columns. This solves a limitation with the `{}`-syntax which cannot allow fine grained control of multi-column properties.

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

In this example `<return-property>` was used in combination with the `{}`-syntax for injection. This allows users to choose how they want to refer column and properties.

Falls Ihr Mapping über einen Diskriminator verfügt, so müssen Sie `<return-discriminator>` verwenden, um die Diskriminator-Spalte festzulegen.

### 18.2.2. Die Verwendung gespeicherter Prozeduren für Anfragen

Hibernate3 provides support for queries via stored procedures and functions. Most of the following documentation is equivalent for both. The stored procedure/function must return a resultset as the first out-parameter to be able to work with Hibernate. An example of such a stored function in Oracle 9 and higher is as follows:

    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;

Um diese Anfrage in Hibernate zu verwenden, müssen Sie sie durch eine benannte Anfrage mappen.

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

Stored procedures currently only return scalars and entities. `<return-join>` and `<load-collection>` are not supported.

#### 18.2.2.1. Regeln/Einschränkungen bei der Verwendung gespeicherter Prozeduren

You cannot use stored procedures with Hibernate unless you follow some procedure/function rules. If they do not follow those rules they are not usable with Hibernate. If you still want to use these procedures you have to execute them via `session.connection()`. The rules are different for each database, since database vendors have different stored procedure semantics/syntax.

Stored procedure queries cannot be paged with `setFirstResult()/setMaxResults()`.

The recommended call form is standard SQL92: `{ ? = call functionName(<parameters>) }` or `{ ? = call procedureName(<parameters>}`. Native call syntax is not supported.

Für Oracle gelten die folgenden Regeln:

- A function must return a result set. The first parameter of a procedure must be an `OUT` that returns a result set. This is done by using a `SYS_REFCURSOR` type in Oracle 9 or 10. In Oracle you need to define a `REF CURSOR` type. See Oracle literature for further information.

Für Sybase oder MS SQL Server gelten die folgenden Regeln:

- The procedure must return a result set. Note that since these servers can return multiple result sets and update counts, Hibernate will iterate the results and take the first result that is a result set as its return value. Everything else will be discarded.

- Falls Sie `SET NOCOUNT ON` in Ihrer Prozedur aktivieren können, so würde sie wahrscheinlich effizienter. Dies ist jedoch keine Voraussetzung.

## 18.3. Anwenderspezifische SQL für "create" (erstellen), "update" (aktualisieren) und "delete" (löschen)

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

Beispiel 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 Beispiel 18.12, „Custom CRUD XML“.

Beispiel 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 Beispiel 18.13, „Overriding SQL statements for collections using annotations“.

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

## Tipp

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

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

## Tipp

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:

Beispiel 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. Angepasste SQL für das Laden

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

This is just a named query declaration, as discussed earlier. You can reference this named query in a class mapping:

    <class name="Person">

        <id name="id">

            <generator class="increment"/>

        </id>

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

        <loader query-ref="person"/>

    </class>

Das funktioniert sogar mit gespeicherten Prozeduren.

You can even define a query for collection loading:

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

You can also define an entity loader that loads a collection by join fetching:

    <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 Beispiel 18.11, „Custom CRUD via annotations“.

Copyright © 2004 Red Hat, Inc.

- ZurückKapitel 17. "Criteria Queries"
- Nach oben
- Zum Anfang
- WeiterKapitel 19. Das Filtern von Daten