DEV Community

Max
Max

Posted on

What is Object-Relational Mapping (ORM)?

Introduction

As programmers, we are always tasked with representing real life information as code in our programs. To navigate this challenge, we often use different frameworks or design choices to represent more abstract data. For example, a company may want to track information about their employees. We cannot represent everything about each employee but we can define specific, meaningful aspects of interest like an employee's name, address, salary, department, etc.

One method of framing data is Object-Relational Mapping (ORM). Put simply, ORM is a design pattern to follow when using an object-oriented language with a database. Following ORM conventions, you can keep your code better organized and DRY.

Organization

An application may have to access hundreds of thousands of instances within a database. While individual programmers could devise their own ways to communicate with the same database, follwing ORM conventions provides structure to this communication. Following convetions will make code easier for both you and others to understand.

To understand ORM, we should first review object-oriented languages. Object-oriented langauges can use classes to represent a general category of a thing, like cars, cats, or employees.

#code will be written in Ruby

class Employee

def initialize (name, address, salary)
    @name = name
    @address = address
    @salary = salary
    @@all << self
end

end
Enter fullscreen mode Exit fullscreen mode

Each class can have instances, unique indivduals within the class; a Toyota Corolla, a Tabby, or John Smith. Instances can have different vaues among each other, but are ultimately categorized under their shared class.

employee1 = Employee.new("John Smith" , "1684 Ravine Place Los Angeles, CA" , 30000)

puts employee1.class
# => Employee

employee2 = Employee.new("Jane Doe" , "1909 Tower Rd Derry, ME" , 43000)
puts employee2.class
# => Employee
Enter fullscreen mode Exit fullscreen mode

Knowing this, we can now organize our code by 'mapping' it onto a database following ORM conventions. We will create a table for every class in our program with a row to represent each instance of the class. For our Employee class, we will have an employees table and two rows for our two employees. Note that while our class name is singular, the name of our table is plural.

DRY Code

ORM also allows us to keep our code DRY. Going back to our previous example of an employee listing, our program will need to have some sort of connection to the database itself. For this example, SQLite3 will be used.

database_connection = SQLite3::Database.new('db/company.db')
Enter fullscreen mode Exit fullscreen mode

Now we will have to make a table to store our employee information. The table is created by passing SQL as an argument of of the execute method. We will ask our program to create a table named employees, if it does not already exist. We will also name each column, with their data type, for our employees table which will store the different values within our Employee class.

database_connection.execute("CREATE TABLE IF NOT EXISTS employees( id INTEGER PRIMARY KEY, name TEXT, address TEXT, salary INTEGER)")
Enter fullscreen mode Exit fullscreen mode

Our table has been created, so next we will use attributes of our instances to fill the rows and respective columns within our database table. Note that we are not directly saving the object itself into the table, but rather accessing attributes within the instances and placing them in the appropriate columns of our table. Any modifications to the record or instance itself will not automatically change the other.

database_connection.execute("INSERT INTO employees (name, address, salary, VALUES ("John Smith" , "1684 Ravine Place Los Angeles, CA" , 30000)")

database_connection.execute("INSERT INTO employees (name, address, salary, VALUES ("Jane Doe" , "1909 Tower Rd Derry, ME" , 43000)")
Enter fullscreen mode Exit fullscreen mode

However, as you can see, manually inserting each instance is repetitive and will grow exponentially more difficult and tedious to support the more employees we add. So instead, we can create an instance method for our class which we can reuse.

class Employee
~~~~
def save(database_connection)
    database_connection.execute("INSERT INTO employees (name, address, salary) VALUES (?, ?, ?)", self.name, self.address, self.age)
end
Enter fullscreen mode Exit fullscreen mode

Our #save method will take a database_connection as a parameter and insert our instance's values into the appropriate columns of the database we provide. Now we can simply call our save instance method on each employee instance and pass our database connection as its argument.

database_connection = SQLite3::Database.new('db/company.db')

Employee.all.each do |employee|
    employee.save(database_connection)
end
Enter fullscreen mode Exit fullscreen mode

Even as our application grows, we will no longer have to write separate lines of code to save our classes into our database.

Summary

While there are multiple libraries across different programming langauges that help map objects onto databases. These libraries are simply following specific conventions which help simplify your code as long as you abide by their schema. In short, Object-Relational Mapping (ORM) is a design pattern that can be used when working with an object-oriented language and a database. While there may be different approaches, ORM follows the conventions of mapping Classes to Tables and rows to instances.

Top comments (0)