随笔-244  评论-80  文章-1  trackbacks-3
  2007年8月1日
最近,我开始使用Eclipse作为我的开发环境。这部分是由于它支持我开发时所使用的许多平台,部分是由于Eclipse是开放的可扩展环境的杰出代表,大家都可以为它的发展贡献自己的力量。我正开始调查人们已经提出的一些扩展。例如,我使用了一个叫做XMLBuddy的小插件来处理XML文件,它很有用。所以,我开始好奇是否有人已经编写了使用Hibernate的插件,最近在编写Developer's Notebook一书时,我做了大量这方面的工作。结果我了解到,这方面确实有一些工作正在进行,本文将探讨其中之一——Hibernate Synchronizer

Hibernate Synchronizer
  迄今为止,在我找到的插件中,Hibernate Synchronizer最令我感兴趣,因为看起来它对以映射为中心的工作流提供了最好的支持,而我的Developer's Notebook一书中就采用了这种工作流。(Hibernate可以用于多种用途,所以请查看可用的
其他插件,如果您的环境需要其他方法,这些插件将更有帮助。)事实上,Hibernate Synchronizer插件让您在修改映射文档时,无需考虑更新Java代码,它会在您进行编辑的时候以一种非常类似于Eclipse的方式自动更新Java代码。通过为每个被映射的对象创建一对类,它比Hibernate的内置代码生成工具更为先进。它“拥有”一个基类,当您修改映射时,它可以随意重写这个基类。它还提供一个扩展了这个基类的子类,可以在这个子类中加入业务逻辑和其他代码,而无需担心它会在您眼皮底下消失。

  因为要适用于以Hibernate映射文档为中心的方法,Hibernate Synchronizer还包括一个用于Eclipse的新编辑器组件,为此类文档提供智能辅助和代码自动完成功能。优秀的DTD驱动的XML编辑器(比如前面提到过的XMLBuddy)可以实现其中的一些功能,但是Hibernate Synchronizer利用对文档语义的理解做得更好。它还提供了一个映射中的属性和关系的图形化视图、创建新元素的“向导”界面,以及其他类似的优点。而且如前所述,在其默认配置中,编辑器会在用户编辑映射文档时自动重新生成数据访问类。

  Hibernate Synchronizer还有其他的功能。它在Eclipse的New菜单中加入了一个区域,为创建Hibernate配置和映射文件提供向导,并在包的资源管理器和其他适当的位置中添加了上下文菜单项,使用户可以轻松访问相关的Hibernate操作。

  好了,现在已经有了足够多的抽象描述,是时候开始实践了!毕竟,您很可能对此产生了兴趣,要不您就不会阅读本文。那么,如何安装与使用Hibernate Synchronizer呢?

安装
  可以使用Eclipse的内置Update Manager安装Hibernate Synchronizer。该插件为Eclipse 2.1和即将发布的Eclipse 3的用户提供独立的更新站点。因为我把Eclipse用于任务关键型的工作,所以我使用的仍然是生产版本,即2.1。在我撰写本文时,Eclipse 3已经进入了“发布候选(release candidate)”阶段。今年夏末,当我从JavaOne会议回来的时候,我非常希望能够升级到生产版本3。(我提及这一点的主要原因是强调下面的指导是基于Eclipse 2而编写的;在Eclipse 3中,某些命令和界面无疑会有所不同,所以如果您准备使用它,确保在遵照这些步骤的同时还要加入自己的判断!我的印象是Hibernate Synchronizer自己的
安装指导是针对Eclipse 3编写的,也许这对您会有所帮助。)

  启动Eclipse,选择Help -> Software Updates -> Update Manager,便可以打开Update Manager。Install/Update透视图打开之后,在Feature Updates视图中右击(或者控件单击(control-click),如果您使用的是单按钮鼠标),选择New -> Site Bookmark,如图1所示。

Figure 1
图1. 向Update Manager添加Hibernate Synchronizer插件站点

  在出现的对话框中,输入所需插件版本的URL。输入的URL取决于您的Eclipse版本:

  还需为新的书签指定一个名称,“Hibernate Synchronizer”就很好。图2显示的对话框包括了我的Eclipse 2.1.2环境中的所有必需信息。

Figure 2
图2. Hibernate Synchronizer插件更新站点的书签

  单击Finish之后,新的书签将出现在Feature Updates视图中,如图3所示。

Figure 3
图3. Hibernate Synchronizer站点现在可用了

  为了实际安装插件,单击书签左侧的三角形展开符号,然后单击其中的下一个三角形展开符号,重复这个过程,直到出现插件的图标为止。单击该图标,Preview视图就会更新,从而显示一个允许安装插件的界面,如图4所示。

Figure 4
图4. 准备安装插件

  单击Install Now,实际安装插件,让Eclipse引领您完成整个过程(图5-10)。

Figure 5
图5.安装Hibernate Synchronizer

