用Hibernate3作为一个JDBC框架

作者:Gavin King
翻译:ChiKai          

        最近围绕着简单的JDBC框架,像iBATIS,总有一些喧闹声。就我个人观点,在一个只有单一事务,实体间有着难懂的关联图,而且并不需要面向对象的域模式的应用,我自己是喜欢iBATIS的。假如你正在进行解决一些“极愚蠢的”遗留数据库问题的工作,一个JDBC框架也是可以产生强的判断力的。ORM的解决方案更趋向于假定那些同样具有正确完整性约束参考的,比较清晰的外键联合关系被体现出来的实体(Hiberante3这方面比Hibernate2.x少的多)。
        有些人甚至建议JDBC框架是对ORM比较好的可供选择的办法,甚至对那些最适合用ORM的系统:具有一目了然的关系模型的面向对象的系统.他们说服自己说,你手写的SQL代码总是会比生成的SQL状况好。我并不认为这是正确的,并非仅仅是因为大多数应用所需要的有压倒性的显得重要的SQL代码是单调乏味,且不需要人们干涉的,而是因为,JDBC框架是在ORM的不同语义级别起作用的。作为一个解决方案,iBATIS关于SQL发布的语义和作为结果的资料这方面了解得非常少。这意味着非常少的时机为了性能优化,例如,有效的缓存,(依据“有效”,我提及到的大体上地有效缓存无效策略,这对缓存的使用是至关紧要的)。而且,无论什么时候我们手写SQL,我们都知道N+1的Selects问题。那是非常单调乏味的为了每个我可能需要连接在一起的关联结合手写一个新的SQL查询。HQL在这方面有值得关注的帮助,因为HQL是决不比SQL详细的。如果说,一个JDBC框架能做ORM所能做的几分性能优化,那将不得不成了一个类似诡辩的级别。本质上,它将需要成为一个ORM,减少SQL的产生。事实上,我们已经开始注意到现有的JDBC框架发生的这种演变。这开始侵蚀任何一个规定的好处:需求的简单。
         这也会增加对下列问题有趣味的思考:如果,逐渐地增加原料,一个JDBC框架最后将会作为ORM而结束,减少SQL的产生,为什么不接受一个现有的ORM解决方案呢?就像,呵呵,嗯,Hibernate,或许...和减去SQL的产生。
 
         Hibernate团队经过长期的验证,需要混合和匹配产生SQL在那些特殊场合来手写查询。在Hibernate的老一版本,我们的解决方案就是简单地暴露Hibernate正在使用的JDBC连接,因此,你能执行你自己的prepared statement。这将刚刚开始改变,马克斯.安徒生最近在这方面已经做了很多工作了。现在,在Hibernate3,写一个没有产生SQL的应用实体将是可能的,这个时候仍然可以利用Hiberante的所有其它特性。
  
         我们真的期望和打算人们以这种方式来用Hibernate?那好,并不是真正的-我怀疑有好多人会真正享受整天写INSERT,UPDATE,DELETE 陈述语句的乐趣。另一方面,我也认为相当少的人需要自定义特殊场合的查询。除每一点的证明之外,我将给你展示怎么用Hibernate来做,如果你真的想那么做的话。
        让我们拿一个简单的Person-Employment-Organization 域模式来做为例子,(你可以在org.hibernate.test.sql 测试包里面找到这些代码,因此我并不打算在拷贝它们到这里。)最简单的类是Person;
  这里就是它的映射文件:
  <class name="Person" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
   
    <property name="name" not-null="true"/>
   
    <loader query-ref="person"/>
   
    <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>
        首先我们要注意的是手写INSERT, UPDATE and DELETE 陈述语句的时候,这个?参数的次序是匹配被列在上面的属性的(我们最后将不得不支持指定的参数,我猜想)。我猜这没什么引起感兴趣的地方。

        更多的是对这<loader>标签感兴趣:它定义一个将会在当我们用get(),load(),或者延迟关联来加载一个person实体的任何时候被用在指定的查询的参考。特殊的,这指定的查询可能是一个原生SQL查询,既然这样,下面的就是:
