文章来源:http://anonhibernate.labs.jboss.com/trunk/HibernateExt/metadata/doc/reference/zh_cn/modules/entity.xml
Tags: hibernatehibernate | annotationsannotations | EJBEJB | JPAJPA
2007-2-4 13:53:05 | 编辑
EJB3.0-JPA实体的注解规范以及Hibernate特有的扩展
它们的映射都通过JDK5.0注解来定义(EJB3规范已经定义了对应的XML描述语法).
注解分为两个部分,分别是逻辑映射注解和物理映射注解,
通过逻辑映射注解可以描述对象模型,类之间的关系等等,
而物理映射注解则描述了物理的schema,表,列,索引等等.
下面我们在代码中将混合使用这两种类型的注解.
大部分和JDK5兼容的IDE(象Eclipse, IntelliJ IDEA 和Netbeans等等)都提供了注解接口和属性的自动完成功能.
(这些不需要IDE提供特别的EJB3支持模块,因为EJB3注解是标准的JDK5注解)
@Entity
public class Flight implements Serializable {
Long id;
@Id
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
}
其他的映射定义是隐式的.这种以隐式映射为主体,以显式映射为例外的配置方式在新的EJ3规范中处于非常重要的位置,
和以前的版本相比有了质的飞跃.
在上面这段代码中:Flight类映射到Flight表,并使用id列作为主键列.
EJ3规范要求在需要访问的元素上进行注解声明,例如,如果访问类型为
如果访问类型为
Hibernate根据
通过
如果没有定义
@Entity
@Table(name="tbl_sky")
public class Sky implements Serializable {
...
和一个
结合使用
(对于绑定到单列的唯一约束,请参考
)
注意
默认的EJB3命名策略将物理字段名当作逻辑字段名来使用.
注意该字段名和它对应的属性名可能不同(如果字段名是显式指定的话).
除非你重写了NamingStrategy,否则不用担心这些区别..
@Entity
public class Flight implements Serializable {
...
@Version
@Column(name="OPTLOCK")
public Integer getVersion() { ... }
}
entity manager使用该字段来检测更新冲突(防止更新丢失,请参考last-commit-wins策略).
Hibernate支持任何自定义类型,只要该类型实现了
entity bean is considered persistent, unless you annotate it as
property is equivalent to the appropriate
annotation. The
declare the fetching strategy for a 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
entity manager将忽略这些字段和属性.
而
这几个属性则是被定义为可持久化和可获取的.对于简单属性来说,默认的获取方式是即时获取(early fetch).
当一个实体Bean的实例被创建时,Hibernate会将这些属性的值从数据库中提取出来,保存到Bean的属性里.
与即时获取相对应的是延迟获取(lazy fetch).如果一个属性的获取方式是延迟获取
(比如上面例子中的
Hibernate在创建一个实体Bean的实例时,不会即时将这个属性的值从数据库中读出.
只有在该实体Bean的这个属性第一次被调用时,Hibernate才会去获取对应的值.
通常你不需要对简单属性设置延迟获取(lazy simple property),千万不要和延迟关联获取(lazy association fetch)混淆了
(译注:这里指不要把lazy simple property和lazy association fetch混淆了).
字节码将被织入原始类中来实现延迟获取功能,
详情参考Hibernate参考文档.如果不对类文件进行字节码特殊处理,
那么属性级的延迟获取将被忽略.
这些基本类型包括所有的Java基本类型,及其各自的wrapper类和serializable类.
Hibernate Annotations还支持将内置的枚举类型映射到一个顺序列(保存了相应的序列值)
或一个字符串类型的列(保存相应的字符串).默认是保存枚举的序列值,
但是你可以通过
因此处理时间类型数据时,你还需要定义将其存储在数据库中所预期的精度.
在数据库中,表示时间类型的数据有
和
可使用
具体取决于属性的类型,
而
serializable类型则被持久化为Blob类型.
@Lob
public String getFullText() {
return fullText;
}
@Lob
public byte[] getFullCode() {
return fullCode;
}
并且没有在该属性上使用
那么Hibernate将使用自带的
使用该注解来覆盖默认值(关于默认值请参考EJB3规范).
在属性级使用该注解的方式如下:
(只针对Hibernate )
@Entity
public class Flight implements Serializable {
...
@Column(updatable = false, name = "flight_name", nullable = false, length=50)
public String getName() { ... }
该字段不允许为空,长度为50,并且是不可更新的(也就是属性值是不变的).
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
可选,列十进制精度(decimal precision)(默认值0)
可选,如果列十进制数值范围(decimal scale)可用,在此设置(默认值0)
甚至覆盖该实体中原有的列映射.
组件类必须在类一级定义
在特定的实体的关联属性上使用
@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;
...
}
@Embeddable
public class Address implements Serializable {
String city;
Country nationality; //no overriding here
}
@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; }
...
}
(注意:这可以通过使用Hibernate提供的
分别是
我们可以看到
但是Hibernate自动检测其对应的Address类中的
并将其看作一个持久化组件.对于Country中已映射的属性,
则使用
正如你所看到的,
这里和
目前EJB3规范还不支持覆盖多层嵌套(即嵌入式对象中还包括其他嵌入式对象)的列映射.
不过Hibernate通过在表达式中使用"."符号表达式提供了对此特征的支持.
@AttributeOverrides( {
@AttributeOverride(name="city", column = @Column(name="fld_city") )
@AttributeOverride(name="
@AttributeOverride(name="
//nationality columns in homeAddress are overridden
} )
Address homeAddress;
Hibernate注解支持很多EJB3规范中没有明确定义的特性.
例如,可以在嵌入式对象上添加
这样可以将其父类的属性持久(详情请查阅
而EJB3规范尚不支持这样的用法.你可以使用
其默认列名是无效的:至少要对其中一个进行明确声明.
Hibernate在这方面走在了EJB3规范的前面,
Hibernate提供了
通过
在默认的EJB3NamingStrategy上进行了小小的提升,
允许在同一实体中使用两个同类型的嵌入对象而无须额外的声明.
如果属性为单一类型,则映射为@Basic
否则,如果属性对应的类型定义了@Embeddable注解,则映射为@Embedded
否则,如果属性对应的类型实现了Serializable,
则属性被映射为@Basic并在一个列中保存该对象的serialized版本
否则,如果该属性的类型为java.sql.Clob 或 java.sql.Blob,则作为@Lob并映射到适当的LobType.
该属性的值可以通过应用自身进行设置,
也可以通过Hiberante生成(推荐).
使用
AUTO - 可以是identity column类型,或者sequence类型或者table类型,取决于不同的底层数据库.
TABLE - 使用表保存id值
IDENTITY - identity column
SEQUENCE - sequence
@Id @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_STORE")
public Integer getId() { ... }
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
public Long getId() { ... }
多个
通过
每一个identifier生成器都有自己的适用范围,可以是应用级(application level)和类一级(class level).
类一级的生成器在外部是不可见的,而且类一级的生成器可以覆盖应用级的生成器.
应用级的生成器则定义在XML级(请参阅
table="GENERATOR_TABLE"
pk-column-name="key"
value-column-name="hi"
pk-column-value="EMP"
allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.TableGenerator(
name="EMP_GEN",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "hi"
pkColumnValue="EMP",
allocationSize=20
)
<sequence-generator name="SEQ_GEN"
sequence-name="my_sequence"
allocation-size="20"/>
//and the annotation equivalent
@javax.persistence.SequenceGenerator(
name="SEQ_GEN",
sequenceName="my_sequence",
allocationSize=20
)
如果JPA XML(如
那么该文件中定义的
和
(max_lo为20)的id生成器(该生成器将id的信息存在数据库的某个表中.).
id的hi值保存在
在该表中
而
其使用名为
该hilo算法基于sequence,该sequence分配的大小为20.
注意,现在这个版本还不能处理sequence生成器的
默认分配的大小为50,因此如果你打算使用sequence,并且希望每次都重新获取新的值,务必将
分配的大小设置为1.
其对应的sequence名为
注意目前Hibernate Annotations还不支持sequence 生成器中的
@Entity
@javax.persistence.SequenceGenerator(
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; }
}
注意在org.hibernate.test.metadata.id包下的测试代码有更多演示Hibernate Annotations用法的例子..
将组件类注解为@Embeddable,并将组件的属性注解为@Id
将组件的属性注解为@EmbeddedId
将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id
但是对于Hibernate的用户来说就是一个崭新的用法.
组合主键类对应了一个实体类中的多个字段或属性,
而且主键类中用于定义主键的字段或属性和
实体类中对应的字段或属性在类型上必须一致.下面我们看一个例子:
public class Footballer {
//part of the id key
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
//part of the id key
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
}
@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;
}
每个类一张表(Table per class)策略: 在Hibernate中对应<union-class>元素:
每个类层次结构一张表(Single table per class hierarchy)策略:在Hibernate中对应<subclass>元素
连接的子类(Joined subclasses)策略:在Hibernate中对应 <joined-subclass>元素
你可以用
这个注解需要在每个类层次结构(class hierarchy) 最顶端的实体类上使用.
这种策略有很多缺点(例如:多态查询和关联),EJB3规范, Hibernate参考手册,
Hibernate in Action,以及其他许多地方都对此进行了描述和解释.
Hibernate使用
通常使用场合是在一个继承层次结构的顶端:
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Flight implements Serializable {
这种策略支持双向的一对多关联.
这里不支持
当然,一旦使用这种策略就意味着你不能使用
他们的实例通过一个辨别符(discriminator)列来区分.:
@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 { ... }
最后,对于继承层次结构中的每个类,
辨别符列的名字默认为
为DiscriminatorType.STRING.
其他的如继承策略,辨别标志字段的类型都是自动设定的.
和
注解定义了每个子类表关联到父类表的主键:
@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 { ... }
而
Boat.id = AmericaCupClass.BOAT_ID进行关联.
同时还不用将该父类作为映射的实体(也就是该实体没有对应的表).
这个时候你需要使用
public class BaseEntity {
@Basic
@Temporal(TemporalType.TIMESTAMP)
public Date getLastUpdate() { ... }
public String getLastUpdater() { ... }
...
}
@Entity class Order extends BaseEntity {
@Id public Integer getId() { ... }
...
}
该表拥有
注意这种情况下的父类不再处在继承层次结构的顶端.
否则将从继承层次结构的根实体中继承访问类型(包括字段或方法)
只需要使用
(虽然这种方式不会纳入EJB3标准)
或
你可以通过
这个注解只能在继承层次结构的顶端使用.
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 {
...
}
表的
以及那些对象为
一对一关联有三种情况:
一是关联的实体都共享同样的主键,
二是其中一个实体通过外键关联到另一个实体的主键
(注意要模拟一对一关联必须在外键列上添加唯一约束).
三是通过关联表来保存两个实体之间的连接关系
(注意要模拟一对一关联必须在每一个外键上添加唯一约束).
@Entity
public class Body {
@Id
public Long getId() { return id; }
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
public Heart getHeart() {
return heart;
}
...
}
@Entity
public class Heart {
@Id
public Long getId() { ...}
}
@Entity
public class Customer implements Serializable {
@OneToOne(cascade = CascadeType.ALL)
public Passport getPassport() {
...
}
@Entity
public class Passport implements Serializable {
@OneToOne(
public Customer getOwner() {
...
}
表中名为的
该注解和
但是多了一个名为
该参数定义了所关联目标实体中的联接列.
注意,当
关联的目标类必须实现
还要注意的是所映射的属性对应单个列(否则映射无效).
有且仅有一端是作为主体(owner)端存在的:主体端负责维护联接列(即更新).
对于不需要维护这种关系的从表则通过mappedBy属性进行声明.
在上面这个例子中,
最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
在主表(owner table)中将创建联接列,
列名为:主体的关联属性名+下划线+被关联端的主键列名.
在上面这个例子中是
因为
exotic.
@Entity
public class Customer implements Serializable {
@OneToOne(cascade = CascadeType.ALL)
joinColumns = @JoinColumn(name="customer_fk"),
inverseJoinColumns = @JoinColumns(name="passport_fk")
)
public Passport getPassport() {
...
}
@Entity
public class Passport implements Serializable {
@OneToOne(
public Customer getOwner() {
...
}
外键指向
而
该信息定义为
@Entity()
public class Flight implements Serializable {
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
(one to one)关联的情况相似,
列名为:主体的关联属性名+下划线+被关联端的主键列名.
在这个例子中是
因为关联的属性是
该参数定义了目标实体名.通常不需要定义该参数,
因为在大部分情况下默认值(表示关联关系的属性类型)就可以很好的满足要求了.
不过下面这种情况下这个参数就显得有意义了:使用接口作为返回值而不是常见的实体.
@Entity()
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE},
@JoinColumn(name="COMP_ID")
public Company getCompany() {
return company;
}
...
}
public interface Company {
...
通过
该关联表包含了指回实体表的外键(通过
以及指向目标实体表的外键(通过
@Entity()
public class Flight implements Serializable {
@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
joinColumns = @JoinColumn(name="FLIGHT_ID"),
inverseJoinColumns = @JoinColumns(name="COMP_ID")
)
public Company getCompany() {
return company;
}
...
}
(指有序列表, 而不是索引列表),
EJB3规范定义了怎么样使用
注解来对有序列表进行映射:
该注解接受的参数格式:用逗号隔开的(目标实体)属性名及排序指令,
如firstname asc, age desc,如果该参数为空,则默认以id对该集合进行排序.
如果某个集合在数据库中对应一个关联表(association table)的话,你不能在这个集合属性上面使用@OrderBy注解.
对于这种情况的处理方法,请参考
EJB3 允许你利用目标实体的一个属性作为Map的key,
这个属性可以用
如果使用
系统默认使用目标实体的主键.
map的key使用和属性相同的列:不需要为map key定义专用的列,因为map key实际上就表达了一个目标属性.
注意一旦加载,key不再和属性保持同步,
也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新
(请参考
很多人被
其他它们有两点区别.
我们的JIRA缺陷系统.
注意一旦加载,key不再和属性保持同步,
也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新.
(Hibernate 3中Map支持的方式在当前的发布版中还未得到支持).
从上面可以明确地看到,没有@org.hibernate.annotations.IndexColumn
注解的java.util.List集合将被看作bag类.
(详情参考
@OneToMany(mappedBy="city")
public List<Street> getStreets() {
return streets;
}
...
}
@Entity public class Street {
return streetName;
}
@ManyToOne
public City getCity() {
return city;
}
...
}
@Entity
public class Software {
@OneToMany(mappedBy="software")
public Map<String, Version> getVersions() {
return versions;
}
...
}
@Entity
@Table(name="tbl_version")
public class Version {
@ManyToOne
public Software getSoftware() { ... }
...
}
中包括了以
而
key和以
这个注解属性接受的参数为目标实体的class.
而一对多这端的关联注解为
)
public class Troop {
@OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
@Entity
public class Soldier {
@ManyToOne
@JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
属性和
在
你需要删除
很明显,这种方案不会得到什么明显的优化,而且还会增加一些附加的UPDATE语句.
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() {
...
}
我们强烈建议通过一个联接表(join table)来实现这种关联(下一节会对此进行解释).
可以通过
@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
}
@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
}
其中外键
(
而外键
(
表名由以下三个部分组成:主表(owner table)表名+下划线+从表(the other side table)表名.
指向主表的外键名:主表表名+下划线+主表主键列名
指向从表的外键名:主表所对应实体的属性名+下划线+从表主键列名
指向从表的外键定义为唯一约束,用来表示一对多的关联关系.
@Entity
public class Trainer {
@OneToMany
public Set<Tiger> getTrainedTigers() {
...
}
@Entity
public class Tiger {
... //no bidir
}
通过联接表
其中外键
(主表表名,
而外键
(属性名称,
同时,你也需要通过注解
如果是双向关联,其中一段必须定义为owner,另一端必须定义为inverse(在对关联表进行更新操作时这一端将被忽略):
@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;
}
...
}
@Entity
public class Employee implements Serializable {
@ManyToMany(
cascade={CascadeType.PERSIST, CascadeType.MERGE},
mappedBy="employees"
targetEntity=Employer.class
)
public Collection getEmployers() {
return employers;
}
}
下面我们将深入介绍
联接列数组(注解中定义数组的格式为{ A, B, C }),
以及inverse联接列数组.
后者是关联表中关联到
只需要一个简单的
当双向多对多关联中没有定义任何物理映射时,Hibernate根据以下规则生成相应的值.
关联表名:主表表名+
关联到主表的外键名:主表名+
关联到从表的外键名:主表中用于关联的属性名+
以上规则对于双向一对多关联同样有效.
@Entity
public class Store {
@ManyToMany(cascade = CascadeType.PERSIST)
public Set<City> getImplantedIn() {
...
}
}
@Entity
public class City {
... //no bidirectional relationship
}
而
关联表名: :主表表名+
关联到主表的外键名:从表用于关联的属性名+
关联到从表的外键名:主表用于关联的属性名+
以上规则对于双向一对多关联同样有效.
@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() {
...
}
}
而
在EJB3中的cascade的概念和Hibernate中的传播性持久化以及cascade操作非常类似,
但是在语义上有细微的区别,支持的cascade类型也有点区别:
CascadeType.PERSIST: 如果一个实体是受管状态, 或者当persist()函数被调用时, 触发级联创建(create)操作
CascadeType.MERGE: 如果一个实体是受管状态, 或者当merge()函数被调用时, 触发级联合并(merge)操作
CascadeType.REMOVE: 当delete()函数被调用时, 触发级联删除(remove)操作
CascadeType.REFRESH: 当refresh()函数被调用时, 触发级联更新(refresh)操作
CascadeType.ALL: 以上全部
或者
而
EJBQL提供了
这对于提高性能来说非常有效,应该根据实际的用例来判断是否选择fetch关键字.
和
还有一种方式是使用
serializable以及实现
你也可以如
@Entity
public class RegionalArticle implements Serializable {
@Id
public RegionalArticlePk getPk() { ... }
}
@Embeddable
public class RegionalArticlePk implements Serializable { ... }
@Entity
public class RegionalArticle implements Serializable {
@EmbeddedId
public RegionalArticlePk getPk() { ... }
}
public class RegionalArticlePk implements Serializable { ... }
除非显式使用了Hibernate的
而
定义了关联的组合外键(如果不使用缺省值的话).
显式指明
否则,Hibernate认为你使用的列顺序和主键声明的顺序一致.
@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
...
}
@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
}
@Embeddable
public class ParentPk implements Serializable {
String firstName;
String lastName;
...
}
使用
注解中的
@Entity
@Table(name="MainCat")
@SecondaryTable(name="Cat1", pkJoinColumns={
@PrimaryKeyJoinColumn(name="cat_id", referencedColumnName="id")
),
@SecondaryTable(name="Cat2", uniqueConstraints={@UniqueConstraint(columnNames={"storyPart2"})})
})
public class Cat implements Serializable {
private Integer id;
private String name;
private String storyPart1;
private String storyPart2;
@Id @GeneratedValue
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getStoryPart1() {
return storyPart1;
}
public String getStoryPart2() {
return storyPart2;
}
(和
对
但是它们的定义在session factory/entity manager factory范围中是都可见的.
命名式查询通过它的名字和实际的查询字符串来定义.
<named-query name="plane.getAll">
<query>select p from Plane p</query>
</named-query>
...
</entity-mappings>
...
@Entity
@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")
public class Night {
...
}
public class MyDao {
doStuff() {
Query q = s.getNamedQuery("night.moreRecentThan");
q.setDate( "date", aMonthAgo );
List results = q.list();
...
}
...
}
属性为查询提供一些hint信息.
不过这需要你使用
(如果你打算定义多个结果集映射,可是使用
但是
下面我们会看到,
结果集映射定义了通过本地化查询返回值和实体的映射.
该实体中的每一个字段都绑定到SQL结果集中的某个列上.
该实体的所有字段包括子类的所有字段以及
关联实体的外键列都必须在SQL查询中有对应的定义.
如果实体中的属性和SQL查询中的列名相同,这种情况下可以不进行定义字段映射.
+ " night.night_date, area.id aid, night.area_id, area.name "
+ "from Night night, Area area where night.area_id = area.id",
@SqlResultSetMapping(name="joinMapping", entities={
@EntityResult(entityClass=org.hibernate.test.annotations.query.Night.class, fields = {
@FieldResult(name="id", column="nid"),
@FieldResult(name="duration", column="night_duration"),
@FieldResult(name="date", column="night_date"),
@FieldResult(name="area", column="area_id"),
discriminatorColumn="disc"
}),
@EntityResult(entityClass=org.hibernate.test.annotations.query.Area.class, fields = {
@FieldResult(name="id", column="aid"),
@FieldResult(name="name", column="name")
})
}
)
和
该映射返回两个实体,分别为
和
列名通过查询获取.下面我们看一个隐式声明属性和列映射关系的例子.
@NamedNativeQuery(name="implicitSample", query="select * from SpaceShip", resultSetMapping="implicit")
public class SpaceShip {
private String name;
private String model;
private double speed;
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name="model_txt")
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
}
属性和列名之间的映射借助实体中包含映射信息来完成.
在这个例子中,
如果和相关实体的关联设计到组合主键,
那么应该使用
定义这种关系的属性名字+"."+主键名或主键列或主键属性.
@SqlResultSetMapping(name="compositekey",
entities=@EntityResult(entityClass=org.hibernate.test.annotations.query.SpaceShip.class,
fields = {
@FieldResult(name="name", column = "name"),
@FieldResult(name="model", column = "model"),
@FieldResult(name="speed", column = "speed"),
@FieldResult(name="captain.lastname", column = "lastn"),
@FieldResult(name="dimensions.length", column = "length"),
@FieldResult(name="dimensions.width", column = "width")
}),
columns = { @ColumnResult(name = "surface"),
@ColumnResult(name = "volume") } )
@NamedNativeQuery(name="compositekey",
query="select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip",
resultSetMapping="compositekey")
} )
public class SpaceShip {
private String name;
private String model;
private double speed;
private Captain captain;
private Dimensions dimensions;
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne(fetch= FetchType.LAZY)
@JoinColumns( {
@JoinColumn(name="fname", referencedColumnName = "firstname"),
@JoinColumn(name="lname", referencedColumnName = "lastname")
} )
public Captain getCaptain() {
return captain;
}
public void setCaptain(Captain captain) {
this.captain = captain;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public Dimensions getDimensions() {
return dimensions;
}
public void setDimensions(Dimensions dimensions) {
this.dimensions = dimensions;
}
}
@Entity
@IdClass(Identity.class)
public class Captain implements Serializable {
private String firstname;
private String lastname;
@Id
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@Id
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
}
EJB3实现不必支持这个特征,但是我们做到了:-)
<