`
cobo85
  • 浏览: 114866 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Hibernate之Native SQL查询

    博客分类:
  • J2EE
阅读更多

       随着项目越来越庞大,功能越来越多,性能便成了一个大问题。随着对项目越来越深入的了解,发现性能下降并不是因功能强大引起,大部分是由对hibernate查询的滥用引起!特别是有些递归查询还在用hql查询!hibernate面向对象的查询虽然简单易用,但并不是上面地方都合适的,下面把hibernate对Native SQL的支持贴过来,以备需要的童鞋查阅。

      1、创建一个基于SQL的Query

      SQL查询是通过SQLQuery接口来控制的,它是通过调用Session.createSQLQuery()方法来获得

     

List cats = sess.createSQLQuery("select {cat.*} from cats cat")
        .addEntity("cat", Cat.class);
        .setMaxResults(50);
        .list();

   
  

这个查询指定了: 

SQL查询语句,它带一个占位符,可以让Hibernate使用字段的别名. 

查询返回的实体,和它的SQL表的别名. 

addEntity()方法将SQL表的别名和实体类联系起来,并且确定查询结果集的形态。 

addJoin()方法可以被用于载入其他的实体和集合的关联,TODO:examples! 

原生的SQL查询可能返回一个简单的标量值或者一个标量和实体的结合体。 

Double max = (Double) sess.createSQLQuery("select max(cat.weight) as maxWeight from cats cat")
        .addScalar("maxWeight", Hibernate.DOUBLE);
        .uniqueResult();
2、别名和属性引用
上面使用的{cat.*}标记是 "所有属性" 的简写.你可以显式地列出需要的字段,但是你必须让Hibernate 为每一个属性注入字段的别名.这些字段的站位符是以字段别名为前导,再加上属性名.在下面的例子里,我们从一个其他的表(cat_log) 中获取Cat对象,而非Cat对象原本在映射元数据中声明的表.注意我们甚至在where子句中也可以使用属性别名. 对于命名查询,{}语法并不是必需的.你可以在第 3 节 “命名SQL查询”得到更多的细节. 

String sql = "select cat.originalId as {cat.id}, " +
    "cat.mateid as {cat.mate}, cat.sex as {cat.sex}, " +
    "cat.weight*10 as {cat.weight}, cat.name as {cat.name} " +
    "from cat_log cat where {cat.mate} = :catId"
    
List loggedCats = sess.createSQLQuery(sql)
    .addEntity("cat", Cat.class)
    .setLong("catId", catId)
    .list();
注意:如果你明确地列出了每个属性,你必须包含这个类和它的子类的属性! and its subclasses! 

3、 命名SQL查询
可以在映射文档中定义查询的名字,然后就可以象调用一个命名的HQL查询一样直接调用命名SQL查询.在这种情况下,我们不 需要调用addEntity()方法. 

<sql-query name="mySqlQuery">
    <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 'Hiber%'
</sql-query>
List people = sess.getNamedQuery("mySqlQuery")
    .setMaxResults(50)
    .list();
一个命名查询可能会返回一个标量值.你必须使用<return-scalar>元素来指定字段的别名和 Hibernate类型 

<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>
<return-join>和<load-collection>元素分别用作 外连接和定义那些初始化集合的查询 

4、 使用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>来指定识别器字段 
5、 使用存储过程来查询
Hibernate 3引入了对存储过程查询的支持. 存储过程必须返回一个结果集,作为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> 

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

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

对于Oracle有如下规则: 

存储过程必须返回一个结果集.它通过返回SYS_REFCURSOR实现(在Oracle9或10),在Oracle里你需要定义一个REF CURSOR 类型 

推荐的格式是 { ? = call procName(<parameters>) } 或 { ? = call procName }(这更像是Oracle规则而不是Hibernate规则) 

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

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

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

7、 定制SQL用来create,update和delete
Hibernate3能够使用定制的SQL语句来执行create,update和delete操作。在Hibernate中,持久化的类和集合已经 包含了一套配置期产生的语句(insertsql, deletesql, updatesql等等),这些映射标记 <sql-insert>, <sql-delete>, and <sql-update>重载了 这些语句。 

<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>
这些SQL直接在你的数据库里执行,所以你可以自由的使用你喜欢的任意语法。但如果你使用数据库特定的语法, 这当然会降低你映射的可移植性。 

如果设定callable,则能够支持存储过程了。 

<class name="Person">
    <id name="id">
        <generator class="increment"/>
    </id>
    <property name="name" not-null="true"/>
    <sql-insert callable="true">{call createPerson (?, ?)}</sql-insert>
    <sql-delete callable="true">{? = call deletePerson (?)}</sql-delete>
    <sql-update callable="true">{? = call updatePerson (?, ?)}</sql-update>
</class>
参数的位置顺序是非常重要的,他们必须和Hibernate所期待的顺序相同。 

你能够通过设定日志调试级别为org.hiberante.persister.entity,来查看Hibernate所期待的顺序。在这个级别下, Hibernate将会打印出create,update和delete实体的静态SQL。如果想看到预想中的顺序。记得不要将定制SQL包含在映射文件里, 因为他们会重载Hibernate生成的静态SQL。 

在大多数情况下(最好这么做),存储过程需要返回插入/更新/删除的行数,因为Hibernate对语句的成功执行有些运行时的检查。 Hibernate常会把进行CUD操作的语句的第一个参数注册为一个数值型输出参数。 

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;
8、 定制装载SQL
你可能需要声明你自己的SQL(或HQL)来装载实体 

<sql-query name="person">
    <return alias="p" class="Person" lock-mode="upgrade"/>
    SELECT NAME AS {p.name}, ID AS {p.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>
这也可以用于存储过程 

TODO: 未完成的例子 

<sql-query name="organizationEmployments">
    <load-collection alias="empcol" role="Organization.employments"/>
    SELECT {empcol.*}
    FROM EMPLOYMENT empcol
    WHERE EMPLOYER = :id
    ORDER BY STARTDATE ASC, EMPLOYEE ASC
</sql-query>

<sql-query name="organizationCurrentEmployments">
    <return alias="emp" class="Employment"/>
    <synchronize table="EMPLOYMENT"/>
    SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer},
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, ID AS {emp.id}
    FROM EMPLOYMENT
    WHERE EMPLOYER = :id AND ENDDATE IS NULL
    ORDER BY STARTDATE ASC
</sql-query>

 

PS:虽然ibatis提供了更大的灵活性,但是一个项目里面引入了两种orm势必会增加维护的复杂度,通过hibernate对Native SQL的支持,也足够满足项目需要。(调用sql及存储过程需记得加载实体映射文件,以及配置返回属性文件)

分享到:
评论
1 楼 tiansskk 2012-05-15  
有代码,有解释,格式也清晰,不错,好文

相关推荐

    Hibernate之原生Native SQL查询

    该方式是原生SQL查询的一种方式,需要个人自己书写SQL语句进行操作,用法比较灵活多变,适合比较复杂的SQL查询.该压缩包是本人对该查询方式的一种总结练习

    hibernate将本地SQL查询结果封装成对象

    hibernate将本地SQL查询结果封装成对象。如进行复杂的统计等本地sql语句查询后,直接放到对象里了,而不会再使用Object进行强转

    hibernate将本地SQL查询结果封装成对象(最终)

    使用本地sql语句查询后,无需再使用Object对查询结果进行强制转换,而是直接将查询结果放到实体Bean里了。 PS: 其实只有一版,这里只所以叫最终版是因为该附件我上传了好几天传不上去,到最后报告说‘资源已经存在...

    hibernate3中通过nativesql或取部分字段并映射为具体对象的实现

    博文链接:https://bicashy.iteye.com/blog/135925

    hibernate3.jar

    hibernate native Sql Rounding necessary 解决

    Hibernate框架参考文档

    16. Native SQL查询; 17. 过滤数据; 18. XML映射; 19. 提升性能; 20. 工具箱指南; 21. 示例:父子关系(Parent Child Relationships); 22. 示例:Weblog 应用程序; 23. 示例:复杂映射实例; 24. 最佳实践(Best ...

    Hibernate3开发.pdf

    包括:使用Hibernate实现增删改查、HQL语句、条件查询、Native SQL、批量处理 第五章:关系映射 包括:集合映射、一对一、一对多、多对多 第六章:性能提升和二级缓存 包括:抓取策略、集合的性能、二级缓存、...

    HIBERNATE_QUERY

    Hibernate支持强大且易于使用的面向对象查询语言(HQL)。 如果希望通过编程的方式创建查询,Hibernate... 你也可以用原生SQL(native SQL)描述Hibernate查询,Hibernate额外提供了将结果集(result set)转化为对象的支持。

    Hibernate+中文文档

    16. Native SQL查询 16.1. 使用SQLQuery 16.1.1. 标量查询(Scalar queries) 16.1.2. 实体查询(Entity queries) 16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体...

    hibernate3.2中文文档(chm格式)

    16. Native SQL查询 16.1. 使用SQLQuery 16.1.1. 标量查询(Scalar queries) 16.1.2. 实体查询(Entity queries) 16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体...

    HibernateAPI中文版.chm

    16. Native SQL查询 16.1. 使用SQLQuery 16.1.1. 标量查询(Scalar queries) 16.1.2. 实体查询(Entity queries) 16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体...

    Hibernate 3.6.0.Final Reference PDF 手册

    第 18 章 Native SQL 查询 第 19 章 过滤数据 第 20 章 XML 映射 第 21 章 提升性能 第 22 章 工具箱指南 第 23 章 Additional modules 第 24 章 示例:父子关系(Parent/Child) 第 25 章 ...

    hibernate.properties

    ## DO NOT specify hibernate.connection.sqlDialect ## InterClient #hibernate.connection.driver_class interbase.interclient.Driver #hibernate.connection.url jdbc:interbase://localhost:3060/C:/firebird...

    Hibernate中文详细学习文档

    16. Native SQL查询 16.1. 使用SQLQuery 16.1.1. 标量查询(Scalar queries) 16.1.2. 实体查询(Entity queries) 16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体...

    最全Hibernate 参考文档

    16. Native SQL查询 16.1. 创建一个基于SQL的Query 16.2. 别名和属性引用 16.3. 命名SQL查询 16.3.1. 使用return-property来明确地指定字段/别名 16.3.2. 使用存储过程来查询 16.3.2.1. 使用存储过程的规则和限制 ...

    Hibernate 中文 html 帮助文档

    16. Native SQL查询 16.1. 使用SQLQuery 16.1.1. 标量查询(Scalar queries) 16.1.2. 实体查询(Entity queries) 16.1.3. 处理关联和集合类(Handling associations and collections) 16.1.4. 返回多个实体(Returning...

    精通 Hibernate:Java 对象持久化技术详解(第2版).part2

     18.4.4 本地SQL查询  18.4.5 查询结果转换器  18.5 查询性能优化  18.5.1 iterate()方法  18.5.2 查询缓存  18.6 小结  18.7 思考题 第19章 Hibernate高级配置  19.1 配置数据库连接池  19.1.1 使用默认的...

    hibernate 3中的缓存小结

    (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。 2) 把获得的所有数据对象根据ID放入到第二级缓存中。 3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果...

Global site tag (gtag.js) - Google Analytics