<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>
        (原生SQL查询可能会返回实体的多重的“列”;这就是最简单的例子,在这里仅仅只有一个实体被返回。)

        Employment 是有些复杂,特殊的,并不是所有的属性被包括在INSERT和UPDATE 陈述语句中:

<class name="Employment" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
   
    <many-to-one name="employee" not-null="true" update="false"/>
    <many-to-one name="employer" not-null="true" update="false"/>
    <property name="startDate" not-null="true" update="false"
        insert="false"/>
    <property name="endDate" insert="false"/>
    <property name="regionCode" update="false"/>
   
    <loader query-ref="employment"/>
   
    <sql-insert>
        INSERT INTO EMPLOYMENT
            (EMPLOYEE, EMPLOYER, STARTDATE, REGIONCODE, ID)
            VALUES (?, ?, CURRENT_DATE, UPPER(?), ?)
    </sql-insert>
    <sql-update>UPDATE EMPLOYMENT SET ENDDATE=? WHERE ID=?</sql-update>
    <sql-delete>DELETE FROM EMPLOYMENT WHERE ID=?</sql-delete>
</class>

<sql-query name="employment">
    <return alias="emp" class="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 ID = ?
</sql-query>
在Organization实体的映射中有一个Employments的collection 集合:
<class name="Organization" lazy="true">
    <id name="id" unsaved-value="0">
        <generator class="increment"/>
    </id>
   
    <property name="name" not-null="true"/>
   
    <set name="employments"
        lazy="true"
        inverse="true">
       
        <key column="employer"/> <!-- only needed for DDL generation -->
       
        <one-to-many class="Employment"/>
       
        <loader query-ref="organizationEmployments"/>
    </set>
   
    <loader query-ref="organization"/>
   
    <sql-insert>
        INSERT INTO ORGANIZATION (NAME, ID) VALUES ( UPPER(?), ? )
    </sql-insert>
    <sql-update>
     UPDATE ORGANIZATION SET NAME=UPPER(?) WHERE ID=?
  </sql-update>
    <sql-delete>DELETE FROM ORGANIZATION WHERE ID=?</sql-delete>
</class>

        这里的<loader>查询并不仅仅是为了Organization对象,也是为了它的Employments集合:
<sql-query name="organization">
    <return alias="org" class="Organization"/>
    SELECT NAME AS {org.name}, ID AS {org.id} FROM ORGANIZATION
    WHERE ID=?
</sql-query>

<sql-query name="organizationEmployments">
    <return alias="empcol" collection="Organization.employments"/>
    <return alias="emp" class="Employment"/>
    SELECT {empcol.*},
        EMPLOYER AS {emp.employer}, EMPLOYEE AS {emp.employee},
        STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate},
        REGIONCODE as {emp.regionCode}, ID AS {emp.id}
    FROM EMPLOYMENT empcol
    WHERE EMPLOYER = :id AND DELETED_DATETIME IS NULL
</sql-query> 
         当我写这代码时,我才真正开始感受到让Hibernate帮我写这些SQL的好处。
在这个简单的例子中,我将已经排除至少35行以后不得不拥护的代码。
         最后,为了一个特别的查询,我们可以用一个原生SQL查询(一个指定的查询,或者一个嵌入到java中的代码)。例子如下:
<sql-query name="allOrganizationsWithEmployees">
    <return alias="org" class="Organization"/>
    SELECT DISTINCT NAME AS {org.name}, ID AS {org.id}
    FROM ORGANIZATION org
    INNER JOIN EMPLOYMENT e ON e.EMPLOYER = org.ID
</sql-query>
         我更宁愿亲自在Java中比在XML中写代码,为了我的嗜好,因此所有的原料是那么大量的臃肿的XML。我认为我将会坚持从事SQL的生成,无论哪里什么地方。那并不是因为我不喜欢SQL,事实上,我是一个SQL的狂热迷,并仅仅喜欢当我打开Hibernate的日志时候观察过往查询的翻动。那是因为Hibernate比我会写代码多了。

发布了38 篇原创文章 · 获赞 0 · 访问量 6万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章