本文详解 jpa `@access` 注解如何在单个实体中混合使用 `field` 和 `property` 访问策略,通过代码示例、测试验证与关键注意事项,帮助开发者精准控制字段读写时机(如 setter 前置处理),避免默认策略导致的逻辑遗漏。
在 JPA 规范中,实体的访问类型(Access Type)决定了持久化上下文如何读取和写入实体成员——是直接操作字段(AccessType.FIELD),还是通过 getter/setter 方法(AccessType.PROPERTY)。默认情况下,JPA 采用“就近声明决定全局访问类型”规则:若 @Id 注解标注在字段上,则整个实体默认为 FIELD 访问;若标注在 getter 上,则默认为 PROPERTY 访问。但这一全局设定并非不可打破——@Access 注解允许你在特定字段或属性级别显式覆盖默认行为,实现细粒度控制。
以 Student 实体为例:
@Entity
public class Student {
@Id
@GeneratedValue
private Long ID; // ← @Id 在字段 → 默认全局为 FIELD
private String firstName; // ← 未加 @Access → 按默认 FIELD 访问:绕过 setter/getter
@Access(AccessType.PROPERTY) // ← 显式声明:lastName 必须走 getter/setter
private String lastName;
// ... getter/setter(注意:getlastName() 是笔误,应为 getLastName())
public String getFirstName() {
System.out.println("~~~~~~~~getFirstName~~~~~~~~~");
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = "FirstName: " + firstName; // ← 此逻辑在 FIELD 模式下*不会被调用*
System.out.println("~~~~~~~~setFirstName~~~~~~~~~");
}
public String getLastName() { // 修正方法名
System.out.println("~~~~~~~~getLastName~~~~~~~~~");
return lastName;
}
public void setLastName(String lastName) {
this.lastName = "LastName: " + lastName; // ← 此逻辑在 PROPERTY 模式下*会被调用*
System.out.println("~~~~~~~~setLastName~~~~~~~~~");
}
}
关键点在于:
✅ firstName 使用默认 FIELD 访问 → JPA 直接读写字段值,完全跳过 setFirstName() 和 getFirstName() 方法;
✅ lastName 受 @Access(AccessType.PROPERTY) 控制 → JPA 强制通过 setLastName() 初始化、通过 getLastName() 读取,确保前置/后置逻辑生效。
为实证该行为,可编写如下集成测试(需配置内存数据库如 H2):