返回介绍

Hibernate 标注

发布于 2025-04-21 21:42:13 字数 6083 浏览 0 评论 0 收藏

如果你以前从没有接触过标注,本章的代码示例将看起来有点奇怪,所以,现在最好先花点时间讨论一下 Java 标注的历史和目的。基本上,一个标注就是为一段代码添加一些信息(在 Java 世界中,通常是一个类、字段或方法),以便帮助其他工具理解如何使用这段代码,或是进行某些自动化处理以节约你的工作量。与持久化映射不同的是,为了与源代码保持一致,不用将标注放到一个单独的文件中,而是直接将标注放在它所影响的源代码文件中。采用这种方法,你就不用担心与源代码分离保存的文件是否会变得与源代码不同步了。早在 Java 5 为这种编码风格提供健壮的支持以前,人们就通过利用 JavaDoc 工具可扩展的特性,发现了一种取得这种支持的“后门”。

JavaDoc 也是一种形式的标注,从 Java 开始就一直存在,它的目的是让开发人员为他们的类和 API 生成高质量的文档,而且还不必维护除源代码以外的任何文件。JavaDoc 的工作效果非常好,所以人们就想用它来做些其他事情。XDoclet( [1] )项目是一个很流行,而且也比较复杂的框架,它以多种有趣的方式对 JavaDoc 进行了扩展。Hibernate 也开发了一套丰富的 Hibernate XDoclet 标签。

也正因为这种方法的功能如此强大,所以 Sun 在 Java 5 中内建了完整的、通用的标注支持。这样,就不再需要那些借用 JavaDoc 注释的复杂工具了。现在 Java 编译器本身就可以处理标注(并提供了它自己的一些标注来控制单独的类、方法甚至字段上的特定警告信息)。利用反射(reflection)机制来解析标注(如果将它们配置为要保留在编译后的类中),也能够非常容易地定义自己的标注类。所以,Hibernate 自然采用了 Java 5 的原生(native)标注。

趋同进化(convergent evolution)的速度如此之快,使用标注为类配置映射的能力具有的强大吸引力,已经让 Hibernate 自己的标签深深地影响了 EJB 3 规范。如此有用的 Hibernate 风格的持久化要是能够在成熟的 Java Enterprise Edition(EE)环境以外也可以使用,这给人们留下深刻的印象,所以他们定义了 Java Persistence API(经常称为 JPA),作为一种可以在普通的 Java Standard Edition(SE)环境可以独立使用的持久化组件。因为有规范作为基础,Hibernate 自然会采用直接支持它们,所以现在通过 EJB 3 和 JPA 接口以及标注,也可以使用 Hibernate 能够提供的许多功能,而应用程序本身根本不必依赖(或知道)Hibernate。

我们不打算介绍那么多功能,因为我们在 Hibernate 中使用的一些功能需要使用它自己的标签。事实上,在你习惯使用 Hibernate,并领会到它是一种非常灵活而强大的体系,可以“按照你需要的任何方式,持久化任何普通 Java 对象”以后,你会发现按照 JPA 规范的要求来封装你的代码将不得不考虑太多的限制,除非项目确实强制要求需要屏蔽底层的持久化实现(可能你会想办法尽量避免这样的项目)。不过,如果你正在使用 Hibernate,有时使用标注而不是 XML 文件来配置映射,也是一种不错的选择。

为何在意

在熟悉了标注的语法以后(一些像 Eclipse 之类的 IDE 已经提供了惊人的能力,支持标注的理解、文档化以及自动完成,甚至自定义供你自己使用的标注,第 14 章将介绍这种技术),编写映射文档就只需要涉及一种文件格式的建立和读取了。

注意:什么!为什么不早点介绍这么酷的东西?

如前所述,如果将数据对象和映射规定都放在同一个文件中,那么它们不能保持同步的可能性就很小,阅读代码也可以马上理解正在进行的处理。添加标注后的代码将是一种自文档化的(self-documenting)代码。一些标注提供的默认设置经常也可以减少你需要进行配置的工作量。用标注指定所有设置,你会发现标注语法比 XML 文件更精简,而且需要输入的文字量比较少,或者让 IDE 帮你完成输入后,只要读一下就可以了。

当你可以直接控制数据库时,这种方法可能会很有成效,只是这种方法会将映射和数据对象紧密地耦合在一起(或者,如果你正在设计数据对象以处理特定的、固定格式的数据库结构,这样也同样会产生紧密耦合)。这意味着,如果你想用一个对象模型来支持多个数据库,使用标注就不是一种好的选择了;在这种情况下,外部映射文件的独立性其实是它的优势,因为不需要修改 Java 源代码,只要为不同的数据库提供单独的映射文件就可以了。

许多其他 Java 工具也采用标注作为配置和集成的手段,第 13 章将介绍相关内容。在使用这些工具时,同时也使用 Hibernate 的标注也是非常方便的。其实,其他工具可能也要依赖 Hibernate 标注。就算我们不计划在本书的新版中介绍标注的使用,介绍 Spring 的那一章也会强制我们面对这一问题。

对于标注的批评之一就是它让数据库细节分散到 Java 源代码的各个角落。所幸,一些有用的 Hibernate 工具可以根据 Hibernate XML 映射文件和 Hibernate 标注生成 Hibernate 映射文档。有关 hbm2doc 的更多信息,请参阅第 12 章的 12.8.2 节。

