1. 简介
在本教程中,我们将了解实体的基础知识,以及在 JPA 中定义和自定义实体的各种注释。
2. 实体
JPA 中的实体只不过是表示可以持久保存到数据库的数据的 POJO。实体表示存储在数据库中的表。实体的每个实例都表示表中的一行。
2.1. 实体注释
假设我们有一个名为 Student 的 POJO,它表示学生的数据,我们希望将其存储在数据库中:
public class Student {
// fields, getters and setters
}
为了做到这一点,我们应该定义一个实体,以便JPA知道它。
因此,让我们通过使用@Entity注释来定义它。我们必须在类级别指定此注释。我们还必须确保实体具有 no-arg 构造函数和主键:
@Entity
public class Student {
// fields, getters and setters
}
实体名称默认为类的名称。我们可以使用 name 元素更改它的名称:
@Entity(name="student")
public class Student {
// fields, getters and setters
}
因为各种 JPA 实现会尝试对我们的实体进行子类化以提供它们的功能,所以实体类不能被声明为 final。
2.2. ID 注释
每个 JPA 实体都必须有一个唯一标识它的主键。@Id注释定义主键。我们可以通过不同的方式生成标识符,这些标识符由@GeneratedValue注释指定。
我们可以从四种带有策略元素的 id 生成策略中进行选择。该值可以是自动、表、序列或标识:
@Entity
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
// getters and setters
}
如果我们指定生成类型。AUTO,JPA 提供程序将使用它想要生成标识符的任何策略。
如果我们注释实体的字段,JPA 提供程序将使用这些字段来获取和设置实体的状态。除了字段访问之外,我们还可以进行属性访问或混合访问,这使我们能够在同一实体中同时使用字段和属性访问。
2.3. 表格注释
在大多数情况下,数据库中表的名称和实体的名称不会相同。
在这些情况下,我们可以使用@Table注释指定表名:
@Entity
@Table(name="STUDENT")
public class Student {
// fields, getters and setters
}
我们还可以使用架构元素提及架构:
@Entity
@Table(name="STUDENT", schema="SCHOOL")
public class Student {
// fields, getters and setters
}
架构名称有助于将一组表与另一组表区分开来。
如果我们不使用@Table注释,则表的名称将是实体的名称。
2.4. 列注释
就像@Table注释一样,我们可以使用@Column注释来提及表中列的详细信息。
@Column注释具有许多元素,例如名称、长度、可为空和唯一:
@Entity
@Table(name="STUDENT")
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Column(name="STUDENT_NAME", length=50, nullable=false, unique=false)
private String name;
// other fields, getters and setters
}
name 元素指定表中列的名称。长度元素指定其长度。可为空元素指定列是否可为空,唯一元素指定列是否唯一。
如果我们不指定此注释,则表中列的名称将是字段的名称。
2.5. 瞬态注释
有时,我们可能希望使字段非持久性。 我们可以使用@Transient注释来做到这一点。它指定不会保留该字段。
例如,我们可以从出生之日起计算学生的年龄。
因此,让我们用@Transient注释来注释字段年龄:
@Entity
@Table(name="STUDENT")
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Column(name="STUDENT_NAME", length=50, nullable=false)
private String name;
@Transient
private Integer age;
// other fields, getters and setters
}
因此,字段年龄不会保留到表中。
2.6. 时间标注
在某些情况下,我们可能必须在表中保存时态值。
为此,我们有@Temporal注释:
@Entity
@Table(name="STUDENT")
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Column(name="STUDENT_NAME", length=50, nullable=false, unique=false)
private String name;
@Transient
private Integer age;
@Temporal(TemporalType.DATE)
private Date birthDate;
// other fields, getters and setters
}
但是,在JPA 2.2中,我们还支持java.time.LocalDate,java.time.LocalTime,java.time.LocalDateTime,java.time.OffsetTime和java.time.OffsetDateTime。
2.7. 枚举注释
有时,我们可能希望持久化 Java 枚举类型。
我们可以使用@Enumerated注释来指定枚举是按名称还是按序号(默认)持久化:
public enum Gender {
MALE,
FEMALE
}
@Entity
@Table(name="STUDENT")
public class Student {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
@Column(name="STUDENT_NAME", length=50, nullable=false, unique=false)
private String name;
@Transient
private Integer age;
@Temporal(TemporalType.DATE)
private Date birthDate;
@Enumerated(EnumType.STRING)
private Gender gender;
// other fields, getters and setters
}
实际上,如果我们要按枚举的序数保留性别,我们根本不需要指定@Enumerated注释。
但是,为了保留按枚举名称划分的性别,我们使用 EnumType.STRING 配置了注释。
3. 结论
在本文中,我们学习了什么是 JPA 实体以及如何创建它们。我们还了解了可用于进一步自定义实体的不同注释。
本文的完整代码可以在Github上找到。