http://blog.csdn.net/wangpeng047/article/details/8744063
1. 一对一(One-to-one)
使用@OneToOne注解可以建立实体bean之间的一对一的关联.一对一关联有三种情况:一是关联的实体都共享同样的主键,二是其中一个实体通过外键关联到另一个实体的主键(注意要模拟一对一关联必须在外键列上添加唯一约束).三是通过关联表来保存两个实体之间的连接关系(注意要模拟一对一关联必须在每一个外键上添加唯一约束).
首先,我们通过共享主键来进行一对一关联映射:
- @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() { ...}
- }
上面的例子通过使用注解@PrimaryKeyJoinColumn定义了一对一关联.
下面这个例子使用外键列进行实体的关联.
- @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 通过Customer表中名为的passport_fk 外键列和 Passport关联.@JoinColumn注解定义了联接列(join column).该注解和@Column注解有点类似,但是多了一个名为referencedColumnName的参数. 该参数定义了所关联目标实体中的联接列.注意,当referencedColumnName关联到非主键列的时候,关联的目标类必须实现Serializable,还要注意的是所映射的属性对应单个列(否则映射无效).
一对一关联可能是双向的.在双向关联中,有且仅有一端是作为主体(owner)端存在的:主体端负责维护联接列(即更新).对于不需要维护这种关系的从表则通过mappedBy属性进行声明.mappedBy的值指向主体的关联属性.在上面这个例子中,mappedBy的值为 passport.最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
如果在主体没有声明@JoinColumn,系统自动进行处理:在主表(owner table)中将创建联接列,列名为:主体的关联属性名+下划线+被关联端的主键列名.在上面这个例子中是passport_id,因为Customer中关联属性名为passport,Passport的主键是id.
第三种方式也许是最另类的(通过关联表).
- @Entity
- public class Customer implements Serializable {
- @OneToOne(cascade = CascadeType.ALL)
- @JoinTable(name = "CustomerPassports",
- joinColumns = @JoinColumn(name="customer_fk"),
- inverseJoinColumns = @JoinColumn(name="passport_fk")
- )
- public Passport getPassport() {
- ...
- }
- @Entity
- public class Passport implements Serializable {
- @OneToOne(mappedBy = "passport")
- public Customer getOwner() {
- ...
- }
Customer通过名为 CustomerPassports的关联表和 Passport关联; 该关联表拥有名为passport_fk的外键列,该 外键指向Passport表,该信息定义为inverseJoinColumn的属性值, 而customer_fk外键列指向Customer表, 该信息定义为 joinColumns的属性值.
这种关联可能是双向的.在双向关联中, 有且仅有一端是作为主体端存在的:主体端负责维护联接列(即更新). 对于不需要维护这种关系的从表则通过mappedBy属性进行声明. mappedBy的值指向主体的关联属性. 在上面这个例子中,mappedBy的值为 passport. 最后,不必也不能再在被关联端(owned side)定义联接列了,因为已经在主体端进行了声明.
你必须明确定义关联表名和关联列名.
2. 多对一(Many-to-one)
在实体属性一级使用@ManyToOne注解来定义多对一关联:
- @Entity()
- public class Flight implements Serializable {
- @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
- @JoinColumn(name="COMP_ID")
- public Company getCompany() {
- return company;
- }
- ...
- }
其中@JoinColumn是可选的,关联字段默认值和一对一 (one to one)关联的情况相似, 列名为:主体的关联属性名+下划线+被关联端的主键列名. 在这个例子中是company_id, 因为关联的属性是company, Company的主键是id.
@ManyToOne注解有一个名为targetEntity的参数, 该参数定义了目标实体名.通常不需要定义该参数, 因为在大部分情况下默认值(表示关联关系的属性类型)就可以很好的满足要求了. 不过下面这种情况下这个参数就显得有意义了:使用接口作为返回值而不是常见的实体.
- @Entity()
- public class Flight implements Serializable {
- @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE}, targetEntity=CompanyImpl.class )
- @JoinColumn(name="COMP_ID")
- public Company getCompany() {
- return company;
- }
- ...
- }
- public interface Company {
- ...
对于多对一也可以通过关联表的方式来映射。 通过@JoinTable注解可定义关联表, 该关联表包含了指回实体表的外键(通过@JoinTable.joinColumns) 以及指向目标实体表的外键(通过@JoinTable.inverseJoinColumns).
- @Entity()
- public class Flight implements Serializable {
- @ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
- @JoinTable(name="Flight_Company",
- joinColumns = @JoinColumn(name="FLIGHT_ID"),
- inverseJoinColumns = @JoinColumn(name="COMP_ID")
- )
- public Company getCompany() {
- return company;
- }
- ...
- }
3. 一对多(One-to-many)
在属性级使用 @OneToMany注解可定义一对多关联.一对多关联可以是双向关联.
● 双向
在EJB3规范中多对一这端几乎总是双向关联中的主体(owner)端, 而一对多这端的关联注解为@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端不必也不能再定义任何物理映射
对于一对多的双向映射,如果要一对多这一端维护关联关系, 你需要删除mappedBy元素并将多对一这端的 @JoinColumn的insertable和updatable设置为false. 很明显,这种方案不会得到什么明显的优化,而且还会增加一些附加的UPDATE语句.
- @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注解来描述这种单向关联关系.
- @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通过 CUST_ID列和Ticket 建立了单向关联关系.
● 通过关联表处理单向关联
通过联接表处理单向一对多关联是首选方式.这种关联通过@JoinTable注解来进行描述.
- @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
- }
上面这个例子中,Trainer通过 TrainedMonkeys表和 Monkey 建立了单向关联. 其中外键trainer_id关联到Trainer (joinColumns), 而外键monkey_id关联到 Monkey (inversejoinColumns).
● 默认处理机制
通过联接表来建立单向一对多关联不需要描述任何物理映射. 表名由以下三个部分组成:主表(owner table)表名+下划线+从表(the other side table)表名. 指向主表的外键名:主表表名+下划线+主表主键列名 指向从表的外键名:主表所对应实体的属性名+下划线+从表主键列名 指向从表的外键定义为唯一约束,用来表示一对多的关联关系.
- @Entity
- public class Trainer {
- @OneToMany
- public Set<Tiger> getTrainedTigers() {
- ...
- }
- @Entity
- public class Tiger {
- ... //no bidir
- }
上面这个例子中,Trainer和Tiger 通过联接表 Trainer_Tiger建立单向关联关系, 其中外键trainer_id关联到Trainer (主表表名, _(下划线), trainer id), 而外键trainedTigers_id关联到Tiger (属性名称, _(下划线), Tiger表的主键列名).
4. 多对多(Many-to-many)
你可以通过@ManyToMany注解可定义的多对多关联. 同时,你也需要通过注解@JoinTable描述关联表和关联条件. 如果是双向关联,其中一段必须定义为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;
- }
- }
至此,我们已经展示了很多跟关联有关的声明定义以及属性细节. 下面我们将深入介绍@JoinTable注解,该注解定义了联接表的表名, 联接列数组(注解中定义数组的格式为{ A, B, C }), 以及inverse联接列数组. 后者是关联表中关联到Employee主键的列(the "other side").
正如前面所示,被关联端不必也不能描述物理映射: 只需要一个简单的mappedBy参数,该参数包含了主体端的属性名,这样就绑定双方的关系.
和其他许多注解一样,在多对多关联中很多值是自动生成. 当双向多对多关联中没有定义任何物理映射时,Hibernate根据以下规则生成相应的值. 关联表名:主表表名+_下划线+从表表名, 关联到主表的外键名:主表名+_下划线+主表中的主键列名. 关联到从表的外键名:主表中用于关联的属性名+_下划线+从表的主键列名. 以上规则对于双向一对多关联同样有效.
- @Entity
- public class Store {
- @ManyToMany(cascade = CascadeType.PERSIST)
- public Set<City> getImplantedIn() {
- ...
- }
- }
- @Entity
- public class City {
- ... //no bidirectional relationship
- }
上面这个例子中,Store_Table作为联接表. Store_id列是联接到Store表的外键. 而implantedIn_id列则联接到City表.
当双向多对多关联中没有定义任何物理映射时, Hibernate根据以下规则生成相应的值 关联表名: :主表表名+_下划线+从表表名, 关联到主表的外键名:从表用于关联的属性名+_下划线+主表中的主键列名. 关联到从表的外键名:主表用于关联的属性名+_下划线+从表的主键列名. 以上规则对于双向一对多关联同样有效.
- @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() {
- ...
- }
- }
在上面这个例子中,Store_Customer作为联接表. stores_id列是联接到Store表的外键, 而customers_id列联接到City表.
5. 其他
① 集合类型
你可以对 Collection ,List (指有序列表, 而不是索引列表), Map和Set这几种类型进行映射. EJB3规范定义了怎么样使用@javax.persistence.OrderBy 注解来对有序列表进行映射: 该注解接受的参数格式:用逗号隔开的(目标实体)属性名及排序指令, 如firstname asc, age desc,如果该参数为空,则默认以id对该集合进行排序. 如果某个集合在数据库中对应一个关联表(association table)的话,你不能在这个集合属性上面使用@OrderBy注解. 对于这种情况的处理方法,请参考Hibernate Annotation Extensions. EJB3 允许你利用目标实体的一个属性作为Map的key, 这个属性可以用@MapKey(name="myProperty")来声明. 如果使用@MapKey注解的时候不提供属性名, 系统默认使用目标实体的主键. map的key使用和属性相同的列:不需要为map key定义专用的列,因为map key实际上就表达了一个目标属性。 注意一旦加载,key不再和属性保持同步, 也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新 (请参考Hibernate Annotation Extensions). 很多人被<map>和@MapKey弄糊涂了。 其他它们有两点区别.@MapKey目前还有一些限制,详情请查看论坛或者 我们的JIRA缺陷系统。 注意一旦加载,key不再和属性保持同步, 也就是说,如果你改变了该属性的值,在你的Java模型中的key不会自动更新. (Hibernate 3中Map支持的方式在当前的发布版中还未得到支持).
Hibernate将集合分以下几类.
语义 | Java实现类 | 注解 |
Bag 语义 | java.util.List, java.util.Collection | @org.hibernate.annotations.CollectionOfElements或@OneToMany或@ManyToMany |
List 语义 | java.util.List | (@org.hibernate.annotations.CollectionOfElements或@OneToMany或@ManyToMany)以及@org.hibernate.annotations.IndexColumn |
Set 语义 | java.util.Set | @org.hibernate.annotations.CollectionOfElements 或@OneToMany或@ManyToMany |
Map 语义 | java.util.Map | (@org.hibernate.annotations.CollectionOfElements或@OneToMany或@ManyToMany)以及(空或@org.hibernate.annotations.MapKey/MapKeyManyToMany(支持真正的map),或@javax.persistence.MapKey |
EJB3规范不支持原始类型,核心类型,嵌入式对象的集合.但是Hibernate对此提供了支持
- @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排序的Street的集合. 而Software中包括了以codeName作为 key和以Version作为值的Map.
除非集合为generic类型,否则你需要指定targetEntity. 这个注解属性接受的参数为目标实体的class.
② 用cascade实现传播性持久化
也许你已经注意到了cascade属性接受的值为CascadeType数组. 在EJB3中的cascade的概念和Hibernate中的传播性持久化以及cascade操作非常类似, 但是在语义上有细微的区别,支持的cascade类型也有点区别:
● CascadeType.PERSIST: 如果一个实体是受管状态, 或者当persist()函数被调用时, 触发级联创建(create)操作
● CascadeType.MERGE: 如果一个实体是受管状态, 或者当merge()函数被调用时, 触发级联合并(merge)操作
● CascadeType.REMOVE: 当delete()函数被调用时, 触发级联删除(remove)操作
● CascadeType.REFRESH: 当refresh()函数被调用时, 触发级联更新(refresh)操作
● CascadeType.ALL: 以上全部
③ 关联关系获取
通过Hibernate你可以获得直接或者延迟获取关联实体的功能. fetch参数可以设置为FetchType.LAZY 或者 FetchType.EAGER. EAGER通过outer join select直接获取关联的对象, 而LAZY(默认值)在第一次访问关联对象的时候才会触发相应的select操作. EJBQL提供了fetch关键字,该关键字可以在进行特殊查询的时候覆盖默认值. 这对于提高性能来说非常有效,应该根据实际的用例来判断是否选择fetch关键字.
④ 映射复合主键与外键
组合主键使用一个可嵌入的类作为主键表示,因此你需要使用@Id 和@Embeddable两个注解. 还有一种方式是使用@EmbeddedId注解.注意所依赖的类必须实现 serializable以及实现equals()/hashCode()方法.
- @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 { ... }
@Embeddable 注解默认继承了其所属实体的访问类型, 除非显式使用了Hibernate的@AccessType注解(这个注解不是EJB3标准的一部分). 而@JoinColumns,即@JoinColumn数组, 定义了关联的组合外键(如果不使用缺省值的话). 显式指明referencedColumnNames是一个好的实践方式, 否则,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;
- ...
- }
注意上面的 referencedColumnName显式使用方式.
相关推荐
使用hibernate注解,必须要使用库hibernate-commons-annotations,hibernate-core,hibernate-jpa,ejb3-persistence,javassist等
本文档将深入探讨Hibernate注解的使用,帮助开发者更好地理解和利用这些注解来实现对象关系映射(ORM)。 一、Hibernate注解基础 Hibernate注解是一种元数据方式,用于在Java类和属性上声明数据库映射信息,从而...
本主题将深入探讨Hibernate注解的相关知识点。 1. **注解概述**: 注解(Annotations)是Java 5引入的一种元数据,它提供了在源代码中嵌入信息的方式,这些信息可以被编译器或者在运行时的Java虚拟机使用。在...
### Hibernate注解API知识点概述 #### 一、Hibernate注解简介 Hibernate 是一款非常流行的 Java 持久层框架,它极大地简化了数据库操作。在 Hibernate 中,可以通过使用注解来映射对象与数据库表之间的关系,从而...
这个"springmvc+hibernate注解框架整合 demo"是一个示例项目,展示了如何通过注解方式将这两个框架无缝集成。 **Spring MVC 框架** Spring MVC 是 Spring 框架的一部分,它提供了处理HTTP请求、转发响应以及管理...
在Java的ORM框架Hibernate中,注解是一种强大的工具,它允许开发者无需XML配置就能实现对象关系映射。本文主要总结了Hibernate中常见的注解用法,涵盖了类级别和属性级别的注解,以及与主键和非主键相关的注解。 1....
在实际项目中,除了这三个核心的Hibernate注解库,可能还需要其他的依赖,例如: - **jta.jar**:Java Transaction API,用于支持分布式事务处理。 - **javax.persistence-api.jar**:JPA规范的API,包含了一些基础...
SSH框架,全称为Struts2、Spring和Hibernate的组合,是Java Web开发中常见的三大开源框架。本教程将深入探讨如何使用注解来构建SSH框架,以实现更简洁、高效的应用程序开发。 **Struts2** 是MVC设计模式的一个实现...
《Hibernate注解详解》 Hibernate 是一款强大的Java对象关系映射(ORM)框架,它极大地简化了数据库操作。在Hibernate中,注解是用于描述Java类及其属性如何映射到数据库表的重要工具。本文将全面解析Hibernate中的...
Hibernate 注解映射 Hibernate 注解映射是 Hibernate 框架中的一种映射方式,它使用 Java 注解来描述实体类和数据库表之间的映射关系,提高开发效率。 一、 环境搭建和基本映射 在使用 Hibernate 注解映射之前,...
本实例"spring-hibernate注解配置源码"旨在展示如何在不使用XML配置的情况下,通过注解的方式整合Struts2、Spring和Hibernate,创建一个完整的MVC(模型-视图-控制器)架构的应用。下面将详细介绍这个过程中的关键...
下面我们将详细讲解如何配置和使用Spring+Hibernate注解声明式事务: 1. **配置Spring**: - 首先,我们需要在Spring配置文件中启用事务管理器,通常是`HibernateTransactionManager`,并配置数据源。 - 然后,...
hibernate 注解 annotation 教程
一。实体Bean 每个持久化POJO类都是一个实体Bean, 通过在类的定义中使用 @Entity 注解来进行声明。...Hibernate 可以对类的属性或者方法进行注解。属性对应field类别,方法的 getXxx()对应property类别。
在本文中,我们将深入探讨Hibernate注解的几个核心方面:级联关系、增删改查操作、二级缓存、日志配置以及注解解析。 1. **级联关系**: Hibernate中的级联关系允许我们在一个实体的操作中自动处理与其关联的其他...
在Hibernate中,注解是用于替代传统的XML配置文件,以元数据的形式标注在类、属性或者方法上,实现对象与数据库表之间的映射。本文将深入探讨Hibernate中的注解,帮助你更好地理解和应用它们。 1. **实体注解(@...
在 Hibernate 中,注解是一种简洁且强大的工具,用于替代传统的 XML 配置文件来描述对象模型和数据库之间的映射关系。这篇文档将深入探讨 Hibernate 注解的使用。 ### 第 1 章 创建一个注解项目 在开始使用 ...
在Java的持久化框架Hibernate中,注解是用于简化对象关系映射(ORM)的一种方式。本篇文章将详细探讨在Hibernate中如何使用注解来处理各种关联关系,特别是`mappedBy`属性的用法。 首先,`@OneToMany`注解用于表示...
《Hibernate注解详解》 在Java开发领域,Hibernate作为一个强大的对象关系映射(ORM)框架,极大地简化了数据库操作。而随着Java注解的普及,Hibernate也开始广泛采用注解方式来替代XML配置,使得代码更加简洁,...
总的来说,Hibernate注解使得Java对象与数据库表之间的映射变得更加直观和简洁,极大地减少了代码量,提高了开发效率。通过熟练掌握这些注解,开发者可以更好地利用Hibernate框架进行数据库操作。