📖 Business
Object-Relational Mapping Patterns
The object-relational impedance mismatch is the fundamental tension between how objects live in memory (graphs of interconnected instances with behavior) and how data lives in relational databases (flat tables with foreign keys and no behavior). Fowler catalogs a suite of patterns for bridging this gap, from simple row-to-object mappers to sophisticated solutions for identity tracking, change detection, and lazy loading. Understanding these patterns explains why ORMs like Entity Framework, Hibernate, and Prisma are designed the way they are — and why they sometimes behave in surprising ways. Each pattern solves a specific aspect of the mismatch, and production ORM usage requires understanding several of them working together.
2
Minutes
2
Concepts
+45
XP
1
How It Works
- Data Mapper — A layer of mappers that moves data between domain objects and the database while keeping them independent. The domain objects have no knowledge of the database; the mapper handles SQL, result sets, and column-to-property translation. This separates domain logic from persistence concerns completely.
Domain Object ←→ Data Mapper ←→ Database
Order (behavior + data) | OrderMapper (SQL + mapping) | orders table
Contrast with Active Record, where the domain object itself contains persistence methods (order.Save()), which couples domain logic to database structure.
- Identity Map — Ensures that each database row is represented by exactly one object in memory within a session. When you load Order #42 twice, the Identity Map returns the same object reference both times. This prevents inconsistencies (two objects with different state representing the same row) and improves performance by avoiding redundant queries. Every modern ORM implements this internally.
- Unit of Work — Tracks all objects loaded from and modified during a business transaction, then coordinates writing changes back to the database in a single batch. It determines which objects are new (INSERT), modified (UPDATE), or deleted (DELETE), and executes the SQL in the correct order to satisfy foreign key constraints. Entity Framework's
DbContextand Hibernate'sSessionare both Unit of Work implementations.
- Lazy Load — Defers loading of associated objects until they're actually accessed. An Order is loaded without its OrderLines; the lines are fetched only when
order.Linesis first referenced. Four implementations: lazy initialization, virtual proxy, value holder, and ghost. The danger: N+1 query problems where iterating over a collection triggers one query per item instead of a single batch query.
- Inheritance mapping strategies — Three approaches for mapping class hierarchies to tables:
- Single Table Inheritance — All classes in one table with a discriminator column. Simple queries, wasted space for null columns.
- Class Table Inheritance — One table per class with joins. Normalized, but queries require multiple joins.
- Concrete Table Inheritance — One table per concrete class with duplicated columns. No joins, but refactoring hierarchies is painful.