技术开发 频道

实体标识的自动生成

        实体标识自动生成

  在上面的小节中,我们了解了和实体标识自动生成相关的注释,接下来我们将结合一个简单的例子讲述如何分别使用这些实体标识自动生成策略实现实体标识的自动生成。

  我们首先假设有一个 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

  要使用 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 语句

1 CREATE TEXT TABLE ANIMAL (
2   ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 0) NOT NULL PRIMARY KEY,
3   NAME VARCHAR(255) NOT NULL
4 )
5

  在数据库部分将表的主键字段设置为自动增长字段后,在实体 Animal 的定义中,我们需要将 id 字段 GeneratedValue 注释的 stragety 属性的值设置为 GenerationType.IDENTITY。Animal 实体类修改后的代码片段如下。

  清单 5. 标识由自增长字段生成的 Animal 实体类

1 1.    import javax.persistence.Entity;
2 2.    import javax.persistence.GeneratedValue;
3 3.    import javax.persistence.GenerationType;
4 4.    import javax.persistence.Id;
5 5.    
6 6.    @Entity
7 7.    public class Animal {
8 8.        @Id
9 9.        @GeneratedValue(strategy=GenerationType.IDENTITY)
10 10.        private long id;
11 11.        private String name;
12 12.      
13 13.      …
14 14.    
15 15.    }
16

  序列号(Sequence)

  序列号是 Oracle、PostgreSQL 等数据库提供的一种特性,用于为数据库的记录提供自动增长的编号,使用 Oracle、PostgreSQL 等数据库应用程序的设计者通常期望将实体标识的自动生成委托给数据库的这种特性,OpenJPA 框架中的实体标识能够满足应用程序设计者的要求,使用数据库的序列号为实体自动生成标识。

  要将实体标识的自动生成委托给数据库的序列号特性,需要数据库和实体定义的双方配合才能够达到:首先,必须在数据库中创建合适的序列号,另外还需要为实体标识字段提供 SequenceGenerator 注释,设置它的参数,为实体类提供关于序列号的信息,同时将实体类中实体标识字段的 GeneratedValue 注释的 stragety 属性的值设置为 GenerationType.SEQUENCE,将 generator 属性的值设置为 SequenceGenerator 注释的 name 属性的值。

  我们以 Animal 实体在 Oracle 数据库中的持久化来说明如何使用自增长字段自动生成实体标识所需要采取的步骤:

  首先,在 Oracle 数据库中运行下面的 SQL 语句创建名为 HelloWorldSequence 的序列号,序列号支持 cache,大小为 50:

  清单 6. 创建序列号的 SQL 语句

1 CREATE SEQUENCE HELLOWORLDSEQUENCE
2 START WITH 0
3 INCREMENT BY 1
4 MINVALUE 1
5 CACHE 50
6 NOCYCLE
7 NOORDER
8

  然后,在 Oracle 数据库中,我们使用下面的 SQL 语句创建 ANIMAL 表:

  清单 7. 创建 ANIMAL 表

1 CREATE TABLE EOS52.ANIMAL
2 (
3 ID CHAR(10),
4 NAME VARCHAR2(100) NOT NULL,
5 CONSTRAINT PK_ANIMAL PRIMARY KEY (ID )
6 )
7

  在数据库部分创建合适的序列号和相应的数据库表后,在实体 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.    }
0
相关文章