Figure 6
图6. 插件许可证协议

  可参见下面的权衡部分,其中有关于许可证协议的一些讨论。在决定在自己的项目中使用Hibernate Synchronizer之前,您可能想仔细阅读一下它。我认为这是很好的做法,但是令人困惑的是,它基于GPL,实际上并非是开源的。

Figure 7
图7. 选择安装插件的位置,使用默认的就可以

Figure 8
图8.对没有签名的插件发出的标准警告

Figure 9
图9.正在安装

Figure 10
图10.安装完毕

  现在插件已经安装完毕,需要退出并重新运行Eclipse,以便使其生效。出现的对话框似乎暗示Eclipse将自动重启,但是根据我的经验,单击Yes只会退出环境,必须手动重启。这可能是Eclipse 2.1的Mac OS X实现的一个局限性;Eclipse 3将成为首个承诺对OS X提供一流支持的版本。不管怎么说,这是一个小问题。如果需要重启Eclipse,现在就重启吧,因为应该开始配置插件了!

配置
  Eclipse重新启动之后,可以关闭Install/Update透视图。打开一个使用Hibernate的Java项目。如果您读过
Developer's Notebook一书中的例子,那么有几个目录可供选择。我将选用第3章中的例子,因为这一章是可以在线阅读的样章。您还可以从该书的站点下载所有例子的源代码。

  如果您准备创建一个新的Eclipse项目,以便使用示例源代码目录中的一个,只需选择File -> New -> Project。选择创建一个Java项目,然后单击Next,为其命名(我使用的是“Hibernate Ch3”,如图11所示),取消对Use default复选框的选择,以便可以告诉Eclipse现有项目目录的位置,然后单击Browse按钮,定位它在驱动器上的具体位置。现在可以单击Finish,创建该项目,但是我一般喜欢单击Next,然后再次检查Eclipse的选择。(当然,如果有什么出错,您始终可以返回并修改项目属性,但是我发现,如果存在库丢失之类的错误,马上就会面对大量的错误和警告,这实在是一件麻烦的事情。)

