使用 Apache OpenJPA 开发 EJB 3.0 应用,第 5 部分: 实体标识的自动生成
2009-03-24 10:24
666 查看
JPA(Java Persistence API)是 EJB 3.0 新引入的数据持久化编程模型。JPA 充分利用了注释(Annotation)和对象/关系映射,为数据持久化提供了更简单、易用的编程方式。OpenJPA 是 Apache 组织提供的 JPA 标准实现。本文是 使用 Apache OpenJPA 开发 EJB 3.0 应用系列 的第五部分,介绍在 OpenJPA 中如何自动生成实体的唯一标识,包括使用容器自动生成实体标识,借助于数据库的自动编号、序列号、数据库表等技术自动生成实体标识等,并且通过简单的例子描述了这几种情况下的操作过程。
数据的唯一性是所有应用程序非常基本的要求,由开发者或者用户来维护这种唯一性存在着较大的风险,因此,由系统自动产生唯一标识是一种常见的做法。OpenJPA 中支持四种不同的实体标识自动生成策略:
容器自动生成的实体标识;
使用数据库的自动增长字段生成实体标识;
根据数据库序列号(Sequence)技术生成实体标识;
使用数据库表的字段生成实体标识;
这四种方式各有优缺点,开发者可以根据实际情况进行选择。
可选择的注释
要让容器和数据库结合管理实体标识的自动生成,根据实际情况的不同,开发者可以选择
@javax.persistence.GeneratedValue
每一个需要自动生成实体标识的实体都需要为它的实体标识字段提供
使用
表示实体标识由 OpenJPA 容器自动生成,这也是 Strategy 属性的默认值。
OpenJPA 容器将使用数据库的自增长字段为新增加的实体对象赋唯一值,作为实体的标识。这种情况下需要数据库提供对自增长字段的支持,常用的数据库中,HSQL、SQL Server、MySQL、DB2、Derby 等数据库都能够提供这种支持。
表示使用数据库的序列号为新增加的实体对象赋唯一值,作为实体的标识。这种情况下需要数据库提供对序列号的支持,常用的数据库中,Oracle、PostgreSQL 等数据库都能够提供这种支持。
表示使用数据库中指定表的某个字段记录实体对象的标识,通过该字段的增长为新增加的实体对象赋唯一值,作为实体的标识。
@javax.persistence.SequenceGenerator
如果实体标识的自动生策略是
表 1. SequenceGenerator 注释属性说明
@javax.persistence.TableGenerator
如果实体标识的自动生策略是 GenerationType.TABLE,开发者需要为实体标识字段提供 TableGenerator 注释,它的参数描述了使用数据库表生成实体标识的具体细节。该注释支持下列属性:
表 2. TableGenerator 注释属性说明
实体标识自动生成
在上面的小节中,我们了解了和实体标识自动生成相关的注释,接下来我们将结合一个简单的例子讲述如何分别使用这些实体标识自动生成策略实现实体标识的自动生成。
我们首先假设有一个
容器自动生成
OpenJPA 容器默认的实体标识自动生成策略是由容器管理实体标识的自动生成,容器管理的实体标识可以支持数值型和字符型两种。当容器管理的实体标识是数字型时,OpenJPA 容器自动创建一个数据库表
当容器管理的实体标识是字符串类型时,OpenJPA 支持使用 uuid-string 和 uuid-hex 两种方式生成相应的实体标识。如果我们选择使用 uuid-string 方式生成实体标识时,OpenJPA 框架会自动为实体生成一个 128 位的 UUID,并且将这个 UUID 转化为使用 16 位字符表示的字符串。如果我们选择使用 uuid-hex 方式生成实体标识时,OpenJPA 框架会自动为实体生成一个 128 位的 UUID,并且将这个 UUID 转化为使用 32 位字符表示的 16 进制的字符串。
数值标识
容器管理的实体标识可以是数值型的,OpenJPA 框架管理的实体标识借助于数据库的表来实现,在运行时 OpenJPA 框架会自动在数据库中创建表
我们要使用注释描述
清单 1. 标识由容器自动生成的 Animal 实体类
保存
容器管理实体标识的情况下,为了获得实体标识,应用程序将不得不频繁地和数据库交互,这会影响应用程序的运行效率。OpenJPA 中使用实体标识缓存机制解决这个问题。默认情况下,当应用程序第一次获取实体标识时,OpenJPA 框架从数据库中一次性获取 50 个连续的实体标识缓存起来,当下一次应用程序需要获取实体标识时,OpenJPA 将首先检测缓存中是否存在实体标识,如果存在,OpenJPA 将直接使用缓存中的实体标识,如果不存在,OpenJPA 框架将会从数据库中再次获取 50 个连续的实体标识缓存起来,如此类推。这样的处理方式可以大大减少由于获取实体标识而产生的数据库交互,提升应用程序的运行效率。
当实体标识成功获取之后,OpenJPA 框架会把当前实体标识的最大值 +1 后持久化到数据库中。由于实体标识缓存的原因,当我们第一次获取实体标识后,OpenJPA 会将
OpenJPA 缓存的实体标识不是永久存在的,只能在同一个
容器管理的实体标识还有一个非常重要的特性:所有被容器管理的实体标识都是共享的。不管 OpenJPA 容器中存在多少个不同的被容器管理的实体标识,它们都会从同一个实体标识缓存中获取实体标识。我们可以用下面的例子说明这种情况:假设 OpenJPA 容器中存在两个实体类
uuid-string
要使用 uuid-string 机制自动生成实体标识,我们需要将实体主键字段的
清单 2. 使用 uuid-string 机制自动生成实体标识
uuid-hex
要使用 uuid-hex 机制自动生成实体标识,我们必须将实体主键字段的
清单 3. 使用 uuid-hex 机制自动生成实体标识
自增长字段
自增长字段是 HSQL、SQL Server、MySQL、DB2、Derby 等数据库提供的一种特性,用于为数据库的记录提供自动增长的编号,应用程序的设计者通常期望将实体标识的自动生成委托给数据库的这种特性,OpenJPA 框架中的实体标识能够满足应用程序设计者的要求,使用数据库的自增长字段为实体自动生成标识。
要将实体标识的自动生成委托给数据库的自增长字段特性,需要数据库和实体定义的双方配合才能够达到:首先,必须将实体标识字段对应的数据库列修改为自动增长列,另外还需要将实体类中实体标识字段的
我们以 Animal 实体在 HSQL 数据库中的持久化来说明如何使用自增长字段自动生成实体标识所需要采取的步骤:
首先,我们使用下面的 SQL 语句创建 Animal 表,把它的
清单 4. 将 ID 字段设置为自动增长类型的 SQL 语句
在数据库部分将表的主键字段设置为自动增长字段后,在实体
清单 5. 标识由自增长字段生成的 Animal 实体类
序列号(Sequence)
序列号是 Oracle、PostgreSQL 等数据库提供的一种特性,用于为数据库的记录提供自动增长的编号,使用 Oracle、PostgreSQL 等数据库应用程序的设计者通常期望将实体标识的自动生成委托给数据库的这种特性,OpenJPA 框架中的实体标识能够满足应用程序设计者的要求,使用数据库的序列号为实体自动生成标识。
要将实体标识的自动生成委托给数据库的序列号特性,需要数据库和实体定义的双方配合才能够达到:首先,必须在数据库中创建合适的序列号,另外还需要为实体标识字段提供
我们以 Animal 实体在 Oracle 数据库中的持久化来说明如何使用自增长字段自动生成实体标识所需要采取的步骤:
首先,在 Oracle 数据库中运行下面的 SQL 语句创建名为
清单 6. 创建序列号的 SQL 语句
然后,在 Oracle 数据库中,我们使用下面的 SQL 语句创建
清单 7. 创建 ANIMAL 表
在数据库部分创建合适的序列号和相应的数据库表后,在实体
清单 8. 标识由序列号生成的 Animal 实体类
数据库表
除了使用容器生成的实体标识,或者借助于数据库的自增长字段或者序列号等方式生成实体标识之外,我们还可以选择借助数据库表来自动生成实体标识。原理是我们提供一个独立的数据库表,该表的主键列 ( 假设列名
要将实体标识的自动生成委托给数据库表,需要数据库和实体定义的双方配合才能够达到:首先,必须在数据库中创建合适的保存实体标识的表,另外还需要为实体标识字段提供
我们以 Animal 实体类来说明使用数据库表自动生成实体标识所需要采取的步骤:我们假设存在这样的场景,Animal 实体的标识由应用程序中自定义的数据库表
首先,在数据库中使用下面的 SQL 语句创建名为
清单 9. 创建数据库表 MY_KEYS
在数据库部分创建合适的数据库表后,在实体 Animal 的定义中,我们需要将
清单 10. 标识由数据库表生成的 Animal 实体类
调用代码
上面的章节中我们学习了分别使用四种方式来自动生成实体的标识,由于这四种情况下,Animal 实体的标识都由 OpenJPA 和数据库协作后自动生成,对于开发者而言,这个过程是透明的,因此我们可以使用相同的方式来创建这些实体:创建新的 Animal 实例的时候不再需要为主键字段提供属性值,只需要设置 Animal 实例的非标识字段
清单 11. Animal 实例的持久化代码
总结
本文介绍了开发者使用 OpenJPA 实现实体标识自动生成时可选择使用的注释,并且结合简单的例子,分别介绍了 OpenJPA 中实现容器管理的实体标识自动生成、结合数据库自增长字段、序列号、数据库表等特性实现实体标识自动生成时注释的具体用法和操作步骤。
参考资料
学习
" 使用 Apache OpenJPA 开发 EJB 3.0 应用系列 ":查看本系列完整的文章列表。
"通过 WebSphere Application Server V6.1 利用 OpenJPA"(developerWorks,2007 年 2 月 25 日):通过一个完整的示例来介绍 OpenJPA,并演示了如何通过 IBM® WebSphere® Application Server V6.1 来利用 OpenJPA 的部分功能。
"Spring 2 和 JPA 简介"(developerWorks,2006 年 8 月 28 日):通过这份教程,您将学习如何用 Spring 2 框架和 JPA 从头开始创建服务器应用程序。
"使用 EJB 3.0 Java Persistence API 设计应用程序程序"(developerWorks,2006 年 5 月 11 日):研究一个利用 JPA 验证 Java EE 设计模式并演示关键设计决策的示例应用程序。
EJB 3.0 规范:在 JCP 网站上查看 EJB 3.0 规范的标准文档。
OpenJPA 在线文档:在 OpenJPA 官方网站上查看 OpenJPA 相关的技术文档。
Java 技术专区:数百篇关于 Java 编程各方面的文章。
开源软件技术专区:获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
获得产品和技术
Apache OpenJPA 官方网站:获取 OpenJPA 项目的最新发布包和源代码。
Sun 公司 Java 站点:获取 JDK 最新的安装包。
MySQL 网站:获取最新的 MySQL 数据库。
HSQL 网站:获取最新的 HSQL 数据库。
关于作者
数据的唯一性是所有应用程序非常基本的要求,由开发者或者用户来维护这种唯一性存在着较大的风险,因此,由系统自动产生唯一标识是一种常见的做法。OpenJPA 中支持四种不同的实体标识自动生成策略:
容器自动生成的实体标识;
使用数据库的自动增长字段生成实体标识;
根据数据库序列号(Sequence)技术生成实体标识;
使用数据库表的字段生成实体标识;
这四种方式各有优缺点,开发者可以根据实际情况进行选择。
可选择的注释
要让容器和数据库结合管理实体标识的自动生成,根据实际情况的不同,开发者可以选择
javax.persistence.*包下面的
GeneratedValue、
SequenceGenerator、
TableGenerator三个注释来描述实体的标识字段。
@javax.persistence.GeneratedValue
每一个需要自动生成实体标识的实体都需要为它的实体标识字段提供
GeneratedValue注释和相应的参数,OpenJPA 框架会根据注释和参数来处理实体标识的自动生成。
使用
GeneratedValue注释自动生成的实体标识可以是数值类型字段如
byte、
short、
int、
long等,或者它们对应的包装器类型
Byte、
Short、
Integer、
Long等,也可以是字符串类型。
GeneratedValue注释可以支持两个属性
strategy和
generator。
strategy
strategy是
GenerationType类型的枚举值,它的内容将指定 OpenJPA 容器自动生成实体标识的方式。
strategy属性可以是下列枚举值:
GeneratorType.AUTO
表示实体标识由 OpenJPA 容器自动生成,这也是 Strategy 属性的默认值。
GenerationType.IDENTITY
OpenJPA 容器将使用数据库的自增长字段为新增加的实体对象赋唯一值,作为实体的标识。这种情况下需要数据库提供对自增长字段的支持,常用的数据库中,HSQL、SQL Server、MySQL、DB2、Derby 等数据库都能够提供这种支持。
GenerationType.SEQUENCE
表示使用数据库的序列号为新增加的实体对象赋唯一值,作为实体的标识。这种情况下需要数据库提供对序列号的支持,常用的数据库中,Oracle、PostgreSQL 等数据库都能够提供这种支持。
GenerationType.TABLE
表示使用数据库中指定表的某个字段记录实体对象的标识,通过该字段的增长为新增加的实体对象赋唯一值,作为实体的标识。
String generator
generator属性中定义实体标识生成器的名称。如果实体的标识自动生成策略不是
GenerationType.AUTO或者
GenerationType.IDENTITY,就需要提供相应的
SequenceGenerator或者
TableGenerator注释,然后将
generator属性值设置为注释的
name属性值。
@javax.persistence.SequenceGenerator
如果实体标识的自动生策略是
GenerationType.SEQUENCE,开发者需要为实体标识字段提供
SequenceGenerator注释,它的参数描述了使用序列号生成实体标识的具体细节。该注释支持以下四个属性:
表 1. SequenceGenerator 注释属性说明
属性 | 说明 |
---|---|
name | 该属性是必须设置的属性,它表示了 SequenceGenerator注释在 OpenJPA 容器中的唯一名称,将会被 GeneratedValue注释的 generator属性使用。将实体标识的自动生成委托给数据库的序列号特性时,实体标识字段的 GeneratedValue注释的 generator属性的值必须和某个 SequenceGenerator注释的 name属性值保持一致。 |
sequenceName | 实体标识所使用的数据库序列号的名称。该属性是可选的,如果我们没有为该属性设置值,OpenJPA 框架将自动创建名为 OPENJPA_SEQUENCE的序列号。如果一个 OpenJPA 容器中管理的多个实体都选择使用序列号机制生成实体标识,而且实体类中都没有指定标识字段的 sequenceName属性,那么这些实体将会共享系统提供的默认名为 OPENJPA_SEQUENCE的序列号。这可能引起实体类编号的不连续。我们可以用下面的这个简单例子说明这种情况:假设 OpenJPA 容器中存在两个实体类 Dog 和 Fish,它们的实体标识字段都是数值型,并且都选择使用序列号生成实体标识,但是实体类中并没有提供 sequenceName属性值。当我们首先持久化一个 Dog 对象时,它的实体标识将会是 1,紧接着我们持久化一个 Fish 对象,它的实体标识就是 2,依次类推。 |
initialValue | 该属性设置所使用序列号的起始值。 |
allocationSize | 一些数据库的序列化机制允许预先分配序列号,比如 Oracle,这种预先分配机制可以一次性生成多个序列号,然后放在 cache 中,数据库用户获取的序列号是从序列号 cache 中获取的,这样就避免了在每一次数据库用户获取序列号的时候都要重新生成序列号。allocationSize属性设置的就是一次预先分配序列号的数目,默认情况下 allocationSize属性的值是 50。 |
如果实体标识的自动生策略是 GenerationType.TABLE,开发者需要为实体标识字段提供 TableGenerator 注释,它的参数描述了使用数据库表生成实体标识的具体细节。该注释支持下列属性:
表 2. TableGenerator 注释属性说明
属性 | 说明 |
---|---|
name | 该属性是必须设置的属性,它表示了 TableGenerator注释在 OpenJPA 容器中的唯一名称,将会被 GeneratedValue注释的 generator属性所使用。将实体标识的自动生成委托给数据库表时,实体标识字段的 GeneratedValue注释的 generator属性的值必须和某个 TableGenerator注释的 name属性值保持一致。 |
table | 该属性设置的是生成序列号的表的名称。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认的表名 OPENJPA_SEQUENCES_TABLE。 |
schema | 该属性设置的是生成序列号的表的 schema。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会默认使用当前数据库用户对应的 schema。 |
catalog | 该属性设置的是生成序列号的表的 catalog。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认当前数据库用户对应的 catalog。 |
pkColumnName | 该属性设置的是生成序列号的表中的主键字段的名称,该字段将保存代表每个实体对应的标识值对应的特征字符串。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认值 ID。 |
valueColumnName | 该属性设置的是生成序列号的表中记录实体对应标识最大值的字段的名称。该属性并不是必须设置的属性,如果开发者没有为该 属性设置值,OpenJPA 容器将会使用默认值 SEQUENCE_VALUE。 |
pkColumnValue | 该属性设置的是生成序列号的表中的主键字段的特征字符串值 ( 比如 customID ),该字段将保存代表每个实体对应的标识值对应的特征字符串。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认值 DEFAULT。可以为多个实体设置相同的 pkColumnValue属性值,这些实体标识的生成将通过同一列的值的递增来实现。 |
initialValue | 该属性设置的是生成序列号的表实体标识的初始值。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认值 0 。 |
allocationSize | 为了降低标识生成时频繁操作数据库造成 的性能上的影响,实体标识生成的时候会一次性的获取多个实体标识,该属性设置的就是一次性获取实体标识的数目。该属性并不是必须设置的属性,如果开发者没有为该属性设置值,OpenJPA 容器将会使用默认值 50 。 |
|
在上面的小节中,我们了解了和实体标识自动生成相关的注释,接下来我们将结合一个简单的例子讲述如何分别使用这些实体标识自动生成策略实现实体标识的自动生成。
我们首先假设有一个
Animal实体需要被持久化,它包括
ID和
NAME属性,其中
ID是它的主键字段。
Animal实体的标识需要自动生成,我们将分析在这四种不用的情况下,如何使用 OpenJPA 提供的注释,结合具体数据库支持的特性,如自增长字段、序列号等来实现实体标识的自动生成。
容器自动生成
OpenJPA 容器默认的实体标识自动生成策略是由容器管理实体标识的自动生成,容器管理的实体标识可以支持数值型和字符型两种。当容器管理的实体标识是数字型时,OpenJPA 容器自动创建一个数据库表
OPENJPA_SEQUENCE_TABLE,用其中的
SEQUENCE_VALUE字段来记录实体的实体标识的增长。
当容器管理的实体标识是字符串类型时,OpenJPA 支持使用 uuid-string 和 uuid-hex 两种方式生成相应的实体标识。如果我们选择使用 uuid-string 方式生成实体标识时,OpenJPA 框架会自动为实体生成一个 128 位的 UUID,并且将这个 UUID 转化为使用 16 位字符表示的字符串。如果我们选择使用 uuid-hex 方式生成实体标识时,OpenJPA 框架会自动为实体生成一个 128 位的 UUID,并且将这个 UUID 转化为使用 32 位字符表示的 16 进制的字符串。
数值标识
容器管理的实体标识可以是数值型的,OpenJPA 框架管理的实体标识借助于数据库的表来实现,在运行时 OpenJPA 框架会自动在数据库中创建表
OPENJPA_SEQUENCE_TABLE。它有两个字段:
ID和
SEQUENCE_VALUE,这两个字段都是数值类型,其中
ID是表的主键字段,它的内容是查询当前实体标识时所使用的关键词,默认值是 0。而
SEQUENCE_VALUE记录了当前 OpenJPA 框架中当前实体标识的历史数据,内容是已经被获取实体标识的最大数值加 1。
我们要使用注释描述
Animal实体的标识由容器自动生成,只需要为它的标识字段提供
GeneratedValue注释,并且把它的
strategy属性设置为
GenerationType.AUTO,
Animal实体类的代码片断如下:
清单 1. 标识由容器自动生成的 Animal 实体类
1. import javax.persistence.Entity; 2. import javax.persistence.GeneratedValue; 3. import javax.persistence.GenerationType; 4. import javax.persistence.Id; 5. 6. @Entity 7. public class Animal { 8. @Id 9. @GeneratedValue(strategy=GenerationType.AUTO) 10. private long id; 11. private String name; 12. 13. … 14. 15. } |
Animal实体的第一个实例时,OpenJPA 框架自动调用 SQL 语句
SELECT SEQUENCE_VALUE FROM OPENJPA_SEQUENCE_TABLE WHERE ID=0,从默认保存实体标识的
OPENJPA_SEQUENCE_TABLE表中获取实体的标识,如果不存在
ID为 0 的记录,OpenJPA 框架自动将实体的标识设置为 1。
容器管理实体标识的情况下,为了获得实体标识,应用程序将不得不频繁地和数据库交互,这会影响应用程序的运行效率。OpenJPA 中使用实体标识缓存机制解决这个问题。默认情况下,当应用程序第一次获取实体标识时,OpenJPA 框架从数据库中一次性获取 50 个连续的实体标识缓存起来,当下一次应用程序需要获取实体标识时,OpenJPA 将首先检测缓存中是否存在实体标识,如果存在,OpenJPA 将直接使用缓存中的实体标识,如果不存在,OpenJPA 框架将会从数据库中再次获取 50 个连续的实体标识缓存起来,如此类推。这样的处理方式可以大大减少由于获取实体标识而产生的数据库交互,提升应用程序的运行效率。
当实体标识成功获取之后,OpenJPA 框架会把当前实体标识的最大值 +1 后持久化到数据库中。由于实体标识缓存的原因,当我们第一次获取实体标识后,OpenJPA 会将
OPENJPA_SEQUENCE_TABLE表的
SEQUENCE_VALUE的值设置为 51,当 OpenJPA 多次从数据库中获取实体标识后,
SEQUENCE_VALUE的值会以 50 为单位递增,变为 101、151、201 …。
OpenJPA 缓存的实体标识不是永久存在的,只能在同一个
EntityManagerFactory管理范围内起作用,也就是说,当获取实体标识的
EntityManagerFactory对象被关闭后,这些被获取的实体标识中没有用掉的那一部分标识就丢失了,这会造成实体标识的不连续。由同一个
EntityManagerFactory对象创建的
EntityManager上下文之间则能够共享 OpenJPA 框架获取的实体标识,这意味着,我们可以使用同一个
EntityManagerFactory对象创建多个
EntityManager对象,用它来持久化实体,然后关闭它,在持久化过程中所需要的实体表示将会使用同一个实体标识的缓存区,因此不会引起实体标识的丢失。
容器管理的实体标识还有一个非常重要的特性:所有被容器管理的实体标识都是共享的。不管 OpenJPA 容器中存在多少个不同的被容器管理的实体标识,它们都会从同一个实体标识缓存中获取实体标识。我们可以用下面的例子说明这种情况:假设 OpenJPA 容器中存在两个实体类
Dog和
Fish,它们的实体标识字段都是数值型,并且都由 OpenJPA 管理。当我们首先持久化一个
Dog对象时,它的实体标识将会是 1,紧接着我们持久化一个
Fish对象,它的实体标识就是 2,依次类推。
uuid-string
要使用 uuid-string 机制自动生成实体标识,我们需要将实体主键字段的
GeneratedValue注释的
strategy属性设置为
GenarationType.AUTO,然后将
GeneratedValue注释的
generator属性设置为
uuid-string。以 Animal 实体类为例,我们只需要将 Animal 实体修改为如下内容:
清单 2. 使用 uuid-string 机制自动生成实体标识
1. import javax.persistence.Entity; 2. import javax.persistence.GeneratedValue; 3. import javax.persistence.GenerationType; 4. import javax.persistence.Id; 5. 6. @Entity 7. public class Animal { 8. @Id 9. @GeneratedValue(strategy=GenerationType.AUTO, generator = "uuid-string") 10. private String id; 11. private String name; 12. 13. … 14. 15. } |
要使用 uuid-hex 机制自动生成实体标识,我们必须将实体主键字段的
GeneratedValue注释的
strategy属性设置为
GenarationType.AUTO,然后将
GeneratedValue注释的
generator属性设置为
uuid-hex。以 Animal 实体类为例,我们只需要将 Animal 实体修改为如下内容:
清单 3. 使用 uuid-hex 机制自动生成实体标识
1. import javax.persistence.Entity; 2. import javax.persistence.GeneratedValue; 3. import javax.persistence.GenerationType; 4. import javax.persistence.Id; 5. 6. @Entity 7. public class Animal { 8. @Id 9. @GeneratedValue(strategy=GenerationType.AUTO, generator = "uuid-hex") 10. private String id; 11. private String name; 12. 13. … 14. 15. } |
自增长字段是 HSQL、SQL Server、MySQL、DB2、Derby 等数据库提供的一种特性,用于为数据库的记录提供自动增长的编号,应用程序的设计者通常期望将实体标识的自动生成委托给数据库的这种特性,OpenJPA 框架中的实体标识能够满足应用程序设计者的要求,使用数据库的自增长字段为实体自动生成标识。
要将实体标识的自动生成委托给数据库的自增长字段特性,需要数据库和实体定义的双方配合才能够达到:首先,必须将实体标识字段对应的数据库列修改为自动增长列,另外还需要将实体类中实体标识字段的
GeneratedValue注释的
stragety属性的值设置为
GenerationType.IDENTITY。
我们以 Animal 实体在 HSQL 数据库中的持久化来说明如何使用自增长字段自动生成实体标识所需要采取的步骤:
首先,我们使用下面的 SQL 语句创建 Animal 表,把它的
ID字段设置为自动增长类型:
清单 4. 将 ID 字段设置为自动增长类型的 SQL 语句
CREATE TEXT TABLE ANIMAL ( ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY, NAME VARCHAR(255) NOT NULL ) |
Animal的定义中,我们需要将
id字段
GeneratedValue注释的
stragety属性的值设置为
GenerationType.IDENTITY。Animal 实体类修改后的代码片段如下。
清单 5. 标识由自增长字段生成的 Animal 实体类
1. import javax.persistence.Entity; 2. import javax.persistence.GeneratedValue; 3. import javax.persistence.GenerationType; 4. import javax.persistence.Id; 5. 6. @Entity 7. public class Animal { 8. @Id 9. @GeneratedValue(strategy=GenerationType.IDENTITY) 10. private long id; 11. private String name; 12. 13. … 14. 15. } |
序列号是 Oracle、PostgreSQL 等数据库提供的一种特性,用于为数据库的记录提供自动增长的编号,使用 Oracle、PostgreSQL 等数据库应用程序的设计者通常期望将实体标识的自动生成委托给数据库的这种特性,OpenJPA 框架中的实体标识能够满足应用程序设计者的要求,使用数据库的序列号为实体自动生成标识。
要将实体标识的自动生成委托给数据库的序列号特性,需要数据库和实体定义的双方配合才能够达到:首先,必须在数据库中创建合适的序列号,另外还需要为实体标识字段提供
SequenceGenerator注释,设置它的参数,为实体类提供关于序列号的信息,同时将实体类中实体标识字段的
GeneratedValue注释的
stragety属性的值设置为
GenerationType.SEQUENCE,将
generator属性的值设置为
SequenceGenerator注释的
name属性的值。
我们以 Animal 实体在 Oracle 数据库中的持久化来说明如何使用自增长字段自动生成实体标识所需要采取的步骤:
首先,在 Oracle 数据库中运行下面的 SQL 语句创建名为
HelloWorldSequence的序列号,序列号支持 cache,大小为 50:
清单 6. 创建序列号的 SQL 语句
CREATE SEQUENCE HELLOWORLDSEQUENCE START WITH 0 INCREMENT BY 1 MINVALUE 1 CACHE 50 NOCYCLE NOORDER |
ANIMAL表:
清单 7. 创建 ANIMAL 表
CREATE TABLE EOS52.ANIMAL ( ID CHAR(10), NAME VARCHAR2(100) NOT NULL, CONSTRAINT PK_ANIMAL PRIMARY KEY (ID ) ) |
Animal的定义中,我们需要将
id字段
GeneratedValue注释的
stragety属性的值设置为
GenerationType.SEQUENCE,设置它的
generator属性的值为
SeqGenerator。我们还需要为
id字段提供另外一个相关的注释
SequenceGenerator,设置它的
name属性为
SeqGenerator,设置它
sequenceName属性为
HelloWorldSequence。Animal 实体类修改后的代码片段如下。
清单 8. 标识由序列号生成的 Animal 实体类
1. import javax.persistence.Entity; 2. import javax.persistence.GeneratedValue; 3. import javax.persistence.GenerationType; 4. import javax.persistence.Id; 5. 6. @Entity 7. public class Animal { 8. @Id 9. @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SeqGenerator") 10. @SequenceGenerator(name = "SeqGenerator", sequenceName = " HelloWorldSequence") 11. private long id; 12. private String name; 13. 14. … 15. 16. } |
除了使用容器生成的实体标识,或者借助于数据库的自增长字段或者序列号等方式生成实体标识之外,我们还可以选择借助数据库表来自动生成实体标识。原理是我们提供一个独立的数据库表,该表的主键列 ( 假设列名
ID) 记录实体编号的特征字符串 ( 假设存在一条记录的
ID为
customID),另外一列 ( 假设列名为
SEQUENCE_VALUE) 记录该特征字符串对应实体标识的最大值。编写实体代码时,我们指定实体标识由数据库表中指定的特征字符串 ( 如
customID) 对应的列
SEQUENCE_VALUE处理,当有新的实体被持久化时,容器将获取行
customID、列
SEQUENCE_VALUE对应的数值 +1 后作为新实体的标识,同时将该列的值也自动 +1。
要将实体标识的自动生成委托给数据库表,需要数据库和实体定义的双方配合才能够达到:首先,必须在数据库中创建合适的保存实体标识的表,另外还需要为实体标识字段提供
TableGenerator注释,设置它的参数,为实体类提供关于数据库表、字段的信息,同时将实体类中实体标识字段的
GeneratedValue注释的
stragety属性的值设置为
GenerationType.Table,将
generator属性的值设置为
SequenceGenerator注释的
name属性的值。
我们以 Animal 实体类来说明使用数据库表自动生成实体标识所需要采取的步骤:我们假设存在这样的场景,Animal 实体的标识由应用程序中自定义的数据库表
MY_KEYS自动生成,
MY_KEYS表中有两列,一列是
KEYID,它保存实体标识的特征值,一列是
KEYVALUE,它保存实体当前的最大编号,除此之外,我们还决定使用
ANIMALID作为 Animal 实体标识的特征字符串。
首先,在数据库中使用下面的 SQL 语句创建名为
MY_KEYS的数据库表。在 OpenJPA 容器中,如果我们没有创建
MY_KEYS表,OpenJPA 容器将帮我们自动生成对应的表结构。
清单 9. 创建数据库表 MY_KEYS
CREATE TABLE MY_KEYS ( KEYID VARCHAR(255) NOT NULL, KEYVALUE BIGINT, PRIMARY KEY (KEYID) ) |
id字段
GeneratedValue注释的
stragety属性的值设置为
GenerationType.TABLE,设置它的
generator属性的值为
TableGenerator。我们还需要为
id字段提供另外一个注释
TableGenerator,设置它的
name属性为
TableGenerator,设置它的
table属性为
MYKEYS、
pkColumnName属性为
KEYID、
valueColumnName属性为
KEYVALUE、
ANIMALID属性为
ANIMALID。Animal 实体类修改后的代码片段如下。
清单 10. 标识由数据库表生成的 Animal 实体类
1. import javax.persistence.Entity; 2. import javax.persistence.GeneratedValue; 3. import javax.persistence.GenerationType; 4. import javax.persistence.Id; 5. 6. @Entity 7. public class Animal { 8. @Id 9. @GeneratedValue(strategy = GenerationType.TABLE, generator = " TableGenerator ") 10. @TableGenerator(name = " TableGenerator", table = "MY_KEYS", pkColumnName = "KEYID", valueColumnName = "KEYVALUE", pkColumnValue = "ANIMALID") 11. private long id; 12. private String name; 13. 14. … 15. 16. } |
|
上面的章节中我们学习了分别使用四种方式来自动生成实体的标识,由于这四种情况下,Animal 实体的标识都由 OpenJPA 和数据库协作后自动生成,对于开发者而言,这个过程是透明的,因此我们可以使用相同的方式来创建这些实体:创建新的 Animal 实例的时候不再需要为主键字段提供属性值,只需要设置 Animal 实例的非标识字段
name的值即可。下面的代码演示了 Animal 实例的持久化代码,请注意代码中并没有调用 Animal 实例的 setId 方法。
清单 11. Animal 实例的持久化代码
1. EntityManagerFactory factory = Persistence. 2. createEntityManagerFactory( 3. "jpa-unit", System.getProperties()); 4. EntityManager em = factory.createEntityManager(); 5. em.getTransaction().begin(); 6. 7. Animal animal = new Animal(); 8. // 此处不需要调用 animal 的 setId 方法 9. animal.setName("ba guai!"); 10. em.persist(animal); 11. 12. em.getTransaction().commit(); 13. em.close(); 14. em2.close(); 15. factory.close(); |
|
本文介绍了开发者使用 OpenJPA 实现实体标识自动生成时可选择使用的注释,并且结合简单的例子,分别介绍了 OpenJPA 中实现容器管理的实体标识自动生成、结合数据库自增长字段、序列号、数据库表等特性实现实体标识自动生成时注释的具体用法和操作步骤。
参考资料
学习
" 使用 Apache OpenJPA 开发 EJB 3.0 应用系列 ":查看本系列完整的文章列表。
"通过 WebSphere Application Server V6.1 利用 OpenJPA"(developerWorks,2007 年 2 月 25 日):通过一个完整的示例来介绍 OpenJPA,并演示了如何通过 IBM® WebSphere® Application Server V6.1 来利用 OpenJPA 的部分功能。
"Spring 2 和 JPA 简介"(developerWorks,2006 年 8 月 28 日):通过这份教程,您将学习如何用 Spring 2 框架和 JPA 从头开始创建服务器应用程序。
"使用 EJB 3.0 Java Persistence API 设计应用程序程序"(developerWorks,2006 年 5 月 11 日):研究一个利用 JPA 验证 Java EE 设计模式并演示关键设计决策的示例应用程序。
EJB 3.0 规范:在 JCP 网站上查看 EJB 3.0 规范的标准文档。
OpenJPA 在线文档:在 OpenJPA 官方网站上查看 OpenJPA 相关的技术文档。
Java 技术专区:数百篇关于 Java 编程各方面的文章。
开源软件技术专区:获得丰富的 how-to 信息、工具和项目更新,帮助您用开放源码技术进行开发,并与 IBM 产品结合使用。
获得产品和技术
Apache OpenJPA 官方网站:获取 OpenJPA 项目的最新发布包和源代码。
Sun 公司 Java 站点:获取 JDK 最新的安装包。
MySQL 网站:获取最新的 MySQL 数据库。
HSQL 网站:获取最新的 HSQL 数据库。
关于作者
肖菁是自由撰稿人,致力于研究和推广 Java EE、Open Source、SOA 技术,发起了开源项目 BuildFileDesigner(buildfiledesign.sourceforge.net) 和 V-Security(v-security.sourceforge.net)。可以通过电子邮件 cto4u.cn@gmail.com 和他取得联系。 |
相关文章推荐
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 5 部分: 实体标识的自动生成
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 4 部分: 实体关联
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 6 部分: 处理实体生命周期事件的回调
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 3 部分: 实体继承
- 使用 Apache OpenJPA 开发 EJB 3.0 应用- 处理实体生命周期事件的回调
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 1 部分: OpenJPA 与 EJB 3.0
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 2 部分: 开发第一个 Open JPA 应用
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 7 部分: 使用 Eclipse Dali 开发 OpenJPA 应用
- 使用 Apache OpenJPA 开发 EJB 3.0 应用,第 8 部分: 在 Java EE 环境下开发、部署 OpenJPA 应用
- EJB3.0开发指南:使用实体Bean
- EJB 3.0开发指南之使用实体Bean
- Jdeveloper 12c 实体主键自动生成 使用 oracle.jbo.server.SequenceImpl 报错
- 使用Eclipse3.0+lomboz3.0+jboss3.2.3开发EJB
- EJB 3.0 开发指南之实体Bean(二)
- 关于使用SQL自动生成,程序转化工具,提高开发数据库的效率
- 使用 Eclipse 开发 J2EE 应用、使用 Eclipse 开发 EJB
- Unity开发Hololens应用,自动生成包裹物体大小的三维旋转和缩放边框
- 使用MyBatis Generator自动生成实体、mapper和dao层
- 使用Eclipse3.0+lomboz3.0+jboss3.2.3开发EJB(一)
- 使用.net开发手机管理软件 (六) OBEX应用——文件传输部分