Object-Oriented Databases — From Zero to Hero
A practical, visual guide that takes you from "what even is a database?" to confidently reasoning about object models, identity, navigation, inheritance, and how modern systems like Workday persist data as objects rather than rows. Built for analysts, BI developers, and anyone who keeps hearing the term but has never had it explained from the ground up.
What Is a Database, Really?
A database is just an organised collection of data that a computer can read from and write to efficiently, reliably, and concurrently. That's it. Everything else — tables, objects, JSON documents, graphs — is a way of organising that data.
The hard part isn't storing data. The hard part is asking: how do we shape the data so that the questions we want to ask are easy and fast to answer? Different "shapes" became different families of databases.
The Five Families of Databases
What Every Database Must Do (ACID)
No matter the shape, a "real" database must guarantee four things — known by the acronym ACID:
| Letter | Means | Example |
|---|---|---|
| Atomicity | A transaction either fully happens or doesn't happen at all. | Transferring £100 between accounts can't deduct from one without crediting the other. |
| Consistency | The database always moves from one valid state to another. | If a worker must have a position, the DB rejects a half-saved worker. |
| Isolation | Concurrent transactions don't see each other's half-done work. | Two payroll runs can't read the same row at the same time and double-pay someone. |
| Durability | Once a transaction commits, it survives crashes, power loss, etc. | Your hire decision doesn't vanish when the server reboots. |
Relational Databases & Their Limits
If you've used Excel, you've used something shaped like a relational database. A relational DB stores data in tables (rows + columns), connects tables via keys, and lets you query with SQL. It's the dominant shape since the 1970s — and for very good reason.
Quick Refresher
So Why Did Anyone Invent Anything Else?
The problem starts when your real-world thing doesn't fit cleanly in a flat table. Imagine a single Worker that has:
- multiple addresses (home, mailing, emergency)
- multiple phone numbers (mobile, home, work)
- multiple skills (each with a proficiency level)
- multiple dependents
- multiple compensation plans (salary + bonus + stock)
- a manager who has all of the above too
In code you'd model this as a single Worker object with arrays of child objects inside it. In a relational database, you have to shred that one rich object into 7+ tables and then re-assemble it with joins every time you load it. That gap between "how the program thinks" and "how the DB stores" is famous enough to have a name: the object-relational impedance mismatch.
The Impedance Mismatch — Pictured
Where Relational Still Wins
None of this means relational is bad — it remains the right choice for most reporting, analytics, and transactional workloads. Set-based math on rows-and-columns is genuinely powerful, and SQL is the most widely understood data language on earth. The point is just: it's not the only shape, and for richly-nested, deeply-related data, a different shape can be simpler.
Meet the Object
Before we can talk about an object-oriented database, we need to be crystal-clear on what an object is. If you've never written code, this is the chapter that demystifies it.
The Recipe Analogy
Imagine a recipe for chocolate cake. The recipe is not a cake — it's a description of how to make one. When you bake one, you get an actual cake. You can bake many cakes from the same recipe; each cake is its own thing (you can eat one without affecting another), but they all share the recipe's structure.
Recipe = Class
A template. Defines what attributes (ingredients) every cake has and what methods (bake, slice, frost) can be done to it.
Each Cake = Instance (Object)
A real, individual thing produced from the recipe. Each instance has its own specific values for the attributes.
Anatomy of an Object
The Two Things Inside Every Object
| Part | What it is | Worker example |
|---|---|---|
| Attributes (also: fields, properties) | The data the object remembers. | name, hireDate, salary, position |
| Methods (also: functions, behaviours) | The things the object can do. | promote(), terminate(), calculatePay() |
A Mental Model You Can Reuse Forever
Throughout the rest of this guide, whenever you see "object" think:
// An object is just:
{
attributes: { stuff it knows },
methods: { stuff it can do },
identity: "its unchanging name in the universe" // ← we'll come back to this
}
The Four Pillars of Object-Orientation
Object-orientation isn't just "data in a bundle". It rests on four specific ideas. Memorise these and you'll understand every OO database, framework, and Workday concept that follows.
Encapsulation
Bundle data and behaviour together. Hide internals behind a controlled interface.
Inheritance
Build new types by extending existing ones. Share what's common; override what differs.
Polymorphism
Same call, different behaviour depending on the type of the object.
Identity
Every object has a unique, unchanging identity, regardless of its values.
1 · Encapsulation — the Bundle
Encapsulation packs an object's data + the rules for changing that data into one capsule. Outside code can ask the capsule to do things, but can't reach in and break it.
// Without encapsulation — anyone can mess with salary:
worker.salary = -10000; // ← oops, allowed
// With encapsulation — the object guards itself:
worker.giveRaise(5000); // the method validates, audits, then applies
2 · Inheritance — the Family Tree
If two types of object share a lot in common, you can define the common parts once in a parent class, then create child classes that inherit and only add what's different.
3 · Polymorphism — Same Verb, Different Outcome
"Polymorphism" sounds intimidating but means something simple: the same method name can do different things depending on the object's actual type.
// All three are Workers. Same call, different logic:
employee.pay() → base salary / 12
contractor.pay() → hourlyRate × hoursWorked
intern.pay() → fixed stipend
// You can write code that just says "pay everyone" and
// the right calculation runs for each one. That's polymorphism.
for (w in allWorkers) w.pay();
4 · Identity — the Hidden Star of OODB
This is the one that matters most for databases. Every object has an identity — a permanent, unique label — that is separate from its data. Two objects can have identical attributes (same name, same hire date) and still be different objects, because they have different identities.
What Makes a Database "Object-Oriented"?
An Object-Oriented Database (OODB) stores data as objects — with their attributes, methods, relationships, inheritance, and identity intact — instead of flattening them into tables. In other words: it speaks the same language as your application code.
The Core Idea: Persistence Without Translation
The OODB Manifesto (1989) — Plain English Version
In 1989, a group of researchers (Atkinson, Bancilhon, DeWitt, Dittrich, Maier, Zdonik) published The Object-Oriented Database Manifesto, listing 13 features a "real" OODB must have. Here they are, paraphrased:
- Complex objects — supports nested types (lists, sets, tuples of objects).
- Object identity — every object has a unique OID independent of attribute values.
- Encapsulation — objects expose methods; internals are hidden.
- Types & classes — has a type system that the DB understands.
- Inheritance — classes can extend other classes.
- Overriding + late binding — polymorphism works.
- Computationally complete — you can express any computation, not just queries.
- Extensibility — users can define new types.
- Persistence — any object can be made to survive program shutdown.
- Secondary storage management — handles indexes, buffering, clustering automatically.
- Concurrency — multiple users at once (ACID).
- Recovery — survives crashes.
- Ad-hoc query facility — a query language (usually OQL).
OODB vs ORM — Don't Confuse Them
An ORM (Object-Relational Mapper) like Hibernate or Entity Framework lets your code look object-oriented while the data still lives in a relational database underneath. An OODB is the database itself being object-oriented.
| OODB | ORM over Relational | |
|---|---|---|
| Storage shape | Objects | Tables (rows + columns) |
| Translation layer | None | The ORM library |
| Inheritance, methods | Native | Simulated |
| Query language | OQL / path-based | SQL + ORM API |
| Performance for nested objects | Excellent | Many joins, often slow |
| Performance for set-based analytics | Variable | Excellent (SQL strength) |
| Examples | db4o, ObjectDB, Versant, Objectivity/DB | Hibernate + Postgres, EF Core + SQL Server |
Object Identity (OID) — the Secret Sauce
Of the four pillars, identity is the one that transforms an object system into an object database. Understanding OID is what separates surface-level knowledge from real comfort.
OID vs Primary Key
| Primary Key (Relational) | OID (Object-Oriented) | |
|---|---|---|
| What it is | A column value you chose | An internal identity assigned by the system |
| Visible to users? | Yes — you see it ("EMP-10042") | Usually hidden — system-generated |
| Can it change? | Yes (sometimes painfully) | Never. OID is immutable. |
| Derived from data? | Often (e.g., SSN) | Independent of data |
| Uniqueness scope | Within a table | Across the entire database |
| Used for joins? | Yes — JOIN ON id | No — direct references replace joins |
Equality vs Identity — the Most Useful Distinction in OO
This trips up almost everyone the first time. There are two different questions you can ask about two objects:
Equality (==)
"Do they have the same values?" Two coins of the same denomination are equal.
workerA.name == workerB.name
// might be true
Identity (===)
"Are they literally the same object?" Even two identical coins are different coins.
workerA === workerB
// only true if same OID
Why This Matters in a Real System
Imagine you change Jane Doe's last name (after marriage). In a relational world, you update the row — and any code that compared by name silently breaks. In an OODB world, her OID never changes, so every reference to her object still points to the right person.
Relationships — References & Navigation
In a relational database, two tables relate via a foreign key — a column whose values point to another table's primary key. You stitch them together at query time with JOIN. In an OODB, an object simply holds a reference to another object, the way a variable in code holds a pointer. There's no join — you just walk the reference.
The Big Picture
One-to-One, One-to-Many, Many-to-Many
All three exist in OODBs, expressed as either a single reference or a collection of references.
| Cardinality | How it's modelled | Worker example |
|---|---|---|
| One-to-One | A single reference | worker.primaryPosition → Position |
| One-to-Many | A collection (set/list) | worker.dependents → [Dependent, Dependent, ...] |
| Many-to-Many | Collections on both sides | worker.projects → [Project] AND project.workers → [Worker] |
Bidirectional References
OODBs can maintain references both ways automatically. If you set worker.manager = mark, the system can also add worker to mark.directReports. Relational systems have to be told the same thing in two places — easy to get wrong.
Walking the Object Graph
// Find the email of Jane's manager's manager:
jane.manager.manager.workEmail
// Same thing in SQL:
SELECT m2.email
FROM workers w
JOIN workers m1 ON m1.id = w.manager_id
JOIN workers m2 ON m2.id = m1.manager_id
WHERE w.id = '10042';
Inheritance & Polymorphism Inside the Database
In Module 4 we met inheritance as a code concept. The killer feature of an OODB is that the database itself understands class hierarchies. You can query a parent class and get back instances of every subclass — no UNION required.
A Hierarchy Stored in the Database
Why This Is Powerful
- One report writer query can produce headcount across every worker type without UNION ALL.
- New subclasses appear automatically in existing reports if they extend the parent.
- Subclass-specific fields are visible only on instances that have them — no NULL columns.
- Methods like
pay()resolve to the right calculation for each subclass automatically.
Filtering by Subclass
You can narrow the query to a specific subclass when you need to:
// All workers
SELECT w FROM Worker w
// Just contractors (subclass filter)
SELECT w FROM Worker w WHERE w IS Contractor
// Employees hired in 2025
SELECT w FROM Worker w
WHERE w IS Employee AND w.hireDate >= '2025-01-01'
Querying an OODB — OQL & Path Expressions
OQL (Object Query Language) is to OODBs what SQL is to relational databases. It was standardised by the ODMG group and deliberately keeps SQL-style syntax so that anyone who knows SQL can pick it up fast. The big additions are path expressions (dot-walks) and method calls.
OQL by Example
// 1. Simple: all workers' names
SELECT w.name FROM Worker w
// 2. Path expression: walk references inline
SELECT w.name, w.position.title, w.position.costCenter.name
FROM Worker w
// 3. Call a method on each object
SELECT w.name, w.tenureInYears()
FROM Worker w
WHERE w.tenureInYears() > 5
// 4. Iterate over a collection inside an object
SELECT d.name
FROM Worker w, w.dependents d
WHERE w.name = 'Jane Doe'
// 5. Nested results: structure-preserving
SELECT struct(
manager: w.manager.name,
team: (SELECT r.name FROM w.manager.directReports r)
)
FROM Worker w
WHERE w.name = 'Jane Doe'
The Two Big Differences from SQL
Path expressions
No JOIN — you just dot-walk. w.position.costCenter.name reads exactly like the object graph you drew.
Method calls
Queries can invoke object behaviour. w.tenureInYears() runs the method defined on the class, no view or function needed.
Side-by-side: SQL vs OQL
| Question | SQL | OQL |
|---|---|---|
| Worker name + manager email | SELECT w.name, m.email FROM workers w JOIN workers m ON m.id = w.manager_id |
SELECT w.name, w.manager.email FROM Worker w |
| Workers with > 3 dependents | Subquery counting child rows | SELECT w FROM Worker w WHERE count(w.dependents) > 3 |
| Workers earning more than their manager | Self-join + comparison | SELECT w FROM Worker w WHERE w.salary > w.manager.salary |
OODB in Practice — Where You'll See It Today
Pure object-oriented databases are a small but real niche today. More importantly, the ideas from OODB are now everywhere — in document stores, graph stores, ORMs, and enterprise platforms like Workday. Knowing OODB makes all of those easier to learn.
The Landscape Today
Why Workday Is the OODB You'll Most Likely Touch
If you're working in HR analytics, Workday is the largest object-oriented data model most people will ever interact with. Everything we've covered shows up directly:
| Concept (this guide) | What it's called in Workday |
|---|---|
| Class | Business Object (Worker, Position, Cost Center…) |
| Instance | An individual record (a specific worker, a specific position) |
| OID | WID (Workday ID) |
| Attribute | Field |
| Method | Calculated Field / Business Process action |
| Reference | Reference field — the basis of "dot-walking" |
| Path expression | Worker → Manager → Work Email Address in Report Writer |
| Inheritance | Worker is the parent of Employee and Contingent Worker |
| Polymorphic query | "All Workers" data source returning both subtypes |
When Should You Choose an OODB (or OO Model)?
Good fit
- Deeply nested, richly related domain (HR, finance, CAD, telecom)
- Application thinks in objects all day
- Heavy navigation, light set-math
- Many subtypes that share a parent
Bad fit
- Big aggregations across millions of rows (use a warehouse)
- Wide ecosystem of BI & SQL tools required
- Frequent ad-hoc reporting by non-developers
- Cross-domain analytics blending unrelated systems
One-Page OODB Cheat Sheet
Relational ↔ OODB translation
| Table | Class |
| Row | Instance (object) |
| Column | Attribute |
| Primary Key | OID |
| Foreign Key | Reference |
| JOIN | Path expression / navigation |
| SQL | OQL |
| Stored Procedure | Method on the class |
| View | (no equivalent — methods replace) |
| UNION across child tables | Polymorphic SELECT on parent |
Workday ↔ OODB translation
| Business Object | Class |
| Instance | Object instance |
| WID | OID |
| Field | Attribute |
| Reference Field | Reference |
| Dot-walk | Path expression |
| Calculated Field | Method |
| Data Source | Polymorphic query result |
| Worker (parent BO) | Parent class |
| Employee / Contingent | Subclasses |
The four pillars (memorise)
- Encapsulation — data + behaviour in one bundle
- Inheritance — children extend parents
- Polymorphism — same call, different result by type
- Identity — every object has a permanent OID
When to pick what
| Rich nested data | OODB / Document |
| BI & ad-hoc analytics | Relational warehouse |
| Relationship traversal | Graph DB |
| Cache / sessions | Key-Value |
| Enterprise HR/Fin app | OO model (Workday) |
Glossary
| Object | A bundle of attributes (data) and methods (behaviour) with a unique identity. |
| Class | The template/blueprint from which objects are created. |
| Instance | A concrete object created from a class. |
| Attribute | A piece of data on an object (a.k.a. property, field). |
| Method | A behaviour the object can perform. |
| Encapsulation | Bundling data + behaviour and hiding internals behind an interface. |
| Inheritance | Building new classes by extending existing ones. |
| Polymorphism | Same operation behaves differently depending on the object type. |
| Identity / OID | A permanent, system-generated unique label for each object. |
| Reference | A pointer from one object to another (replaces foreign keys). |
| Path expression | Walking a chain of references inside a query (worker.manager.email). |
| OQL | Object Query Language — the SQL-like language for OODBs. |
| ORM | Object-Relational Mapper — a library that makes a relational DB look object-oriented to code. |
| Impedance mismatch | The friction between object-shaped code and table-shaped storage. |
| Persistence | The ability of objects to survive program shutdown and live in the DB. |
| Polymorphic query | A query on a parent class that returns instances of all child classes. |