使用hibernate进行对象的关系映射
--------------------------------------------------------------------------------
原文连接:http://www.meagle.com:8080/hibernate.jsp
hibernate是业界比较推崇的orm(o/r mapping),目前的版本已经非常稳定和成熟,而且的他的文档也极其丰富。
http://www.jdon.com和http://www.chinaxp.com 这两个技术网站对hibernate的讨论很多也很精到。
这篇文章是一篇让出学者入门的文章。现意译如下,英文好的可以跳过直接阅读原文。
--译者按
在java技术中有许多方法可以对数据进行持久化,持久层也是java应用程序中最重要的部分之一。在当今关系型数据库仍为如主流数据库的时候,我个人认为持久层技术常常并没有得到应用的重视。在这篇文章里将涉及到几个著名的以java技术实现的持久性框架,随后我们会谈到一些最新的持久性框架譬如:hibernate,让我们就从那些著名的框架以极其通用apis开是这次旅行吧。
1.jdbc
大多数java开发员都是用jdbc来和数据库进行通信,它可以通过dao(data access object)模式来进行改善和提高,然而,这种方式在大型应用程序中则会造成维护的"高消费"。不过大多数的开发员对于jdbc的操作以及其api都是非常熟悉,因此,我相信不管怎样它仍然是目前最被广泛应用的数据持久技术之一。( 不要着急"噩梦马上就要结束了",译者注)
2.ejb
据调查ejb通常是在数据持久技术上的第二个选择,它是通过entity beans来对数据进行持久化,这听起来就好像是java持久框架界中的"银单"一样(真的有银弹??),我的意思是在巨大的市场下(潜在的一块大蛋糕?)。然而事实上并不是这样的:首先你需要购买一个价位合理的ejb容器--j2ee应用服务器,采用开源项目的免费ejb容器是一种不错的选择:),比如jboss(恕我直言);其次全面采用entity bean需要花"大量"的时间来理解ejb规范。在采用ejb之前你通常想在熟练掌握它的api;再有就是,你需要知道在每一个容器除了ejb-jar.xml以外所专有的部署描述符,而且很多商业ejb容器的性能和技术支持也不敢恭维。对于java开发员,在ejb中实现jdbc也比较复杂。ejb中最为被关注的可能是无状态的会话bean(stateless-session beans)和消息驱动bean(messaging driver beans)
3.更多持久框架
通过对前面两种规范一阵"游移不定和踌躇"以后,你可能发现它们都不是完美的解决方案。jdo的出现似乎有了一些改观,但是jdo1.0不论是从操作方式上还是其功能上对于java开发员来说似乎"天还是灰蒙蒙的","而且没有一个好的免费的产品",终究jdo1.0仍然没有带来太大改变,人们仍停留在用它来学习的阶段,这种情况有待jdo的成熟来解决。(目前sun已经加入jdocentorl.com来着手jdo2.0,我们唯有等待那丝曙光了。)
那么我们该怎么做呢?如果你抛开主流于非主流的概念,你会发现你将有更多的选择(也许是更好的?),如果是这样,你不会不注意到这样两个名字"hibernate"和"ojb",这两种持久框架跟前面的提到的jdbc、ejb和jdo在某些方面有很大的不同,它们不需要任何容器,提供简单易用并符合odmg3-style apis,而且它们免费、开源、有丰富的文档和稳定的开发背景。
也许你所要做的仅仅是选择?
4"冬眠"hibernate
就让我们现在开始吧,在这些"新鲜的"持久框架中我选择了hibernate,同时当初我也把ojb列入我的选择之列,不过最后之所以选择hibernate的原因要归功于它丰富的文档,我知道有许多其它的人使用ojb也取得过成功。不管怎么样,hibernate和ojb都可以让你完全操纵持久层。
请访问http://hibernate.blumears.net/4.html来获得hibernate的功能介绍。
下面我想首先说一下我的运行环境:hibernate 2.0 rc1 (2.0 beta 5)+ tomcat 4.1.18+websphere application server 5.0.1(请到相应的官方网站获取各个的最新版本,译者注),我的例子在其他的容器中也能正常的运行,不过我没有进行这方面的测试。hibernate网站有关于在jboss下的配置信息,通常几乎所有的应用服务器和数据库都能整合hibernate。
4.1 mapping relationships with hibernate
下面我们开讨论如何使用hibernate进行对象的关系映射。我将介绍如何使用hibernate进行"一对一"、"一对多"、"多对多"的映射。在我提供的代码中只是为了测试而没有考虑到更多的"模式啦规则啦"等等,这就说这里的例子只是一个启蒙,让我们从代码中学习hibernate的api以及如何进行开发,请再你自己书写的时候注意必要的模式和编码规范。
然后我们看看我的例子中都有哪些对象,让们开始为他们建模吧,如下图:
下图为数据表关系图:
4.2 配置hibernate
点击此处获得本文中的实例代码,这样你可以对本例有更深的了解。
为了运行实例,请确信你已经下载过hibernate和log4j的最新发布包,同时也要把数据库驱动放到classpath中。下载以后的压缩包中有example_schema.ddl文件用来生成数据库表。
接下来作者给我们讲述了一个hibernate.properties文件,它是在配置hibernate的时候最先接触到的,它在应用程序启动的时候为我们进行初始化工作(译者注:有了hibernate.cfg.xml,hibernate.properties变的可有可无了,不是吗?)
hibernate.connection.driver_class=com.ibm.db2.jdbc.net.db2driver
hibernate.connection.url=jdbc:db2://server1/sample
hibernate.connection.username=db2admin
hibernate.connection.password=password
hibernate.default_schema=db2admin
hibernate.dialect=net.sf.hibernate.dialect.db2dialect
hibernate.show_sql=true
# the maximum number of active connections that can be allocated # from this pool at the same time, or zero for no limit.
hibernate.dbcp.maxactive 100
# action to take in case of an exhausted dbcp statement pool
# ( 0 = fail, 1 = block, 2= grow)
hibernate.dbcp.whenexhaustedaction 1
hibernate.dbcp.maxwait 120000
# the maximum number of active connections that can remain
# idle in the pool, without extra ones being released, or zero
# for no limit.
hibernate.dbcp.maxidle 10
# the sql query that will be used to validate
# connections from this pool before returning them to the caller.
# hibernate.dbcp.validationquery=todo
## prepared statement cache
hibernate.dbcp.ps.maxactive 100
# action to take in case of an exhausted dbcp statement
#pool ( 0 = fail, 1 = block, 2= grow)
hibernate.dbcp.ps.whenexhaustedaction 1
# the maximum number of milliseconds that the pool will
# wait (when there are no available connections) for a connection
# to be returned before throwing an exception, or -1 to
# wait indefinitely.
hibernate.dbcp.ps.maxwait 120000
hibernate.dbcp.ps.maxidle 100
上边的代码中,首先指明了和数据连接有关的属性元素:database driver、jdbc url、用户账号和密码、dialect("数据库"方言、土语、地方话)等等,dialect为我们使用的每一个数据库进行最佳优化,在hibernate使用手册中你可以到得到每一个数据库的dialect.最后,hibernate.show_sql当设定为"真"的时候,我们可以在hibernate的debug信息中看到hql在执行的时候的sql语句。
剩下的属性元素是用来配置连接池的,这里使用的是用jakarta dbcp(详细信息到jakarta官方网站查看)来实现连接池,同样hibernate也可以用其它的方式来实现此功能,如:c3po(没听说过,呵呵。。)。详细信息进入hibernate文档。
4.3 创建持久对象
在hibernate运行环境搭起来以后,我们开始创建持久对象或是映射文件来开始我们的工作。(通常创建对象和创建映射文件做其一即可,另一个可以通过做好的来自动完成),这里我们从创建持久对象开始,下面是完成以后的代码,hibernate所需要的"持久对象"符合我们经常写的对象的规范,它们没什么差别:
package dbdemo;
import java.util.date;
import java.util.set;
/**
* @hibernate.class table="users"
*
* @author meagle
*
* represents a user
*/
public class user {
private string userid;
private string username;
private string password;
private string emailaddress;
private date lastlogon;
private set contacts;
private set books;
private address address;
/**
* @hibernate.property column="emailaddress" type="string"
* @return string
*/
public string getemailaddress() {
return emailaddress;
}
/**
* @hibernate.property column="lastlogon" type="date"
* @return date
*/
public date getlastlogon() {
return lastlogon;
}
/**
* @hibernate.property column="password" type="string"
* @return string
*/
public string getpassword() {
return password;
}
/**
* @hibernate.id generator-class="assigned" type="string"
* column="logonid"
* @return string
*/
public string getuserid() {
return userid;
}
/**
* @hibernate.property column="name" type="string"
* @return string
*/
public string getusername() {
return username;
}
/**
* @param string
*/
public void setemailaddress(string string) {
emailaddress = string;
}
/**
* @param string
*/
public void setlastlogon(date date) {
lastlogon = date;
}
/**
* @param string
*/
public void setpassword(string string) {
password = string;
}
/**
* @param string
*/
public void setuserid(string string) {
userid = string;
}
/**
* @param string
*/
public void setusername(string string) {
username = string;
}
/**
* @hibernate.set role="contacts" table="contacts"
* cascade="all" readonly="true"
* @hibernate.collection-key column="user_id"
* @hibernate.collection-one-to-many class="dbdemo.contact"
* @return java.util.set
*/
public set getcontacts() {
return contacts;
}
/**
* @param set
*/
public void setcontacts(set set) {
contacts = set;
}
/**
* @hibernate.set role="books" table="book_user_link"
* cascade="all" eadonly="true"
* @hibernate.collection-key column="userid"
* @hibernate.collection-many-to-many
* class="dbdemo.book" column="bookid"
* @return java.util.set
*/
public set getbooks() {
return books;
}
/**
* @param set
*/
public void setbooks(set set) {
books = set;
}
/**
* @hibernate.one-to-one class="dbdemo.address"
* @return dbdemo.address
*/
public address getaddress() {
return address;
}
/**
* @param address
*/
public void setaddress(address address) {
this.address = address;
}
}
4.4 ant and xdoclet
如果你仔细看了上边的代码,你会发现它有一点和我们以前的"不太一样",它javadoc中多了许多特定的javadoc,是的,那是hibernatedoclet。它和xdoclet是"姊妹篇",xdoclet是这样一种工具:它通过和apache ant一起来产生应用程序的部署描述符。因此除非你乐意书写xml映射文件,否要就会用到xdoclet(但是我还是建议初学者还是要给自己些机会手写**.hbm.xml)。 进入查看hibernatedoclet的详细信息,下面我们看看,这里是如果利用对象来产生映射文件的,下面,看看build.xml:
<!-- this file uses apache ant 1.5.3 beta 1 -->
<project name="hibernate example" default="about" basedir=".">
<!-- the location where your xdoclet jar files reside -->
<property name="xdoclet.lib.home" value="c:/java_api/xdoclet-1.2b3/lib"/>
<target name="clean" depends="init" description="removes all directories related to this build">
<delete dir="${dist}"/>
</target>
<target name="init" description="initializes properties that are used by other targets.">
<property name="dist" value="dist"/>
</target>
<target name="prepare" depends="init,clean" description="creates dist directory">
<echo message="creating required directories..."/>
<mkdir dir="${dist}"/>
</target>
<target name="hibernate" depends="prepare"
description="generates hibernate class descriptor files.">
<taskdef name="hibernatedoclet" classname="xdoclet.modules.hibernate.hibernatedoclettask"> <classpath>
<fileset dir="${xdoclet.lib.home}">
<include name="*.jar"/>
</fileset>
</classpath>
</taskdef>
<!-- execute the hibernatedoclet task -->
<hibernatedoclet
destdir="."
excludedtags="@version,@author,@todo"
force="true"
verbose="true"
mergedir="${dist}">
<fileset dir=".">
<include name="**/dbdemo/*.java"/>
</fileset>
<hibernate version="2.0"/>
</hibernatedoclet>
</target>
<target name="about" description="about this build file" depends="init">
<echo message=" use this format for the arguments:"/>
<echo message=" ant hibernate"/>
<echo message=""/>
</target>
</project>
下面试运行时模拟的一个结果:
c:eclipseworkspacehibernateexample>ant hibernate
buildfile: build.xml
init:
clean:
[delete] deleting directory c:eclipseworkspacehibernateexampledist
prepare:
[echo] creating required directories...
[mkdir] created dir: c:eclipseworkspacehibernateexampledist
hibernate:
[hibernatedoclet] running <hibernate/>
[hibernatedoclet] generating mapping file for dbdemo.contact.
[hibernatedoclet] generating mapping file for dbdemo.book.
[hibernatedoclet] generating mapping file for dbdemo.address.
[hibernatedoclet] generating mapping file for dbdemo.user.
build successful
total time: 2 seconds
c:eclipseworkspacehibernateexample>
接下来然我们看看,"生成"了什么样的映射文件(*.hbm.xml):
<?xml version="1.0"?>
<!doctype hibernate-mapping public
"-//hibernate/hibernate mapping dtd//en"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class
name="dbdemo.user"
table="users"
>
<id
name="userid"
column="logonid"
type="string"
>
<generator class="assigned">
</generator>
</id>
<one-to-one
name="address"
class="dbdemo.address"
cascade="none"
outer-join="auto"
constrained="false"
/>
<set
name="books"
table="book_user_link"
lazy="false"
inverse="true"
cascade="all"
sort="unsorted"
>
<key
column="userid"
>
</key>
<many-to-many
class="dbdemo.book"
column="bookid"
/>
</set>
<set
name="contacts"
table="contacts"
lazy="false"
inverse="true"
cascade="all"
sort="unsorted"
>
<key
column="user_id"
>
</key>
<one-to-many
class="dbdemo.contact"
/>
</set>
<property
name="emailaddress"
type="string"
column="emailaddress"
not-null="false"
unique="false"
/>
<property
name="lastlogon"
type="date"
column="lastlogon"
not-null="false"
unique="false"
/>
<property
name="password"
type="string"
column="password"
not-null="false"
unique="false"
/>
<property
name="username"
type="string"
column="name"
not-null="false"
unique="false"
/>
<!--
to add non xdoclet properties, create a file named
hibernate-properties-user.xml
containing the additional properties and place it in your merge dir.
-->
</class>
</hibernate-mapping>
一旦创建完映射文件(放在classpath 中,并且和对象是"一对一"关系),你就可以通过hibernate的接口和方法来操纵系统对象。
最后说一下,本文中例子下载包中的内容,每一个单独的例子都有main方法来运行:第一个例子:hibernatedemo.java,增加两个users,并且和address相关联("一对一");第二个例子:hibernatedemoonetomany.java,学习用hibernate进行"一对多"映射;最后,第三个例子:hibernatedemomanytomany.java,学习用hibernate进行"多对多"映射。建议你按照顺序运行,如果你没有使用db2 (e.g. sequences).的话,你也可以跟改数据库。例子包中还有一个例子:hibernatedemohql利用前面的例子产生的数据来说明如何用hql来操纵数据。
作者提供的例子中很简单,但对于初学者却是非常好的一个学习的机会(希望初学者对作者的代码进行运行尝试,有许多东西本文并没有说,不过你可以通过作者的代码得到答案)。希望你能在学习hibernate的时候从此处得到些帮助。
闽公网安备 35060202000074号