使用@OneToOne 你可以通过一对一关系把实体Beans 关联起来.一对一关系有两种情况:关联的实体共享主键 或者 通过外键关联起来(注意: FK Column (外键列)在数据库中应该限制为唯一的,来模仿一对一的 多样性 ) (note that this FK column in the database should be constrained unique to simulate one-to-one multiplicity).
首先, 我们使用共享主键映射一个一对一关联:
@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(generate = GeneratorType.NONE) public Long getId() { ...} }通过@PrimaryKeyJoinColumn annotation 该 one to one 被标记为 true.
下一个例子中, 关联实体通过外键列(foreign key column) 关联起来:
@Entity public class Customer implements Serializable { @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name="passport_fk") public Passport getPassport() { ... } @Entity public class Passport implements Serializable { @OneToOne(mappedBy = "passport") public Customer getOwner() { ... }利用Customer表中名为passport_fk 的外键列,一个Customer 被关联域一个 Passport. 就像@Column annotation 声明列属性一样 利用@JoinColumn 声明关联的列(join column). 有一个名字为 referencedColumnName的参数.该参数用来声明外键指向列的列名 (This parameter declares the column in the targeted entity that will be used to the join).注意:当使用referencedColumnName 参数引用一个不是主键的列,被关联的类必须是可序列化的.还要注意: referencedColumnName 要引用一个映射为单列(single column )的属性 (否则可能不会正确工作).
关联可能是双向的. 在双向的关联中, 只有一 端是(所有者)owner: the owner 负责被关联的列的更新. 另外一 端(不对关联关系负责)用属性 mappedBy 映射. mappedBy 引用在owner端的关联的属性名(mappedBy refers to the property name of the association on the owner side) [( 译注: 对于mappedBy的取值该处说的并不是很清楚. 如果@Entity注释使用@Entity(access = AccessType.PROPERTY) 也就是用getter/setter访问属性 那么mappedBy的取值就是getXXX()方法get后面的字符串 XXX 首字符大小写转换规则和java bean的命名规则一样,如果@Entity注释使用@Entity(access = AccessType.FIELD) 也就是利用属性直接访问 ,那么 mappedBy的取值就是你声明的属性变量名 如: public Passport pass; 那么取值就是 pass 建议读者自己动手实践一下 )]. 在上面的例子中, mappedBy引用 passport. 就如你看到的一样,在 owners 一 端已经声明了The join column 那么在另外一 端你就不需要再一次声明了..
如果在 The owner 一 端没有声明@JoinColumn , 则会利用默认值. 在 owner 表中一个 join column(s)将要被创建,该 join column的名字[( 译注: 该列的列名, @JoinColumn(name="XXX") 的XXX的取值 )]的默认值是 owner一 端的关系的属性名字[( 译注: 也就是mappedBy 的取值 )]加 下划线 再加 owner一 端的主键名字( its name will be the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column(s) in the owned side). 在上例中是passport_id ( 因为 属性名字是passport Passport列的id名字是 id).
(译注: 该段很容易让人迷惑.经过查看相关资料再解释一下:
双向一对一关系需要在 关系维护端(the owner side)的@OneToOne Annotition定义 mappedBy属性。建表时在 关系被维护端(另外一端)建立外键列指向关系维护端的主键列。在上面例子中Passport 是owner side.????????????????????????????????????????????????????
在@JoinColumn(name="XXX") 中的name取值, 默认值有不同的情况 如果只给出了@JoinColumn 没有name属性那么 默认值为 mappedBy 的取值 + _ 没有 加 id 不知道为什么? 如果要是什么都没有声明 也就是@JoinColumn都没有给出 则默认值是 mappedBy 的取值 + _ + id 有点意思 ^_^
)
在property level(属性级别)利用@ManyToOne 注释来声明 多对一关联:
@Entity() public class Flight implements Serializable { @ManyToOne( cascade = {CascadeType.CREATE, CascadeType.MERGE} ) @JoinColumn(name="COMP_ID") public Company getCompany() { return company; } ... }@JoinColumn 属性是可选的,默认值和 一对一(one-to-one)的规则一样, owner一 端的关系的属性名字[( 译注: 也就是mappedBy 的取值 )] 加 下划线 再加 owner一 端的主键名字( the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side). 在该例子中为 company_id 因为属性名字是 company 并且Company的ID列名是 id.
@ManyToOne 有一个名字为 targetEntity 的参数 ,该参数用来描述目标实体Bean (target entity name). 通常你不需要使用该参数,因为默认值(the type of the property that stores the association)在大多数情况下都是很好的. 然而 当你想使用 接口(interfaces)作为返回类型 而不是正常实体( regular entity)的时候 该参数还是很有用的.
@Entity() public class Flight implements Serializable { @ManyToOne( cascade = {CascadeType.CREATE, CascadeType.MERGE}, targetEntity=CompanyImpl.class ) @JoinColumn(name="COMP_ID") public Company getCompany() { return company; } ... } public interface Company { ...你可以映射 Collection, List (ie ordered lists, not indexed lists), Map and Set.EJB3 规范描述了怎样映射一个 ordered list (例如: a list ordered at load time/载入时排序的List) 通过使用 @javax.persistence.OrderBy annotation(注释): 该注释利用一些用逗号(,)分开的(目标实体| target entity)属性来排序计划(如: firstname asc, age desc), 如果OrderBy取值是空的 那么将使用id来排序. @OrderBy 目前仅仅工作在没有群集的表 (association table). 如果是真正索引的集合(For true indexed collections), 请参考 Hibernate Annotation Extensions. EJB3 允许你利用目标实体的一个属性作为Key(利用@MapKey(name="myProperty"来注释))来映射 Maps [ using as a key one of the target entity property using @MapKey(name="myProperty") (myProperty is a property name in the target entity) ]. 当使用 @MapKey (没有属性 name), 将会使用目标实体的主键. 要明白 一旦装载, Key 不再和属性保持同步, 也就是, 如果你改变了属性的值, 在你的 Java model中 Key不会自动的更改 (Map support the way Hibernate 3 does is currently not supported in this release).
Hibernate 有几种集合观念(notions of collections).
Table 2.1. 集合语义(Collections semantics)
Semantic语义
java representationjava 类型
annotations注释
Bag semanticjava.util.List, java.util.Collection@org.hibernate.annotations.CollectionOfElements, @OneToMany, @ManyToManyList semanticjava.util.List@org.hibernate.annotations.CollectionOfElements, @OneToMany, @ManyToMany + @OrderBy, @org.hibernate.annotations.IndexColumnSet semanticjava.util.Set@org.hibernate.annotations.CollectionOfElements, @OneToMany, @ManyToManyMap semanticjava.util.Map@org.hibernate.annotations.CollectionOfElements, @OneToMany, @ManyToMany + @MapKey 特别 的, java.util.List collections wo @OrderBy nor @org.hibernate.annotations.IndexColumn 将和 bags 一样被对待.EJB3规范不支持 Collection of primitive(原始类型集合), core type or embedded objects(嵌入的对象) . 然而 Hibernate Annotations 支持 (参考 Hibernate Annotation Extensions).
@Entity public class City { @OneToMany(mappedBy="city") @OrderBy("streetName") public List<Street> getStreets() { return streets; } ... } @Entity public class Street { public String getStreetName() { return streetName; } @ManyToOne public City getCity() { return city; } ... } @Entity public class Software { @OneToMany(mappedBy="software") @MapKey(name="codeName") public Map<String, Version> getVersions() { return versions; } ... } @Entity @Table(name="tbl_version") public class Version { public String getCodeName() {...} @ManyToOne public Software getSoftware() { ... } ... }因此在集合装载时, City 有一个以streetName(of street)排序的 Streets集合.Software 有一个键为codeName(of Version) 的map Versions .
(So City has a collection of Streets that are ordered by streetName (of Street) when the collection is loaded. Software has a map of Versions which key is the Version codeName.)
除非集合是普通的, 否则你不得不定义 targetEntity.这是一个以实体类作为值的注释属性.
(Unless the collection is a generic, you will have to define targetEntity. This is a annotation attribute that take the target entity class as a value.)
一对多关联在属性级别利用@OneToMany Annotation 声明One-to-many associations 可能是双向的.
因为在EJB3规范中many to one总是双向关联的关系维护端(owner side), one to many association 被@OneToMany( mappedBy=... )注释.
Since many to one are (almost) always the owner side of a bidirectional relationship in the EJB3 spec, the one to many association is annotated by @OneToMany( mappedBy=... )
@Entity public class Troop { @OneToMany(mappedBy="troop") public Set<Soldier> getSoldiers() { ... } @Entity public class Soldier { @ManyToOne @JoinColumn(name="troop_fk") public Troop getTroop() { ... }通过troop 属性的联系,Troop 有一个对Soldier的双向一对多关联 .在 mappedBy一方(side),你不需要定义任何物理映射(physical mapping)在 mappedBy side.
为了映射一个双向的(one-to-many side作为关系维护端(owner side)),你必须移动 mappedBy 元素并且设置 在 many to one @JoinColumn中的 insertable 和 updatable 为false. 这种解决办法显然不是最好的.(This solution is obviously not optimized from the number of needed statements).
@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() { ... }在owned entity 中维护一个外键列来维护一个单向的一对多关联 不是很常见的 也不是值得推荐的. 我们强烈建议你用一个 Join table 来处理这种情况(在下一节讨论). 这种关联用 @JoinColumn声明.(A unidirectional one to many using a foreign key column in the owned entity is not that common and not really recommended. We strongly advise you to use a join table for this kind of association (as explained in the next section). This kind of association is described through a @JoinColumn)
@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 }Customer 利用Join column CUST_ID 定义一个与Ticket的单向关联.
一个带join table 的单向一对多关联是首选的. 通过@JoinTable来声明.
@Entity() public class Trainer { @OneToMany @JoinTable( table=@Table(name="TrainedMonkeys"), joinColumns = { @JoinColumn( name="trainer_id") }, inverseJoinColumns = @JoinColumn( name="monkey_id") ) public Set<Monkey> getTrainedMonkeys() { ... } @Entity public class Monkey { ... //no bidir }Trainer 利用join table TrainedMonkeys 声明一个与Monkey的单向关联 ,该表具有一个外键trainer_id(指向Trainer) 和 一个外键 monkey_id (指向 Monkey) [ with a foreign key trainer_id to Trainer (joinColumns) and a foreign key monkey_id to Monkey (inversejoinColumns)].
没有任何物理描述的映射, 一个单向一对多关联将用 join table. 表的名字是 owner table name 加 下划线 "_" 加 另外一端的table name . 指向owner table的外键名字是 owner table 加 下划线 "_" 加 owner 的主键列的名字. 另外一个外键名字是 owner property name 加 下划线"_" 加 另外一端的主键列名. 一个唯一约束被添加到指向非owner的主键列.( Without describing any physical mapping, a unidirectional one to many with join table is used. The table name is the concatenation of the owner table name, _, and the other side table name. The foreign key name(s) referencing the owner table is the concatenation of the owner table, _, and the owner primary key column(s) name. The foreign key name(s) referencing the other side is the concatenation of the owner property name, _, and the other side primary key column(s) name. A unique constraint is added to the foreign key referencing the other side table to reflect the one to many.)
@Entity() public class Trainer { @OneToMany public Set<Tiger> getTrainedTigers() { ... } @Entity public class Tiger { ... //no bidir }Trainer 利用join table Trainer_Tiger 描述了一个与Tiger的单向关联,带有两个外键trainer_id 和 trainedTigers_id分别指向 Trainer 和 Tiger ( with a foreign key trainer_id to Trainer (table name, _, trainer id) and a foreign key trainedTigers_id to Monkey (property name, _, Tiger primary column)).
在逻辑上用@ManyToMany annotation 来定义多对多关联.你也必须利用@JoinTable annotation 描述关联表和连接条件.如果关联是双向的,一端必须是 owner 另一端 必须是 inverse end.(A many-to-many association is defined logically using the @ManyToMany annotation. You also have to describe the association table and the join conditions using the @JoinTable annotation. If the association is bidirectional, one side has to be the owner and one side has to be the inverse end (ie. it will be ignored when updating the relationship values in the association table)):
@Entity public class Employer implements Serializable { @ManyToMany( targetEntity=org.hibernate.test.metadata.manytomany.Employee.class, cascade={CascadeType.CREATE, CascadeType.MERGE} ) @JoinTable( table=@Table(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.CREATE, CascadeType.MERGE}, mappedBy="employees" targetEntity=Employer.class ) public Collection getEmployers() { return employers; } }我们已经看到了很多关联的声明和细节了.下面我们将深入描述一下 @JoinTable. 她定义一个@Table, 一个join columns 的数组(an array in annotation is defined using { A, B, C }) 和 一个inverse join columns 的数组. 最后一个是指向Employee 主键(the other side)的关联表的列 [We've already shown the many declarations and the detailed attributes for associations. We'll go deeper in the @JoinTable description, it defines a @Table, an array of join columns (an array in annotation is defined using { A, B, C }), and an array of inverse join columns. The latter ones are the columns of the association table which refer to the Employee primary key (the "other side")].
就像以前看到的,(另外一端)the other side 没有必要定义物理映射.一个简单的mappedby 参数就可以了 [As seen previously, the other side don't have to (must not) describe the physical mapping: a simple mappedBy argument containing the owner side property name bind the two.]
和别的annotations一样, many to many relationship中的大部分值被猜测. 在单向many to many中不用写任何物理映射则将会使用下面的规则来构造默认值. table name 是 owner table name 加 下划线"_" 加 the other side table name .指向owner table 的外键列的名字是 owner table name 加下划线"_" 加owner 的主键列名. 指向the other side 的外键名是 owner property name,加 _, 加 the other side primary key column(s). 和单向 one to many relationship的规则一样.
@Entity() public class Store { @ManyToMany(cascade = CascadeType.PERSIST) public Set<City> getImplantedIn() { ... } } @Entity() public class City { ... //no bidirectional relationship }join table名将是 Store_Table . Store_id column 是指向 Store table的外键. implantedIn_id column 是指向 City table的外键.
没有描述任何物理映射的 bidirectional many to many 关联将会使用下面的规则. The table name是 owner table name加 _ 加 the other side table name. 指向owner table的foreign key name(s) 是 the other side property name加 _ 加and the owner primary key column(s). 指向the other side的foreign key name(s) 是the owner property name加 _ 加 the other side primary key column(s). 和unidirectional one to many 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() { ... } }join table 名字是 Store_Customer . stores_id column是指向 Store table的外键.customers_id column是指向 City table的外键.