Anyone with experience in developing web based applications or using a web framework has dealt with relational databases, such as MySQL or PostgreSQL. Dealing with SQL can be a bit scary at times, especially when dealing with data across different tables and applying various filters.
This is where ORM comes to the rescue.
What is ORM ?
ORM is the acronym for Object Relational Mapping.
Object-relational mapping (ORM, O/RM, and O/R mapping) in computer software is a programming technique for converting data between incompatible type systems in object-oriented programming languages. This creates, in effect, a “virtual object database” that can be used from within the programming language. There are both free and commercial packages available that perform object-relational mapping, although some programmers opt to create their own ORM tools. — Wikipedia
In other words, an ORM framework is written in an object oriented language (like Ruby, Python, PHP etc.) and wrapped around a relational database. The object classes are mapped to the data tables in the database and the object instances are mapped to rows in those tables.
Advantages
The following are advantages of ORM as a concept. It’s worth noting that not all ORM implementations support all the following advantages.
Database Independent
This is probably the biggest advantage of using ORM in an application. No need to write code specific to a particular database. Simply start a project using SQLite and, later on, change to MySQL or PostgreSQL. Changing a few lines of code in the database configuration adapter makes it work with another database.
Domain Model Pattern
When using ORM for building an application, it lets the business code access objects rather than the database structure itself, using the mapping between the business model and the database.
Reduce Code and Increase Efficiency
ORM provides an abstraction which allows the developers to focus on their business logic, rather than complex database querries, resulting in a huge reduction of code and increase in efficiency of the developer.
Rich Query Interface
ORM frameworks usually come with a rich query interface, freeing the developer from the complex semantics of SQL.
Relationships
ORM provides a hassle free management of database relationships. The related objects are automatically loaded when a query is translated to its corresponding SQL.
Concurrency
ORM supports concurrency, allowing multiple users to update the same set of data simultaneously.
Caching
Objects can be cached in the memory, reducing the load on the database.
Transactions
The changes to an object can be bound to a transaction, which can either be committed or rolled back. Multiple transactions can be active at a given point of time, but all these transactions are isolated from one another.
Disadvantages
Overhead
As ORM framework adds a layer of abstraction, speeding up the development and lowering the complexity, However, it also adds overhead to the application. Using ORM will consume more memory and increase CPU usage.
Learning Curve
Applying ORM to an application expects the developer to have experience working with a particular framework. There is a learning curve involved with most ORM frameworks.
Performance
Some actions, such as bulk insert, update, delete, etc. are slower when implemented using ORM. These are, usually, more efficiently handled using native SQL queries.
SQL Ignorance
While ORM makes life easier for developers, it often results in the developer not learning SQL and database internals unless the developers are keen in exploring the same.
ORM Frameworks in Ruby
There are a lot of ORM frameworks written in Ruby. Some of the more popular ones are briefly described here.
ActiveRecord
ActiveRecord is the ORM that comes out-of-the-box with Rails. ActiveRecord (or AR) attempts to follow the Active Record design pattern. It plays the role of Model in the MVC architecture employed by Rails.
ActiveRecord creates the abstraction from the database using business objects, thereby relieving the developers from dealing with the underlying database complexities. One of ActiveRecord’s main tenets is “convention over configuration”. It automatically detects the configuration if the correct conventions are followed for the table names, variable names etc. However, it is always possible to override the default configurations, if specified explicitly.
Some of the notable features of ActiveRecord are as follows:
- Data is represented as models.
- Associations can be implemented using these models.
- Inheritance can be achieved through related models.
- Validation of data before being saved into the database.
- Object oriented way of dealing with database operations.
Although ActiveRecord is included with Rails, it can be installed as an independent gem.
gem install activerecord
Here is a quick sample demonstrating the usage of ActiveRecord:
require 'rubygems'
require 'activerecord'
ActiveRecord::Base.establish_connection(
:adapter=> "mysql",
:host => "localhost",
:database=> "articles"
)
class Article < ActiveRecord::Base
end
#Creating a new article
Article.create(title:'ORM', author:'Manu')
#Fetching an article
article = Article.find(:first)
#Destroy an article
Article.find(:first).destroy
The major disadvantages of ActiveRecord is that it breaks the Single Responsibility Principle(SRP). Since the domain objects also handle persistance, an extra responsibility, the complexity of the objects increase.
DataMapper
DataMapper was developed to address perceived shortcomings in the ActiveRecord library and to create an ORM framework which is fast, thread-safe, and feature rich. It follows the DataMapper design pattern
While in Active Record, the Domain Model encapsulates both the business logic and the responsibility of database access, DataMapper is ignorant of the database, resulting in Plain old Ruby Objects(POROs) with business logic. DataMapper allows the database logic and object model to evolve independently, isolating them from each other, thereby following the Single Responsibility Principle(SRP).
Advantages of Datamapper are as follows:
- No need to write migrations
- Scoped relations or Collection chaining
- Lazy loading on certain attribute types, like text fields, which can make the query slow.
- Eager loading, avoiding the N+1 query problem
- Dirty tracking, only saves the fields that actually changed
- Respects the Single Responsibility Principle
Simple example of using DataMapper:
require 'rubygems'
require 'dm-core'
require 'dm-migrations'
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, 'mysql://localhost/test')
class Author
include DataMapper::Resource
property :id, Serial
property :name, String, :required => true
end
DataMapper.auto_migrate!
#Create a new record
Author.create(:name => 'Manu')
#Fetching a record using DM
Author.first(:name => 'Manu')
#Destroying a record
author = Author.get(7)
author.destroy
In the above example, DataMapper::Resource is added to the Author class in order to handle persistance.
Sequel
Sequel is designed to provide an easy, database independent API for interacting with SQL databases. It is also based on the ActiveRecord pattern. There are many similarities between ActiveRecord and Sequel.
Advantages are:
- Provides thread safety, connection pooling, and a concise DSL for constructing SQL queries and table schemas
- Includes a comprehensive ORM layer for mapping records to Ruby objects and handling associated records
- Supports advanced database features such as prepared statements, bound variables, stored procedures, savepoints, two-phase commit, transaction isolation, master/slave configurations, and database sharding
- Currently has adapters for ADO, Amalgalite, CUBRID, DataObjects, DB2, DBI, Firebird, IBM_DB, Informix, JDBC, MySQL, Mysql2, ODBC, OpenBase, Oracle, PostgreSQL, SQLAnywhere, SQLite3, Swift, and TinyTDS
Simple example using Sequel:
require "rubygems"
require "sequel"
DB = Sequel.sqlite
DB.create_table :author do
primary_key :id
String :name
end
authors = DB[:authors]
authors.insert(:name => 'Manu')
Conclusion
ORM aims to solve a problem and make the developer’s life easier, with many different options for acheiving the same goal. While ActiveRecord has dominiated the Ruby ORM landscape, DataMapper and Sequel (and others) are legitimate options. If you haven’t already, you should choose an ORM strategy that fits your use case.
Frequently Asked Questions (FAQs) about ORM in Ruby
What are the benefits of using ORM in Ruby?
ORM, or Object-Relational Mapping, is a programming technique that allows developers to interact with their database, like they would with SQL. In other words, you can manipulate your data as objects. One of the main benefits of using ORM in Ruby is that it abstracts and simplifies the process of database interaction. It allows developers to write less SQL code, making the code easier to write and maintain. ORM also provides a more intuitive way to handle database operations, which can lead to increased productivity and efficiency.
How does ORM work in Ruby on Rails?
In Ruby on Rails, ORM works by mapping database tables to classes and rows to objects. Each row in the table corresponds to an object in the application. This allows developers to interact with databases using Ruby code instead of SQL. The ORM system in Rails, known as ActiveRecord, provides methods for retrieving and manipulating data stored in a relational database.
What is ActiveRecord in Ruby on Rails?
ActiveRecord is the M in MVC – Model – which is the layer of the system responsible for representing business data and logic. It is the ORM system used in Ruby on Rails and provides an interface between the database and the Ruby application. ActiveRecord simplifies the code needed to create, read, update, and delete records in the database using Ruby.
How can I use ORM in my Ruby project?
To use ORM in your Ruby project, you first need to set up your database and create your tables. Then, you can create Ruby classes that correspond to these tables. These classes will inherit from the ActiveRecord::Base class, which provides a wide range of methods for interacting with the database.
What are some alternatives to ActiveRecord in Ruby?
While ActiveRecord is the most commonly used ORM system in Ruby, there are several alternatives available. These include Sequel, DataMapper, and ROM (Ruby Object Mapper). Each of these alternatives has its own strengths and weaknesses, and the best choice depends on the specific needs of your project.
Can I use ORM with non-relational databases in Ruby?
Yes, ORM can be used with non-relational databases in Ruby. However, the process is slightly different compared to using ORM with relational databases. There are specific ORM systems designed for non-relational databases, such as Mongoid for MongoDB.
How does ORM handle database relationships in Ruby?
ORM handles database relationships in Ruby by providing methods to define associations between models. For example, you can define one-to-many, many-to-many, and one-to-one relationships between your models using methods like has_many, belongs_to, and has_one.
What are some common challenges when using ORM in Ruby?
While ORM in Ruby offers many benefits, it can also present some challenges. These can include performance issues, as ORM can sometimes generate inefficient SQL queries. It can also be difficult to handle complex queries and database operations with ORM.
How can I optimize the performance of ORM in Ruby?
There are several strategies to optimize the performance of ORM in Ruby. These include eager loading, where you load all the data you need in one query instead of multiple queries. You can also use indexing to speed up query performance, and batch processing to reduce memory usage.
Can I use ORM in Ruby without Rails?
Yes, you can use ORM in Ruby without Rails. While Rails includes the ActiveRecord ORM system, there are several standalone ORM libraries available for Ruby, such as Sequel and ROM. These can be used in any Ruby project, not just Rails applications.
Tech Entrepreneur. Writes Elixir, Ruby, Go for a living. Into functional paradigms DDD/CQRS/EventSourcing architecture these days. @manusajith on the interwebs. Lazy blogger who scribbles at Til.Codes