【IT168 技术文章】本文中我们将以实体之间的一对一关联关系为例,深入地讲述如何使用 OpenJPA 框架提供的注释,实现企业应用中实体之间的关联关系。文中将提供一个简单的例子,详细的说明如何定义类和类之间的一对一关联关系的步骤,同时会重点讲述这些注释所支持的属性。一对多、多对一和多对多这三种关联关系在 OpenJPA 中的实现过程和一对一关联关系的实现过程是一致的,只是需要选择使用不同的注释,在本文的最后,会对实现这三种关联关系进行简单说明,读者可以参考一对一关系的实现过程来实现一对多、多对一和多对多的关联关系。
一对一关系
在面向对象的世界里,类 A 和类 B 之间形成一对一关系必须满足如下条件:
对象 A1 引用了对象 B1;
类 A 的其它对象 An 不能引用同样的对象 B1。
在关系数据库中,我们通常使用唯一外键的方式来实现一对一关系,下面这个图说明了这种的情况。
图 1. 关系数据库中的一对一关系
下面开始介绍 OpenJPA 中实现实体之间一对一关联关系的相关知识,为了说明的需要,我们首先定义一个简单的应用场景。
模拟场景
假定开发者要完成一个图书馆管理系统,我们需要记录书的基本信息如编号、书名、出版日期等基本信息,还需要记录书的前言,序等信息。
为了说明实体之间的一对一关系,我们将书设计成一个类(Book),包括书的编号和名称两个属性,同时将书的前言设计成另外一个类(BookExtend),它包括书的编号和前言两个属性。由于一本书有前言而且也不可能有其它书的前言部分会和它一样,所以类 Book 和 BookExtend 之间很自然的形成了一对一的关系。这两个类的属性以及类之间的关系如下图所示。
图 2. 类之间的一对一关系
[注]:为了说明的简单,本例子设计时每个对象只选择了必要的属性。
描述一对一关系
在 OpenJPA 中,开发者用来描述实体之间一对一关系时可选择的注释包括 javax.persistence.OneToOne 和 javax.persistence.JoinColumn。其中 javax.persistence.OneToOne 注释是必须使用的,它被用来声明类和类之间存在着一对一关系,javax.persistence.JoinColumn 注释是可选的,开发者使用 JoinColumn 注释来声明两个类在数据库中对应的表之间关联时的细节,包括主表中关联字段的名称、从表中使用什么字段来进行关联等。
javax.persistence.OneToOne
javax.persistence.OneToOne 注释支持如下 5 个属性,它们可以被开发者用来定义实体和实体之间一对一关联关系的细节内容。
target Entity
targetEntity 属性是 Class 类型的属性。定义实体一对一关系中处于从属地位的实体类的类型。如果没有为该属性设置值,OpenJPA 容器默认 targetEntity 属性的值是该成员属性对应的类类型,所以实体关系定义时通常不需要为 targetEntity 属性设置值。
mappedBy
mappedBy 属性是 String 类型的属性。mappedBy 属性的值是当前实体在关联实体中的属性名称,使用 mappedBy 可以定义实体类之间的双向关系。如果类之间是单向关系,不需要提供定义,如果类和类之间形成双向关系,我们就需要使用这个属性进行定义,否则可能引起数据一致性的问题。
以演示场景中 Book 和 BookExtend 实体为例,假设我们只定义 Book 类有 BookExtend 类型的属性,而 BookExtend 并没有 Book 类型的属性,那么说明 Book 和 BookExtend 实体之间是单向关系;如果 BookExtend 中也定义了 Book 属性,那么 Book 和 BookExtend 实体之间就构成了双向关系。
cascade
cascade 属性的类型是 CascadeType[] 类型。cascade 属性定义实体和实体之间的级联关系。使用 cascade 属性定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。
以演示场景中 Book 和 BookExtend 实体为例:如果设置 Book 和 BookExtend 存在级联关系,那么删除 Book 时将同时删除它所对应的 BookExtend 对象。而如果 BookExtend 还和其它的对象之间有级联关系,那么这样的操作会一直递归执行下去。
cascade 的值只能从 CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个更方便的选择是使用 CascadeType.ALL,表示选择上面全部四项。
fetch
fetch 属性是 FetchType 类型的属性。可选择项包括:FetchType.EAGER 和 FetchType.LAZY。前者表示关联关系的从类在主类加载的时候同时加载,后者表示关联关系的从类在自己被访问时才加载。默认值是 FetchType.EAGER。
optional
optional 属性是 boolean 类型的属性。optional 属性用于定义关联关系的从类对象是否必须存在。如果设置为 false,那么该属性就不能设置为 null。默认值是 true。
javax.persistence.OneToOne 用法举例
2 // 其它实体映射内容…
3 /*
4 * 使用 OneToOne 注释表示该属性和 Book 类形成一对一关系,OneToOne
5 * 注释的 option 属性设为 True 表示该对象可以不存在,cascade 属性
6 * 设置为 CascadeType.ALL,表示 Book 和 BookExtend 对象级联新建、 更新、删除、刷新
7 */
8 @OneToOne(optional=true,cascade=CascadeType.ALL)
9 public BookExtend bookExtend;
10 }
11
javax.persistence.JoinColumn
javax.persistence.JoinColumn 注释可以和 javax.persistence.OneToOne 注释一起使用,用于定义关联关系中的主类在数据库中对应的表通过什么字段和关联关系中的从类的主键进行关联,这个注释是可选的,如果不提供该注释,OpenJPA 会默认使用”对象名_ID”和关联表的主键字段进行关联。
以演示场景中 Book 和 BookExtend 实体为例:如果 Book 的 bookExtend 属性没有使用 javax.persistence.JoinColumn 注释进行声明,我们使用 OpenJPA 提供的 Mapping Tool 工具生成表格的时候,Book 类对应的表 Book 中将自动加入列 bookExtend_ID,它的类型将和 BookExtend 对应表的主键字段id类型保持一致。
JoinColumn 注释支持两个重要属性:name 和 referencedColumnName 属性。
name
name 属性的类型是 String 类型。name 属性用于指定关联关系中的主类对应的表中和关联关系中的从类的主键进行关联的字段的名称。以演示场景中 Book 和 BookExtend 实体的关系为例:如果 Book 实体对应的表使用“beID”字段和 BookExtend 实体对应表的主键进行对应,我们可以在 Book 类中为 bookExtend 属性提供 javax.persistence.JoinColumn 注释,设置它的 name 属性为“beID”。
referencedColumnName
referencedColumnName 属性的类型是 String 类型。referencedColumnName 属性指定关联关系中的从类与关联关系中的主类对应的表之间形成关联关系的字段名称,通常用于关联关系中的从类的关联字段不是自己的主键的情况。以演示场景中 Book 和 BookExtend 实体的关系为例:BookExtend 表中默认使用 Id 字段和 Book 类的某个字段进行关联,但如果实际情况下 BookExtends 表需要使用“myID“字段和 Book 表进行关联,我们就可以设置 javax.persistence.JoinColumn 注释的属性值为“myID”。
javax.persistence.JoinColumn 用法举例
2 // 其它内容…
3
4 /*
5 * 使用 OneToOne 注释表示该属性和 Book 类形成一对一关系,OneToOne
6 * 注释的 option 属性设为 True 表示该对象可以不存在,cascade 属性
7 * 设置为 CascadeType.ALL,表示 Book 和 BookExtend 对象级联新建、 更新、删除、刷新
8 */
9 @OneToOne(optional = true, cascade = CascadeType.ALL)
10 /*
11 * 使用 JoinColumn 注释设置两个对象对应数据库表之间的关联字段
12 * name 属性指定关联关系中主类对应表中参与关联关系的字段名称,
13 * referencedColumnNam 属性指定关联关系中从类对应表中参与关
14 * 联关系的字段名称,
15 */
16 @JoinColumn(name = "beID", referencedColumnName = "myID")
17 public BookExtend bookExtend;
18 }
19