数据的唯一性是所有应用程序非常基本的要求,由开发者或者用户来维护这种唯一性存在着较大的风险,因此,由系统自动产生唯一标识是一种常见的做法。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 实体类 |