Careeroza
Embeddable Value Objects
@Embeddable and @Embedded
Some groups of fields belong together but have no identity of their own: an address, a money amount with currency, a GPS location. Map them as an @Embeddable. Their columns live in the owner's table — there is no separate table and no id.
@Embeddable
public class Address {
private String line1;
private String city;
private String state;
@Column(length = 6)
private String pincode;
protected Address() { }
public Address(String line1, String city, String state, String pincode) { ... }
// getters
}
@Entity
@Table(name = "users")
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
private String fullName;
@Embedded
private Address address;
}create table users (
id bigint not null auto_increment,
email varchar(255) not null,
full_name varchar(255),
line1 varchar(255),
city varchar(255),
state varchar(255),
pincode varchar(6),
primary key (id),
constraint uk_users_email unique (email)
)The Same Embeddable Twice: @AttributeOverrides
An Order has a shipping address and a billing address. Both would want the column city, so rename the columns of one (or both):
@Entity
@Table(name = "orders")
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "line1", column = @Column(name = "ship_line1")),
@AttributeOverride(name = "city", column = @Column(name = "ship_city")),
@AttributeOverride(name = "state", column = @Column(name = "ship_state")),
@AttributeOverride(name = "pincode", column = @Column(name = "ship_pincode"))
})
private Address shippingAddress;
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "line1", column = @Column(name = "bill_line1")),
@AttributeOverride(name = "city", column = @Column(name = "bill_city")),
@AttributeOverride(name = "state", column = @Column(name = "bill_state")),
@AttributeOverride(name = "pincode", column = @Column(name = "bill_pincode"))
})
private Address billingAddress;
}Tip: Since Hibernate 6.2 an embeddable can be a Java record: @Embeddable public record Address(String line1, String city, String state, String pincode) {}. Records are immutable, which suits value objects well.
Flattening, Simulated
Example
This is how an embedded value becomes prefixed columns in the owner row:
import java.lang.reflect.RecordComponent;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Address ship = new Address("12 MG Road", "Bengaluru", "Karnataka", "560001");
Address bill = new Address("4 Park Street", "Kolkata", "West Bengal", "700016");
Map<String, Object> row = new LinkedHashMap<>();
row.put("id", 1001L);
flatten("ship_", ship, row);
flatten("bill_", bill, row);
row.forEach((col, val) -> System.out.printf("%-13s = %s%n", col, val));
}
static void flatten(String prefix, Record value, Map<String, Object> row) throws Exception {
for (RecordComponent rc : value.getClass().getRecordComponents()) {
row.put(prefix + rc.getName(), rc.getAccessor().invoke(value));
}
}
}
record Address(String line1, String city, String state, String pincode) {}Output
id = 1001 ship_line1 = 12 MG Road ship_city = Bengaluru ship_state = Karnataka ship_pincode = 560001 bill_line1 = 4 Park Street bill_city = Kolkata bill_state = West Bengal bill_pincode = 700016