From: <hib...@li...> - 2006-06-23 15:45:50
|
Author: epbernard Date: 2006-06-23 11:45:46 -0400 (Fri, 23 Jun 2006) New Revision: 10042 Added: trunk/HibernateExt/metadata/doc/reference/zh_cn/ trunk/HibernateExt/metadata/doc/reference/zh_cn/images/ trunk/HibernateExt/metadata/doc/reference/zh_cn/images/hibernate_logo_a.png trunk/HibernateExt/metadata/doc/reference/zh_cn/master.xml trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/ trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/entity.xml trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/lucene.xml trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/setup.xml trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/validator.xml trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/xml-overriding.xml trunk/HibernateExt/metadata/doc/reference/zh_cn/styles/ trunk/HibernateExt/metadata/doc/reference/zh_cn/styles/fopdf.xsl trunk/HibernateExt/metadata/doc/reference/zh_cn/styles/html.css trunk/HibernateExt/metadata/doc/reference/zh_cn/styles/html.xsl trunk/HibernateExt/metadata/doc/reference/zh_cn/styles/html_chunk.xsl Modified: trunk/HibernateExt/metadata/doc/reference/build.xml Log: ANN-378 Chinese translation Modified: trunk/HibernateExt/metadata/doc/reference/build.xml =================================================================== --- trunk/HibernateExt/metadata/doc/reference/build.xml 2006-06-22 20:07:07 UTC (rev 10041) +++ trunk/HibernateExt/metadata/doc/reference/build.xml 2006-06-23 15:45:46 UTC (rev 10042) @@ -12,7 +12,10 @@ <param name="docname" value="hibernate_annotations"/> <param name="lang" value="en"/> </antcall> - + <antcall target="lang.all"> + <param name="docname" value="hibernate_annotations"/> + <param name="lang" value="zh_cn"/> + </antcall> </target> </project> Added: trunk/HibernateExt/metadata/doc/reference/zh_cn/images/hibernate_logo_a.png =================================================================== (Binary files differ) Property changes on: trunk/HibernateExt/metadata/doc/reference/zh_cn/images/hibernate_logo_a.png ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/HibernateExt/metadata/doc/reference/zh_cn/master.xml =================================================================== --- trunk/HibernateExt/metadata/doc/reference/zh_cn/master.xml 2006-06-22 20:07:07 UTC (rev 10041) +++ trunk/HibernateExt/metadata/doc/reference/zh_cn/master.xml 2006-06-23 15:45:46 UTC (rev 10042) @@ -0,0 +1,294 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3CR3//EN" +"../../../../../Hibernate3/doc/reference/support/docbook-dtd/docbookx.dtd" [ +<!ENTITY setup SYSTEM "modules/setup.xml"> +<!ENTITY entity SYSTEM "modules/entity.xml"> +<!ENTITY xml-overriding SYSTEM "modules/xml-overriding.xml"> +<!ENTITY validator SYSTEM "modules/validator.xml"> +<!ENTITY lucene SYSTEM "modules/lucene.xml"> +]> +<book lang="zh_cn"> + <bookinfo> + <title>Hibernate Annotations</title> + + <subtitle>参考文档</subtitle> + + <releaseinfo>3.2.0 CR1</releaseinfo> + + <mediaobject> + <imageobject> + <imagedata fileref="images/hibernate_logo_a.png" format="png" /> + </imageobject> + </mediaobject> + </bookinfo> + + <toc></toc> + + <preface id="preface" revision="2"> + <title>前言</title> + + <para>WARNING! This is a translated version of the English Hibernate + reference documentation. The translated version might not be up to date! + However, the differences should only be very minor. Consult the English + reference documentation if you are missing information or encounter a + translation error. If you like to contribute to a particular translation, + contact us on the Hibernate developer mailing list.</para> + + <para>Translator(s): RedSaga Translate Team 满江红翻译团队 + <ca...@ya...></para> + + <sect1 id="preface-translate-comments-zh-cn"> + <title>翻译说明</title> + + <para>本文档的翻译是在网络上协作进行的,也会不断根据Hibernate的升级进行更新。提供此文档的目的是为了减缓学习Hibernate的坡度,而非代替原文档。我们建议所有有能力的读者都直接阅读英文原文。若您对翻译有异议,或发现翻译错误,敬请不吝赐教,报告到如下地址:http://wiki.redsaga.com/confluence/display/HART/Home</para> + + <table frame="topbot" id="redsaga-translate-team"> + <title>Hibernate Annotation v3翻译团队</title> + + <tgroup cols="6" colsep="1" rowsep="1"> + <colspec colname="c1" colwidth="1*" /> + + <colspec colname="c2" colwidth="2*" /> + + <colspec colname="c3" colwidth="2*" /> + + <colspec colname="c4" colwidth="2*" /> + + <colspec colname="c5" colwidth="2*" /> + + <thead> + <row> + <entry align="center">序号</entry> + + <entry align="center">标题</entry> + + <entry align="center">中文标题</entry> + + <entry align="center">翻译</entry> + + <entry align="center">1审</entry> + + <entry align="center"> 2审</entry> + </row> + </thead> + + <tbody> + <row> + <entry>--</entry> + + <entry>Contents</entry> + + <entry>目录</entry> + + <entry>Liu Chang</entry> + + <entry></entry> + + <entry></entry> + </row> + + <row> + <entry>#1</entry> + + <entry>Setting up an annotations projec</entry> + + <entry>创建一个注解项目</entry> + + <entry>melthaw</entry> + + <entry>Zheng Shuai</entry> + + <entry>superq</entry> + </row> + + <row> + <entry>#2</entry> + + <entry>Entity Beans-Introduction</entry> + + <entry>实体Bean-简介</entry> + + <entry>melthaw</entry> + + <entry>Zheng Shuai</entry> + + <entry>superq</entry> + </row> + + <row> + <entry>#3</entry> + + <entry>Entity Beans-Mapping with EJB3 Annotations</entry> + + <entry>实体Bean-用EJB3注解进行映射</entry> + + <entry>melthaw</entry> + + <entry>Zheng Shuai</entry> + + <entry>superq, Liu Chang, Sean Chan</entry> + </row> + + <row> + <entry>#4</entry> + + <entry>Entity Beans-Mapping Queries</entry> + + <entry>实体Bean-映射查询</entry> + + <entry>melthaw</entry> + + <entry>Zheng Shuai</entry> + + <entry>superq, Liu Chang, Sean Chan</entry> + </row> + + <row> + <entry>#5</entry> + + <entry>Entity Beans-Hibernate Annotation Extensions</entry> + + <entry>实体Bean-Hibernate独有的注解扩展</entry> + + <entry>Sean Chan</entry> + + <entry>morning</entry> + + <entry>melthaw</entry> + </row> + + <row> + <entry>#6</entry> + + <entry>Overriding metadata through XML</entry> + + <entry>通过XML覆写元数据</entry> + + <entry>icess</entry> + + <entry>melthaw</entry> + + <entry>Sean Chan</entry> + </row> + + <row> + <entry>#7</entry> + + <entry>Hibernate Validator</entry> + + <entry>Hibernate验证器</entry> + + <entry>DigitalSonic</entry> + + <entry>morning</entry> + + <entry>melthaw</entry> + </row> + + <row> + <entry>#8</entry> + + <entry>Hibernate Lucene Integration</entry> + + <entry>Hibernate与Lucene集成</entry> + + <entry>mochow</entry> + + <entry>morning</entry> + + <entry>melthaw</entry> + </row> + + <row> + <entry>#9</entry> + + <entry>Appendix:Glossary</entry> + + <entry>附录:术语表</entry> + + <entry>mochow</entry> + + <entry>Liu Chang</entry> + + <entry>曹晓钢</entry> + </row> + </tbody> + </tgroup> + </table> + + <para>关于我们</para> + + <variablelist spacing="compact"> + <varlistentry> + <term>满江红.开源, http://www.redsaga.com</term> + + <listitem> + <para>从成立之初就致力于Java开放源代码在中国的传播与发展,与国内多个Java团体及出版社有深入交流。坚持少说多做的原则,目前有两个团队,“OpenDoc团队”与“翻译团队”,本翻译文档即为翻译团队作品。OpenDoc团队已经推出包括Hibernate、iBatis、Spring、WebWork的多份开放文档,并于2005年5月在Hibernate开放文档基础上扩充成书,出版了原创书籍:《深入浅出Hibernate》,本书400余页,适合各个层次的Hibernate用户。(http://www.redsaga.com/hibernate_book.html)敬请支持。</para> + </listitem> + </varlistentry> + + <varlistentry> + <term>致谢</term> + + <listitem> + <para>在我们翻译Hibernate + Annotation参考文档的同时,还有一位热心的朋友也在进行着同样的工作,这位朋友就是icess(冰雨),由icess翻译的中文版的地址: + http://icess.my.china.com/hibernate/a/ref/index.htm</para> + </listitem> + </varlistentry> + </variablelist> + </sect1> + + <sect1 id="preface-translate-licence-zh-cn"> + <title>版权声明</title> + + <para>Hibernate英文文档属于Hibernate发行包的一部分,遵循LGPL协议。本翻译版本同样遵循LGPL协议。参与翻译的译者一致同意放弃除署名权外对本翻译版本的其它权利要求。</para> + + <para>您可以自由链接、下载、传播此文档,或者放置在您的网站上,甚至作为产品的一部分发行。但前提是必须保证全文完整转载,包括完整的版权信息和作译者声明,并不能违反LGPL协议。这里“完整”的含义是,不能进行任何删除/增添/注解。若有删除/增添/注解,必须逐段明确声明那些部分并非本文档的一部分。</para> + </sect1> + </preface> + + <preface> + <title>前言</title> + + <para>正如其他的ORM工具,Hibernate同样需要元数据来控制在不同数据表达形式之间的转化. 在Hibernate + 2.x里,多数情况下表示映射关系的元数据保存在XML文本文件中. + 还有一种方式就是Xdoclet,它可以在编译时利用Javadoc中的源码注释信息来进行预处理. + 现在新的JDK标准(JDK1.5以上)也支持类似的注解功能,但相比之下很多工具对此提供了更强大更好用的支持. 以IntelliJ + IDEA和Eclipse为例,这些IDE工具为JDK 5.0注解功能提供了自动完成和语法高亮功能. 注解被直接编译到字节码里,并 + 在运行时(对于Hibernate来讲就是启动的时候)通过反射读取这些注解, 因此外部XML文件就不再需要了.</para> + + <para>EJB3规范最终认可了透明化ORM的成功范例以及市场对于这种技术的兴趣. + EJB3规范标准化了ORM的基础API而且在任何ORM持久化机制中使用元数据. <emphasis>Hibernate + EntityManager</emphasis>实现了EJB3持久化规范中定义的编程接口和生命周期规则. 在<emphasis>Hibernate + Core</emphasis>的基础上再结合 <emphasis>Hibernate + Annotations</emphasis>就实现了一套完整(并且独立)的EJB3持久化解决方案. + 你可以结合三者来使用,也可以抛开EJB3编程接口和生命周期规则而独立使用注解, 甚至只单独使用<emphasis>Hibernate + Core</emphasis>. 这些都取决于项目的商业和技术上的实际需求. Hibernate允许你直接使用native APIs,如果有需要, + 甚至可以直接操作JDBC和SQL.</para> + + <para>注意本文档基于Hibernate Annotations的预览版(遵从EJB 3.0/JSR-220最终草案). + 这个版本和新规范中定义的最终概念已经非常接近了.我们的目标是提供一套完整的ORM注解, + 包括EJB3的标准注解以及Hibernate3的扩展(后者是EJB3规范中没有涉及到的). 最终通过注解你可以完成任何可能的映射.详情参考<xref + linkend="compliance" />.</para> + + <para>EJB3最终草案修改了部分注解, + http://www.hibernate.org/371.html提供了从上一个版本到最新版本的迁移指南.</para> + </preface> + + &setup; + + &entity; + + &xml-overriding; + + &validator; + + &lucene; + + <appendix> + <title id="glossary">术语表</title> + + <para>Redsaga的wiki上维护了本文翻译过程中所参照的中英文对照的术语表,地址:http://wiki.redsaga.com/confluence/display/HART/glossary.</para> + </appendix> +</book> \ No newline at end of file Added: trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/entity.xml =================================================================== --- trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/entity.xml 2006-06-22 20:07:07 UTC (rev 10041) +++ trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/entity.xml 2006-06-23 15:45:46 UTC (rev 10042) @@ -0,0 +1,2857 @@ +<?xml version="1.0" encoding="UTF-8"?> +<chapter id="entity"> + <title>实体Bean</title> + + <sect1 id="entity-overview" revision="1"> + <title>简介</title> + + <para>本章内容覆盖了EJB3.0实体bean的注解规范以及Hibernate特有的扩展.</para> + </sect1> + + <sect1 id="mapping" revision="1"> + <title>用EJB3注解进行映射</title> + + <para>现在EJB3实体Bean是纯粹的POJO.实际上这表达了和Hibernate持久化实体对象同样的概念. + 它们的映射都通过JDK5.0注解来定义(EJB3规范中的XML描述语法至今还没有最终定下来). + 注解分为两个部分,分别是逻辑映射注解和物理映射注解, + 通过逻辑映射注解可以描述对象模型,类之间的关系等等, + 而物理映射注解则描述了物理的schema,表,列,索引等等. + 下面我们在代码中将混合使用这两种类型的注解.</para> + + <para>EJB3注解的API定义在<literal>javax.persistence.*</literal>包里面. + 大部分和JDK5兼容的IDE(象Eclipse, IntelliJ IDEA 和Netbeans等等)都提供了注解接口和属性的自动完成功能. + (这些不需要IDE提供特别的EJB3支持模块,因为EJB3注解是标准的JDK5注解)</para> + + <para>请阅读JBoss EJB 3.0指南或者直接阅读Hibernate Annotations测试代码以获取更多的可运行实例.Hibernate Annotations提供的大部分单元测试代码都演示了实际的例子,是一个获取灵感的好地方.</para> + + <sect2> + <title>声明实体bean</title> + + <para>每一个持久化POJO类都是一个实体bean,这可以通过在类的定义中使用<literal>@Entity</literal>注解来进行声明:</para> + + <programlisting> +@Entity +public class Flight implements Serializable { + Long id; + + @Id + public Long getId() { return id; } + + public void setId(Long id) { this.id = id; } +} + </programlisting> + + <para>通过<literal>@Entity</literal>注解将一个类声明为一个实体bean(即一个持久化POJO类), + <literal>@Id</literal>注解则声明了该实体bean的标识属性. + 其他的映射定义是隐式的.这种以隐式映射为主体,以显式映射为例外的配置方式在新的EJ3规范中处于非常重要的位置, + 和以前的版本相比有了质的飞跃. + 在上面这段代码中:Flight类映射到Flight表,并使用id列作为主键列. + </para> + + <para>在对一个类进行注解时,你可以选择对它的的属性或者方法进行注解,根据你的选择,Hibernate的访问类型分别为 + <literal>field</literal>或<literal>property</literal>. + EJ3规范要求在需要访问的元素上进行注解声明,例如,如果访问类型为 + <literal>property</literal>就要在getter方法上进行注解声明, + 如果访问类型为 <literal>field</literal>就要在字段上进行注解声明.应该尽量避免混合使用这两种访问类型. + Hibernate根据<literal>@Id</literal> 或 <literal>@EmbeddedId</literal>的位置来判断访问类型.</para> + + <sect3> + <title>定义表(Table)</title> + + <para><literal>@Table</literal>是类一级的注解, + 通过<literal>@Table</literal>注解可以为实体bean映射指定表(table),目录(catalog)和schema的名字. + 如果没有定义<literal>@Table</literal>,那么系统自动使用默认值:实体的短类名(不附带包名).</para> + + <programlisting> +@Entity +@Table(name="tbl_sky") +public class Sky implements Serializable { +... + </programlisting> + + <para><literal>@Table</literal>元素包括了一个<literal>schema</literal> + 和一个 <literal>catalog</literal>属性,如果需要可以指定相应的值. + 结合使用<literal>@UniqueConstraint</literal>注解可以定义表的唯一约束(unique constraint) + (对于绑定到单列的唯一约束,请参考<literal>@Column</literal>注解) + </para> + + <programlisting>@Table(name="tbl_sky", + <emphasis role="bold">uniqueConstraints = {@UniqueConstraint(columnNames={"month", "day"})}</emphasis> + )</programlisting> + + <para>上面这个例子中,在month和day这两个字段上定义唯一约束. + 注意<literal>columnNames</literal>数组中的值指的是逻辑列名.</para> + + <remark>Hibernate在NamingStrategy的实现中定义了逻辑列名. + 默认的EJB3命名策略将物理字段名当作逻辑字段名来使用. + 注意该字段名和它对应的属性名可能不同(如果字段名是显式指定的话). + 除非你重写了NamingStrategy,否则不用担心这些区别..</remark> + </sect3> + + <sect3> + <title>乐观锁定版本控制</title> + + <para>你可以在实体bean中使用<literal>@Version</literal>注解,通过这种方式可添加对乐观锁定的支持:</para> + + <programlisting> +@Entity +public class Flight implements Serializable { +... + @Version + @Column(name="OPTLOCK") + public Integer getVersion() { ... } +} </programlisting> + + <para>上面这个例子中,version属性将映射到 <literal>OPTLOCK</literal>列, + entity manager使用该字段来检测更新冲突(防止更新丢失,请参考last-commit-wins策略).</para> + + <para>根据EJB3规范,version列可以是numeric类型(推荐方式)也可以是timestamp类型. + Hibernate支持任何自定义类型,只要该类型实现了<classname>UserVersionType</classname>.</para> + </sect3> + </sect2> + + <sect2> + <title>映射简单属性</title> + + <sect3> + <title>声明基本的属性映射</title> + + <para>Every non static non transient property (field or method) of an + entity bean is considered persistent, unless you annotate it as + <literal>@Transient</literal>. Not having an annotation for your + property is equivalent to the appropriate <literal>@Basic</literal> + annotation. The <literal>@Basic</literal> annotation allows you to + declare the fetching strategy for a property:</para> + + <para>实体bean中所有的非static非transient的属性都可以被持久化, + 除非你将其注解为<literal>@Transient</literal>.所有没有定义注解的属性等价于在其上面添加了@Basic注解. + 通过 <literal>@Basic</literal>注解可以声明属性的获取策略(fetch strategy):</para> + + <programlisting>public transient int counter; //transient property + +private String firstname; //persistent property + +@Transient +String getLengthInMeter() { ... } //transient property + +String getName() {... } // persistent property + +@Basic +int getLength() { ... } // persistent property + +@Basic(fetch = FetchType.LAZY) +String getDetailedComment() { ... } // persistent property + +@Temporal(TemporalType.TIME) +java.util.Date getDepartureTime() { ... } // persistent property + +@Enumerated(STRING) +Starred getNote() { ... } //enum persisted as String in database</programlisting> + + <para>上面这个例子中,<literal>counter</literal>是一个transient的字段, + <literal>lengthInMeter</literal>的getter方法被注解为<literal>@Transient</literal>, + entity manager将忽略这些字段和属性. + 而<literal>name</literal>,<literal>length</literal>,<literal>firstname</literal> + 这几个属性则是被定义为可持久化和可获取的.对于简单属性来说,默认的获取方式是即时获取(early fetch). + 当一个实体Bean的实例被创建时,Hibernate会将这些属性的值从数据库中提取出来,保存到Bean的属性里. + 与即时获取相对应的是延迟获取(lazy fetch).如果一个属性的获取方式是延迟获取 + (比如上面例子中的<literal>detailedComment</literal>属性), + Hibernate在创建一个实体Bean的实例时,不会即时将这个属性的值从数据库中读出. + 只有在该实体Bean的这个属性第一次被调用时,Hibernate才会去获取对应的值. + 通常你不需要对简单属性设置延迟获取(lazy simple property),千万不要和延迟关联获取(lazy association fetch)混淆了 + (译注:这里指不要把lazy simple property和lazy association fetch混淆了). + </para> + + <note> + <para>为了启用属性级的延迟获取,你的类必须经过特殊处理(instrumented): + 字节码将被织入原始类中来实现延迟获取功能, + 详情参考Hibernate参考文档.如果不对类文件进行字节码特殊处理, + 那么属性级的延迟获取将被忽略.</para> + </note> + + <para>推荐的替代方案是使用EJB-QL或者Criteria查询的投影(projection)功能.</para> + + <para>Hibernate和EJB3都支持所有基本类型的属性映射. + 这些基本类型包括所有的Java基本类型,及其各自的wrapper类和serializable类. + Hibernate Annotations还支持将内置的枚举类型映射到一个顺序列(保存了相应的序列值) + 或一个字符串类型的列(保存相应的字符串).默认是保存枚举的序列值, + 但是你可以通过<literal>@Enumerated</literal>注解来进行调整(见上面例子中的note属性).</para> + + <para>在核心的Java API中并没有定义时间精度(temporal precision). + 因此处理时间类型数据时,你还需要定义将其存储在数据库中所预期的精度. + 在数据库中,表示时间类型的数据有<literal>DATE</literal>, <literal>TIME</literal>, + 和 <literal>TIMESTAMP</literal>三种精度(即单纯的日期,时间,或者两者兼备). + 可使用<literal>@Temporal</literal>注解来调整精度.</para> + + <para><literal>@Lob</literal>注解表示属性将被持久化为Blob或者Clob类型, + 具体取决于属性的类型, + <classname>java.sql.Clob</classname>, + <classname>Character[]</classname>, + <classname>char[]</classname> 和 + <classname>java.lang.String</classname>这些类型的属性都被持久化为Clob类型, + 而<classname>java.sql.Blob</classname>, + <classname>Byte[]</classname>, + <classname>byte[]</classname> 和 + serializable类型则被持久化为Blob类型.</para> + + <programlisting> +@Lob +public String getFullText() { + return fullText; +} + +@Lob +public byte[] getFullCode() { + return fullCode; +} + </programlisting> + + <para>如果某个属性实现了<classname>java.io.Serializable</classname>同时也不是基本类型, + 并且没有在该属性上使用<literal>@Lob</literal>注解, + 那么Hibernate将使用自带的<literal>serializable</literal>类型. + </para> + </sect3> + + <sect3> + <title>声明列属性</title> + + <para>使用 <literal>@Column </literal>注解可将属性映射到列. + 使用该注解来覆盖默认值(关于默认值请参考EJB3规范). + 在属性级使用该注解的方式如下:</para> + + <itemizedlist> + <listitem> + <para>不进行注解</para> + </listitem> + + <listitem> + <para>和 <literal>@Basic</literal>一起使用</para> + </listitem> + + <listitem> + <para>和 <literal>@Version</literal>一起使用</para> + </listitem> + + <listitem> + <para>和 <literal>@Lob</literal>一起使用</para> + </listitem> + + <listitem> + <para>和 <literal>@Temporal</literal>一起使用</para> + </listitem> + + <listitem> + <para>和 + <literal>@org.hibernate.annotations.CollectionOfElements</literal>一起使用 + (只针对Hibernate )</para> + </listitem> + </itemizedlist> + + + <programlisting> +@Entity +public class Flight implements Serializable { +... +@Column(updatable = false, name = "flight_name", nullable = false, length=50) +public String getName() { ... } + </programlisting> + + <para>在上面这个例子中,<literal>name</literal>属性映射到<literal>flight_name</literal>列. + 该字段不允许为空,长度为50,并且是不可更新的(也就是属性值是不变的).</para> + + <para>上面这些注解可以被应用到正规属性上例如<literal>@Id</literal> 或<literal>@Version</literal>属性.</para> + + <programlistingco> + <areaspec> + <area coords="2 55" id="hm1" /> + + <area coords="3 55" id="hm2" /> + + <area coords="4 55" id="hm3" /> + + <area coords="5 55" id="hm4" /> + + <area coords="6 55" id="hm5" /> + + <area coords="7 55" id="hm6" /> + + <area coords="8 55" id="hm7" /> + + <area coords="9 55" id="hm8" /> + + <area coords="10 55" id="hm9" /> + + <area coords="11 55" id="hm10" /> + </areaspec> + + <programlisting>@Column( + name="columnName"; + boolean unique() default false; + boolean nullable() default true; + boolean insertable() default true; + boolean updatable() default true; + String columnDefinition() default ""; + String table() default ""; + int length() default 255; + int precision() default 0; // decimal precision + int scale() default 0; // decimal scale</programlisting> + + <calloutlist> + <callout arearefs="hm1"> + <para><literal>name</literal> 可选,列名(默认值是属性名)</para> + </callout> + + <callout arearefs="hm2"> + <para><literal>unique</literal> 可选,是否在该列上设置唯一约束(默认值false)</para> + </callout> + + <callout arearefs="hm3"> + <para><literal>nullable</literal> 可选,是否设置该列的值可以为空(默认值false)</para> + </callout> + + <callout arearefs="hm4"> + <para><literal>insertable</literal> 可选,该列是否作为生成的insert语句中的一个列(默认值true)</para> + </callout> + + <callout arearefs="hm5"> + <para><literal>updatable</literal> 可选,该列是否作为生成的update语句中的一个列(默认值true)</para> + </callout> + + <callout arearefs="hm6"> + <para><literal>columnDefinition</literal> 可选: 为这个特定列覆盖SQL DDL片段 (这可能导致无法在不同数据库间移植)</para> + </callout> + + <callout arearefs="hm7"> + <para><literal>table</literal> 可选,定义对应的表(默认为主表)</para> + </callout> + + <callout arearefs="hm8"> + <para><literal><literal>length</literal></literal> 可选,列长度(默认值255)</para> + </callout> + + <callout arearefs="hm8"> + <para><literal><literal>precision</literal></literal> + 可选,列十进制精度(decimal precision)(默认值0)</para> + </callout> + + <callout arearefs="hm10"> + <para><literal><literal>scale</literal></literal> + 可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)</para> + </callout> + </calloutlist> + </programlistingco> + </sect3> + + <sect3> + <title>嵌入式对象(又名组件)</title> + + <para>在实体中可以定义一个嵌入式组件(embedded component), + 甚至覆盖该实体中原有的列映射. + 组件类必须在类一级定义<literal>@Embeddable</literal>注解. + 在特定的实体的关联属性上使用<literal>@Embedded</literal>和 + <literal>@AttributeOverride</literal>注解可以覆盖该属性对应的嵌入式对象的列映射:</para> + + <programlisting> +@Entity +public class Person implements Serializable { + + // Persistent component using defaults + Address homeAddress; + + @Embedded + @AttributeOverrides( { + @AttributeOverride(name="iso2", column = @Column(name="bornIso2") ), + @AttributeOverride(name="name", column = @Column(name="bornCountryName") ) + } ) + Country bornIn; + ... +} + </programlisting> + + <programlisting> +@Embeddable +public class Address implements Serializable { + String city; + Country nationality; //no overriding here +} + </programlisting> + + <programlisting> +@Embeddable +public class Country implements Serializable { + private String iso2; + @Column(name="countryName") private String name; + + public String getIso2() { return iso2; } + public void setIso2(String iso2) { this.iso2 = iso2; } + + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + ... +} + </programlisting> + + <para>嵌入式对象继承其所属实体中定义的访问类型 + (注意:这可以通过使用Hibernate提供的<literal>@AccessType</literal>注解来覆盖原有值)(请参考 <xref + linkend="entity-hibspec" />).</para> + + <para>在上面的例子中,实体bean <literal>Person</literal> 有两个组件属性, + 分别是<literal>homeAddress</literal>和<literal>bornIn</literal>. + 我们可以看到<literal>homeAddress</literal> 属性并没有注解. + 但是Hibernate自动检测其对应的Address类中的<literal>@Embeddable</literal>注解, + 并将其看作一个持久化组件.对于Country中已映射的属性, + 则使用<literal>@Embedded</literal>和<literal>@AttributeOverride + </literal>注解来覆盖原来映射的列名. + 正如你所看到的, <literal>Address</literal>对象中还内嵌了<literal>Country</literal>对象, + 这里和<literal>homeAddress</literal>一样使用了Hibernate和EJB3自动检测机制. + 目前EJB3规范还不支持覆盖多层嵌套(即嵌入式对象中还包括其他嵌入式对象)的列映射. + 不过Hibernate通过在表达式中使用"."符号表达式提供了对此特征的支持.</para> + + <para><programlisting> @Embedded + @AttributeOverrides( { + @AttributeOverride(name="city", column = @Column(name="fld_city") ) + @AttributeOverride(name="<emphasis role="bold">nationality.iso2</emphasis>", column = @Column(name="nat_Iso2") ), + @AttributeOverride(name="<emphasis role="bold">nationality.name</emphasis>", column = @Column(name="nat_CountryName") ) + //nationality columns in homeAddress are overridden + } ) + Address homeAddress;</programlisting> + Hibernate注解支持很多EJB3规范中没有明确定义的特性. + 例如,可以在嵌入式对象上添加 <literal>@MappedSuperclass</literal>注解, + 这样可以将其父类的属性持久(详情请查阅<literal>@MappedSuperclass</literal>).</para> + + <para>Hibernate现在支持在嵌入式对象中使用关联注解(如<literal>@*ToOne</literal>和<literal>@*ToMany</literal>). + 而EJB3规范尚不支持这样的用法。你可以使用 <literal>@AssociationOverride</literal>注解来覆写关联列.</para> + + + <para>在同一个实体中使用两个同类型的嵌入对象, + 其默认列名是无效的:至少要对其中一个进行明确声明. + Hibernate在这方面走在了EJB3规范的前面, + Hibernate提供了<classname>NamingStrategy</classname>, 在使用Hibernate时, + 通过<classname>NamingStrategy</classname>你可以对默认的机制进行扩展. + <classname>DefaultComponentSafeNamingStrategy</classname> + 在默认的EJB3NamingStrategy上进行了小小的提升, + 允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.</para> + + </sect3> + + <sect3> + <title>无注解之属性的默认值</title> + + <para>如果某属性没有注解,该属性将遵守下面的规则:</para> + + <itemizedlist> + <listitem> + 如果属性为单一类型,则映射为@Basic + </listitem> + + <listitem> + 否则,如果属性对应的类型定义了@Embeddable注解,则映射为@Embedded + </listitem> + + <listitem> + 否则,如果属性对应的类型实现了Serializable, + 则属性被映射为@Basic并在一个列中保存该对象的serialized版本 + </listitem> + + <listitem> + 否则,如果该属性的类型为java.sql.Clob 或 java.sql.Blob,则作为@Lob并映射到适当的LobType. + </listitem> + </itemizedlist> + + </sect3> + </sect2> + + <sect2 id="mapping-identifier" label="" + xreflabel="Mapping identifier properties"> + <title>映射主键属性</title> + + <para>使用<literal>@Id</literal>注解可以将实体bean中的某个属性定义为标识符(identifier). + 该属性的值可以通过应用自身进行设置, + 也可以通过Hiberante生成(推荐). + 使用 <literal>@GeneratedValue</literal>注解可以定义该标识符的生成策略: + </para> + + <itemizedlist> + <listitem> + AUTO - 可以是identity column类型,或者sequence类型或者table类型,取决于不同的底层数据库. + </listitem> + + <listitem> + TABLE - 使用表保存id值 + </listitem> + + <listitem> + IDENTITY - identity column + </listitem> + + <listitem> + SEQUENCE - sequence + </listitem> + </itemizedlist> + + <para>和EJB3规范相比,Hibernate提供了更多的id生成器.详情请查阅 <xref linkend="entity-hibspec" /> .</para> + + + <para>下面的例子展示了使用SEQ_STORE配置的sequence生成器</para> + + <programlisting> +@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") +public Integer getId() { ... } + </programlisting> + + <para>下面这个例子使用的是identity生成器</para> + + <programlisting> +@Id @GeneratedValue(strategy=GenerationType.IDENTITY) +public Long getId() { ... } + </programlisting> + + <para><literal>AUTO</literal>生成器适用于可移植的应用(在多个DB间切换). + 多个<literal>@Id</literal>可以共享同一个identifier生成器,只要把generator属性设成相同的值就可以了. + 通过<literal>@SequenceGenerator</literal> 和<literal>@TableGenerator</literal>,你可以配置不同的identifier生成器. + 每一个identifier生成器都有自己的适用范围,可以是应用级(application level)和类一级(class level). + 类一级的生成器在外部是不可见的, + 而且类一级的生成器可以覆盖应用级的生成器. + 应用级的生成器则定义在包一级(package level)(如<classname>package-info.java</classname>):</para> + + <programlisting> +...@ja...rsistence.TableGenerator( + name="EMP_GEN", + table="GENERATOR_TABLE", + pkColumnName = "key", + valueColumnName = "hi" + pkColumnValue="EMP", + allocationSize=20 +) +...@ja...quenceGenerator( + name="SEQ_GEN", + sequenceName="my_sequence" +) +package org.hibernate.test.metadata; + </programlisting> + + <para> + 如果在<classname>org.hibernate.test.metadata</classname>包下面的 + <classname>package-info.java</classname>文件用于初始化EJB配置, + 那么该文件中定义的 <literal>EMP_GEN</literal> + 和<literal>SEQ_GEN</literal>都是应用级的生成器. + <literal>EMP_GEN</literal>定义了一个使用hilo算法 + (max_lo为20)的id生成器(该生成器将id的信息存在数据库的某个表中.). + id的hi值保存在<literal>GENERATOR_TABLE</literal>中. + 在该表中 <literal>pkColumnName</literal>"key"等价于 + <literal>pkColumnValue</literal> "<literal>EMP</literal>", + 而<literal>valueColumnName</literal> "<literal>hi</literal>"中存储的是下一个要使用的最大值. + </para> + + <para><literal>SEQ_GEN</literal>则定义了一个sequence 生成器, + 其对应的sequence名为 <literal>my_sequence</literal>. + 注意目前Hibernate Annotations还不支持sequence 生成器中的 + <literal>initialValue</literal>和 <literal>allocationSize</literal>参数.</para> + + <para>下面这个例子展示了定义在类范围(class scope)的sequence生成器:</para> + + <programlisting> +@Entity +...@ja...quenceGenerator( + name="SEQ_STORE", + sequenceName="my_sequence" +) +public class Store implements Serializable { + private Long id; + + @Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE") + public Long getId() { return id; } +} + </programlisting> + + <para>在这个例子中,Store类使用名为my_sequence的sequence,并且SEQ_STORE 生成器对于其他类是不可见的. + 注意在org.hibernate.test.metadata.id包下的测试代码有更多演示Hibernate Annotations用法的例子..</para> + + <para>下面是定义组合主键的几种语法:</para> + + <itemizedlist> + <listitem> + 将组件类注解为@Embeddable,并将组件的属性注解为@Id + </listitem> + + <listitem> + 将组件的属性注解为@EmbeddedId + </listitem> + + <listitem> + 将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id + </listitem> + </itemizedlist> + + <para>对于EJB2的开发人员来说 <literal>@IdClass</literal>是很常见的, + 但是对于Hibernate的用户来说就是一个崭新的用法. + 组合主键类对应了一个实体类中的多个字段或属性, + 而且主键类中用于定义主键的字段或属性和 + 实体类中对应的字段或属性在类型上必须一致.下面我们看一个例子:</para> + + <programlisting>@Entity +<emphasis role="bold">@IdClass(FootballerPk.class)</emphasis> +public class Footballer { + //part of the id key + <emphasis role="bold">@Id</emphasis> public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + //part of the id key + <emphasis role="bold">@Id</emphasis> public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public String getClub() { + return club; + } + + public void setClub(String club) { + this.club = club; + } + + //appropriate equals() and hashCode() implementation +} + +@Embeddable +public class FootballerPk implements Serializable { + //same name and type as in Footballer + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + //same name and type as in Footballer + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + //appropriate equals() and hashCode() implementation +} +</programlisting> + + + <para>如上, <literal>@IdClass</literal>指向对应的主键类.</para> + + <para>Hibernate支持在组合标识符中定义关联(就像使用普通的注解一样),而EJB3规范并不支持此类用法. + </para> + + <programlisting>@Entity +@AssociationOverride( name="id.channel", joinColumns = @JoinColumn(name="chan_id") ) +public class TvMagazin { + @EmbeddedId public TvMagazinPk id; + @Temporal(TemporalType.TIME) Date time; +} + +@Embeddable +public class TvMagazinPk implements Serializable { + @ManyToOne + public Channel channel; + public String name; + @ManyToOne + public Presenter presenter; +} +</programlisting> + + </sect2> + + <sect2> + <title>映射继承关系</title> + + <para>EJB3支持三种类型的继承映射:</para> + + <itemizedlist> + <listitem> + 每个类一张表(Table per class)策略: 在Hibernate中对应<union-class>元素: + </listitem> + + <listitem> + 每个类层次结构一张表(Single table per class hierarchy)策略:在Hibernate中对应<subclass>元素 + </listitem> + + <listitem> + 连接的子类(Joined subclasses)策略:在Hibernate中对应 <joined-subclass>元素 + </listitem> + </itemizedlist> + + <para> + 你可以用 <literal>@Inheritance</literal>注解来定义所选择的策略. + 这个注解需要在每个类层次结构(class hierarchy) 最顶端的实体类上使用. + </para> + + <note> + <para>目前还不支持在接口上进行注解.</para> + </note> + + <sect3> + <title>每个类一张表</title> + + <para> + 这种策略有很多缺点(例如:多态查询和关联),EJB3规范, Hibernate参考手册, + Hibernate in Action,以及其他许多地方都对此进行了描述和解释. + Hibernate使用<literal>SQL UNION</literal>查询来实现这种策略. + 通常使用场合是在一个继承层次结构的顶端:</para> + + <programlisting> +@Entity +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public class Flight implements Serializable { + </programlisting> + + <para> + 这种策略支持双向的一对多关联. + 这里不支持<literal>IDENTITY</literal>生成器策略,因为id必须在多个表间共享. + 当然,一旦使用这种策略就意味着你不能使用 + <literal>AUTO </literal>生成器和<literal>IDENTITY</literal>生成器. + </para> + </sect3> + + <sect3> + <title>每个类层次结构一张表</title> + + <para>整个继承层次结构中的父类和子类的所有属性都映射到同一个表中, + 他们的实例通过一个辨别符(discriminator)列来区分.:</para> + + <programlisting> +@Entity +@Inheritance(strategy=InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn( + name="planetype", + discriminatorType=DiscriminatorType.STRING +) +@DiscriminatorValue("Plane") +public class Plane { ... } + +@Entity +@DiscriminatorValue("A320") +public class A320 extends Plane { ... } + </programlisting> + + <para>在上面这个例子中,<classname>Plane</classname>是父类,在这个类里面将继承策略定义为 + <literal>InheritanceType.SINGLE_TABLE</literal>,并通过 + <literal>@DiscriminatorColumn</literal>注解定义了辨别符列(还可以定义辨别符的类型). + 最后,对于继承层次结构中的每个类,<literal>@DiscriminatorValue</literal>注解指定了用来辨别该类的值. + 辨别符列的名字默认为 <literal>DTYPE</literal>,其默认值为实体名(在<literal>@Entity.name</literal>中定义),其类型 + 为DiscriminatorType.STRING. + <classname>A320</classname>是子类,如果不想使用默认的辨别符,只需要指定相应的值即可. + 其他的如继承策略,辨别标志字段的类型都是自动设定的.</para> + + <para><literal>@Inheritance</literal> 和 + <literal>@DiscriminatorColumn</literal> 注解只能用于实体层次结构的顶端.</para> + + </sect3> + + <sect3> + <title>连接的子类</title> + + <para>当每个子类映射到一个表时, <literal> @PrimaryKeyJoinColumn</literal> + 和<literal>@PrimaryKeyJoinColumns</literal> + 注解定义了每个子类表关联到父类表的主键:</para> + + <programlisting> +@Entity +@Inheritance(strategy=InheritanceType.JOINED) +public class Boat implements Serializable { ... } + +@Entity +public class Ferry extends Boat { ... } + +@Entity +@PrimaryKeyJoinColumn(name="BOAT_ID") +public class AmericaCupClass extends Boat { ... } + </programlisting> + + <para>以上所有实体都使用了<literal>JOINED</literal>策略, + <literal>Ferry</literal>表和<literal>Boat</literal>表使用同名的主键. + 而<literal>AmericaCupClass</literal>表和<literal>Boat</literal>表使用了条件 + <code>Boat.id = AmericaCupClass.BOAT_ID</code>进行关联. + </para> + </sect3> + + <sect3> + <title>从父类继承的属性</title> + + <para>有时候通过一个(技术上或业务上)父类共享一些公共属性是很有用的, + 同时还不用将该父类作为映射的实体(也就是该实体没有对应的表). + 这个时候你需要使用<literal>@MappedSuperclass</literal>注解来进行映射.</para> + + <programlisting>@MappedSuperclass +public class BaseEntity { + @Basic + @Temporal(TemporalType.TIMESTAMP) + public Date getLastUpdate() { ... } + public String getLastUpdater() { ... } + ... +} + +@Entity class Order extends BaseEntity { + @Id public Integer getId() { ... } + ... +}</programlisting> + + <para>在数据库中,上面这个例子中的继承的层次结构最终以<literal>Order</literal>表的形式出现, + 该表拥有<literal>id</literal>, <literal>lastUpdate</literal> 和 + <literal>lastUpdater</literal>三个列.父类中的属性映射将复制到其子类实体. + 注意这种情况下的父类不再处在继承层次结构的顶端.</para> + + <note> + + <para>注意,没有注解为<literal>@MappedSuperclass</literal>的父类中的属性将被忽略.</para> + </note> + + <note> + + <para>除非显式使用Hibernate annotation中的<literal>@AccessType</literal>注解, + 否则将从继承层次结构的根实体中继承访问类型(包括字段或方法)</para> + </note> + + <note> + + <para>这对于<literal>@Embeddable</literal>对象的父类中的属性持久化同样有效. + 只需要使用<literal>@MappedSuperclass</literal>注解即可 + (虽然这种方式不会纳入EJB3标准)</para> + + </note> + + <note> + + <para>可以将处在在映射继承层次结构的中间位置的类注解为<literal>@MappedSuperclass</literal>.</para> + </note> + + <note> + + <para>在继承层次结构中任何没有被注解为<literal>@MappedSuperclass</literal> + 或<literal>@Entity</literal>的类都将被忽略.</para> + </note> + + <para> + 你可以通过 <literal>@AttributeOverride</literal>注解覆盖实体父类中的定义的列. + 这个注解只能在继承层次结构的顶端使用.</para> + + <programlisting>@MappedSuperclass +public class FlyingObject implements Serializable { + + public int getAltitude() { + return altitude; + } + + @Transient + public int getMetricAltitude() { + return metricAltitude; + } + + @ManyToOne + public PropulsionType getPropulsion() { + return metricAltitude; + } + ... +} + +@Entity +@AttributeOverride( name="altitude", column = @Column(name="fld_altitude") ) +@AssociationOverride( name="propulsion", joinColumns = @JoinColumn(name="fld_propulsion_fk") ) +public class Plane extends FlyingObject { + ... +}</programlisting> + + <para>在上面这个例子中,<literal>altitude</literal>属性的值最终将持久化到<literal>Plane</literal> + 表的<literal>fld_altitude</literal>列.而名为propulsion的关联则保存在<literal>fld_propulsion_fk</literal>外间列.</para> + + <para>你可以为<literal>@Entity</literal>和<literal>@MappedSuperclass</literal>注解的类 + 以及那些对象为<literal>@Embeddable</literal>的属性定义 + <literal>@AttributeOverride</literal>和<literal>@AssociationOverride</literal>.</para> + + </sect3> + </sect2> + + <sect2 id="entity-mapping-association"> + <title>映射实体Bean的关联关系</title> + + <sect3> + <title>一对一(One-to-one)</title> + + <para>使用<literal>@OneToOne</literal>注解可以建立实体bean之间的一对一的关联. + 一对一关联有三种情况: + 一是关联的实体都共享同样的主键, + 二是其中一个实体通过外键关联到另一个实体的主键 + (注意要模拟一对一关联必须在外键列上添加唯一约束). + 三是通过关联表来保存两个实体之间的连接关系 + (注意要模拟一对一关联必须在每一个外键上添加唯一约束).</para> + + <para>首先,我们通过共享主键来进行一对一关联映射:</para> + + <programlisting> +@Entity +public class Body { + @Id + public Long getId() { return id; } + + @OneToOne(cascade = CascadeType.ALL) + @PrimaryKeyJoinColumn + public Heart getHeart() { + return heart; + } + ... +} + </programlisting> + + <programlisting> +@Entity +public class Heart { + @Id + public Long getId() { ...} +} + </programlisting> + + <para>上面的例子通过使用注解<literal>@PrimaryKeyJoinColumn</literal>定义了一对一关联.</para> + + <para>下面这个例子使用外键列进行实体的关联.</para> + + <programlisting> +@Entity +public class Customer implements Serializable { + @OneToOne(cascade = CascadeType.ALL) + <emphasis role="bold">@JoinColumn(name="passport_fk")</emphasis> + public Passport getPassport() { + ... + } + +@Entity +public class Passport implements Serializable { + @OneToOne(<emphasis role="bold">mappedBy = "passport"</emphasis>) + public Customer getOwner() { + ... +} + </programlisting> + + <para>上面这个例子中,<classname>Customer</classname> 通过<literal>Customer</literal> + 表中名为的<literal>passport_fk</literal> 外键列和 <classname>Passport</classname>关联. + <literal>@JoinColumn</literal>注解定义了联接列(join column). + 该注解和<literal>@Column</literal>注解有点类似, + 但是多了一个名为<literal>referencedColumnName</literal>的参数. + 该参数定义了所关联目标实体中的联接列. + 注意,当<literal><literal>referencedColumnName</literal></literal>关联到非主键列的时候, + 关联的目标类必须实现<classname>Serializable</classname>, + 还要注意的是所映射的属性对应单个列(否则映射无效). + </para> + + <para>一对一关联可能是双向的.在双向关联中, + 有且仅有一端是作为主体(owner)端存在的:主体端负责维护联接列(即更新). + 对于不需要维护这种关系的从表则通过mappedBy属性进行声明. + <literal>mappedBy</literal>的值指向主体的关联属性. + 在上面这个例子中,<literal>mappedBy</literal>的值为 <literal>passport</literal>. + 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.</para> + + <para>如果在主体没有声明<literal>@JoinColumn</literal>,系统自动进行处理: + 在主表(owner table)中将创建联接列, + 列名为:主体的关联属性名+下划线+被关联端的主键列名. + 在上面这个例子中是<literal>passport_id</literal>, + 因为<literal>Customer</literal>中关联属性名为<literal>passport</literal>, + <literal>Passport</literal>的主键是<literal>id</literal>.</para> + + <para>The third possibility (using an association table) is very + exotic.</para> + + <para>第三种方式也许是最另类的(通过关联表).</para> + + <programlisting> +@Entity +public class Customer implements Serializable { + @OneToOne(cascade = CascadeType.ALL) + <emphasis role="bold">@JoinTable(name = "CustomerPassports" + joinColumns = @JoinColumn(name="customer_fk"), + inverseJoinColumns = @JoinColumns(name="passport_fk")</emphasis> + ) + public Passport getPassport() { + ... + } + +@Entity +public class Passport implements Serializable { + @OneToOne(<emphasis role="bold">mappedBy = "passport"</emphasis>) + public Customer getOwner() { + ... +} + </programlisting> + + <para><classname>Customer</classname>通过名为 <literal>CustomerPassports</literal>的关联表和 + <classname>Passport</classname>关联; 该关联表拥有名为<literal>passport_fk</literal>的外键列,该 + 外键指向<literal>Passport</literal>表,该信息定义为<literal>inverseJoinColumn</literal>的属性值, + 而<literal>customer_fk</literal>外键列指向<literal>Customer</literal>表, + 该信息定义为 <literal>joinColumns</literal>的属性值.</para> + + <para>这种关联可能是双向的.在双向关联中, + 有且仅有一端是作为主体端存在的:主体端负责维护联接列(即更新). + 对于不需要维护这种关系的从表则通过mappedBy属性进行声明. + <literal>mappedBy</literal>的值指向主体的关联属性. + 在上面这个例子中,<literal>mappedBy</literal>的值为 <literal>passport</literal>. + 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.</para> + + <para>你必须明确定义关联表名和关联列名.</para> + + </sect3> + + <sect3> + <title>多对一(Many-to-one)</title> + + <para>在实体属性一级使用<literal>@ManyToOne</literal>注解来定义多对一关联:</para> + + <programlisting> +@Entity() +public class Flight implements Serializable { + <emphasis role="bold">@ManyToOne</emphasis>( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) + @JoinColumn(name="COMP_ID") + public Company getCompany() { + return company; + } + ... +} + </programlisting> + + <para>其中<literal>@JoinColumn</literal>是可选的,关联字段默认值和一对一 + (one to one)关联的情况相似, + 列名为:主体的关联属性名+下划线+被关联端的主键列名. + 在这个例子中是<literal>company_id</literal>, + 因为关联的属性是<literal>company</literal>, + <literal>Company</literal>的主键是<literal>id</literal>.</para> + + <para><literal>@ManyToOne</literal>注解有一个名为<literal>targetEntity</literal>的参数, + 该参数定义了目标实体名.通常不需要定义该参数, + 因为在大部分情况下默认值(表示关联关系的属性类型)就可以很好的满足要求了. + 不过下面这种情况下这个参数就显得有意义了:使用接口作为返回值而不是常见的实体.</para> + + <programlisting> +@Entity() +public class Flight implements Serializable { + @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, <emphasis + role="bold">targetEntity=CompanyImpl.class</emphasis> ) + @JoinColumn(name="COMP_ID") + public Company getCompany() { + return company; + } + ... +} + +public interface Company { + ... + </programlisting> + + <para>对于多对一也可以通过关联表的方式来映射。 + 通过<literal>@JoinTable</literal>注解可定义关联表, + 该关联表包含了指回实体表的外键(通过<literal>@JoinTable.joinColumns</literal>) + 以及指向目标实体表的外键(通过<literal>@JoinTable.inverseJoinColumns</literal>).</para> + + <programlisting> +@Entity() +public class Flight implements Serializable { + @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} ) + <emphasis role="bold">@JoinTable(name="Flight_Company", + joinColumns = @JoinColumn(name="FLIGHT_ID"), + inverseJoinColumns = @JoinColumns(name="COMP_ID") + )</emphasis> + public Company getCompany() { + return company; + } + ... +} + </programlisting> + </sect3> + + <sect3 id="entity-mapping-association-collections"> + <title>集合类型</title> + + <sect4> + <title>概况</title> + + <para>你可以对 <classname>Collection </classname>,<literal>List</literal> + (指有序列表, 而不是索引列表), + <literal>Map</literal>和<classname>Set</classname>这几种类型进行映射. + EJB3规范定义了怎么样使用<literal>@javax.persistence.OrderBy</literal> + 注解来对有序列表进行映射: + 该注解接受的参数格式:用逗号隔开的(目标实体)属性名及排序指令, + 如<code>firstname asc, age desc</code>,如果该参数为空,则默认以id对该集合进行排序. + 如果某个集合在数据库中对应一个关联表(association table)的话,你不能在这个集合属性上面使用@OrderBy注解. + 对于这种情况的处理方法,请参考<xref linkend="entity-hibspec" />. + EJB3 允许你利用目标实体的一个属性作为Map的key, + 这个属性可以用<literal>@MapKey(name="myProperty")</literal>来声明. + 如果使用<literal>@MapKey</literal>注解的时候不提供属性名, + 系统默认使用目标实体的主键. + map的key使用和属性相同的列:不需要为map key定义专用的列,因为map key实际上就表达了一个目标属性。 + 注意一旦加载,key不再和属性保持同步, + 也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新 + (请参考<xref linkend="entity-hibspec" />). + 很多人被<literal><map></literal>和<literal>@MapKey</literal>弄糊涂了。 + 其他它们有两点区别.<literal>@MapKey</literal>目前还有一些限制,详情请查看论坛或者 + 我们的JIRA缺陷系统。 + + + 注意一旦加载,key不再和属性保持同步, + 也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新. + (Hibernate 3中Map支持的方式在当前的发布版中还未得到支持).</para> + + <para>Hibernate将集合分以下几类.</para> + + <para></para> + + <table> + <title>集合语义</title> + + <tgroup cols="3"> + <colspec colname="c1" /> + + <colspec colname="c2" /> + + <colspec colname="c3" colnum="2" /> + + <thead> + <row> + <entry>语义</entry> + + <entry>Java实现类</entry> + + <entry> 注解</entry> + </row> + </thead> + + <tbody> + <row> + <entry>Bag 语义</entry> + + <entry>java.util.List, java.util.Collection</entry> + + <entry>@org.hibernate.annotations.CollectionOfElements 或 + @OneToMany 或 @ManyToMany</entry> + </row> + + <row> + <entry>List 语义</entry> + + <entry>java.util.List</entry> + + <entry>(@org.hibernate.annotations.CollectionOfElements 或 + @OneToMany 或 @ManyToMany) + 以及 + @org.hibernate.annotations.IndexColumn</entry> + </row> + + <row> + <entry>Set 语义</entry> + + <entry>java.util.Set</entry> + + <entry>@org.hibernate.annotations.CollectionOfElements 或 + @OneToMany 或 @ManyToMany</entry> + </row> + + <row> + <entry>Map 语义</entry> + + <entry>java.util.Map</entry> + + <entry>(@org.hibernate.annotations.CollectionOfElements 或 + @OneToMany 或 @ManyToMany) + 以及 + (空 + 或 + @org.hibernate.annotations.MapKey/MapKeyManyToMany(支持真正的map), + 或 + @javax.persistence.MapKey</entry> + + </row> + </tbody> + </tgroup> + </table> + + <remark> + 从上面可以明确地看到,没有@org.hibernate.annotations.IndexColumn + 注解的java.util.List集合将被看作bag类. + </remark> + + <para>EJB3规范不支持原始类型,核心类型,嵌入式对象的集合.但是Hibernate对此提供了支持 + (详情参考 <xref linkend="entity-hibspec" />).</para> + + <programlisting>@Entity public class City { + @OneToMany(mappedBy="city") + <emphasis role="bold">@OrderBy("streetName")</emphasis> + public List<Street> getStreets() { + return streets; + } +... +} + +@Entity public class Street { + <emphasis role="bold">public String getStreetName()</emphasis> { + return streetName; + } + + @ManyToOne + public City getCity() { + return city; + } + ... +} + + +@Entity +public class Software { + @OneToMany(mappedBy="software") + <emphasis role="bold">@MapKey(name="codeName")</emphasis> + public Map<String, Version> getVersions() { + return versions; + } +... +} + +@Entity +@Table(name="tbl_version") +public class Version { + <emphasis role="bold">public String getCodeName()</emphasis> {...} + + @ManyToOne + public Software getSoftware() { ... } +... +}</programlisting> + + <para>上面这个例子中,<literal>City</literal> + 中包括了以<literal>streetName</literal>排序的<literal>Street</literal>的集合. + 而<literal>Software</literal>中包括了以<literal>codeName</literal>作为 + key和以<literal>Version</literal>作为值的Map.</para> + + <para>除非集合为generic类型,否则你需要指定<literal>targetEntity</literal>. + 这个注解属性接受的参数为目标实体的class.</para> + </sect4> + + <sect4 id="entity-mapping-association-collection-onetomany" + revision="2"> + <title>一对多(One-to-many)</title> + + <para>在属性级使用 <literal>@OneToMany</literal>注解可定义一对多关联.一对多关联可以是双向关联.</para> + + <sect5> + <title>双向(Bidirectional)</title> + + <para>在EJB3规范中多对一这端几乎总是双向关联中的主体(owner)端, + 而一对多这端的关联注解为<literal>@OneToMany( mappedBy=... + )</literal></para> + + <programlisting>@Entity +public class Troop { + @OneToMany(mappedBy="troop") + public Set<Soldier> getSoldiers() { + ... +} + +@Entity +public class Soldier { + @ManyToOne + @JoinColumn(name="troop_fk") + public Troop getTroop() { + ... +} </programlisting> + + <para><classname>Troop</classname> 通过<literal>troop</literal> + 属性和<literal>Soldier</literal>建立了一对多的双向关联. + 在<literal>mappedBy</literal>端不必也不能再定义任何物理映射</para> + + <para>对于一对多的双向映射,如果要一对多这一端维护关联关系, + 你需要删除<literal>mappedBy</literal>元素并将多对一这端的 + <literal>@JoinColumn</literal>的insertable和updatable设置为false. + 很明显,这种方案不会得到什么明显的优化,而且还会增加一些附加的UPDATE语句.</para> + + <programlisting>@Entity +public class Troop { + @OneToMany + @JoinColumn(name="troop_fk") //we need to duplicate the physical information + public Set<Soldier> getSoldiers() { + ... +} + +@Entity +public class Soldier { + @ManyToOne + @JoinColumn(name="troop_fk", insertable=false, updatable=false) + public Troop getTroop() { + ... +}</programlisting> + </sect5> + + <sect5> + <title>单向(Unidirectional)</title> + + <para>通过在被拥有的实体端(owned entity)增加一个外键列来实现一对多单向关联是很少见的,也是不推荐的. + 我们强烈建议通过一个联接表(join table)来实现这种关联(下一节会对此进行解释). + 可以通过<literal>@JoinColumn</literal>注解来描述这种单向关联关系.</para> + + <programlisting> +@Entity +public class Customer implements Serializable { + @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER) + @JoinColumn(name="CUST_ID") + public Set<Ticket> getTickets() { + ... +} + +@Entity +public class Ticket implements Serializable { + ... //no bidir +} + </programlisting> + + <para><literal>Customer</literal> 通过 + <literal>CUST_ID</literal>列和<literal>Ticket</literal> 建立了单向关联关系.</para> + + </sect5> + + <sect5> + <title>通过关联表处理单向关联</title> + + <para>通过联接表处理单向一对多关联是首选方式.这种关联通过<literal>@JoinTable</literal>注解来进行描述.</para> + + <programlisting> +@Entity +public class Trainer { + @OneToMany + @JoinTable( + name="TrainedMonkeys", + joinColumns = { @JoinColumn( name="trainer_id") }, + inverseJoinColumns = @JoinColumn( name="monkey_id") + ) + public Set<Monkey> getTrainedMonkeys() { + ... +} + +@Entity +public class Monkey { + ... //no bidir +} + </programlisting> + + <para>上面这个例子中,<literal>Trainer</literal>通过 + <classname>TrainedMonkeys</classname>表和 + <classname>Monkey</classname> 建立了单向关联. + 其中外键<literal>trainer_id</literal>关联到<literal>Trainer</literal> + (<literal>joinColumns</literal>), + 而外键<literal>monkey_id</literal>关联到 <literal>Monkey</literal> + (<literal>inversejoinColumns</literal>).</para> + </sect5> + + <sect5> + <title>默认处理机制</title> + + <para>通过联接表来建立单向一对多关联不需要描述任何物理映射. + 表名由以下三个部分组成:主表(owner table)表名+下划线+从表(the other side table)表名. + 指向主表的外键名:主表表名+下划线+主表主键列名 + 指向从表的外键名:主表所对应实体的属性名+下划线+从表主键列名 + 指向从表的外键定义为唯一约束,用来表示一对多的关联关系.</para> + + <programlisting> +@Entity +public class Trainer { + @OneToMany + public Set<Tiger> getTrainedTigers() { + ... +} + +@Entity +public class Tiger { + ... //no bidir +} + </programlisting> + + <para>上面这个例子中,<classname>Trainer</classname>和<classname>Tiger</classname> + 通过联接表 <literal>Trainer_Tiger</literal>建立单向关联关系, + 其中外键<literal>trainer_id</literal>关联到<literal>Trainer</literal> + (主表表名, <keycap>_</keycap>(下划线), trainer id), + 而外键<literal>trainedTigers_id</literal>关联到<literal>Tiger</literal> + (属性名称, <keycap>_</keycap>(下划线), Tiger表的主键列名).</para> + + </sect5> + </sect4> + + <sect4> + <title>多对多(Many-to-many)</title> + + <sect5> + <title>定义</title> + + <para>你可以通过<literal>@ManyToMany</literal>注解可定义的多对多关联. + 同时,你也需要通过注解<literal>@JoinTable</literal>描述关联表和关联条件. + 如果是双向关联,其中一段必须定义为owner,另一端必须定义为inverse(在对关联表进行更新操作时这一端将被忽略):</para> + + <programlisting> +@Entity +public class Employer implements Serializable { + @ManyToMany( + targetEntity=org.hibernate.test.metadata.manytomany.Employee.class, + cascade={CascadeType.PERSIST, CascadeType.MERGE} + ) + @JoinTable( + name="EMPLOYER_EMPLOYEE", + joinColumns={@JoinColumn(name="EMPER_ID")}, + inverseJoinColumns={@JoinColumn(name="EMPEE_ID")} + ) + public Collection getEmployees() { + return employees; + } + ... +} + </programlisting> + + <programlisting> +@Entity +public class Employee implements Serializable { + @ManyToMany( + cascade={CascadeType.PERSIST, CascadeType.MERGE}, + mappedBy="employees" + targetEntity=Employer.class + ) + public Collection getEmployers() { + return employers; + } +} + </programlisting> + + <para>至此,我们已经展示了很多跟关联有关的声明定义以及属性细节. + 下面我们将深入介绍<literal>@JoinTable</literal>注解,该注解定义了联接表的表名, + 联接列数组(注解中定义数组的格式为{ A, B, C }), + 以及inverse联接列数组. + 后者是关联表中关联到<classname>Employee</classname>主键的列(the "other side").</para> + + <para>正如前面所示,被关联端不必也不能描述物理映射: + 只需要一个简单的<literal>mappedBy</literal>参数,该参数包含了主体端的属性名,这样就绑定双方的关系.</para> + </sect5> + + <sect5> + <title>默认值</title> + + <para>和其他许多注解一样,在多对多关联中很多值是自动生成. + 当双向多对多关联中没有定义任何物理映射时,Hibernate根据以下规则生成相应的值. + 关联表名:主表表名+<keycap>_</keycap>下划线+从表表名, + 关联到主表的外键名:主表名+<keycap>_</keycap>下划线+主表中的主键列名. + 关联到从表的外键名:主表中用于关联的属性名+<keycap>_</keycap>下划线+从表的主键列名. + 以上规则对于双向一对多关联同样有效.</para> + + <programlisting> +@Entity +public class Store { + @ManyToMany(cascade = CascadeType.PERSIST) + public Set<City> getImplantedIn() { + ... + } +} + +@Entity +public class City { + ... //no bidirectional relationship +} + </programlisting> + + <para>上面这个例子中,<literal>Store_Table</literal>作为联接表. + <literal>Store_id</literal>列是联接到<literal>Store</literal>表的外键. + 而<literal>implantedIn_id</literal>列则联接到<literal>City</literal>表.</para> + + <para>当双向多对多关联中没有定义任何物理映射时, Hibernate根据以下规则生成相应的值 + 关联表名: :主表表名+<keycap>_</keycap>下划线+从表表名, + 关联到主表的外键名:从表用于关联的属性名+<keycap>_</keycap>下划线+主表中的主键列名. + 关联到从表的外键名:主表用于关联的属性名+<keycap>_</keycap>下划线+从表的主键列名. + 以上规则对于双向一对多关联同样有效.</para> + + <programlisting> +@Entity +public class Store { + @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + public Set<Customer> getCustomers() { + ... + } +} + +@Entity +public class Customer { + @ManyToMany(mappedBy="customers") + public Set<Store> getStores() { + ... + } +} + </programlisting> + + <para>在上面这个例子中,<literal>Store_Customer</literal>作为联接表. + <literal>stores_id</literal>列是联接到<literal>Store</literal>表的外键, + 而<literal>customers_id</literal>列联接到<literal>City</literal>表.</para> + + </sect5> + </sect4> + </sect3> + + + <sect3> + <title>用cascading实现传播性持久化(Transitive persistence)</title> + + <para>也许你已经注意到了<literal>cascade</literal>属性接受的值为<classname>CascadeType</classname>数组. + 在EJB3中的cascade的概念和Hibernate中的传播性持久化以及cascade操作非常类似, + 但是在语义上有细微的区别,支持的cascade类型也有点区别:</para> + + <itemizedlist> + <listitem> + CascadeType.PERSIST: 如果一个实体是受管状态, 或者当persist()函数被调用时, 触发级联创建(create)操作 + </listitem> + + <listitem> + CascadeType.MERGE: 如果一个实体是受管状态, 或者当merge()函数被调用时, 触发级联合并(merge)操作 + </listitem> + + <listitem> + CascadeType.REMOVE: 当delete()函数被调用时, 触发级联删除(remove)操作 + </listitem> + + <listitem> + CascadeType.REFRESH: 当refresh()函数被调用时, 触发级联更新(refresh)操作 + </listitem> + + <listitem> + CascadeType.ALL: 以上全部 + </listitem> + </itemizedlist> + + <para>关于cascading, create/merge的语义请参考EJB3规范的6.3章节.</para> + </sect3> + + <sect3> + <title>关联关系获取</title> + + <para>通过Hibernate你可以获得直接或者延迟获取关联实体的功能. + <literal>fetch</literal>参数可以设置为<literal>FetchType.LAZY</literal> + 或者 <literal>FetchType.EAGER</literal>. + <literal>EAGER</literal>通过<literal>outer join select</literal>直接获取关联的对象, + 而<literal>LAZY</literal>(默认值)在第一次访问关联对象的时候才会触发相应的select操作. + EJBQL提供了<literal>fetch</literal>关键字,该关键字可以在进行特殊查询的时候覆盖默认值. + 这对于提高性能来说非常有效,应该根据实际的用例来判断是否选择fetch关键字.</para> + </sect3> + </sect2> + + <sect2> + <title>映射复合主键与外键</title> + + <para>组合主键使用一个可嵌入的类作为主键表示,因此你需要使用<literal>@Id</literal> + 和<literal>@Embeddable</literal>两个注解. + 还有一种方式是使用<literal>@EmbeddedId</literal>注解.注意所依赖的类必须实现 + serializable以及实现<methodname>equals()</methodname>/<methodname>hashCode()</methodname>方法. + 你也可以如<xref linkend="mapping-identifier" />一章中描述的办法使用<literal>@IdClass</literal>注解.</para> + + <programlisting> +@Entity +public class RegionalArticle implements Serializable { + + @Id + public RegionalArticlePk getPk() { ... } +} + +@Embeddable +public class RegionalArticlePk implements Serializable { ... } + </programlisting> + + <para>或者</para> + + <programlisting> +@Entity +public class RegionalArticle implements Serializable { + + @EmbeddedId + public RegionalArticlePk getPk() { ... } +} + +public class RegionalArticlePk implements Serializable { ... } + </programlisting> + + <para><literal>@Embeddable</literal> 注解默认继承了其所属实体的访问类型, + 除非显式使用了Hibernate的<literal>@AccessType</literal>注解(这个注解不是EJB3标准的一部分). + 而<literal>@JoinColumns</literal>,即<literal>@JoinColumn</literal>数组, + 定义了关联的组合外键(如果不使用缺省值的话). + 显式指明<literal>referencedColumnNames</literal>是一个好的实践方式, + 否则,Hibernate认为你使用的列顺序和主键声明的顺序一致.</para> + + <programlisting> +@Entity +public class Parent implements Serializable { + @Id + public ParentPk id; + public int age; + + @OneToMany(cascade=CascadeType.ALL) + @JoinColumns ({ + @JoinColumn(name="parentCivility", referencedColumnName = "isMale"), + @JoinColumn(name="parentLastName", referencedColumnName = "lastName"), + @JoinColumn(name="parentFirstName", referencedColumnName = "firstName") + }) + public Set<Child> children; //unidirectional + ... +} + </programlisting> + + <programlisting> +@Entity +public class Child implements Serializable { + @Id @GeneratedValue + public Integer id; + + @ManyToOne + @JoinColumns ({ + @JoinColumn(name="parentCivility", referencedColumnName = "isMale"), + @JoinColumn(name="parentLastName", referencedColumnName = "lastName"), + @JoinColumn(name="parentFirstName", referencedColumnName = "firstName") + }) + public Parent parent; //unidirectional +} + </programlisting> + + <programlisting> +@Embeddable +public class ParentPk implements Serializable { + String first... [truncated message content] |