5.2 Entity Mappings & Relationships
← Back to Chapter 5: Spring Data JPA, Hibernate & Database
Modeling relational associations in Java requires defining association multiplicity, ownership sides, cascading rules, and fetching strategies.
@Entity
@Entity tells JPA:
This class should be managed as a persistent entity.
Example:
@Entity
public class Customer {
}
Without @Entity, JPA doesn't treat the class as a database entity.
@Table
You can specify the database table name.
@Entity
@Table(name = "customer")
public class Customer {
}
If class name and table name are different:
Java class → Customer
Database → customer_details
then:
@Table(name = "customer_details")
@Id
Every entity needs an identifier.
@Id
private Long id;
This maps to the primary key.
id
↓
Database
(PRIMARY KEY)
@GeneratedValue
If the database should generate IDs:
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
For MySQL auto-increment:
1
2
3
4
...
Hibernate can use the generated value.
Core Mapping Annotations
@Entity
@Table(name = "customers", indexes = {
@Index(name = "idx_customer_email", columnList = "email", unique = true)
})
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String fullName;
@Column(nullable = false, unique = true, length = 150)
private String email;
@CreationTimestamp
@Column(updatable = false)
private Instant createdAt;
@UpdateTimestamp
private Instant updatedAt;
}
Column Mapping
You can customize a column:
@Column(name = "mobile_number")
private String mobile;
nullable
You can specify whether the column can be null:
@Column(nullable = false)
private String name;
This means the database column should not accept null values when the schema reflects that mapping.
unique
@Column(unique = true)
private String email;
This indicates the column should have a uniqueness constraint when schema generation is used.
length
@Column(length = 100)
private String name;
This can map to a suitable string column length.
@Transient
This is different from Spring's @Transactional.
If you don't want a field to be persisted:
@Transient
private String temporaryMessage;
Hibernate will not map this field to a database column.
Association Multiplicities & Ownership
The Rule of Ownership:
The entity containing the Foreign Key in the database table is the Owner Side. The non-owning side must use mappedBy.
One-to-Many / Many-to-One (Bidirectional Best Practice)
// Non-owning side (Parent)
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// mappedBy points to the field name on Employee!
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
// Mandatory helper methods to keep bidirectional state in sync in memory!
public void addEmployee(Employee emp) {
employees.add(emp);
emp.setDepartment(this);
}
public void removeEmployee(Employee emp) {
employees.remove(emp);
emp.setDepartment(null);
}
}
// Owning side (Child, holds foreign key)
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne(fetch = FetchType.LAZY) // Always LAZY!
@JoinColumn(name = "department_id", nullable = false)
private Department department;
}
FetchType: LAZY vs EAGER
FetchType.LAZY(Recommended Default): Related entities are loaded on demand when the getter is accessed.FetchType.EAGER(Dangerous in Production): Automatically joins or executes extra queries immediately.
Crucial Rule: JPA defaults
@ManyToOneand@OneToOnetoFetchType.EAGER. Always explicitly override them toFetchType.LAZYto prevent catastrophic accidental table queries across your entire database graph!