Add Hibernate 7 batches 2-5: mapping styles, JPA annotations, natural IDs, @Immutable, stored procedures, in-memory test databases, JNDI mocking, proxies, associations, temporal mapping and named queries (Hibernate 7.4.5.Final + Spring Boot 4.1.1 + JDK 25)
This commit is contained in:
+76
@@ -0,0 +1,76 @@
|
||||
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.
|
||||
*
|
||||
* <p>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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user