There are many articles on the topic that show this is a real JPA inconvenience:
Most of them suggest solutions based on normalised relational database, with a header-entity as one table and its value-objects as other separate tables.
My frustration was augmented with the necessity to integrate with a non-normalized read-only table. The table had no id field and meant to store object-values. No bindings with a header-entity table. To map it with JPA was a problem, because only entities with id are mapped.
The solution was to wrap MyValueObject class with MyEntity class, making MyValueObject its composite key:
@Data
@Entity
@Table(schema = "my_schema", name = "my_table")
public class MyEntity {
@EmbeddedId MyValueObject valueObject;
}
As a slight hack, to bypass JPA requirements for default empty constructor and not to break the immutability of Value Object, we add it as private and sacrifice final modifier for fields. Privacy and absence of setters conforms the initial DDD idea of Value Object:
// @Value // Can't use, unfortunately.
@Embeddable
@Immutable
@AllArgsConstructor
@Getter
@NoArgsConstructor(staticName = "private") // Makes MyValueObject() private.
public class MyValueObject implements Serializable {
@Column(name = "field_one")
private String myString;
@Column(name = "field_two")
private Double myDouble;
@Transient private Double notNeeded;
}
Also there is a handful Lombok's @Value
annotaion to configure value objects.