一、前言
JDBC是Java操作数据库最常用的数据库接口,它隔离了数据库的复杂度,使程序员可以将主要精力放到程序逻辑上来。而JDBC也只是提供了和数据库交互的简单方式,如打开数据库表,执行SQL语句等。这对于复杂的程序也需要编写大量的代码,因此,近几年在Java数据库领域出现了许多框架,这此框架重新对JDBC进行了更高层次的封装。如早期的iBATIS。这个框架使用起来非常方便。它也没有基于复杂的面向对象模型。也不工作在复杂的关系图中。这种最初级的框架将数据库和应用程序隔离开来。使程序员只操作逻辑数据库。但由于这种框架不是基本面向对象和关系模型,在对付大规模应用程序时仍然捉襟见肘。基于这些不足,最近许多基于关系映射(ORM)的数据库框架开始流行起来。Hibernate就是其中的佼佼者。ORM的基本思想是使用外键和适当的约束将抽象不同的数据库。在最新的Hibernate3.x中,增强了对约束的控制,使用功能更强,也更容易使用。
在这些ORM框架刚兴起的时候,有很多人认为ORM框架最多只是从多选择之一。即使某个ORM框架做的非常出钯,将对象和数据库之间映射的非常完美也是如此。他们认为直接使用SQL写程序才是王道,而使用自动生成的SQL的效率低下,而且很不灵活。但随着程序规模越来越大,这种想法越来越站不住脚。这主要并不只是因为直接使用SQL将产生大量的代码,而是因为使用ORM框架,我们将操作一个完全不同的层:ORM层。直接使用SQL也可能产生其他的问题,如我们经常会遇到的N+1选择问题。而且在连接很多表时,我们会一遍一遍地重复写非常类似的SQL语句。如果我们使用Hibernate,这些问量就将荡然无存。我们可以使非常简单的HQL来完成上述复杂的问题。象Hibernate一样ORM框架还应该能进行各种优化,以使操作达到最佳化。就目前来看,这些框架的优化功能已经越来越强,正在逐步取代用JDBC和SQL操作数据库的方式。
虽然可以用ORM框架来编写大多数程序,但有时也需要直接使用SQL来操作。也许Hibernate的开发团队也意识到了这一点,也为Hibernate提供了直接执行SQL的功能。在早期的Hibernate版本中,解决方案是直接将JDBC连接暴露给用户,这样程序员就可以直接使用prepared statment来执行SQL了。但在新的Hibernate3.x中,这种情况已经被改变了。现在,Hibernate3.x可以不使用一条SQL编写整个应用程序,而且这并不会影响Hibernate的灵活性,同时也可以使用Hibernate的所有其他特性。
二、Hibernate3.x功能演示
上面说了很多Hibernate的好处,也面就让我们来体会一下Hibernate3.x在这方面的卓越表现。我们将使用一个简单的Person-Employment-Oranization模型来说明。最简单的类是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>
看了上面的定义,可能我们会首先注意到三个手写的SQL语句:INSERT、UPDATE和DELETE。其中的?将匹配上面所列的两个属性(这两个属性是id和name)。除了这个,这三条语句没有什么之处。
也许很多读者最感兴趣的是<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>