Hibernate.org Community Documentation
你也可以使用你的数据库的 Native SQL 语言来查询数据。这对你在要使用数据库的某些特性的时候(比如说在查询提示或者 Oracle 中的 CONNECT
关键字),这是非常有用的。这就能够扫清你把原来直接使用 SQL/JDBC 的程序迁移到基于 Hibernate 应用的道路上的障碍。
Hibernate3 允许你使用手写的 sql 来完成所有的 create、update、delete 和 load 操作(包括存储过程)
对原生 SQL 查询执行的控制是通过 SQLQuery
接口进行的,通过执行Session.createSQLQuery()
获取这个接口。下面来描述如何使用这个 API 进行查询。
最基本的 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
调用自行定义。
上面的查询都是返回标量值的,也就是从 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() 就能正常运作。
通过提前抓取将 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 查询所能做到的最大可能了。下面的问题即将出现:返回多个同样类型的实体怎么办?或者默认的别名/字段不够又怎么办?
到目前为止,结果集字段名被假定为和映射文件中指定的的字段名是一致的。假若 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()
大多数情况下,都需要上面的属性注射,但在使用更加复杂的映射,比如复合属性、通过标识符构造继承树,以及集合类等等情况下,也有一些特别的别名,来允许 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.*}
|
可以对原生 sql 查询使用 ResultTransformer。这会返回不受 Hibernate 管理的实体。
sess.createSQLQuery("SELECT NAME, BIRTHDATE FROM CATS")
.setResultTransformer(Transformers.aliasToBean(CatDTO.class))
这个查询指定:
-
SQL 查询字符串
-
结果转换器(result transformer)
上面的查询将会返回 CatDTO
的列表,它将被实例化并且将 NAME 和 BIRTHDAY 的值注射入对应的属性或者字段。
原生 SQL 查询假若其查询结果实体是继承树中的一部分,它必须包含基类和所有子类的所有属性。
原生查询支持位置参数和命名参数:
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();
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.
使用 <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>
来指定识别器字段。
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>
。
为了在 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
,这可能会效率更高,但这不是必需的。
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;
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.