Introduction
Primary key design is one of the most important decisions in database design.
A poorly chosen key can cause:
❌ Complex joins
❌ Difficult JPA mappings
❌ Large indexes
❌ Slow queries
❌ Complicated APIs
❌ Difficult migrations
Many legacy Oracle systems use composite keys extensively, while modern applications increasingly use surrogate keys or UUIDs.
This article covers:
✅ Primary key strategies
✅ Composite keys
✅ EmbeddedId
✅ IdClass
✅ Natural keys
✅ Surrogate keys
✅ Shared primary keys
✅ UUID keys
✅ JPA best practices
What is a Primary Key?
A primary key uniquely identifies a row.
EMPLOYEE
---------
EMP_ID
NAME
EMAIL
PRIMARY KEY (EMP_ID)
Properties:
- Unique
- Not Null
- Stable
- Immutable
Primary Key Strategies
| Strategy | Example | Recommendation |
|---|---|---|
| Sequence | EMP_ID | Excellent |
| Identity | IDENTITY | Good |
| UUID | UUID | Good |
| Composite | ORDER_ID + ITEM_ID | Situational |
| Natural Key | Avoid |
Surrogate Keys
Generated identifiers.
EMP_ID NUMBER(19)
Advantages:
✅ Small indexes
✅ Fast joins
✅ Easy JPA mappings
✅ Simple APIs
Natural Keys
Example:
EMAIL VARCHAR2(200)
Problems:
- Email changes.
- Business rules change.
- Larger indexes.
Avoid:
PRIMARY KEY (EMAIL)
Sequence Keys
Oracle recommendation:
CREATE SEQUENCE EMP_SEQ;
JPA:
@Id
@GeneratedValue(
strategy =
GenerationType.SEQUENCE,
generator = "emp_seq")
UUID Keys
Java:
UUID id = UUID.randomUUID();
Database:
EMP_ID RAW(16)
Advantages:
- Globally unique.
- Distributed systems.
- Microservices.
Disadvantages:
- Larger indexes.
- Random insertion.
When to Use UUID
| Scenario | Recommendation |
|---|---|
| Monolith | Sequence |
| Banking | Sequence |
| ERP | Sequence |
| Microservices | UUID |
| Event-driven systems | UUID |
Composite Keys
A composite key consists of multiple columns.
Example:
ORDER_ITEM
----------
ORDER_ID
PRODUCT_ID
QUANTITY
Primary key:
PRIMARY KEY
(
ORDER_ID,
PRODUCT_ID
)
When Composite Keys Are Appropriate
Good examples:
- Order Items
- Student Courses
- User Roles
- Product Categories
When to Avoid Composite Keys
Avoid:
❌ Large tables
❌ Frequently joined entities
❌ REST APIs
❌ Microservices
JPA Composite Key Approaches
JPA supports:
- EmbeddedId
- IdClass
EmbeddedId
Recommended approach.
Step 1: Create Key Class
@Embeddable
public class OrderItemId
implements Serializable {
private Long orderId;
private Long productId;
}
Requirements
The key class must:
✅ Implement Serializable
✅ Have default constructor
✅ Override equals()
✅ Override hashCode()
Step 2: Entity
@Entity
public class OrderItem {
@EmbeddedId
private OrderItemId id;
private Integer quantity;
}
Database
ORDER_ITEM
----------
ORDER_ID
PRODUCT_ID
QUANTITY
Accessing Fields
orderItem.getId()
.getOrderId();
IdClass
Alternative approach.
Key Class
public class OrderItemId
implements Serializable {
private Long orderId;
private Long productId;
}
Entity
@Entity
@IdClass(OrderItemId.class)
public class OrderItem {
@Id
private Long orderId;
@Id
private Long productId;
}
EmbeddedId vs IdClass
| Feature | EmbeddedId | IdClass |
|---|---|---|
| Encapsulation | Yes | No |
| Cleaner code | Yes | Moderate |
| Reusable | Yes | Limited |
| Recommended | Yes | Less common |
Shared Primary Keys
Example:
Employee
|
1
|
1
Passport
Database
PASSPORT
---------
EMP_ID PK
EMP_ID FK
Entity
@Entity
public class Passport {
@Id
private Long id;
@MapsId
@OneToOne
private Employee employee;
}
@MapsId
Allows:
- Shared primary key.
- Parent-child identity.
Composite Key with Relationships
Example:
@Embeddable
public class EnrollmentId {
private Long studentId;
private Long courseId;
}
Entity:
@Entity
public class Enrollment {
@EmbeddedId
private EnrollmentId id;
@ManyToOne
@MapsId("studentId")
private Student student;
@ManyToOne
@MapsId("courseId")
private Course course;
}
Junction Entity
Instead of:
@ManyToMany
private Set<Role> roles;
Prefer:
UserRole
Example
@Entity
public class UserRole {
@EmbeddedId
private UserRoleId id;
}
Advantages:
- Additional columns.
- Audit fields.
- Better control.
Equals and HashCode
Mandatory for key classes.
Example:
@Override
public boolean equals(Object o) {
}
@Override
public int hashCode() {
}
Why Important?
Hibernate uses:
- HashSet
- HashMap
- Persistence context
Incorrect implementations cause:
- Duplicate entities.
- Cache issues.
- Unexpected behavior.
UUID as Primary Key
@Id
private UUID id;
Oracle:
EMP_ID RAW(16)
Hibernate:
@GeneratedValue
private UUID id;
Advantages
✅ Global uniqueness.
✅ No sequence calls.
✅ Suitable for distributed systems.
Disadvantages
❌ Larger indexes.
❌ More storage.
❌ Random insertion.
REST API Impact
Composite key:
/orders/10/products/20
Surrogate key:
/order-items/100
Simpler APIs.
Caching Considerations
Composite keys:
(10,20)
Surrogate keys:
100
Smaller cache entries.
Migration Challenges
Changing primary keys later is difficult.
Impacts:
- Foreign keys
- APIs
- JPA mappings
- Caches
Choose carefully.
Recommended Strategy
| Table Type | Recommendation |
|---|---|
| Master tables | Sequence |
| Reference data | Sequence |
| Large tables | Sequence |
| Microservices | UUID |
| Junction tables | Composite |
| Relationship tables | Composite |
Example Enterprise Model
EMPLOYEE
---------
EMP_ID
ORDER
------
ORDER_ID
ORDER_ITEM
----------
ORDER_ID
PRODUCT_ID
Common Mistakes
Business Key as Primary Key
EMAIL
Bad.
Large Composite Keys
COUNTRY
STATE
CITY
ZIP
Bad.
Missing equals/hashCode
Causes bugs.
UUID Stored as VARCHAR(100)
Wasteful.
Best Practices
✅ Prefer surrogate keys.
✅ Use sequences in Oracle.
✅ Use EmbeddedId.
✅ Keep keys immutable.
✅ Use UUID for distributed systems.
✅ Use composite keys only for relationship tables.
✅ Implement equals and hashCode.
✅ Use RAW(16) for UUID storage.
Interview Questions
Difference between EmbeddedId and IdClass?
EmbeddedId encapsulates key fields.
IdClass duplicates fields.
When should composite keys be used?
Junction tables.
Why avoid natural keys?
Business values change.
Why use RAW(16) for UUID?
Smaller indexes.
What does @MapsId do?
Maps relationship to primary key.
Summary
This article covered:
✅ Surrogate keys
✅ Natural keys
✅ Composite keys
✅ EmbeddedId
✅ IdClass
✅ UUID keys
✅ Shared keys
✅ MapsId
✅ Key design
Next Article
Part 8 – Lazy Loading, N+1 Queries and Hibernate Performance Tuning
Topics:
- Lazy loading
- Eager loading
- N+1 problem
- Join fetch
- Entity graphs
- DTO projections
- Batch fetching
- Second-level cache
- Query optimization
- Hibernate statistics