Hibernate JPA 数据访问策略:AccessType

使用 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 来访问所有数据库操作的字段 idname 和 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,从而提供注入其他数据处理逻辑的机会。

 

请登录后发表评论

    没有回复内容