Hibernate.org Community Documentation

- 上一页
- 下一页

## 第 18 章 Native SQL 查询

你也可以使用你的数据库的 Native SQL 语言来查询数据。这对你在要使用数据库的某些特性的时候（比如说在查询提示或者 Oracle 中的 `CONNECT` 关键字），这是非常有用的。这就能够扫清你把原来直接使用 SQL/JDBC 的程序迁移到基于 Hibernate 应用的道路上的障碍。

Hibernate3 允许你使用手写的 sql 来完成所有的 create、update、delete 和 load 操作（包括存储过程）

## 18.1. 使用 `SQLQuery`

对原生 SQL 查询执行的控制是通过 `SQLQuery` 接口进行的，通过执行`Session.createSQLQuery()`获取这个接口。下面来描述如何使用这个 API 进行查询。

### 18.1.1. 标量查询（Scalar queries）

最基本的 SQL 查询就是获得一个标量（数值）的列表。

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

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

它们都将返回一个 Object 数组（Object[]）组成的 List，数组每个元素都是 CATS 表的一个字段值。Hibernate 会使用 ResultSetMetadata 来判定返回的标量值的实际顺序和类型。

如果要避免过多的使用 `ResultSetMetadata`，或者只是为了更加明确的指名返回值，可以使用 `addScalar()`：

    sess.createSQLQuery("SELECT * FROM CATS")

     .addScalar("ID", Hibernate.LONG)

     .addScalar("NAME", Hibernate.STRING)

     .addScalar("BIRTHDATE", Hibernate.DATE)

这个查询指定：

- SQL 查询字符串

- 要返回的字段和类型

它仍然会返回 Object 数组,但是此时不再使用 `ResultSetMetdata`，而是明确的将 ID，NAME 和 BIRTHDATE 按照 Long，String 和 Short 类型从 resultset 中取出。同时，也指明了就算 query 是使用 `*` 来查询的，可能获得超过列出的这三个字段，也仅仅会返回这三个字段。

对全部或者部分的标量值不设置类型信息也是可以的。

    sess.createSQLQuery("SELECT * FROM CATS")

     .addScalar("ID", Hibernate.LONG)

     .addScalar("NAME")

     .addScalar("BIRTHDATE")

基本上这和前面一个查询相同,只是此时使用 `ResultSetMetaData` 来决定 NAME 和 BIRTHDATE 的类型，而 ID 的类型是明确指出的。

关于从 ResultSetMetaData 返回的 java.sql.Types 是如何映射到 Hibernate 类型，是由方言（Dialect）控制的。假若某个指定的类型没有被映射，或者不是你所预期的类型，你可以通过 Dialet 的 `registerHibernateType` 调用自行定义。

### 18.1.2. 实体查询（Entity queries）

上面的查询都是返回标量值的，也就是从 resultset 中返回的“裸”数据。下面展示如何通过 `addEntity()` 让原生查询返回实体对象。

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

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

这个查询指定：

- SQL 查询字符串

- 要返回的实体

假设 Cat 被映射为拥有 ID，NAME 和 BIRTHDATE 三个字段的类，以上的两个查询都返回一个 List，每个元素都是一个 Cat 实体。

假若实体在映射时有一个 `many-to-one` 的关联指向另外一个实体，在查询时必须也返回那个实体，否则会导致发生一个 "column not found" 的数据库错误。这些附加的字段可以使用 * 标注来自动返回，但我们希望还是明确指明，看下面这个具有指向 `Dog` 的 `many-to-one` 的例子：

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

这样 cat.getDog() 就能正常运作。

### 18.1.3. 处理关联和集合类（Handling associations and collections）

通过提前抓取将 `Dog` 连接获得，而避免初始化 proxy 带来的额外开销也是可能的。这是通过 `addJoin()` 方法进行的，这个方法可以让你将关联或集合连接进来。

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

