简介: Java Persistence API(JPA)中要求每个实体(Entity)类必须有一个主键,并且提供了 Table,Sequence,Identity,Auto 四种主键生成策略。其中 Identity 为表自动增长字段;Sequence 通过序列产生主键;这两种主键生成策略在某些特定的数据库中得到了很好的支持。Table 策略是任何关系型数据库都支持的一种策略,所以如果想要得到良好的可移植行,可以选择这种策略;Auto 策略就是把控制权交给 JPA 本身,让它根据实际情况来自己决定使用哪种策略,默认情况下就是使用这种策略。 下面就分别探究一下它们的用法。
Table 策略 (Table strategy)
这种策略中,持久化引擎 (persistence engine) 使用关系型数据库中的一个表 (Table) 来生成主键。这种策略可移植性比较好,因为所有的关系型数据库都支持这种策略。不同的 J2EE 应用服务器使用不同的持久化引擎。
下面用一个例子来说明这种表生成策略的使用:
清单 1.Table 生成策略
@Entity
public class PrimaryKey_Table {
@TableGenerator(name = "PK_SEQ",
table = "SEQUENCE_TABLE",
pkColumnName = "SEQUENCE_NAME",
valueColumnName = "SEQUENCE_COUNT")
@Id
@GeneratedValue(strategy =GenerationType.TABLE,generator="PK_SEQ")
private Long id;
//Getters and Setters
//为了方便,类里面除了一个必需的主键列,没有任何其他列,以后类似
}
|
首先,清单 1 中使用 @javax.persistence.TableGenerator 这个注解来指定一个用来生成主键的表 (Table),这个注解可以使用在实体类上,也可以像这个例子一样使用在主键字段上。
其中,在这个例子中,name 属性“PK_SEQ” 标示了这个生成器,也就是说这个生成器的名字是 PK_SEQ。这个 Table 属性标示了用哪个表来存贮生成的主键,在这个例子中,用“ SEQUENCE_TABLE” 来存储主键,数据库中有对应的 SEQUENCE_TABLE 表。其中 pkColumnName 属性用来指定的是生成器那个表中的主键,也就是 SEQUENCE_TABLE 这个表的主键的名字。属性 valueColumnName 指定列是用来存储最后生成的那个主键的值。
也可以使用持久化引擎提供的缺省得 Table,例如:
清单 2. 使用确省的表生成器
public class PK implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
// Getters and Setters
}
|
不同的持久化引擎有不同的缺省值,在 glass fish 中,Table 属性的缺省值是 SEQUENCE, pkColumnName 属性缺省值是 SEQ_NAME,,valueColumnName 属性的缺省值是 SEQ_COUNT
Sequence 策略
一些数据库,比如 Oralce,有一种内置的叫做“序列” (sequence)的机制来生成主键。为了调用这个序列,需要使用 @javax.persistence.SequenceGenerator 这个注解。
例如
清单 3.sequence 策略生成主键
@Entity
public class PK_Sequence implements Serializable {
private static final long serialVersionUID = 1L;
@SequenceGenerator(name="PK_SEQ_TBL",sequenceName="PK_SEQ_NAME")
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator="PK_SEQ_TBL")
private Long id;
// Getters and Setters
}
|
其中的 @javax.persistence.SequenceGenerator
定义如下:
清单 4.@SequenceGenerator 注解的定义
@Target(value = {ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface SequenceGenerator {
public String name();
public String sequenceName() default "";
public String catalog() default "";
public String schema() default "";
public int initialValue() default 1;
public int allocationSize() default 50;
}
|
从定义中可以看出这个注解可以用在类上,也可以用在方法和字段上,其中 name 属性指定的是所使用的生成器;sequenceName 指定的是数据库中的序列;initialValue 指定的是序列的初始值,和 @TableGenerator 不同是它的缺省值 1;allocationSize 指定的是持久化引擎 (persistence engine) 从序列 (sequence) 中读取值时的缓存大小,它的缺省值是 50。
Identity 策略
一些数据库,用一个 Identity 列来生成主键,使用这个策略生成主键的时候,只需要在 @GeneratedValue 中用 strategy 属性指定即可。如下所示:
清单 5.strategy 策略生成主键
@Entity
public class PK_Identity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Getters and Setters
}
|
Auto 策略
使用 AUTO 策略就是将主键生成的策略交给持久化引擎 (persistence engine) 来决定,由它自己选择从 Table 策略,Sequence 策略和 Identity 策略三种策略中选择合适的主键生成策略。不同的持久化引擎 (persistence engine) 使用不同的策略,在 galss fish 中使用的是 Table 策略。
使用 AUTO 策略时,我们可以显示使用,如:
清单 6.Auto 策略生成主键
@Entity
public class PK_Auto implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// Getters and Setters
}
|
或则只使用:
@Generated Value
或者干脆什么都不写,因为缺省得主键生成策略就是 AUTO。
复合主键
在对象关系映射模型中,使用单独的一个字段作为主键是一种非常好的做法,但是在实际应用中,经常会遇到复合主键的问题,就是使用两个或两个以上的字段作为主键。比如,在一些历史遗留的数据库表中,经常出现复合主键的问题,为了解决这种问题,JPA2.0 中采用的 @EmbeddedId 和 @IdClass 两种方法解决这种问题。它们都需要将用于主键的字段单独放在一个主键类 (primary key class) 里面,并且该主键类必须重写 equals () 和 hashCode () 方法,必须实现 Serializable 接口,必须拥有无参构造函数。
@EmbeddedId 复合主键
清单 7 中的 NewsId 类被用做主键类,它用 @Embeddable 注解进行了注释,说明这个类可以嵌入到其他类中。之外这个类中还重写了 hashCode () 和 equals () 方法, 因为这个类中的两个属性要用作主键,必须有一种判定它们是否相等并且唯一的途径。
清单 7.@EmbeddedId 中的主键类
@Embeddable
public class NewsId implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
private String language;
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NewsId other = (NewsId) obj;
if ((this.title == null) ? (other.title != null) : !this.title.equals(other.title)) {
return false;
}
if ((this.language == null) ? (other.language != null) : !this.language.equals(
other.language)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 41 * hash + (this.title != null ? this.title.hashCode() : 0);
hash = 41 * hash + (this.language != null ? this.language.hashCode() : 0);
return hash;
}
}
|
清单 8 中的 News 类使用了清单 7 中定义的主键类,可以看倒非常简单,只需要使用 @EmbeddedId 指定主键类即可。
清单 8.News 实体类使用定义好的主键类
@Entity
public class News implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private NewsId id;
private String content;
// Getters and Setters
}
|
清单 9 是持久化后生成的数据库表的结构,可以看出来这个表如我们预想的一样是 Title 和 Language 的联合主键。
清单 9. 使用主键类生成的表结构
CREATE TABLE `news` (
`CONTENT` varchar(255) default NULL,
`TITLE` varchar(255) NOT NULL,
`LANGUAGE` varchar(255) NOT NULL,
PRIMARY KEY (`TITLE`,`LANGUAGE`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
IdClass 复合主键
IdClass 这种复合主键策略,在主键类上和 Embeddable 这种复合主键策略稍有不同。如清单 10,这个策略中的主键类不需要使用任何注解 (annotation),但是仍然必须重写 hashCode() 和 equals() 两个方法。其实也就是将 Embeddable 这种复合主键策略中的主键类的 @Embeddable 注解去掉就可以了。
清单 10. IdClass 复合主键策略中的主键类
public class NewsId implements Serializable {
private static final long serialVersionUID = 1L;
private String title;
private String language;
public String getLanguage() {
return language;
}
public void setLanguage(String language) {
this.language = language;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final NewsId other = (NewsId) obj;
if ((this.title == null) ? (other.title != null) : !this.title.equals(other.title)) {
return false;
}
if ((this.language == null) ? (other.language != null) : !this.language.equals(
other.language)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 41 * hash + (this.title != null ? this.title.hashCode() : 0);
hash = 41 * hash + (this.language != null ? this.language.hashCode() : 0);
return hash;
}
}
|
从清单 11 中可以看出这个 News 实体类首先使用 @IdClass (NewsId.class) 注解指定了主键类。同时在类中也复写了主键类中的两个字段,并分别用 @Id 进行了注解。
清单 11. IdClass 策略中使用复合主键的 News 类
@Entity
@IdClass(NewsId.class)
public class News implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private String title;
@Id
private String language;
private String content;
// Getters and Setters
}
|
从清单 12 中可以看出,两种复合主键的映射策略,持久化后映射到数据库中的表的结构是相同的,一个明显的区别就是在查询的时候稍有不同。如在使用 @EmbeddableId 策略的时候,要使用如下查询语句:
Select n.newsId.title from news n
|
而使用 @IdClass 策略的时候,要使用如下查询语句:
Select n.title from news n
|
另外一点就是使用 @IdClass 这种策略的时候,在复写主键类中的字段的时候务必要保证和主键类中的定义完全一样。
清单 12. @IdClass 策略生成的表结构
CREATE TABLE `news` (
`CONTENT` varchar(255) default NULL,
`TITLE` varchar(255) NOT NULL,
`LANGUAGE` varchar(255) NOT NULL,
PRIMARY KEY (`TITLE`,`LANGUAGE`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
总结
Java EE 项目开发中的持久层,虽然具体的实现方式,也就是持久化引擎会随着你选择的 Java EE 服务器的不同而有所不同,但是在 JPA(java persistence API) 这个规范之下,每个实体的主键生成策略却只有上面几种,也就是说我们主要掌握了上面几种主键生成策略,就可以在以后 Java EE 项目持久层开发中以不变应万变的姿态来面对纷繁复杂的具体情况了。
分享到:
相关推荐
1. **实体类的定义**: 如何使用注解来定义实体类,包括主键生成策略、关系映射等。 2. **查询语言(JPQL)**: JPA提供了自己的查询语言,类似于SQL,用于从数据库检索实体。源代码可能会包含JPQL的例子。 3. **...
JPA和Hibernate都是Java持久化技术的重要组件。JPA提供了一些注解来描述实体Bean的持久化信息,而Hibernate是一个基于JPA规范的ORM框架。通过配置persistence.xml和hibernate.cfg.xml文件,可以实现数据库的持久化和...
在JPA中,持久化对象(Entity)的映射涉及到多个方面,包括实体类的定义、表和字段的映射、主键的生成策略、特殊类型的映射等。例如,使用`@Entity`注解标识一个类为实体类,`@Id`注解标识主键字段,`@Table`注解指定...
JPA (Java Persistence API) 是Java EE 5.0平台标准中的ORM(对象关系映射)规范,旨在简化现有的Java EE和Java SE应用程序的对象持久化开发工作。JPA通过JDK 5.0注解或XML描述对象与关系表的映射关系,并实现运行时...
- **定义**: JPA (Java Persistence API) 是由Sun官方提出的一种Java持久化规范,旨在简化Java应用程序中关系型数据库的操作过程。其核心目标是统一现有的ORM (Object-Relational Mapping) 框架,如Hibernate、...
- **OpenJPA**:作为Apache项目的一部分,OpenJPA是一个开源的Java持久化框架(Java Persistence Framework),它支持Java Persistence API (JPA) 的规范。OpenJPA不仅为开发人员提供了强大的数据持久化功能,还具有...
JPA,即Java Persistence API(Java持久化API),是Sun公司(现已被Oracle收购)在Java EE 5规范中提出的一种Java持久化接口标准。它的目标是简化Java对象的持久化工作,使开发人员能更轻松地处理对象与数据库之间的...
持久化技术的发展** 1.1 JDBC(Java Database Connectivity):JDBC是Java语言访问数据库的标准API,它允许程序员用Java语言与各种数据库进行交互。然而,JDBC对于复杂的数据库操作需要编写大量重复的代码,这促使...
JPA(Java Persistence API)是Java EE 5.0平台中定义的标准对象关系映射(ORM)规范,它允许开发人员将应用程序中的实体对象与数据库中的表格进行映射,并提供了一种统一的方式来管理和持久化这些对象。JPA的设计...
Java Persistence API(JPA)是Java平台上的一个标准,用于管理关系数据库中的对象持久化。它简化了在Java应用程序中存储、检索和管理数据的过程,是Enterprise JavaBeans(EJB)3.0规范的一部分。本套学习资料包含...
- **发展历程**:JPA 首次出现在 Java EE 5.0 版本中,旨在简化企业级应用的数据持久化过程。 - **目标**:提供一套统一的标准接口,让开发人员能够以一致的方式访问持久层,从而提高应用程序的可移植性和可维护性。...
#### 二、Hibernate 数据持久化技术详解 ##### 2.1 Hibernate 概念介绍 Hibernate 是一个开源的对象关系映射 (Object-Relational Mapping, ORM) 框架,用于 Java 应用程序。它的主要目的是简化数据库操作,使得...
- **定义与背景**:OpenJPA是Apache组织提供的一款开源项目,它实现了Java Persistence API(JPA)规范,该规范源自EJB 3.0中的JPA标准。OpenJPA作为一个持久化框架,为开发人员提供了强大且易于使用的数据持久化...
JPA,全称Java Persistence API,是在Java EE 5.0平台下定义的一种对象关系映射(ORM)规范,旨在解决Java应用程序中对象持久化的问题。它汲取了EJB规范早期版本的教训,特别是在吸收了当时流行的ORM框架(如...
JPA的目标是规范和简化Java对象的持久化过程,通过ORM技术将面向对象的编程模型与关系数据库的数据模型相互映射。ORM技术的核心在于对象与数据库表之间的映射,使得开发者无需直接编写SQL,而是通过对象操作来处理...