2 min read

Single Table Inheritance

Single Table Inheritance commonly known as STI is a design pattern used in object-oriented programming where a single database table is used to store multiple types of related objects that share common attributes.

The relational databases don't inherently support inheritance. However, STI is a technique used to represent a hierarchy of classes within a single table. You might ask, why use a single table? The answer lies in optimizing data retrieval. When data is spread across multiple tables, the process of joining records from different tables introduces overhead, making it less efficient to retrieve the desired data.

Let's understand with an example using books in Ruby on Rails.

Suppose we have two types of books Fiction and Non-Fiction. They share common attributes like title and price, but they also have some specific attributes like Fiction has genre and Non-Fiction has plot.

classDiagram class Book { + title: String + price: Float } class FictionBook { + genre: String } class NonFictionBook { + plot: String } Book <|-- fictionbook book <|-- nonfictionbook class booktable { + title: string price: float genre: plot: type: - this column stores the name for sti } < div>

Relational (Postgres)

  1. Create a table books
create_table :books do |t|
  t.string :title
  t.float :price
  t.string :type # This column stores the class name for STI
  t.string :genre # Specific attribute for Fiction books
  t.string :plot # Specific attribute for Non-Fiction books

  t.timestamps
end
  1. Create a model Book, it will be the base model for STI.
class Book < ApplicationRecord
end
  1. Create two subclasses as per example FictionBook and NonFictionBook
class FictionBook < Book
  validates :genre, presence: true
end

class NonFictionBook < Book
  validates :plot, presence: true
end
  1. Usage
fiction_book = FictionBook.create(title: "The Great Gatsby", price: 19.99, genre: "Classic")

non_fiction_book = NonFictionBook.create(title: "Sapiens", price: 24.99, plot: "Twist")

# Retrieve all books
all_books = Book.all

# Retrieve only fiction books
fiction_books = FictionBook.all

# Retrieve only non-fiction books
non_fiction_books = NonFictionBook.all

Non-Relational (MongoDb)

  1. Create a model Book, it will be the base model for STI.
class Book
  include Mongoid::Document
  field :title, type: String
  field :price, type: Float
  field :type, type: String # This field stores the class name for STI
end
  1. Create two subclasses as per example FictionBook and NonFictionBook
class FictionBook < Book
  field :genre, type: String
end

class NonFictionBook < Book
  field :plot, type: Integer
end
  1. Usage
fiction_book = FictionBook.create(title: "The Great Gatsby", price: 19.99, genre: "Classic")

non_fiction_book = NonFictionBook.create(title: "Sapiens", price: 24.99, plot: "Twist")

# Retrieve all books
all_books = Book.all

# Retrieve only fiction books
fiction_books = FictionBook.all

# Retrieve only non-fiction books
non_fiction_books = NonFictionBook.all

The NoSQL databases (MongoDb) doesn't strictly enforce a fixed schema. This flexibility allows you to store documents with different structures in the same collection, which is the underlying concept behind Single Table Inheritance.

Q: You might also ask is how the type field is populated?
A: When a table has a column named type, active record or mongoid will enable STI and add the class name to the type field, that is how the dots get connected.

PUN: Question is if there are no tables in MongoDb should we still call it STI?