Hibernate @AssociationOverride for Customizing Entity Relationships in Spring Boot
The @AssociationOverride annotation in Spring Boot (JPA) allows you to override the mapping of a relationship (association) between entities in an embedded or inheritance context. It’s often used when you want to map an embedded entity's relationship to another entity differently in a specific class.
Use Case Scenario
Let’s say you have an Employee entity that contains an embedded Address entity. The Address entity has a relationship with a City entity. By default, the Employee entity would inherit the relationship of Address to City. But suppose in one particular case, you want to override that relationship in a subclass of Employee, or in a related entity. This is where @AssociationOverride comes into play.
GITHUB LINK
https://github.com/vinosubi/AssociationOverrideAnnotation.git
Example Scenario: Employee with a Work and Home Address
Entities:
- Employee has an embedded
Address(home address by default). - Address has a many-to-one relationship with a
City. - OfficeEmployee extends
Employeeand has a work address (overrides the city relationship ofhomeAddress).
1.City Entity
@Entity
public class City {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Constructors, getters, setters
public City() {}
public City(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}2.Address Embeddable
@Embeddable
public class Address {
private String street;
private String zipCode;
@ManyToOne
@JoinColumn(name = "city_id")
private City city;
// Constructors, getters, setters
public Address() {}
public Address(String street, String zipCode, City city) {
this.street = street;
this.zipCode = zipCode;
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public City getCity() {
return city;
}
public void setCity(City city) {
this.city = city;
}
}@Embeddable
The @Embeddable annotation is used to mark a class whose instances are intended to be embedded in another entity. An embeddable class does not have its own separate identity (i.e., no primary key). Instead, its fields are part of the entity in which it is embedded.
Here, the Address class is marked with @Embeddable, meaning it can be embedded in another entity, such as Employee. The Address class contains attributes like street, zipCode, and a relationship to City. However, the Address entity will not have its own table, and its fields will be embedded in the table of the entity where it is used.
3.Employee Entity
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private Address homeAddress;
// Constructors, getters, setters
public Employee() {}
public Employee(String name, Address homeAddress) {
this.name = name;
this.homeAddress = homeAddress;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getHomeAddress() {
return homeAddress;
}
public void setHomeAddress(Address homeAddress) {
this.homeAddress = homeAddress;
}
}@Embedded
The @Embedded annotation is used to specify that a field of an entity is an instance of an @Embeddable class, and the properties of that @Embeddable class should be included in the table of the containing entity.
In this case, the Employee entity contains the Address object as a field, and it's annotated with @Embedded. This means the homeAddress field in the Employee entity will embed the Address fields (e.g., street, zipCode, and city_id) into the employee table.
How It Works in the Database
When Address is embedded in Employee, it means that the Employee entity’s table will have columns corresponding to the fields of the Address entity, like street, zipCode, and city_id. These fields won't exist in a separate Address table, but rather as part of the employee table.
Key Points:
@Embeddable: Defines a class that can be embedded in another entity.@Embedded: Embeds the fields of the@Embeddableclass into the containing entity’s table.
OfficeEmployee Entity
This entity extends Employee and overrides the relationship for work address using @AssociationOverride.
@Entity
@AssociationOverride(
name = "homeAddress.city",
joinColumns = @JoinColumn(name = "work_city_id") // Overrides the default "city_id"
)
public class OfficeEmployee extends Employee {
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "street", column = @Column(name = "work_street")),
@AttributeOverride(name = "zipCode", column = @Column(name = "work_zipCode"))
})
private Address workAddress;
// Constructors, getters, setters
public OfficeEmployee() {}
public OfficeEmployee(String name, Address homeAddress, Address workAddress) {
super(name, homeAddress);
this.workAddress = workAddress;
}
public Address getWorkAddress() {
return workAddress;
}
public void setWorkAddress(Address workAddress) {
this.workAddress = workAddress;
}
}@AssociationOverride
The @AssociationOverride annotation allows you to override the mapping of a relationship (association) in an embedded object or in an inheritance hierarchy. This is particularly useful when you have an @Embedded object with associations (such as @ManyToOne or @OneToMany), and you need to change the way the relationship is mapped in a subclass or another entity.
- Purpose: In this case, we are overriding the
cityrelationship in thehomeAddressfield (which is an embeddedAddress) for theOfficeEmployeeentity. The@ManyToOnerelationship for thecityfield in thehomeAddressis overridden to use a different column (work_city_id) in the database. - Effect: This allows
OfficeEmployeeto use a different column for its city relationship compared to theEmployeeclass, where the default column forhomeAddress.cityishome_city_id. InOfficeEmployee, we override this withwork_city_idfor the work address.
@AttributeOverrides
The @AttributeOverrides annotation allows you to override the column mappings for simple attributes in an embedded object. This is typically used to change the default column names that are automatically mapped based on the embedded object's fields.
- Purpose: The
@AttributeOverridesannotation is used to override the column names for thestreetandzipCodefields in the embeddedAddressobject when it is mapped toworkAddress.
Effect:
- For
workAddress.street, the column is namedwork_streetinstead ofhome_street(or whatever default column name would be used). - For
workAddress.zipCode, the column is namedwork_zipCodeinstead ofhome_zipCode.
Repository
We need repositories to perform CRUD operations on the entities.
CityRepository
import org.springframework.data.jpa.repository.JpaRepository;
public interface CityRepository extends JpaRepository<City, Long> {}EmployeeRepository
import org.springframework.data.jpa.repository.JpaRepository;
public interface CityRepository extends JpaRepository<City, Long> {}Service Layer
EmployeeService
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Autowired
private CityRepository cityRepository;
@Transactional
public void createEmployees() {
City newYork = new City("New York");
City losAngeles = new City("Los Angeles");
cityRepository.save(newYork);
cityRepository.save(losAngeles);
// Create home address for John
Address homeAddress = new Address("Elm St.", "12345", newYork);
// Create work address for John
Address workAddress = new Address("Maple St.", "67890", losAngeles);
// Create OfficeEmployee John with home and work addresses
OfficeEmployee john = new OfficeEmployee("John", homeAddress, workAddress);
employeeRepository.save(john);
}
public OfficeEmployee getOfficeEmployee(Long id) {
return (OfficeEmployee) employeeRepository.findById(id).orElse(null);
}
}Controller
EmployeeController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/employees")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/create")
public String createEmployee() {
employeeService.createEmployees();
return "Employee created!";
}
@GetMapping("/officeEmployee")
public OfficeEmployee getOfficeEmployee() {
return employeeService.getOfficeEmployee(1L);
}
}Database Schema
city Table

