6.2.11 用@Table和@SecondaryTable进行数据库表映射
可以使用@Table注解指定数据库中用来进行实体持久化的表的细节。如果省略这个注解,Hibernate就使用类名作为表名;所以只有在希望覆盖默认行为时,才需要提供这个注解,@Table注解支持4个属性,可以覆盖表名、编目和模式,还可以在表列上施加唯一约束。在通常情况下,只提供表名,比如@Table(name=”ORDER_HISTORY”)。如果数据库模式是从带注解的类生成的,而且需要补充任何列特有的约束,那么就要应用唯一约束(参见本章后面对@Column和@JoinColumn的讨论)。否则,不施加这些约束。
@SecondaryTable注解可以将实体bean持久化到几个不同的数据库表中。在这种情况下,除了为主数据库表提供@Table注解之外,实体bean还具有一个@SecondaryTable注解或@SecondaryTables注解(@SecondaryTables注解中包含零个或多个@SecondaryTable注解)。@SecondaryTable注解的基本属性与@Table注解相同,但是增加了join属性。join属性定义与主数据库表进行联结的联结列。join属性的值是javax.persistence.PrimaryKeyJoinColumn对象的数组。如果省略join属性,那么表根据同名的主键列表进行联结。
当从辅助表获取实体中的属性时,必须给它标上@Column注解,并且用表属性标识适当的表。代码清单6-10演示如何从辅助表获取Customer实体的一个属性。
代码清单6-10 跨两个表映射的实体的字段访问示例
package com.speakermore.beginhb.ch06;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.SecondaryTable;
import javax.persistence.Table;
@Entity
@Table(name="Customer")
@SecondaryTable(name="Customer_Details")
public class Customer implements Serializable {
@Id
public int id;
public String name;
@Column(table="Customer_Details")
public String address;
}
对于主表或辅助表中的一,可以在@Table或@SecondaryTable的uniqueConstraints属性中添加一个或多个适当的@UniqueConstraint注解,从而指定它们具有唯一的值。例如,使用以下代码将前面声明中的name字段标为唯一的:
@Entity
@Table(
name="Customer"
uniqueConstraints={@UniqueConstraint(columnNames=”name”)}
)
@SecondaryTable(name="Customer_Details")
public class Customer implements Serializable {
…
}
在默认情况下,POJO中的属性和实例变量会被持久化----Hibernate会替你存储它们的值。因此,最简单的映射是对“基本”类型的映射。这些类型包括基本数据类型、包装类、它们的数组、枚举及其它任何实现了Serializable但不是实体的类型。这些类型都被隐式地映射,不需要注解。在默认情况下,这样的字段会映射到单一列,并即时读取(当从数据库中获取这个实体时,会同时获取所有基本字段和属性)。另外,如果字段或属性不是基本数据类型,那么可以作为null值存储和获取。
通过在适当的类成员上应用@Basic注解,可以覆盖这种默认行为。这个注解有两个可选属性:一个叫option,它的值类型是boolean,默认为true。如果将这个属性设置为false,那么在创建数据库模式时,相关联的列应该设置为NOT NULL。第二个属性称为fetch,它的值是FetchType枚举的一个成员,默认值是EAGER。如果设置为LAZY,就会提示在访问相关联的列时进行加载。使用延迟加载在一般情况下意义不大,除非是将大型可序列化对象作为基本类型进行映射(而不是为它们本身设置实体映射),在这种情况下使用延迟加载可以减少获取实体所用的时间。默认值EAGER一定会生效,但是LAZY标志只是一个提示,持久化引擎可以忽略这个标志。
常常省略@Basic注解,并且使用@Column注解替代@Basic注解的可选属性,除非是需要设置NOT NULL行为
一些字段可能只在运行时使用,在对象持久化到数据库中时应该从对象中去掉它们。EJB 3规范为这些瞬时字段提供了@Transient注解。它没有任何属性,只需要根据实体bean的属性访问策略,将它添加到实例变量或获取器方法上即可。
在标例中,在Book类中添加一个名为publicationDate的Date字段,这个字段不需要存储在数据库中。因此,将这个字段标为瞬时的:
@Transient
public Date getPublicationDate(){
return publicationDate;
}
@Column注解用来指定字段或属性映射到的列的细节。一些细节是与数据库相关的,因此只在从带注解的文件生成模式时使用这些信息。其他信息由Hibernate(或EJB 3持久化引擎)在运行时应用。它是可选的,有一组合理的默认值:但是在覆盖默认行为,或者对象模型需要适应现有的数据库模式(默然说话:例如:实体的属性名与数据库表的列名不一致)时,常常需要使用这个注解。它比@Basic注解更常用。通常需要为以下属性提供值:
v name允许显式地指定列名——默认情况下,以属性名作为列名。但如果SQL关键字与默认的列名冲突(比如user),就必须覆盖默认的列名。
v length允许显式地指定用来映射值的列的大小(尤其是对String值)。默认列大小是255,这可能会导致String数据被截断(默然说话:还记得那个有名的“将截断二进制字符串”的SQL错么?255也许对于大多数字符串是足够的,但如果你是一篇文章的内容呢?)
v nullable可以在生成模式(默然说话:再次强调,模式就是表!)时将列标为NOT NULL。在默认情况下,字段应允许为空,但是,如果字段是必须的,就需要覆盖这个设置。
v unique指定列只包括唯一的值。默认设置是false,但是有的非主键字段如果出现重复的值,也会导致问题(比如用户名),在这种情况下就需要设置unique属性。
我们给Book实体的标题字段加上@Column注解,并且指定了其中3个属性:
@Column(name="working_title",length=200,nullable=false)
public String getTitle() {
return title;
}
还有一些不太常用的属性:
v 当实体跨一个或多个辅助表进行映射时,使用table属性。在默认情况下,从主表中获取值,但是可以在这里指定辅助表之一(参见本章@SecondaryTable注解示例)。
v insertable属性的默认值是true。如果设置为false,那么Hibernate生成的插入语句就会忽略注解的字段(也就是不对它进行持久化)。
v updateable属性的默认值是true,如果设置为false,那么Hibernate生成的插入语句就会忽略注解的字段(也就是持久化后不再修改它)。
v columnDefinition属性的默认值可以设置为DDL片段,这个DDL片段用来在数据库中生成这个列。这个属性只在从带注解的实体生成模式时使用,而且应该尽可能避免使用它,因为它可能损害应用程序在不同数据库之间的可移植性。
v precision属性可以在生成模式时指定小数数字列的精度,如果持久化的值不是小数,就会忽略这个属性。这个属性的值表示数字中的位数(通常需要的最小长度是n+1,其中的n是刻度)。
v scale属性可以在生成模式时指定小数数字列的刻度,如何持久化的值不是小数,就会忽略这个属性。这个属性的值表示小数点后面的位数。
EJB 3支持一对一,一对多,多对一和多对多关联。每种关联类型都有对应的注解。在本节中,我们将讨论如何用注解建立各种关联。
1.对嵌入实体(组件)的一对一关联进行映射
如果一个实体的所有字段都维护在与另一个实体相同的表中,那么在Hibernate中就将这个实体称为组件(component)。EJB 3标准将这样的实体称为嵌入的(embedded)实体。
@Embedded和@Embeddable注解用来管理这样的关系。在这个图书数据库示例中,我们按照这种方式在AuthorAddress类和Author类之间建立关联。
AuthorAddress标有@Embeddable注解。可嵌入的实体必须完全由基本数据类型的属性组成。可嵌入的实体只能使用@Basic,@Lob,@Temporal和@Enumerated注解。它不能用Id注解维护自己的主键,因为它的主键就是包含它的实体的主键。
(默然说话:其实嵌入实体就是把一个实体A中的一部分属性单独又做成一个类B,然后在A里声明一个B类型的属性,这样的做法比较适合那种字段多的表,而我们所需要的数据常常只是几个字段,那我们可以把这几个字段独立为一个实体,再把它嵌入到对应表的实体类中。)
@Embeddable本身是一个纯粹的标志性注解,它没有任何属性。通常情况下,可嵌入实体的属性不需要进一步注解。
代码清单6-11 表示一个实体可以嵌入其他实体中
@Embeddable
public class AuthorAddress {
}
然后,包含可嵌入实体的实体应该给适当的字段或getter方法加注解,在使用可嵌入实体的地方加上@Embedded注解,如代码清单6-12所示。
代码清单6-12 标出一个嵌入的属性
@Embedded
public AuthorAddress getAddress(){
return this.address;
}
@Embedded注解会从嵌入的类型获得它的列信息,但是允许用@AttributeOverride和@AttributeOverrides注解覆盖特定的列。例如,代码清单6-13将AuthorAddress的address和country属性的默认列名改为ADDR和NATION
代码清单6-13 覆盖嵌入属性的默认属性
@Embedded
@AttributeOverrides({
@AttributeOverride(name=”address”,column=@Column(name=”ADDR”)),
@AttributeOverride(name=”country”,column=@Column(name=”NATION”))
})
public AuthorAddress getAddress(){
return this.address;
}
Hibernate和EJB 3标准都不支持跨多个表映射嵌入的实体。应该通过传统的一对一关联映射它。
2.对传统的一对一关联进行映射
在使用@OneToOne注解之前,应该考虑是否使用前面介绍的嵌入技术,因为这样的关系常有些危险性。
如果你决定以这种方式声明关联(可能是因为打算以后将它改为一对多或多对多关系),那么应用注解是非常容易的——所有属性都是可选的。代码清单6-14演示如何声明这样的关系。
代码清单6-14 声明简单的一对一关系
@OneToOne
public Address getAddress(){
return this.address;
}
@OneToOne注解允许指定以下可选属性:
v targetEntity可以设置为存储这个注解的实体的类。如果不设置这个属性,就从字段的类型或getter的返回类型推断出适当的类型。
v cascade可以设置为javax.persistence.CascadeType枚举的任何成员。默认情况下不设置这个属性。参见下面的“级联操作”中对这些值的讨论。
v fetch可以设置为FetchType的EAGER或LAZY
v optional表示映射的值是否可空。
v mappedBy表示一个双向一对一关系由指定的实体拥有。拥有关系的实体包含从属实体的主键。
级联操作
如果在两个实体之间建立了关联(例如Human和Pet之间的一对一关联),那么在对一个实体执行某些持久化操作时,常常希望这些操作也应用于它链接到的实体。请考虑以下代码:
Human dave=new Human("dave");
Pet cat=new PetCat(“Tibbles”);
dave.setPet(cat);
session.save(dave);
在最后一行(用粗体突出显示),我们可能希望保存与Human对象关联的Pet对象。在一对一关系中,我们常常希望实体上的所有操作都传播(即级联)到从属实体。但是在其他关联中,这不是我们需要的效果;甚至在一对一关系中,也可能由于特殊的原因,不希望级联删除从属实体(可能是为了进行审计)。
因此,可以使用级联注解指定哪些类型的操作应该通过关联级联到另一个实体。这个注解接受一个由CascadeType枚举成员组成的数组。这些成员与EJB 3持久化所用的EntityManager类的方法名对应,也大致对应于实体上的操作类型:
v ALL:要求所有操作都级联到从属实体。这相当于同时包含MERGE、PERSIST、REFRESH和REMOVE。
v MERGE:级联更新实体在数据库中的状态(即UPDATE…)。
v PERSIST:级联地对实体在数据库中的状态进行最初存储(即INSERT…)。
v REFRESH:级联地从数据库中获取实体的状态(即SELECT…)。
v REMOVE:级联地从数据库中删除实体的状态(即DELETE…)。
v 如果没有指定级联类型,那么没有任何操作会通过关联进行级联。
所以,对于出版商和它的地址之间的关系,合适的注解如下所示:
@OneToOne(cascade=CascadeType.ALL)
public Address getAddress(){
return this.address;
}
3.对多对一或一对多关联进行映射
多对一或一对多关联其实是一样的,只是分别从拥有实体和从属实体的角度来看同一个关联。
在两个实体之间维护多对一关系最简单的方法是,在“多”端实体的表的一列中,管理一对多关系的“一”端实体的外键。
集合的次序
EJB 3没有提供管理有序集合(比如数组或List)的次序的机制——在6.4.3节中,我们将讨论如何用@IndexColumn注解弥补这个不足,然而这种方式不具有可移植性。然而,可以通过使用@orderBy注解,为集合指定在获取数据时用来排序的实体字段。例如,如果希望按照书名对列表进行升序排序,那么可以对适当的方法加注解。
以下代码片段为一个有序集合指定获取次序:
@OneToMany(cascade= CascadeType.ALL,mappedBy=”publisher”)
@OrderBy(“name ASC”)
public List<Book> getBooks(){
return books;
}
可以将@OneToMany注解应用于一个字段或属性值,表示这个值是一个代表关联的“多”端的集合或数组。
v mappedBy属性对于双向关联是必需的,对于意向关联是可选的(隐含的)。
v cascade属性是可选的,它的值是javax.persistence.CascadeType枚举的成员之一,表示映射的实体的级联行为。
v targetEntity属性是可选的,因为常可以从字段或属性的类型推断出它的值。比如在代码清单6-15中,属性表示Book实体的Set,所以目标实体是Book。但是,如果需要的话(例如,由于不希望使用泛型),那么可以在这里提供目标实体的类。
v fetch属性是可选的,可以用javax.persistence.FetchType枚举的成员之一指定即时读取或延迟读取。
代码清单6-15 对Book实体对Publisher实体的一对多关系进行映射
@OneToMany(cascade=CascadeType.ALL,mappedBy=”publisher”)
public Set<Book> getBooks(){
return books;
}
这个关系的多对一端的表达方式与一对多端相似,见代码清单6-16。
代码清单6-16 对Publisher实体到Book实体的多对一关系进行映射
@ManyToOne
@JoinColumn(name=”publisher_id”)
public Publisher getPublisher(){
return publisher;
}
@ManyToOne注解的属性与@OneToMany注解相似。下面解释这些属性,它们都是可选的。
v cascade属性为关联上的操作指定适当的级联策略;在默认情况下,不设置这个属性。
v fetch属性表示使用的抓取策略;它的默认值是LAZY。
v optionl属性表示值是否可空;它的默认值是true。
v targetEntity属性表示存储主键的实体——常常可以从字段或属性的类型(前面示例中的Publisher)推断出这个属性的值。
我们还提供了可选的@JoinColumn属性,从而为关联指定默认列(publisher)之外的外键列——这并不是必须的,但是这演示了这个注解的使用方法。
在建立单向的一对多关联时,可以使用链接表表示这个关系。这需要添加@JoinTable注解,见代码清单6-17。
代码清单6-17 用链接表建立简单的单向一对多关系
@OneToMany(cascade=CascadeType.ALL)
@JoinTable
public Set<Book> getBooks(){
return books;
}
@JoinTable提供的属性可以控制链接表的各个方面。这些属性如下所示:
v name是用来表示这个关联的链接表的名称
v catalog是包含链接表的数据库编目的名称。
v schema是包含链接表的数据库模式的名称。
v joinColumns是一个@JoinColumn属性数组,这些属性表示关联的“一”端上实体的主键。
v inverseJoinColumns是一个@JoinColumn属性数组,这些属性表示关联的“多”端上实体的主键。
代码清单6-18给出一个非常典型的@JoinTable注解,它指定了链接表的名称以及相关实体的外键。
代码清单6-18 用链接表建立单向一对多关联,指定了更多的链接表细节
@OneToMany(cascade=CascadeType.ALL)
@JoinTable(
name=”PublishedBooks”,
joinColumns={@JoinColumn(name=”publisher_id”)},
inverseJoinColumns=@JoinColumn(name=”book_id”)
)
public Set<Book> getBooks(){
return books;
}
4.对多对多关联进行映射
如果多对多关联并不涉及用第一类实体联结关系的两端,那么必须使用链接表维护这个关系。这个链接表可以自动生成,也可以按照6.2.15节“对多对一或一对多关联进行映射”中的描述提供链接表的细节。
所用的注解是@ManyToMany,这个注解具有以下属性:
v mappedBy指出拥有这个关系的字段——这个属性只对双向关联是必需的。如果一个实体提供了这个属性,那么关联的另一端就是关联的拥有者,而且这个属性必须指定该实体的一个字段或属性。
v targetEntity是关联的目标实体的类。同样,常常可以从泛型或数组声明推断出这个属性;只有在不可能进行推断的情况下,才需要指定这个属性。
v cascade指定关联的级联行为,默认情况下不设置。
v fetch指定关联的读邓行为,默认值是LAZY。
这个示例在Book类和Author类之间维护一个多对多关联。Book实体拥有这个关联,所以它的getAuther()方法必须加上适当的@ManyToMany注解,见代码清单6-19
代码清单6-19 多对多关联的Book端
@ManyToMany(cascade=CascadeType.ALL)
public Set<Author> getAuthors(){
return authors;
}
Author实体由Book实体管理。链接表不需要显式地管理,所以如代码清单6-20所示,我们给Author实体标上@ManyToMany注解,并且指出外键由相关联的Book实体的authors属性管理。
代码清单6-19 多对多关联的Author端
@ManyToMany(mappedBy=”authors”)
public Set<Book> getBooks(){
return books;
}
我们也可以指定链接表细节,如代码清单6-21所示:
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(
name=”Books_to_Author”,
joinColumns={@JoinColumn(name=”book_ident”)},
inverseJoinColumns={@JoinColumn(name=”author_ident”)}
)
public Set<Author> getAuthors(){
return authors;
}
EJB3标准和Hibernate都支持3种将继承层次结构映射到数据库中的方法。这3种方法如下所示:
v 单一表(SINGLE_TABLE)
v 联结表(JOINED)
v 每个类一个表(TABLE_PER_CLASS)
按照继承关系联系在一起的持久化对象必须标上@Inheritance注解。这个注解只有一个策略属性,这个属性设置为3个javax.persistence.InheritanceType枚举值之一(前面列表中括号里的值),分别对应于3种映射方法。
单一表方法用一个表管理超类和所有子类型。对于超类中每个映射的字段或属性,以及派生类型中的每个不同字段或属性,这个表中都有对应的列。当采用这种策略时,如果层次结构中的任何字段或属性名发生冲突,就需要对列重新命名。
为了在在从数据库中获取实体时决定要实例化的类型,应该在持久化层次结构的根上(而且只在根上)提供@DiscriminatorColumn注解。这个注解定义一个列,这个列中的值用来区分各个类型。@DiscriminatorColumn注解的属性如下所示:
v name是识别符(discriminator)列的名称。
v discriminatorType是这个列中存储的值的类型,它应该是javax.persistence.DiscriminatorType枚举值STRING、CHAR或INTEGER之一。
v columnDefinition是定义列类型的DDL片段。使用这个属性有可能损害代码在数据库之间的可移植性。
v length是STRING识别符类型的列长度。对于CHAR和INTEGER类型,忽略这个属性。
这些属性(以及这个注解本身)都是可选的,但是我们至少要提供name属性。如果没有指定@DiscriminatorColumn注解,那么使用默认列名DTYPE和STRING类型。
Hibernate将为每个实体提供适当的识别符值。例如,如果使用STRING识别符类型,那么这个列包含的值是实体的名称(在默认情况下就是类名)。也可以通过@DiscriminatorVlue注解使用特定的值覆盖默认行为。如果识别符类型是INTEGER,那么通过@DiscriminatorVlue注解提供的任何值必须能够直接转换为整数。
在代码清单6-22中,我们指定识别符类型为INTEGER,列名为DISCRIMINATOR。在代表Book实体的行中,这一列的值是1;而代码清单6-23中的映射指定,在代表ComputerBook实体的行中,这一列的值是2。
代码清单6-22 用SINGLE_TABLE策略映射的继承层次结构的根
@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
name=”DISCRIMINSTOR”,
discriminatorType= DiscriminatorType.INTEGER
)
@DiscriminatorValue(“1”)
public class Book{
…
}
代码清单6-23 继承层次结构中的派生实体
@Entity
@DiscriminatorValue(“2”)
public class ComputerBook extends Book{
…
}
联结表方法与单一表方法相似。联结表方法也使用一个识别符列,但是各个派生类型的字段存储在不同的表中。除了使用的策略不同之外,指定继承类型的方式是相同的(见代码清单6-24)
代码清单6-24 用JOINED策略映射的继承层次结构的根
@Entity
@Inheritance(strategy= InheritanceType.JOINED)
@DiscriminatorColumn(
name=”DISCRIMINATOR”
)
public class Book{
…
}
最后是每个类一个表方法,继承层次结构中的类型的所有字段存储在不同的表中。因为实体和它的表之间有密切的对应关系,所以这种继承策略不使用@DiscriminatorColumn注解。代码清单6-25演示如何以这种方式映射Book类。
代码清单6-25 用TABLE_PER_CLASS策略映射的继承层次结构的根
@Entity
@Inheritance(strategy= InheritanceType.TABLE_PER_CLASS)
public class Book{
…
}
1.时间数据
在实体中,通常用java.util.DATE或java.util.Calendar类型的字段或属性表示时间数据。在默认情况下,这些数据存储在TIMESTAMP数据类型的列中,但是可以用@Temporal注解覆盖这种默认行为。
这个注解只有一个value属性,它的值来自javax.persistence.TemporalType枚举。这个枚举提供3个值:DATE、TIME和TIMESTAMP。这些分别对应于java.sql.Date,java.sql.Time和java.sql.Timestamp。在生成数据库表时,表列会设置为适当的数据类型。代码清单6-26将一个java.util.Date属性映射为TIME类型——java.sql.Date和java.sql.Time类都派生自java.util.Date类,所以它们都能够表示日期和时间。
代码清单6-26 映射为Time时间字段的Date属性
@Temporal(TemporalType.TIME)
public java.util.Date getStartingTime(){
return this.startingTime;
}
2.大对象
可以给一个持久化属性或字段标上@Lob注解,这表示它将存储为数据库支持的大对象类型。
这个注解没有属性,将从字段或属性的类型推断出使用的底层大对象类型。基于字符串或字符的类型将存储为适当的字符类型。所有其他对象存储为BLOB。代码清单6-27将一个String映射到大对象列类型。
代码清单6-27 大对象属性的示例
@Lob
public String getTitle(){
return this.title;
}
3.映射的超类
继承可能有一种特殊情况:层次结构的根本身不是持久化实体一,但是从它派生出的各个类却是持久化实体一。这种类可以是抽象的,也可以是具体的。可以使用@MappedSuperclass注解处理这种情况。标上@MappedSuperclass的类不是实体,而且是不可查询的(不能将它传递给Session或Manager对象中那些需要实体的方法)。它不能作为关联的目标。
例如,如果示例将Book作为ComputerBook的超类,但是Book对象本身不直接持久化,那么可以给Book标上@MappedSuperclass,见代码清单6-28。
代码清单6-28 将Book类标为映射的超类
@MappedSuperclass//默然说话:书上这里写错了,@Entity和@MappedSuperclass不能联合使用。
public class Book{
…
}
4.命名的查询(HQL或EJB QL)
可以使用@namedQuery和@NamedQueries将一个或多个EJB QL查询与一个实体类的表关联起来。所需的属性如下所示:
v name是查询的名称。
v query是与这个名称相关联的EJB QL(或HQL)查询。
代码清单6-29将一个命名的查询与Author实体关联起来。这个查询按照名称获取Author实体,所以它与Author实体相关联是很自然的——但实际上不要求命名的查询与实体有这种关联。
代码清单6-29 EJB QL命名查询注解
@Entity
@NamedQuery(
name=”findAuthorsByName”,
query=”from Author where name=:author”
)
public class Author{
……
}
还有一个hints属性,该属性的值是Queryhint注解键/值对,可以指定缓存模式、超时值以及各种与平台相关的设置(也可以用来注释查询所生成的SQL)。
不必将查询与它针对的实体直接关联起来,但是一般会这么做。如果一个查询与任何实体都没有自然的关联,那么可以在包级别应用@NamedQuery注解。
@NamedQuery需要使用一个特殊文件package-info.java来包含它。代码清单6-30给出一个示例:
代码清单6-30 package-info.java文件
@javax.annotations.NamedQuery(
name=”findAuthorByName”,
query=”from Author where name=:author”
)
package com.hibernatebook.annotations;
Hibernate会话允许直接访问命名的查询,见代码清单6-31
代码清单6-31 通过会话调用命名的查询
Query query=session.getNamedQuery(“findAuthorsByName”);
query.setParameter(“author”,”Dave”);
List booksByDave=query.list();
System.out.println(“一共有”+booksByDave.size()+”个叫Dave的作者”);
如果有多个@NamedQuery注解要应用于一个实体,那么可以使用@NamedQueries注解,在其中提供@NamedQuery注解的数组。
5.命名的原生查询(SQL)
EJB 3还允许使用数据库的原生查询语言(常常是SQL的一种方言)替代EJB QL。如果使用数据库特有的特性,就可能损害可移植性,但是只要合理地使用通用的SQL,就不会有问题。声明@NamedNativeQuery注解的方式几乎与@NamedQuery注解完全相同。下面的代码给出一个使用命名原生查询的简单示例:
@NamedNativeQuery(
name=”nativeFindAuthorNames”,
query=”select name from author”
)
多个@NamedNativeQuery注解可以用@NamedNativeQueries注解组合在一起。
说明:Hibernate当前对命名原生查询的支持还不完整。
给类加上注解之后,就像XML映射一样,需要将类提供给应用程序的Hibernate配置。在使用注解时,可以在hibernate.cfg.xmlXML配置文档中使用声明式配置,也可以通过程序方式将带注解的类添加到Hibernate的org.hibernate.cfg.AnnotationConfiguration对象中。应用程序可以在同一配置中同时使用带注解的实体的XML映射的实体。
想提供声明式映射,需要使用hibernate.cfg.xml XML配置文件,并且使用映射元素将带注解的类添加到映射中(见代码清单6-32)。注意,我们将带注解的类的名称指定为映射。
代码清单6-32
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<!--
Document : hibernate.cfg.xml
Created on : 2008年12月19日, 下午3:55
Author : mouyong
Description:
Purpose of the document follows.
-->
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.url">
jdbc:sqlserver://localhost;user=sa;password=sa;databasename=books
</property>
<property name="hibernate.connection.driver_class">
com.microsoft.sqlserver.jdbc.SQLServerDriver
</property>
<property name="hibernate.conncetion.username">sa</property>
<property name="hibernate.conncetion.password"></property>
<property name="hibernate.conncetion.pool_size">0</property>
<property name="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</property>
<property name="hibernate.show_sql">true</property>
<!--导入映射文件-->
<mapping class="com.speakermore.beginhb.ch06.annotations.Author" />
<mapping class="com.speakermore.beginhb.ch06.annotations.AuhtorAddress" />
<mapping class="com.speakermore.beginhb.ch06.annotations.Book" />
<mapping class="com.speakermore.beginhb.ch06.annotations.Address" />
<mapping class="com.speakermore.beginhb.ch06.annotations.Publisher" />
<mapping class="com.speakermore.beginhb.ch06.annotations.ComputerBook" />
</session-factory>
</hibernate-configuration>
还可以通过程序方式将带注解的类添加到Hibernate配置中。注解工具集提供一个org.hibernate.cfg.AnnotationConfiguration对象,这个对象扩展了Hibernate的Configuration对象,增加了用来添加映射的方法。AnnotationConfiguration中用来将带注解的类添加到配置的方法如下:
addAnnotated(Class persistentClass) throws MappingException
addAnnotatedClasses(List<Class> classes)
addPackage(String packageName) throws MappingException
1282

被折叠的 条评论
为什么被折叠?