应该怎么做

我们需要做的第一件事就是更新 Maven 的依赖配置,让它取回 Hibernate 标注库。编辑 build.xml 文件中的依赖设置,如例 7-1 所示(增加的部分用粗体显示)。

例 7-1:获得 Hibernate 标注

<artifact:dependencies pathId="dependency.class.path">

<dependency groupId="hsqldb"artifactId="hsqldb"version="1.8.0.7"/>

<dependency groupId="org.hibernate"artifactId="hibernate"

version="3.2.5.ga">

<exclusion groupId="javax.transaction"artifactId="jta"/>

</dependency>

<dependency groupId="org.hibernate"artifactId="hibernate-tools"

version="3.2.0.beta9a"/>

<dependency groupId="org.hibernate"artifactId="hibernate-annotations"

version="3.3.0.ga"/>

<dependency groupId="org.hibernate"

artifactId="hibernate-commons-annotations"

version="3.3.0.ga"/>

<dependency groupId="org.apache.geronimo.specs"

artifactId="geronimo-jta_1.1_spec"version="1.1"/>

<dependency groupId="log4j"artifactId="log4j"version="1.2.14"/>

</artifact:dependencies>

在这个文件中,删除 usertypes 和 codegen 两个构建目标。因为我们要使用标注,所以就不用生成 Java 代码了。另一方面,我们将从 Java 代码文件入手,用它来定义 Hibernate 映射(以及数据库模式),所以在本章中应该废弃这两个构建目标(它们甚至是危险的)。

可以想到,为了适应这种新方法,还需要对 schema 构建目标稍微调整一下,如例 7-2 中突出显示部分所示。

例 7-2:使用标注生成数据库模式

<!--Generate the schemas for annotated classes-->❶

<target name="schema"depends="compile"

description="Generate DB schema from the annotated model classes">

<hibernatetool destdir="${source.root}">

<classpath refid="project.class.path"/>❷

<annotationconfiguration

configurationfile="${source.root}/hibernate.cfg.xml"/>❸

<hbm2ddl drop="yes"/>

</hibernatetool>

</target>

❶需要更新注释和构建目标描述,以反映新的工作方式。

❷我们需要引用编译好的类,以便模式生成工具可以找到其中的标注。注意,这意味着在生成模式之前,需要先把类编译好,这样的处理顺序与该构建目标前面的大多数版本都不相同,但是在第 6 章中增加了这个依赖,所以我们的 schema 构建目标已经依赖编译目标了。

❸最后,让工具使用标注来配置它自己。我们仍旧提供一个全局的 Hibernate 配置文件,以便工具可以知道我们正在使用什么数据库等信息。这个文件也必须列出我们想使用的所有标注类,在调整完所有构建文件后,我们再处理它。

我们的 compile 构建目标需要依赖刚才删除掉的代码生成构建目标,所以在删除这一依赖以前,这个构建目标不能运行。在我们的新方法中,为了支持编译而所有需要做的就是让基本的 prepare 构建目标可以运行。编辑 compile 构建目标,以反映例 7-3 中突出显示部分的变化。

例 7-3:更简单的编译依赖

<!--Compile the java source of the project-->

<target name="compile"depends="prepare"

description="Compiles all Java classes">

<javac srcdir="${source.root}"

destdir="${class.root}"

debug="on"optimize="off"deprecation="on">

<classpath refid="project.class.path"/>

</javac>

</target>

如前面所述,我们需要更新 Hibernate 配置,将原来使用的映射文档列表替换为相应标注过的类(annotated classes)的列表。删除那些映射文档,修改 src 目录下 hibernate.cfg.xml 文件的末尾部分,如例 7-4 所示(同样,需要修改的部分以粗体字显示)。

例 7-4:配置 Hibernate 使用标注

……

<!--Don't echo all executed SQL to stdout-->

<property name="show_sql">false</property>

<!--disable batching so HSQLDB will propagate errors correctly.-->

<property name="jdbc.batch_size">0</property>

<!--List all the annotated classes we're using-->

<mapping class="com.oreilly.hh.data.Album"/>

<mapping class="com.oreilly.hh.data.AlbumTrack"/>

<mapping class="com.oreilly.hh.data.Artist"/>

<mapping class="com.oreilly.hh.data.Track"/>

</session-factory>

</hibernate-configuration>

为什么必须这么做

你可能会问,为什么需要在配置文件中列出标注过的类?Hibernate 不能通过类的标注而自己找到它们?嗯,可以找到它们,如果你修改编码风格,完全依靠 JPA 接口(使用 JPA EntityManager 的 Hibernate 实现,而不是使用 Hibernate Session),不用告诉 Hibernate 到哪找标注过的类,Hibernate 自己就可以找到它们,真令人高兴。但是,如前面所述,本书不打算在标注上做过多介绍。如果坚持使用 Hibernate 的原生接口,就应该显式声明用于执行持久化的类,即使是在使用标注来控制持久化时,也应该这么做。

我们差不多已经做好准备工作了,就差用标注过的类来组成这种方法的核心!接下来就介绍本章真正有趣的部分,看看数据对象的 Java 源代码标注长得什么样儿,它是怎么控制 Hibernate 映射的。

[1] http://xdoclet.sourceforge.net/xdoclet/index.html.

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。