上面这个例子中，返回的 `Cat` 对象，其 `dog` 属性被完全初始化了，不再需要数据库的额外操作。注意，我们加了一个别名（"cat"），以便指明 join 的目标属性路径。通过同样的提前连接也可以作用于集合类，例如，假若 `Cat` 有一个指向 `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");

到此为止，我们碰到了天花板：若不对 SQL 查询进行增强，这些已经是在 Hibernate 中使用原生 SQL 查询所能做到的最大可能了。下面的问题即将出现：返回多个同样类型的实体怎么办？或者默认的别名／字段不够又怎么办？

### 18.1.4. 返回多个实体（Returning multiple entities）

到目前为止,结果集字段名被假定为和映射文件中指定的的字段名是一致的。假若 SQL 查询连接了多个表，同一个字段名可能在多个表中出现多次，这就会造成问题。

下面的查询中需要使用字段别名注射（这个例子本身会失败）：

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

     .addEntity("cat", Cat.class)

     .addEntity("mother", Cat.class)

这个查询的本意是希望每行返回两个 Cat 实例，一个是 cat,另一个是它的妈妈。但是因为它们的字段名被映射为相同的，而且在某些数据库中，返回的字段别名是“c.ID”，"c.NAME" 这样的形式，而它们和在映射文件中的名字（"ID" 和 "NAME"）不匹配，这就会造成失败。

下面的形式可以解决字段名重复：

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

     .addEntity("cat", Cat.class)

     .addEntity("mother", Cat.class)

这个查询指定：

- SQL 查询语句，其中包含占位附来让 Hibernate 注射字段别名

- 查询返回的实体

上面使用的 {cat.*} 和 {mother.*} 标记是作为“所有属性”的简写形式出现的。当然你也可以明确地罗列出字段名，但在这个例子里面我们让 Hibernate 来为每个属性注射 SQL 字段别名。字段别名的占位符是属性名加上表别名的前缀。在下面的例子中，我们从另外一个表（cat_log）中通过映射元数据中的指定获取 Cat 和它的妈妈。注意，要是我们愿意，我们甚至可以在 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. 别名和属性引用（Alias and property references）

大多数情况下，都需要上面的属性注射，但在使用更加复杂的映射，比如复合属性、通过标识符构造继承树，以及集合类等等情况下，也有一些特别的别名，来允许 Hibernate 注入合适的别名。

下表列出了使用别名注射参数的不同可能性。注意：下面结果中的别名只是示例，实用时每个别名需要唯一并且不同的名字。

表 18.1. 别名注射（alias injection names）

描述 | 语法 | 示例
--- | --- | ---
简单属性 | `{[aliasname].[propertyname]` | `A_NAME as {item.name}`
复合属性 | `{[aliasname].[componentname].[propertyname]}` | `CURRENCY as {item.amount.currency}, VALUE as {item.amount.value}`
实体辨别器（Discriminator of an entity） | `{[aliasname].class}` | `DISC as {item.class}`
实体的所有属性 | `{[aliasname].*}` | `{item.*}`
集合键（collection key） | `{[aliasname].key}` | `ORGID as {coll.key}`
集合 id | `{[aliasname].id}` | `EMPID as {coll.id}`
集合元素 | `{[aliasname].element}` | `XID as {coll.element}`
集合元素的属性 | `{[aliasname].element.[propertyname]}` | `NAME as {coll.element.name}`
集合元素的所有属性 | `{[aliasname].element.*}` | `{coll.element.*}`
集合的所有属性 | `{[aliasname].*}` | `{coll.*}`

### 18.1.5. 返回非受管实体（Returning non-managed entities）

可以对原生 sql 查询使用 ResultTransformer。这会返回不受 Hibernate 管理的实体。

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

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

这个查询指定：

- SQL 查询字符串

- 结果转换器（result transformer）

上面的查询将会返回 `CatDTO` 的列表,它将被实例化并且将 NAME 和 BIRTHDAY 的值注射入对应的属性或者字段。

### 18.1.6. 处理继承（Handling inheritance）

原生 SQL 查询假若其查询结果实体是继承树中的一部分，它必须包含基类和所有子类的所有属性。

### 18.1.7. 参数（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. 命名 SQL 查询

Named SQL queries can also be defined in the mapping document and called in exactly the same way as a named HQL query (see 第 11.4.1.7 节 “外置命名查询（Externalizing named queries）”). In this case, you do not need to call `addEntity()`.

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

例 18.2. Execution of a named query

    List people = sess.getNamedQuery("persons")

        .setString("namePattern", namePattern)

        .setMaxResults(50)

        .list();

`<return-join>` 和 `<load-collection>` 元素是用来连接关联以及将查询定义为预先初始化各个集合的。

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

一个命名查询可能会返回一个标量值。你必须使用 `<return-scalar>` 元素来指定字段的别名和 Hibernate 类型：

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

你可以把结果集映射的信息放在外部的 `<resultset>` 元素中，这样就可以在多个命名查询间，或者通过 `setResultSetMapping()` API 来访问。

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

另外，你可以在 java 代码中直接使用 hbm 文件中的结果集定义信息。

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

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

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

        })

        }

    )

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

        }

    }

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

        }

    }

## 提示

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

例 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. 使用 return-property 来明确地指定字段／别名

使用 `<return-property>` 你可以明确的告诉 Hibernate 使用哪些字段别名，这取代了使用 `{}`-语法 来让 Hibernate 注入它自己的别名。例如：

    <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>` 也可用于多个字段，它解决了使用 `{}`-语法不能细粒度控制多个字段的限制。

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

注意在这个例子中，我们使用了 `<return-property>` 结合 `{}` 的注入语法。允许用户来选择如何引用字段以及属性。

如果你映射一个识别器（discriminator），你必须使用 `<return-discriminator>` 来指定识别器字段。

### 18.2.2. 使用存储过程来查询

Hibernate 3 引入了对存储过程查询（stored procedure）和函数（function）的支持。以下的说明中，这二者一般都适用。存储过程／函数必须返回一个结果集，作为 Hibernate 能够使用的第一个外部参数。下面是一个 Oracle9 和更高版本的存储过程例子。

    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;

在 Hibernate 里要要使用这个查询，你需要通过命名查询来映射它。

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

注意存储过程当前仅仅返回标量和实体现在。不支持 `<return-join>` 和 `<load-collection>`。

#### 18.2.2.1. 使用存储过程的规则和限制

为了在 Hibernate 中使用存储过程，你必须遵循一些规则。不遵循这些规则的存储过程将不可用。如果你仍然想使用他们，你必须通过 `session.connection()` 来执行他们。这些规则针对于不同的数据库。因为数据库提供商有各种不同的存储过程语法和语义。

对存储过程进行的查询无法使用 `setFirstResult()/setMaxResults()` 进行分页。

建议采用的调用方式是标准 SQL92: `{ ? = call functionName(<parameters>) }` 或者 `{ ? = call procedureName(<parameters>) }`。原生调用语法不被支持。

对于 Oracle 有如下规则：

- 函数必须返回一个结果集。存储过程的第一个参数必须是 `OUT`，它返回一个结果集。这是通过 Oracle 9 或 10 的 `SYS_REFCURSOR` 类型来完成的。在 Oracle 中你需要定义一个 `REF CURSOR` 类型，参见 Oracle 的手册。

对于 Sybase 或者 MS SQL server 有如下规则：

- 存储过程必须返回一个结果集。注意这些 servers 可能返回多个结果集以及更新的数目。Hibernate 将取出第一条结果集作为它的返回值，其他将被丢弃。

- 如果你能够在存储过程里设定 `SET NOCOUNT ON`，这可能会效率更高，但这不是必需的。

## 18.3. 定制 SQL 用来 create，update 和 delete

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

例 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 例 18.12 “Custom CRUD XML”.

例 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 例 18.13 “Overriding SQL statements for collections using annotations”.

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

## 提示

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

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

## 提示

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:

例 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. 定制装载 SQL

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

这只是一个前面讨论过的命名查询声明，你可以在类映射里引用这个命名查询。

    <class name="Person">

        <id name="id">

            <generator class="increment"/>

        </id>

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

        <loader query-ref="person"/>

    </class>

这也可以用于存储过程

你甚至可以定一个用于集合装载的查询：

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

你甚至还可以定义一个实体装载器，它通过连接抓取装载一个集合：

    <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 例 18.11 “Custom CRUD via annotations”.

版权 © 2004 Red Hat, Inc.

- 上一页第 17 章 条件查询（Criteria Queries）
- 上一级
- 起始页
- 下一页第 19 章 过滤数据