An entity needs two attributes of the same @Embeddable type — for example a billing address and a shipping address — and both would map to identical column names. How do you make that work in JPA/Hibernate, including when the embeddable itself contains a nested embeddable or an association?
answer
- Repeated column in mapping → needs overrides
- name = path inside embeddable, not column
- Dots for nested: geo.latitude
- @AssociationOverride for @ManyToOne inside
- Override replaces the whole @Column, defaults return
basics
~10 sUse @AttributeOverride on each embedded attribute to rename the columns, one per property. Nested embeddable properties are addressed with dotted paths like "geo.latitude". Associations inside the embeddable are renamed with @AssociationOverride instead.
solid answer
~50 sTwo embedded values of the same type would collide on column names, so the *owner* renames them with `@AttributeOverride`: ```java @Embedded @AttributeOverride(name = "street", column = @Column(name = "bill_street")) @AttributeOverride(name = "city", column = @Column(name = "bill_city")) private Address billingAddress; ``` `@AttributeOverride` is repeatable since Java 8, so the `@AttributeOverrides` wrapper is optional. Rules worth stating: - `name` is the **path inside the embeddable**, not the column name; nested embeddables use dots (`name = "geo.latitude"`). - Associations declared inside the embeddable are not covered — use `@AssociationOverride(name = "country", joinColumns = @JoinColumn(name = "bill_country_id"))`. - The same annotations work on `@EmbeddedId`, on `@ElementCollection` element attributes, and on fields inherited from a `@MappedSuperclass`. - Overrides are *mapping-level*, so you must list every colliding property; anything you omit keeps the embeddable's own column name and still collides. Hibernate 6.6+ adds `@EmbeddedColumnNaming("bill_%s")` as a pattern-based shorthand for the repetitive case.
code
java · 16 lines@Entity
public class Order {
@Id @GeneratedValue private Long id;
@Embedded
@AttributeOverride(name = "street", column = @Column(name = "bill_street", nullable = false))
@AttributeOverride(name = "geo.latitude", column = @Column(name = "bill_lat"))
@AssociationOverride(name = "country", joinColumns = @JoinColumn(name = "bill_country_id"))
private Address billing;
@Embedded
@AttributeOverride(name = "street", column = @Column(name = "ship_street"))
@AttributeOverride(name = "geo.latitude", column = @Column(name = "ship_lat"))
@AssociationOverride(name = "country", joinColumns = @JoinColumn(name = "ship_country_id"))
private Address shipping;
}go deeper
Know that duplicate embeddables collide on column names and that @AttributeOverride on the owning field fixes it; be able to write one override correctly.
Explain dotted paths for nested embeddables, @AssociationOverride for associations, repeatability, and that the override replaces the whole @Column.
Point out the constraint-loss trap under schema generation, where else overrides apply (@EmbeddedId, @ElementCollection, @MappedSuperclass), and when the override count signals the value type should be a real table.
Treat it as a reuse-versus-clarity call: shared value types plus overrides keep invariants centralised, but heavy override blocks are a sign the model needs normalising or a Hibernate naming strategy applied consistently across the codebase.
## The problem An `@Embeddable` declares column names once, but a single entity may embed it more than once. Two `Address` fields both wanting `street`, `city`, `zip` cannot coexist in one table, and Hibernate fails at bootstrap with a repeated-column error ("Repeated column in mapping for entity"). The fix belongs to the **owner**, because only the owner knows the context of each copy. ## @AttributeOverride ```java @Entity public class Order { @Id @GeneratedValue private Long id; @Embedded @AttributeOverride(name = "street", column = @Column(name = "bill_street")) @AttributeOverride(name = "city", column = @Column(name = "bill_city")) private Address billing; @Embedded @AttributeOverride(name = "street", column = @Column(name = "ship_street")) @AttributeOverride(name = "city", column = @Column(name = "ship_city")) private Address shipping; } ``` Key details candidates get wrong: - **`name` is the attribute path inside the embeddable**, not a column name. `name = "street"` refers to the Java property; `column = @Column(name = "bill_street")` supplies the new column. - The `@Column` you supply **replaces** the embeddable's own `@Column` for that property. If the original declared `nullable = false, length = 120`, and your override omits them, you get the defaults back. Restate everything you care about. - `@AttributeOverride` is `@Repeatable` (Java 8 / JPA 2.2 onward), so the `@AttributeOverrides({...})` container is optional but still valid. - You must override **every** colliding property. Forgetting `zip` leaves both copies pointing at `zip` and bootstrap fails — which is at least a loud failure, not silent corruption. ## Nested embeddables Embeddables nest freely, and overrides address nested properties by dotted path: ```java @Embeddable class GeoPoint { double latitude; double longitude; } @Embeddable class Address { String street; @Embedded GeoPoint geo; } @Embedded @AttributeOverride(name = "street", column = @Column(name = "ship_street")) @AttributeOverride(name = "geo.latitude", column = @Column(name = "ship_lat")) @AttributeOverride(name = "geo.longitude",column = @Column(name = "ship_lon")) private Address shipping; ``` The path is always rooted at the embeddable being overridden, and only the outermost `@Embedded` carries the overrides — you do not repeat them on the nested field. ## Associations inside embeddables `@AttributeOverride` handles basic and embedded attributes only. If `Address` declares `@ManyToOne Country country`, the join column is renamed with a different annotation: ```java @AssociationOverride(name = "country", joinColumns = @JoinColumn(name = "ship_country_id")) ``` `@AssociationOverride` also carries `joinTable` for collection-valued associations inside the embeddable. ## Where else overrides apply - On `@EmbeddedId`, to rename composite-key columns per entity. - On `@ElementCollection`, where the overrides apply to the *element* attributes stored in the collection table. - On an entity that inherits attributes from a `@MappedSuperclass`, to retarget inherited columns. ## Hibernate-specific shorthand Repeating five overrides twice is noisy. Hibernate 6.6 introduced `@EmbeddedColumnNaming("bill_%s")`, which applies a printf-style pattern to every column of that embedded attribute, including nested ones. It is a Hibernate extension, not portable JPA — mention it as a convenience, and know the standard `@AttributeOverride` route because that is what an interviewer is testing. ## Design note If you find yourself writing a dozen overrides, ask whether the two copies really are the same value type or whether the schema should have been normalised (an `address` table with a foreign key). Overrides are cheap for two or three columns and a smell at ten.
- If the embeddable declares @Column(nullable = false, length = 60) on a property and your @AttributeOverride only sets the name, what happens to the constraints?They are lost. The `@Column` inside `@AttributeOverride` replaces the original wholesale rather than merging with it, so any `nullable`, `length`, `unique` or `columnDefinition` you do not restate reverts to the JPA defaults. With `hbm2ddl` generating the schema, that silently produces a nullable VARCHAR(255).
- Can you avoid overrides entirely by giving the two embedded fields different names and relying on a naming strategy?Not with the standard implicit naming strategies — they derive column names from the embeddable's own property names, so both copies still collide. You can write a custom Hibernate `ImplicitNamingStrategy` that prefixes with the embedded attribute name, or use Hibernate 6.6's `@EmbeddedColumnNaming` pattern; plain JPA offers only `@AttributeOverride`.
saying these in an interview costs you the question
- Thinking @AttributeOverride's name attribute is the target column name
- Putting the overrides on the nested @Embedded field instead of the outermost one
- Using @AttributeOverride to rename a @ManyToOne join column inside an embeddable
- Assuming the overridden @Column merges with the original and keeps nullable/length
- Believing two identical embeddables in one entity 'just work' and last-one-wins