employee Table

office_employee Table

Sample Data and Output
Insert data into City:
INSERT INTO city (name) VALUES ('New York'), ('Los Angeles');Insert data into Employee and OfficeEmployee:
INSERT INTO employee (name, home_street, home_zipCode, home_city_id)
VALUES ('John', 'Elm St.', '12345', 1);
INSERT INTO office_employee (id, work_street, work_zipCode, work_city_id)
VALUES (1, 'Maple St.', '67890', 2);Output Example:
Johnhas a home address in New York (fromhomeAddress.city).Johnhas a work address in Los Angeles (fromworkAddress.city).
Testing
You can use Postman or any other HTTP client to trigger the endpoints.
- Create the Employee and OfficeEmployee
GETrequest to:http://localhost:8080/employees/create
Response:
"Employee created!"
2. Retrieve the OfficeEmployee with home and work addresses
GET request to:
http://localhost:8080/employees/officeEmployee
Sample Output (JSON):
{
"id": 1,
"name": "John",
"homeAddress": {
"street": "Elm St.",
"zipCode": "12345",
"city": {
"id": 1,
"name": "New York"
}
},
"workAddress": {
"street": "Maple St.",
"zipCode": "67890",
"city": {
"id": 2,
"name": "Los Angeles"
}
}
}Database Schema
The schema created by Hibernate will include:
1.city table:
id(primary key)name
2.employee table (inherited by OfficeEmployee):
idnamehome_streethome_zipCodehome_city_id
3.office_employee table (extends employee):
idwork_streetwork_zipCodework_city_id@Embeddableis used to embed theAddressobject in theEmployeeentity.@AssociationOverrideis used in theOfficeEmployeeto override the city relationship for the work address.- The entities are saved, and the output JSON shows how both the home and work addresses are associated with different cities.
👏 If you found my articles useful, please consider giving it claps and sharing it with your friends and colleagues.
To read other topics
- @Formula Annotation in Spring Boot
- Mastering Transaction Propagation and Isolation in Spring Boot
- Git and GitHub Integration in IntelliJ IDEA
- One To One mapping in Spring Boot JPA
- One To Many mapping in Spring Boot JPA
- Pessimistic Locking in JPA with Spring Boot
- Optimistic Locking in JPA with Spring Boot
- Optional in Java 8





