1. Introduction
In this tutorial, we’ll explore what is an Object-Relational Mapping tool, what they do, how they work, and why, in most cases, we should use one. Currently, by far, the most used programming paradigm is object-oriented programming (OOP). It allows us, at the design level, to represent real-world entities as objects and their relationships. Objects contain data (attributes) and code (methods). It’s really great once we get the hang of it.
It brings a lot of nice features to help us code. For instance, we can create classes of objects that share their base definition from a more generic class. Thus we can change only the different methods and attributes, writing much less code. However, one issue is how to persist the objects’ data using non-object-oriented databases, like relational databases, for instance.
In practice, we would create a set of tables that mimic the object attributes and the relations between classes, a process called object-relational mapping. That’s easier said than done.
As the system evolves, changes to classes and their relationships must be followed by adjustments to the mapping. In consequence, we must review the database structure as well as all the related data access codes. Medium to large-sized systems may contain hundreds or thousands of classes. That is when Object-Relational Mapping tools came into place.
2. What Is an ORM Tool?
Designing and deploying information systems nowadays can be truly a hard task. In most cases, we must know multiple programming languages and frameworks. Every application layer has its own tech stack. And, while some evolve very fast, others use decades-old technology. For instance, in most corporations’ old-school relational databases are the data persistence standard still, and rightly so.
Well, relational databases are solid, reliable, and fast enough for most commercial applications. Hence, their use is quite widespread, and they have strong support communities. In this architecture, logical entities are listed in tables that hold each instance’s attributes on their columns. Also, the tables can relate to others to model the logical relation between the entities. They also use a standardized way to add, access, update and delete data, the Structured Query Language (SQL).
An Object-Relational Mapping tool, ORM, is a framework that can help and simplify the translation between the two paradigms: objects and relational database tables. It can use class definitions (models) to create, maintain and provide full access to objects’ data and their database persistence. The figure shows an extract of how an ORM would map a set of classes into relational tables:
3. Advantages
The first advantage is that we can use the backend’s own language to code all data access. For instance, to gather a list of persons we would need to write a function similar to this:
algorithm returnObjectsFromDatabaseUsingSQL(filterKey, filterValue):
// INPUT
// filterKey = the attribute name to filter by
// filterValue = the value of the attribute to filter by
// OUTPUT
// Returns a list of person objects that match the filter criteria.
sql <- "SELECT fullname, occupation FROM persons WHERE " + filterKey + " = :value"
data <- query(sql, filterValue)
personList <- new List()
while row <- data.next():
person <- new Person()
person.fullname <- row.get('fullname')
person.occupation <- row.get('occupation')
personList.add(person)
return personList
Now, the same example, but using an ORM tool:
algorithm returnObjectsFromDatabaseUsingORM(filterKey, filterValue):
// INPUT
// filterKey = the attribute name to filter by
// filterValue = the value of the attribute to filter by
// OUTPUT
// Returns a list of person objects that match the filter criteria using ORM.
personList <- Person.query(filterKey, "equals", filterValue)
return personList
The second excerpt is much nicer to code and read. It makes reference to a class (namely Person) that defines the attributes and business-related methods. Classes that deal with object-relational mappings are called Models. Those classes use as, a template, a class from the ORM’s library of model classes. That way, it inherits all methods needed to create, retrieve, update, and delete from the database.
The models are weakly bounded with the rest of the application. Meaning that changing them shall not do much harm to other components. Moreover, the table relationships are represented in the model classes. That way, foreign table keys will lead to class attributes pointing to the referenced objects.
Also, the ORM can keep track of any changes in our own model classes, synchronizing them to the actual database structure, a process called Migration. Besides, it abstracts the database backend, so we can easily switch databases or write applications compliant with a large number of database vendors and versions.
Finally, ORMs have utility functions to help backup, restore, or recreate the database. This is great to help writing testing functions, among other use cases.
4. Disadvantages
On the other hand, to properly use ORM, we must go through its steep learning curve. Even though it uses the same language our code is based on, ORM has its own usage semantics and syntax.
The ORM ultimately takes care to translate our code into SQL. However, even though most of the time it does a nice job, it can’t compete, on complex queries, against well-written SQL.
Also, even if tries its best to create the best possible SQL clauses, it can go wrong. Especially because sometimes it is hard to gather what the ORM is doing behind the scenes.
5. Should We Use ORM?
In short, yes, we should use an ORM whenever possible. The advantages, for most applications, exceed by far the disadvantages. Besides, we can still use, if needed, raw SQL clauses, though it is highly unadvised. Any major framework has at least one ORM library available, for instance:
- C# and .Net: NHibernate or Entity Framework
- GoLang: GORM
- Java: Hibernate
- NodeJS: TypeORM, Sequelize or KnexJS
- PHP: Propel or Doctrine (Symfony‘s ORM)
- Python: the Django (web framework with a great ORM) or SQLAlchemy
6. How Should I Use ORM?
First, we must always read the documentation and follow it to the core. It takes some time to get acquainted with an ORM, and to avoid many of its pitfalls, we must get familiar with its best practices. ORMs use to have their own measures to avoid issues like SQL injection attacks. However, bad coding can void those measures.
Although the ORM abstracts the database, some of its limitations might show. For instance, bulk inserts and updates. When we are dealing with SQL directly, to bulk operations, performance is an issue, so we must use the appropriate technique. The same goes with ORM, when dealing with high transaction volumes we must look for the best data-access methods.
Also, we must not skip the troubleshooting, debugging, and profiling documentation sections, there are invaluable tools to analyze the application’s behavior and performance.
7. Conclusion
Real-world applications, with hundreds or thousands of classes, can be a nightmare. Writing SQL code to create, read, update, and delete each class can be extenuating. And, if we change a class, we must review all related SQL code.
That is why, for large applications, the use of an ORM tool is a must. In this tutorial, we discussed how an ORM works and how we can benefit from it.