Figure 11
图11. 创建一个使用Hibernate的新项目

  此处我的警告是多余的。Eclipse清楚目录的架构及用法,还找出了我曾下载和安装过的所有第三方库,以便让Hibernate和HSQLDB数据库引擎能够运行。(该过程的详细情况可以参见Developer's Notebook一书的第1章。)这种智能适应性是Eclipse的重要特性之一。图12显示我们的新项目已经打开并准备好用于实验了。它还显示,Eclipse不喜欢把窗口缩到足够小以适应适当的屏幕快照。从现在起,我只能捕捉窗口的一部分。

Figure 12
图12. 第3章中的示例项目

  接下来要创建一个Hibernate Synchronizer可以使用的Hibernate配置文件。src目录中已经存在一个hibernate.properties文件,它说明了书中例子的配置,但是Hibernate Synchronizer只能使用Hibernate的基于XML的配置方法。所以,我们需要把hibernate.properties文件的内容复制到一个新的hibernate.cfg.xml文件中去。从好的方面来说,这使我们可以见识Hibernate Synchronizer的一项特性,即配置文件向导。选择File -> New -> Other,单击新可用的Hibernate类别,选中Hibernate Configuration File,然后单击Next。

Figure 13
图13. 启动Hibernate Configuration File向导

  当向导启动后,它所提供的用于放置文件的目录取决于当前在Eclipse中选中的文件。出于一致性方面的考虑,一定要把它和properties版本一起放在顶级的src目录中。填入向导所需的其余信息,匹配配置的properties版本,如图14所示。注意,与使用Ant控制Hibernate的执行(这是Developer's Notebook一书中所使用的方法)不同,当调用Hibernate时,我们无法控制当前的工作目录,所以需要在URL中使用一条到数据库文件的完全限定路径。我使用的值是(有点难看):jdbc:hsqldb:/Users/jim/Documents/Work/OReilly/Hibernate/Examples/ch03/data/music。(如果有人能告诉我如何让Eclipse或Hibernate Synchironizer对一个项目使用特定的工作目录,我肯定会很感兴趣。我在Eclipse方面还是一个新手,所以如果知道这种情况是可能的,只是我不知道如何去做,我肯定不会感到吃惊。)

Figure 14
图14. 填写配置文件的详细信息

  填写Driver Class时有一点奇怪:需要单击Browse按钮,并开始输入驱动程序的类名。如果输入“jdbcD”,窗口将只会给出两个选择,很容易就可以找出正确的选择,如图15所示。

Figure 15
图15. 指定HSQLDB驱动程序类

  按照图14所示设置适用于您自己的安装的值之后,就可以单击Finish来创建配置文件。Hibernate Synchronizer现在已经可以使用了。它打开了创建的文件,所以可以看到一个Hibernate的XML配置文件的结构和详细信息。

Figure 16
图16. 生成的配置文件

  一种快速测试该配置是否生效的方式是使用其他的向导界面。选择File -> New -> Other,单击新可用的Hibernate类别,选中Hibernate Mapping File,然后单击Next。当向导出现时,它应该填充了刚才输入的所有设置信息,可以单击Refresh按钮来确定它可以与数据库通信,它还会显示找到了一个TRACK表。第一次这样做的时候,由于某种原因,您可能必须确认包含HSQLDB驱动程序的.jar文件的位置,但是这种情况只会发生一次。不管怎样,确认了一切正常之后,单击Cancel,而不是实际创建映射,因为我们想使用手动创建的已有映射文件。

  生成代码
  这很可能是您一直期待的部分。我们可以做些什么特别的呢?马上就有一个可用于Hibernate映射文档的新上下文菜单项。

  如果右击(或控制单击)任意一项,将会看到很多与Hibernate相关的选项(图17),其中包括一个同步选项。这是一种手动让Hibernate Synchronizer生成与映射文档相关的数据访问对象的方式。

Figure 17
图17.映射文档的同步选项

  Add Mapping Reference选项也很有用:它在主Hibernate配置文件中添加了一项,告知有关该映射文档的信息,所以无需在源代码中加入任何内容来要求配置相应的映射。现在我们来看看选择Synchronize Files的结果。

  到这里事情开始变得有趣了。出现了两个新的子包,一个用于Hibernate Synchronizer“拥有的”“基”数据访问对象,可以在任何时候进行改写,而另一个用于为这些DAO生成子类的业务对象,它不会被重写,这为我们提供了一个向数据类添加业务逻辑的机会(如图18所示)。

Figure 18
图18. 经过同步的数据访问对象,显示了可编辑的子类

  这样生成的类比使用常规的Hibernate代码生成工具生成的类要多很多,这有一些优点以及一些潜在的缺点,在稍后的权衡部分中我们将讨论这些。还要注意,可以在项目的属性配置中选择生成其中的哪些类,以及生成它们的包结构。我本来应该演示一下的,但是当前的插件版本有一个bug,它阻止了在Mac OS X上对这个配置界面进行访问。补丁已经开发出来了,但尚未发布。

  基于Hibernate Synchronizer页面上的例子与下面的类,我试图使用这些新的数据访问对象插入一些数据到音乐数据库中。这十分类似于使用标准Hibernate代码生成器的版本(在Hibernate: A Developer's Notebook一书的39-40页),甚至更为简单,因为Hibernate Synchronizer生成的类针对每项数据库操作都创建并提交了一个新事务,所以在像这样简单的场境中,您不需要编写代码来设置事务。(当然了,要让一组操作作为单个事务运行,有很多种方法。)下面是新版本的代码:

package com.oreilly.hh;import java.sql.Time;import java.util.Date;import net.sf.hibernate.HibernateException;import com.oreilly.hh.dao.TrackDAO;import com.oreilly.hh.dao._RootDAO;/** * Try creating some data using the Hibernate Synchronizer approach. */public class CreateTest2 {    public static void main(String[] args) throws HibernateException {        // Load the configuration file        _RootDAO.initialize();                // Create some sample data        TrackDAO dao = new TrackDAO();        Track track = new Track("Russian Trance", "vol2/album610/track02.mp3",            Time.valueOf("00:03:30"), new Date(), (short)0);        dao.save(track);                track = new Track("Video Killed the Radio Star",            "vol2/album611/track12.mp3", Time.valueOf("00:03:49"), new Date(),            (short)0);        dao.save(track);                // We don't even need a track variable, of course:        dao.save(new Track("Gravity's Angel", "/vol2/album175/track03.mp3",            Time.valueOf("00:06:06"), new Date(), (short)0));    }}

  当我编写这些代码时,可以使用Eclipse是一件十分惬意的事情。我已经忘了当我为书籍编写例子时,我多么希望可以使用智能代码完成功能,而且JDT在其他方面也同样能帮上忙。

  为了在Eclipse中运行这个简单的程序,我们需要设置一个新的Run配置。选择Run -> Run...,把CreateTest2.java作为当前的活动编辑器文件。单击New,Eclipse就会知道我们想要在当前项目中运行这个类,因为我们使用main()方法创建它。它指定的默认名称是CreateTest2。界面应该如图19所示。单击Run,试着创建一些数据。

Figure 19
图19.准备好在Eclipse中运行我们的创建测试

  如果您确实遵照了这些步骤,您就会发现执行时首次尝试将会失败:Hibernate抱怨说配置文件没有包含映射引用,而按要求至少要有一个。啊哈!所以,这就是图16底部XMLBuddy出现黄色下划线警告的原因。我们可以很容易地解决这个问题,具体方法是在Package Explorer视图中的Track.hbm.xml映射文档上右击,然后在新的Hibernate Synchronizer子菜单中选择Add Mapping Reference。这对XMLBuddy来说是正确的做法,可以让运行继续。遗憾的是,运行没有继续多久。下一个错误是无法在JNDI中找到JTA UserTransaction初始上下文。显然我并非惟一遇到这种问题的人,在一个论坛主题中相关的讨论如火如荼,但是还没有人找到解决方案。

  因为知道我不需要使用JTA,所以我想知道为什么Hibernate要尝试找到JTA。我打开了Hibernate配置文件(图16),然后寻找Hibernate Synchronizer中的任何可疑之处。无疑有几行是最有嫌疑的:

<property name="hibernate.transaction.factory_class">        net.sf.hibernate.transaction.JTATransactionFactory  </property>  <property name="jta.UserTransaction">        java:comp/UserTransaction  </property> 

  我试着把上述内容注释掉并再次运行,这第三次运行成功了。运行没有出现错误,我的数据出现在数据库中。哇!运行可以信赖的antdb目标(Developer's Notebook一书的第1章中对此有说明)便可以看到所有数据(确实很简单),如图20所示。如果您要这样做,要确保从一个antschema开始创建数据库模式,或者清空来自前面实验中的任何测试数据。

Figure 20
图20.测试程序所创建的数据

  注意,可以在Eclipse中运行Ant目标,具体方法是右击(或控制单击)Package Explorer中的build.xml文件,选择Run An,然后使用Eclipse对话框选择目标。酷吧?

Figure 21
图21.在Eclipse中运行Ant

  使用查询取回数据相当简单,尽管这次的代码很接近于常规的使用Hibernate生成的普通数据访问类所使用的代码。即使Hibernate Synchronizer为处理指定查询生成了大量帮助器方法,我还是认为它们中间没有哪一个方法特别有用,因为它们都坚持运行查询后返回结果列表,而不是提供可以直接使用的Query对象。这使您无法使用Query的方便的类型安全的参数设置方法。因为这一点,我决定一定要让RootDAO对象为我提供一个Hibernate Session,以便使用老式的方法。我认为我可以编辑Hibernate Synchronizer使用的任何模板来生成我想要的任何方法,如果我要使用它来开发一个项目,我几乎肯定我会这么做。

  实际上,进一步考虑的话,因为当获得一个活动的Session时,您只能处理Query,DAO所提供的方法已经达到了最佳效果。如果您想像我在这个例子中所做的那样处理查询,您必须总是自己进行会话管理。可以把会话管理嵌入到“您自己的”那一半DAO所提供的业务逻辑中,这就可以同时利用两方面的好处了。这正是Hibernate Synchronizer提供的拆分类模型如此有用的另一个原因。我将在下面对此进行深入探讨。

  不管怎样,下面是我第一次想出的代码,基本上等同于书中48-49页上给出的代码:

package com.oreilly.hh;import java.sql.Time;import java.util.ListIterator;import net.sf.hibernate.HibernateException;import net.sf.hibernate.Query;import net.sf.hibernate.Session;import com.oreilly.hh.dao.TrackDAO;import com.oreilly.hh.dao._RootDAO;/** * Use Hibernate Synchronizer's DAOs to run a query */public class QueryTest3 {    public static void main(String[] args) throws HibernateException {        // Load the configuration file and get a session        _RootDAO.initialize();        Session session = _RootDAO.createSession();        try {            // Print the tracks that will fit in five minutes            Query query = session.getNamedQuery(                TrackDAO.QUERY_COM_OREILLY_HH_TRACKS_NO_LONGER_THAN);            query.setTime("length", Time.valueOf("00:05:00"));            for (ListIterator iter = query.list().listIterator() ;                 iter.hasNext() ; ) {                Track aTrack = (Track)iter.next();                System.out.println("Track: \"" + aTrack.getTitle() +                                   "\", " + aTrack.getPlayTime());            }        } finally {            // No matter what, close the session            session.close();        }    }}

  TrackDAO提供的一个优秀特性是静态常量,通过它,我们可以请求指定查询,消除任何由于字符串输入错误而引起运行时错误的可能性。我喜欢这一点!为这个测试类设置和执行Run配置,将会生成预期的输出,如图22所示。

Figure 22
图22. Eclipse控制台视图中的查询结果

  我前面提到过,运行这个类之后,我意识到,借助于Hibernate Synchronizer所提供的模型,可以用一种更好的方法来实现它。因为指定查询是与该数据访问对象相关的映射文件的一项特性,所以如果我们将查询放入TrackDAO对象中(这才是它真正属于的地方),那么这个对象看起来应该是下面这个样子:

package com.oreilly.hh.dao;import java.sql.Time;import java.util.List;import net.sf.hibernate.HibernateException;import net.sf.hibernate.Query;import net.sf.hibernate.Session;import com.oreilly.hh.base.BaseTrackDAO;/** * This class has been automatically generated by Hibernate Synchronizer. * For more information or documentation, visit The Hibernate Synchronizer page * at http://www.binamics.com/hibernatesync or contact Joe Hudson at joe@binamics.com. * * This is the object class that relates to the TRACK table. * Any customizations belong here. */public class TrackDAO extends BaseTrackDAO {    // Return the tracks that fit within a particular length of time    public static List getTracksNoLongerThan(Time time)        throws HibernateException    {        Session session = _RootDAO.createSession();        try {            // Print the tracks that will fit in five minutes            Query query = session.getNamedQuery(                QUERY_COM_OREILLY_HH_TRACKS_NO_LONGER_THAN);            query.setTime("length", time);            return query.list();        } finally {            // No matter what, close the session            session.close();        }    }}

  这样做更好更清晰,还进一步简化了QueryTest3中的main()方法:

    public static void main(String[] args) throws HibernateException {        // Load the configuration file and get a session        _RootDAO.initialize();        // Print the tracks that fit in five minutes        List tracks = TrackDAO.getTracksNoLongerThan(Time.valueOf("00:05:00"));        for (ListIterator iter = tracks.listIterator() ;             iter.hasNext() ; ) {            Track aTrack = (Track)iter.next();            System.out.println("Track: \"" + aTrack.getTitle() +                               "\", " + aTrack.getPlayTime());        }    }

  显然,这是一种在使用Hibernate Synchronizer时处理指定查询的方法。做一次快速测试就可以确认它生成同样的输出,而且它的代码也要好很多。

  您是否想使用Hibernate Synchronizer来生成它自己的数据访问对象类型暂且放下,我们还有最后一项重要特性要探讨。

编辑映射
  Hibernate Synchronizer的一个主要吸引力就在于它为映射文档提供的专业化的编辑器。可以配置这个编辑器,以便只要保存文件就自动重新生成相关数据对象,但是这只是一个锦上添花的功能;即使不打算使用该插件的代码生成器,您也可能希望使用这个编辑器。它为您提供映射文档元素的智能完成功能,以及一个图形化的大纲视图,可以在这个视图中操纵这些元素。

  但是,如果从Developer's Notebook一书中的下载源代码开始,就至少得有一项技巧才可以让编辑器工作。在下载的文件中,映射文档的扩展名为“.hbm.xml”,而只有以“.hbm”结尾的文件才能调用编辑器。理论上,可以在Eclipse中配置扩展名映射,以便使具有这两种扩展名的文件都能使用插件的映射文档编辑器,但是我无法使其生效,而且我注意到支持论坛上有人面临着与我相同的问题。所以,至少目前最好的做法就是重命名文件。(如果您坚持使用基于Ant的标准代码生成,请确保更新build.xml中的codegen目标以使用新的扩展名。)

  在我把Track.hbm.xml重命名为Track.hbm之后,它在Package Explorer中的图标就更新为Hibernate徽标,而默认的编辑器则变为插件的编辑器,如图23所示。由于某种原因,其他的Hibernate Synchronizer选项(如图17所示)对于其中任意一种扩展名都是可用的,但是编辑器只对较短的版本可用。

Figure 23
图23. Hibernate映射文档(扩展名为“.hbm”)的上下文菜单

  编辑器为映射文档中添加的所有元素都提供上下文相关的自动完成支持。图24显示了一些例子,但是屏幕截图无法真正捕捉到该特性的深度和有效性。我强烈建议您安装插件并使用它。您很快就会看到它在处理映射文档方面是多么有用。

Figure 24

Figure 25
图24和25.映射文档编辑器中的完成辅助功能

  如图26所示,大纲视图提供了一个关于类层次结构、被映射的元素、指定查询以及映射文档中的各种元素的图形化视图,还提供一个菜单,其中有一些向导可以帮助创建新的属性。

  Figure 26

  Figure 27

图26和27. 映射编辑器的大纲视图和“Add property”向导

  编辑器中的上下文菜单还提供一个Format Source Code选项,可以使用它来整理和重新构造文档。这个编辑器中已经有了很多灵巧和有用的特性,看它们如何发展也是一件有趣的事情。惟一使我感到不满的是(并不是什么大问题),当完成XML属性时,这个编辑器用来帮助管理引号的方法与JDT在Java代码中使用的方法完全不同。在它们之间来回切换会把人弄迷糊。(您需要一些时间适应JDT的工作方式,但是一旦您开始信任它,它就会变得魔力无穷。)

生成数据库模式
  尽管我的第一印象是所有内容都来自映射文档,但是Hibernate Synchronizer目前不支持从映射文档创建或更新数据库模式。支持论坛上已经提出了相关的请求,所以如果将来看到这些特性,我肯定不会吃惊,因为提供这类支持并不很困难。目前来说,如果要从映射文档开发数据库,就必须使用像Hibernate: A Developer's Notebook书中这种Ant驱动之类的方法。此外,下面描述的Hibernator插件支持在Eclipse中进行模式更新。我可能要研究一下是否可以同时安装这两种插件。

  我希望本文能够让您清楚地了解该插件所提供的功能。无论如何,我没有涵盖它的所有功能,所以如果您有兴趣,可以去下载它然后自己进行探索。

权衡
  毫无疑问,可以使用Hibernate Synchronizer来完成一些灵巧的工作。是否要在我自己的Hibernate项目中使用它呢?这样做有优点也有缺点,很可能直到实际采用Hibernate来代替我们正在使用的自己开发的(且过分简单的)轻量级O/R工具时,我才会做出决定。这是一次意义相当重大的改动,而我们把这次改动推迟到了由于其他原因进行架构变换的时候。下面是对我的决定起着重要作用的一些因素。

  正如我们在安装小节中所谈到的那样,在许可证方面还存在着一点问题。插件的论坛中有此方面的讨论。当前的许可证基于对GNU GPL的定制修改,这次修改删除了所有源代码共享方面的条款,但是试图保留“copyleft”保护的其他方面。关于这样做的合法性仍然存在一些问题,而作者正在寻求另一种解决办法。很清楚,目的是要保护插件,而不是阻止其他任何项目使用该插件生成代码,但是有必要仔细阅读当前的许可证,看一看其目的是否已经达到,或者您是否仍然冒着很大的风险。

  同一讨论表明,作者原来是以开源的形式发布插件的,但是又临时收回了这一决定,因为他觉得它还不够完美以用作一个优秀的范例。接着,他与一些莽撞的人通过一些非常不愉快的邮件,这使他不愿再共享源代码。当然,他有权决定是否与我们共享源代码。该插件对于整个世界来说是一份大礼,而作者并不欠我们什么。但是我希望他能与其他用户进行足够的正面交流,这样就能坚定他原来共享源代码的想法。我真的认为能够看到我使用的工具的源代码是一件很有价值的事情,不仅因为这是一个很好的学习机会,还因为这意味着,如果需要的话我可以立刻修复一些小问题。作者在解决用户的问题方面始终很热心,响应也很快,但是一个人无法维持一个社区,因为我们都有繁忙、筋疲力尽和心烦意乱的时候。

  Hibernate Synchronizer使用它自己的模板和机制来生成数据访问类,这既有优点也有缺点。优点是可以获得比Hibernate的“标准”代码生成工具更多的功能。可以使用数据对象的一个自动生成的子类,并在该数据对象中嵌入业务逻辑,而无需担心重新生成访问代码时这些业务逻辑会被改写,这也是一个不错的特性。插件生成的使很多简单的场景更简单的类还提供了其他的优点。

  另一方面,这还意味着,当Hibernate平台增加一些新的特性或者做了改动之后,Hibernate Synchronizer生成的代码就有些滞后于Hibernate了。在对Hibernate不常使用的模式的支持方面,插件代码也存在bug:它的用户群很小,一个人就可以让它保持更新。您可以在讨论论坛上找到这种现象的证据。

  和很多事情一样,潜在的优点是否超过风险要由您来决定。即使不使用代码生成器,您也会发现映射编辑器非常有用。如果您只想使用编辑器的自动完成和辅助功能,可以关掉自动同步选项。

  如果您使用过该插件,并且发现它很有用,我建议您联系其作者,表达您的谢意,并考虑捐出一些资金来帮助支持它的未来发展。

其他插件
  迄今为止,我还找到了另外两个也提供Eclipse中的Hibernate支持的插件。(如果您还知道有其他的插件,或者将来遇到了这样的插件,我很愿意知道它们。)或许将来我还会撰写有关这些插件的文章。

HiberClipse
  
HiberClipse插件看起来也是一种非常有用的工具。它似乎适用于数据库驱动的工作流,在这个工作流中,已经有了一个数据库模式,而您想构建一个Hibernate映射文件和Java类来使用该模式。这是一种很常见的场景,如果您发现自己面临着这样的难题,我强烈推荐您使用这个插件。它提供了一项非常酷的特性:在Eclipse中为所使用的数据库提供图形化的“关系视图”。(我应该指出,如果您想从一个现有的数据库模式开始,Hibernate Synchronizer也会有所帮助的。它的New Mapping File Wizard可以连接到您的数据库,并基于所发现的内容构建映射文件。)

在Eclipse中使用Hibernate
图28. Hibernate Synchronizer的映射向导

Hibernator
  最后一个,
Hibernator似乎倾向于另一个方向,即,从Java代码开始生成简单的Hibernate映射文档,然后让您从映射文档构建(或更新)数据库模式。它还提供在Eclipse中运行数据库查询的能力。在这3种插件中,它所处的开发阶段似乎最早,但是已经值得关注了,特别是因为它的开发者是Hibernate开发团队的成员。
posted @ 2007-08-01 13:43 kenty 阅读(156) 评论(0) 编辑
----摘自http://blog.sina.com.cn/andyfang
这次我们谈一谈JAVA操作数据库的另外一种方式,那就是Hibernate组件。
    Hibernate是中小型项目使用的最多的一种持久化技术。而它最重要的概念就是ORM(对象-关系映射)。对于这个概念,许多资料上的解释都不一样,比较大众化的解释是,将数据库中的记录映射到程序中,使其成为对象来操作,就象操作JAVA的普通类一样。除了这些,它还可以为我们管理连接对象,事务处理等操作。所以使用起来很方便。
    首先我们来看一下一个简单的Hibernate程序的结构。它有两个xml文件很重要,第一个就是*.cfg.xml文件。它描述了Hibernate和数据库之间的配置信息。比如连接池、数据源。
 
    这里不得不说到的一个类就是SessionFactory类,它是Hibernate里面最重要的一个类。基本上所有其他操作API都是从这个类获得实例的。而这个配置文件中,描述了SessionFactory的基本内容,也就是连接信息,和另外一个非常重要的内容,就是:*.hbm.xml文件。其实,如果你使用工具的话(比如MyEclipse,JBuilder),这些东西都可以自动生成。先还是讲一下这些东西都描述的是些什么内容吧。
    myeclipse.connection.profile  它描述的是一个连接的名字
    connection.url  它描述了连接字符串
    connection.username/connection.password  描述了用户名和密码
    connection.driver_class  描述了驱动类
    dialect  这个东西描述了Hibernate对Oracle9i提供支持的类
    在这个文件中还可以描述事务和其他的操作。
    另外一个重要的文件是*.hbm.xml文件,它描述了数据库中的表的信息。不过在说这个文件之前,我们先要用类来描述数据库中的记录。
    public class StuInfo
    {
      private Integer stuID;
      private String stuName;
      private void setStuID(Integer id){this.stuID=id;}
      public Integer getStuID(){return stuID;}
      public void setStuName(String name){this.stuName=name;)
      public String getStuName(){return stuName;}
    }
    需要注意的是:在JAVA中不能用基本数据类型去描述数据库中的类型。然后我们再来看看*.hbm.xml文件中是如何描述的吧。
    需要说明的是,这里使用了Oracle里面的序列来实现自动增长,而且让stuID字段的set方法是私有的。这是因为stuID字段是主键字段,而且又是自动增长的,所以不需要人工修改它的值。
    以上这些都只是使用Hibernate的准备工作,接下来就可以操作Hibernate来实现数据库的逻辑。
    首先必须创建一个Configuration对象来加载我们的*.cfg.xml文件。
    Configuration cfg = new Configuaration();
    cfg.configure("/cfg/hibernate.cfg.xml");//cfg包下面
    然后用这个对象来创建SessionFactory对象。这个对象很重要,它是用来创建Hibernate会话和其他类的重要类。
    SessionFactory factory = cfg.buildSessionFactory();
    Session session = factory.openSession();
    有了Session之后,我们就可以用它来创建各种操作数据库的类。先来说说查询吧。我们需要使用一种叫做HQL的查询语言,它是Hibernate特有的一中查询语言,和我们熟悉的SQL很象。当然Hibernate还提供了其他的查询方式,等下次有机会的时候再说吧。
    Query query = session.createQuery("from StuInfo");
    List list = query.list();//执行查询并获得集合
    //迭代数据
    Iterator it = list.iterator();
    while(it.hasNext()){
      StuInfo stu = (StuInfo)it.next();
      System.out.println(stu.getStuName());
    }
    这就是一个简单的查询。这里需要注意的是,查询语句中的表必须和StuInfo.hbm.xml中映射的表名一样,其他的字段也是。下面说说插入、更新和删除。
    想要修改和更新,或者删除,我们必须先得到要修改的对象。所以先要使用查询来获取对象。而且修改的过程当中有可能会报错,所以我们要使用事务处理。
    Transaction trans = session.beginTransaction();
    trans.begin();//开始事务
    Query query = session.createQuery("from StuInfo where stuID=1");
    List list = query.list();
    if (!list.isEmpty()) {
      Iterator it = list.iterator();
      StuInfo stu = (StuInfo) it.next();
      stu.setStuname("roddick");
      session.update(stu);//修改更新,删除是session.delete(stu);
      trans.commit();//提交事务
      session.close();//关闭会话
    }
    以上就是Hibernate的简单操作,是不是很容易。需要注意的是,这里的表映射是没有主外键关系的,如果表和其他表有主外键关系的话,在创建SessionFactory对象的时候是会报错的。要处理这种情况必须在建立映射关系的时候考虑一对多映射。这种情况我们下次有机会再说吧。
    可能有不正确的地方,欢迎大家指正!!!有事可以联系我的邮箱,或在博客留言,一定回复!!!
===============================================================================================
上次说到了JAVA操作数据库的连接方法,这次我们来谈如何操作数据库。
      操作数据库无非就是熟悉sql语句,只要你对sql语句比较熟的话,也就没有什么可难的了。操作数据库有三大接口,只要熟练掌握了这三大接口的用法,数据库的操作基本上就OK了。
      首先最基础的接口就是Statement接口。它提供了操作数据库的基本方法。通过Connection的createStatement()方法获得实例。
      Statement stmt = conn.createStatement();
      获得Statement接口的实例之后,如果你要执行查询,就需要使用到executeQuery()方法;如果你要执行插入或更新或者删除的话,就使用executeUpdate()方法。需要注意的是executeQuery()方法返回的是ResultSet类型,而executeUpdate()方法返回的是整型。
      ResultSet rs = stmt.executeQuery("select * from stuInfo");
      int i  =stmt.executeUpdate("insert into stuInfo values(1,'andy')");
      如果你执行的查询就用ResultSet来接收查询的结果。如果你执行的插入、删除或者修改的话,就用一个整型来接收。并且可以通过这个值来判断执行是否成功。
      第二个接口就是PreparedStatement接口,它继承自Statement接口,丰富了Statement接口的方法。与Statement不同的地方便是它可以预编译执行。执行的效率要比Statement接口高。它通过Connection对象的prepareStatement()来获得实例。
      PreparedStatement pstmt = conn.prepareStatement("select * from stuInfo where uid=?");
      //设置第一个参数的值为 9
      pstmt.setInt(1, 9);
      ResultSet rs = pstmt.executeQuery();
      在这里,我们使用了预编译参数的设置,然后执行查询,获得记录集。需要注意的是有一个东西不能设置成预编译的参数,那就是表的名字。也就是"select * from ?",这样会报错的,或者执行不成功。增、删、改操作同上。重要的地方就是定义和设置参数的值。
      第三个接口就是CallableStatement接口,这个接口最重要的地方就是调用存储过程或者函数,要知道在项目中使用存储过程或者函数是多么的重要。它继承自PreparedStatement接口,自然也能够预编译。
      CallableStatement cstmt = conn.prepareCall("{call myProcedure(?,?)}");
      //设置参数的值
      cstmt.setInt(1,2);
      cstmt.setString(2,"Andy");
      //返回布尔值,可以用来判断执行是否成功
      cstmt.execute();
      上面这段语句便是调用了存储过程myProcedure(),它有两个参数,分别是整型和字符串类型。调用execute()方法,返回一个布尔值,来判断是否执行成功。以oracle为例,如果调用的是函数,那么还可以用它来获得返回值。例如:
      int result = cstmt.getInt(3);
      这次就写到这里吧,有写的不正确的地方请指正,有不明白的地方可以发邮件给我,或者在博客里留言。下次我们会谈一谈使用轻量级组件来操作数据库。
===========================================================================================
最近很多朋友都发邮件问我关于JAVA操作数据库的问题。其实这些都没什么很难的问题,不过内容还是蛮多的,所以决定写个总结。
    由于内容很多,而且还需要整理,所以先从连接的问题开始吧。想要例子的朋友,可以发邮件给我,有写的不正确的地方也欢迎评论。
    总结了一下,由于数据库的种类很多,连接的字符串也不一样。所以我们只谈连接的方式,而且以Oracle为例。大概有两种连接方式是比较常用的:
    第一种是使用本地驱动连接,这种方式在学习的时候用的很多,但在实际的项目过程中很少会用到。首先当然是要有数据库的驱动包,也就是classes12.jar包。然后在类中注册驱动,并获取连接
    Class.forName("oracle.jdbc.driver.OracleDriver");
    Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ORAC","scott","tiger");
    它是以文本传值的方式注册驱动。它还有另外一种形式:
    DriverManager.registerDriver(new OracleDriver());
    DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:ORAC","scott","tiger");
    它是以匿名产生对象的方式来注册驱动,理论上速度要快一些。
    第二种方式是通过JNDI获取连接池中的连接对象。这种方式使用的也比较多。因为在实际的项目中,我们往往都是通过应用服务器来管理连接对象的创建和销毁。这样会提高资源的利用率。而且也不需要我们自己来管理,两个字--方便。
    以Weblogic为例,首先需要导入weblogic.jar包,当然肯定是要在服务器上配置连接池和数据源。
    //初始化信息,包括weblogic的工厂方法和url信息;
    Hashtable ht=new Hashtable();
    ht.put(Context.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
    ht.put(Context.PROVIDER_URL,"t3://localhost:7001");
    InitialContext ctx=new InitialContext(ht);
    //通过JNDI获得数据源
    DataSource obj=(DataSource)ctx.lookup("MyOracleJNDI");
    //从数据源获得连接对象
    Connection conn=obj.getConnection();
    其实在一些中小项目中,我们会使用到一些轻量级的组件,例如Hibernate,来操作数据库。利用它来管理一些例如事务处理,优化数据库操作之类的事情。
posted @ 2007-08-01 13:09 kenty 阅读(563) 评论(0) 编辑