How do you make one attribute of a JPA entity use property access while the rest of the class uses field access, and when is that worth doing?
answer
- class-level @Access(FIELD) + member-level @Access(PROPERTY)
- backing field must be @Transient or it maps twice
- setter is called during hydration — must accept nulls
- getter must be cheap and stable or dirty checking misfires
- type-wide translation → converter instead
basics
~20 sDeclare the class default explicitly with @Access(AccessType.FIELD), mark the backing field @Transient so it is not mapped twice, then put @Access(AccessType.PROPERTY) plus the mapping annotations on the getter. Hibernate then calls that getter and setter for that one column.
solid answer
~50 s`@Access` works at two levels. At class level it fixes the default; on an individual member it flips just that attribute. The recipe for one property-access attribute inside a field-access entity: 1. Put `@Access(AccessType.FIELD)` on the class so the default is explicit. 2. Mark the backing field `@Transient` — otherwise, under field access it is already a persistent attribute and you would map the same state twice. 3. Put `@Access(AccessType.PROPERTY)` and the mapping annotations (`@Column`, `@Convert`, …) on the getter, and provide a matching setter, because Hibernate calls it when hydrating a row. It earns its keep when the stored representation differs from the in-memory one and the translation is entity-specific: normalising a value on write, composing one column from two fields, decoding a legacy encoding. For a translation that is type-wide rather than entity-specific, an `AttributeConverter` is the better tool; for a read-only derived value, a database-side expression is.
go deeper
Know that @Access exists and that mapping annotations must sit on the member the access type points at; the mixed-access recipe itself is beyond junior scope.
Be able to write the recipe correctly — explicit class-level access, @Transient backing field, annotations plus @Access on the getter, and a matching setter.
Discuss why the setter runs during hydration, why the getter must be stable for dirty checking, and when a converter or embeddable is the better answer.
Treat it as a seam between the storage representation and the domain representation, and argue for keeping such translations in one declarative place rather than scattering per-entity accessors that later readers must reverse-engineer.
## The mechanism Access type in JPA is normally uniform across an entity hierarchy, inferred from where the identifier annotation sits. `@Access` gives you two levers on top of that. Applied to a **class**, it states the access type outright and overrides the inference. Applied to a **member** — a field or a getter — it overrides the access type for that single attribute. Member-level `@Access` is what lets Hibernate call a method for one column while reading every other column straight out of a field. ## Getting it right The common mistake is to add `@Access(AccessType.PROPERTY)` to a getter and stop there. Under a field-access default, *every* non-transient field is already persistent, so the field behind that getter is mapped too, and you end up with two attributes fighting over one piece of state — usually surfacing as a duplicate-column mapping error or a mysterious second column. The complete recipe: ```java @Entity @Access(AccessType.FIELD) public class Account { @Id private Long id; @Transient private String iban; // in-memory form, not mapped directly @Access(AccessType.PROPERTY) @Column(name = "iban") protected String getStoredIban() { return iban == null ? null : iban.replace(" ", "").toUpperCase(); } protected void setStoredIban(String value) { this.iban = value; } } ``` Four things to notice. The class default is explicit rather than inferred. The field is `@Transient`, so it contributes no column of its own. The mapping annotations live on the getter, which is where the provider now looks for this attribute. And the setter must exist and must be reachable — it is invoked while Hibernate hydrates the row, so it has to accept whatever the database can contain, including nulls, and it must not throw on legacy data. Accessors used only for persistence are conventionally `protected` (never `private` — the provider needs to reach them and, if the entity is proxied, the subclass does too) and named so nobody mistakes them for API. ## When it earns its keep - **Canonicalisation on write.** Store a normalised form — trimmed, upper-cased, digits only — while the domain keeps its own representation. - **One column from several fields, or several fields from one column.** A legacy column that packs a flag set, a coordinate pair stored as text, a status encoded with a vendor-specific letter that only this table uses. - **Guarding a field the ORM must not touch directly.** For instance, a field held in a form the database cannot represent, where the getter renders it and the setter parses it. - **Read-time defaulting for legacy rows** where nulls must become a sensible value in memory but must stay null in the table. ## The costs This attribute now behaves under property-access rules while its neighbours do not, and that asymmetry is invisible unless you read the annotations carefully. Dirty checking for it goes through your getter, so the method must be cheap, side-effect free and stable: return a newly allocated object every call and the entity looks dirty on every flush. It must also be null-safe in both directions. And because JPQL refers to attribute names, queries will use the property name, not the field name — `select a from Account a where a.storedIban = :v` — which reads badly if the accessor is named for the storage form. Finally, tooling and readers are used to consistent placement; a lone property-access attribute is a footgun for the next person who adds `@Column` to the wrong member and gets silence. ## Alternatives worth naming If the translation depends only on the *type* and not on this entity — an enum stored as a code, a value object stored as text, a boolean stored as 'Y'/'N' — an `AttributeConverter` expresses it once and applies everywhere, keeps the mapping declarative, and can be reused. If the value is genuinely derived and never written, a database-side expression or a view is cheaper than any Java-side accessor. If the shape is a cluster of related columns, an `@Embeddable` is the better structure. Reach for member-level `@Access` when the mapping is truly entity-specific and stateful in a way none of those handle.
- What breaks if you add @Access(AccessType.PROPERTY) to a getter but leave the backing field untouched in a field-access entity?Under a field-access default the field is already a persistent attribute, so the same state is mapped twice — typically two attributes bound to the same or to unexpectedly different columns. Hibernate usually fails at bootstrap with a repeated-column or duplicate-attribute error, and when it does not, you get a second column you never intended. Marking the field @Transient removes the duplicate.
- When would you prefer an AttributeConverter over a property-access attribute?When the transformation belongs to the type rather than to this one entity — a value object, an enum code, a boolean flag stored as a character. A converter is declarative, reusable across entities, applies to bound query parameters, and keeps the entity free of persistence-only accessors. Property access is the right choice only when the translation is specific to this class or spans more than one field.
saying these in an interview costs you the question
- Adding @Access(PROPERTY) to a getter while leaving the backing field mapped.
- Making the persistence-only getter and setter private, so the provider cannot call them.
- Putting expensive or allocating logic in the getter and being surprised by constant UPDATE statements.
- Writing a setter that validates or rejects nulls, which then throws while loading legacy rows.
- Claiming member-level @Access changes the access type for the whole entity.