package com.ankurm.hibernatedemo.persistenceannotations; import jakarta.persistence.Access; import jakarta.persistence.AccessType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Transient; /** * Default access is FIELD (because {@code @Id} is annotated on the field). One property, * {@code computedLabel}, is explicitly switched to PROPERTY access via {@code @Access} on its * getter, with a derivation the FIELD side does not know about. This reproduces the classic * "mixed access silently reads the wrong value" symptom: a raw field mutation is invisible to * Hibernate once a property is PROPERTY-access, and vice versa. * *

Docs: docs/05-jpa-persistence-annotations.md, Topic 2. */ @Entity public class MixedAccessEntity { @Id @GeneratedValue private Long id; private String rawValue; // FIELD access is the entity default (because @Id is on a field). This backing field is // deliberately never read directly by Hibernate for the "label" property -- only the // PROPERTY-access getter below is. @Transient private int getterCallCount = 0; public MixedAccessEntity() { } public MixedAccessEntity(String rawValue) { this.rawValue = rawValue; } public Long getId() { return id; } public String getRawValue() { return rawValue; } public void setRawValue(String rawValue) { this.rawValue = rawValue; } /** * Explicitly PROPERTY access: Hibernate calls this getter (not a field read) to determine * the persisted value for the "computedLabel" mapped property. */ @Access(AccessType.PROPERTY) @Column(name = "computed_label") public String getComputedLabel() { getterCallCount++; return rawValue == null ? null : rawValue.toUpperCase(); } // Hibernate's default PROPERTY-access strategy REQUIRES a setter even for a logically // read-only derived column -- omitting it throws PropertyNotFoundException at boot // ("Could not locate setter method for property 'computedLabel'"). This setter is a // deliberate no-op: it exists purely to satisfy that requirement. public void setComputedLabel(String ignored) { // no-op: computedLabel is derived from rawValue, never written directly } public int getGetterCallCount() { return getterCallCount; } }