16. Native SQL查询 / 16.1. 使用SQLQuery / 16.1.4. 返回多个实体(Returning multiple entities)

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

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

sess.createSQLQuery("SELECT c.*, m.*  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID")
 .addEntity("cat", Cat.class)
 .addEntity("mother", Cat.class)

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

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

sess.createSQLQuery("SELECT {cat.*}, {mother.*}  FROM CATS c, CATS m WHERE c.MOTHER_ID = c.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()

16.1.4.1. 别名和属性引用(Alias and property references)

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

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

表 16.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.*}