使用 Java Persistence API (JPA) 时,选择实体数据访问策略是一个关键方面,它可以显著影响应用程序的性能。在 JPA 中,有两种主要的访问策略:字段访问 (AccessType.FIELD)
和属性访问 (AccessType.PROPERTY)。
了解每个差异的细微差别将有助于您最有效地管理数据库交互。
今天我们将探讨如何使用 @Access(AccessType.FIELD)
和 AccessType.PROPERTY
来实现对 Hibernate 中字段的不同访问,以及为什么它很重要。
JPA 中的 Access 策略基础
JPA 中的访问策略决定了框架如何读取和写入实体字段的值 — 直接通过字段或通过调用 getter 和 setter 间接读取和写入。
- 字段访问 (
AccessType.FIELD
):JPA 将直接访问实体类字段,绕过 getter 和 setter。如果将 @Id 注释放置在字段上方,则默认使用此行为。 - 属性访问 (
AccessType.PROPERTY):
JPA 将使用 getter 和 setter 来访问值,从而允许您在读取或写入时实现额外的数据处理逻辑。要使用此策略,必须将@Id
注解放在 getter 上。
比如:
CREATE TABLE employees (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
salary NUMERIC(10, 2) NOT NULL
);
使用 AccessType.FIELD 的示例
让我们考虑使用字段访问的实体类 Employee:
import jakarta.persistence.*;
@Access(AccessType.FIELD)
@Entity
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@Column(name = "salary")
private double salary;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
此处不需要注释 @Access(AccessType.FIELD)
本身,因为 @Id
位于字段上,并且仅用于演示目的。在这种情况下,无论我们在 getter 和 setter 中放置什么额外的逻辑,在 Hibernate 与数据库交互期间都不会调用它,因为字段是直接使用的,绕过了 getter 和 setter。
使用 AccessType.PROPERTY
的示例
为了演示属性访问策略 (AccessType.PROPERTY)
的使用,让我们考虑一个类似的 Employee
实体类,其中 JPA 将使用 getter 和 setter 进行所有数据读取和写入操作,包括实体标识。
import jakarta.persistence.*;
@Access(AccessType.PROPERTY)
@Entity
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "employee")
public class Employee {
private Long id;
private String name;
private double salary;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Column(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "salary")
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
// Logging addition logic
System.out.println("Salary updated: " + salary);
}
}
@Access(AccessType.PROPERTY)
的显式指示是多余的,因为 JPA 已经根据 @Id
的位置定义了访问策略;在这里,它仅用于演示目的。
在此示例中,由于在类级别指定了 @Access(AccessType.PROPERTY)
注释,JPA 将使用 getter 和 setter 来访问所有数据库操作的字段 id
、name
和 saliary
。这意味着通过 JPA 进行的任何薪资更新(例如,在保存或更新实体时)都将调用 setSalary
方法,因此,将执行此方法中定义的所有其他逻辑,包括日志记录。
使用 AccessType.PROPERTY
可以将其他数据处理 (如验证或日志记录) 直接嵌入到访问方法中,从而对读取和写入期间实体数据的处理方式提供更高程度的控制。
将@Access应用于字段
当您将 @Access
注释直接应用于实体类中的字段或方法(getter/setter)而不是整个类时,您可以指示 JPA 专门对这些属性使用特定的访问策略。这允许在单个实体中混合访问策略,这在某些情况下可能很有用。
混合访问策略示例
假设大多数字段都应该使用 field access,但是对于一个特定的 field,需要在 getter/setter 中实现验证逻辑或日志记录,因此对于这个 field,我们想要使用 property access。
@Entity
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
// We want use AccessType.PROPERTY for add logic to setter
@Column(name = "salary")
private double salary;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Access(AccessType.PROPERTY)
@Column(name = "salary")
public double getSalary() {
return this.salary;
}
public void setSalary(double salary) {
this.salary = salary;
// Logging addition logic
System.out.println("Salary updated: " + salary);
}
}
- 字段访问权限用于除
salary
之外的所有字段 (id
,name
)。 - 对于 salary,getter 和 setter 用
@Access(AccessType.PROPERTY)
进行注释,指示 Hibernate 对这个字段使用属性访问。这意味着通过 Hibernate 对 salary 进行的任何读取或写入操作都将通过这些方法,从而允许您注入其他逻辑,例如记录更改。
这种方法为管理对实体字段的访问提供了灵活性,并允许在单个类中混合两种访问策略,从而优化数据处理逻辑和通过 Hibernate 与数据库的交互。
总结
在本文中,我们探讨了 JPA 中的访问策略:字段访问 (AccessType.FIELD
) 和属性访问 (AccessType.PROPERTY)。
字段访问允许 JPA 直接与实体字段交互,而属性访问使用 getter 和 setter,从而提供注入其他数据处理逻辑的机会。
没有回复内容