|
一、前言 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的所有其他特性。
上面说了很多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> 下面是命名查询employment的定义: <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的定义:
<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"/> <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> 下面是两个命名查询的定义:
<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的好处,那就是至少我们可以少维护数十行的java代码。而且将这些代码转换成了xml配置文件,这样将使程序更加灵活和可维护。 下面的代码是我们的最后的工作,一个命名查询allorganizationswithemployees的定义:
<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> 虽然在现在为止还是有很多人喜欢直接使用sql,这也包括我在内。但使用以hbiernate为首的orm框架可能会给我们带来更多的好处,如它自动产生的sql一般会比我们手写的更优化。因此,hibernate将成为软件大工业时代的新的操作数据库的标准。
|