Skip to main content

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 @ManyToOne and @OneToOne to FetchType.EAGER. Always explicitly override them to FetchType.LAZY to prevent catastrophic accidental table queries across your entire database graph!