CSC/ECE 517 Fall 2010/ch2 2a CB
A History of Persistence
One problem that has plagued software developer's for as long as persistent storage has existed is utilizing that storage to persist objects in code so that if the objects in memory are lost, for a system restart for instance, then nothing is lost from the user's perspective. Solutions to this problem have ranged from writing text out to a file on disk to serializing objects and writing them to disk.
What is Object Relational Mapping?
Object-relational mapping (or ORM) is the representing of objects as abstract groups of fields that are defined to have certain types with the intent of easily translating these objects between systems of different types. Because of the restriction of some systems, such as databases, the fields within these objects must be scalar and must be primitive types in most languages. Take, for example, an array inside of an object that could contain a potentially unbounded number of objects. To represent this inside of a database one cannot use an unbounded number of columns, but instead must translate this value into a foreign key which maps to another database table which contains rows corresponding to each of the array objects. Likewise, pointers to objects inside of an object cannot easily be represented inside of a database since the pointer will not be valid when the data is read under a different system or state. Therefore, variables like pointers must also be translated into foreign keys which map to rows in another table representing the data contained in that object. At first glance this may seem more trouble than it is worth. However the result is a separation of concern between how the data is used inside of a program and how it is stored in a system.
The ActiveRecord Design Pattern
ActiveRecord, first put forth by Martin Fowler in his book Patterns of Enterprise Application Architecture, utilizes the concept of object-relational mapping to persist objects of a program in databases. Because modern databases are designed to store large sets of data and be able to access them efficiently they offer the perfect storage medium for program objects. Also, because the code and the database used for persistence are separate, it allows the developer to switch the database used, whether it is from a development database to a production database or from an oracle database to a Microsoft database. The goal of the pattern is for the developer to use objects and not have to worry about what SQL code needs to be written to perform the basic create, read, update, or delete (C.R.U.D operations) on those objects. However, as you will see in the following language extensions, the ActiveRecord design pattern has varying levels of involvement on behalf of the programmer.
ActiveRecord in Ruby on Rails
The ActiveRecord package for Ruby (which comes in the default installation of Ruby on Rails), is by far the most popular ActiveRecord pattern implementation available for Ruby. This implementation of the pattern adds both inheritance and object associations to the implementation two important concepts that would otherwise have to be directly managed by the application code.
The following is an example of using ActiveRecord in Ruby (adapted from http://snippets.dzone.com/posts/show/3097):
First a connection to the database must be specified:
ActiveRecord::Base.establish_connection( :adapter => "sqlite3", :dbfile => ":memory:" )
Then the structure of the tables must be established:
ActiveRecord::Schema.define do create_table :albums do |table| table.column :title, :string table.column :performer, :string end create_table :tracks do |table| table.column :album_id, :integer table.column :track_number, :integer table.column :title, :string end end
Associations can then be defined for the classes because of the extra functionality added to ActiveRecord
class Album < ActiveRecord::Base has_many :tracks end class Track < ActiveRecord::Base belongs_to :album end
After this the code can then manipulate the objects utilizing the ActiveRecord functionality:
album = Album.create(:title => 'Black and Blue', :performer => 'The Rolling Stones') album.tracks.create(:track_number => 1, :title => 'Hot Stuff') album.tracks.create(:track_number => 2, :title => 'Hand Of Fate') album.tracks.create(:track_number => 3, :title => 'Cherry Oh Baby ') album.tracks.create(:track_number => 4, :title => 'Memory Motel ') album.tracks.create(:track_number => 5, :title => 'Hey Negrita') album.tracks.create(:track_number => 6, :title => 'Fool To Cry') album.tracks.create(:track_number => 7, :title => 'Crazy Mama') puts Album.find(1).tracks.length puts Album.find_by_title('Black and Blue').title puts Track.find_by_title('Fool To Cry').album_id
Statement about the ease of use and setup in ruby.*****
Java's Hibernate
Java is one of the most popular programming languages in existence today, and it therefore follows that there will be many different options when it comes to ActiveRecord extensions to the language. One of the more popular extensions in use is Hibernate, an ORM solution for Java. Compared the other languages presented here, Java is the most involved, meaning that it is strictly typed and in general written at a lower level than the others. Keeping this in mind, we will see how much more is needed to setup a hibernate environment and how much more involved the actual ORM code is.
One of the first things that Hibernate uses for any interactions is a Session Factory class which is designed as a class factory to return the current Hibernate session object and ensure that only one instance of the session be issued per thread. Without listing the actual code of the Session Factory the code basically allows for getting the session object, opening the session for use, and closing the session after use.
Unlike some of the other languages the tables inside the database must be created by hand outside of Hibernate. This can be done using a specific SQL tool or using Java code that executes SQL code directly. This is a prime example of the additional knowledge a developer must have to correctly create a hibernate project. A developer must know that each object type must have its own table and that the fields within the table must correspond to the types of the object’s member variables.
Based on the tables created, classes must then be created with the member variables and setters/getters associated with the field types created in the database. For example, if the database table contains a field 'name' which is of type varchar and limited to 255 characters, the setter/getter must be of type String and the coder (if they wish) may limit the entry in the setter to 255 characters.
The next step is to create a Hibernate configuration file which essentially defines the database connection including the url, username, database type, password, etc. After this, the developer must explicitly tell Hibernate the relationships between the member variables of the class and the fields of the database with either an xml file or using Java annotations inside of the class. This is a prime example of the extra effort that has to be taken by the developer, but which also gives the developer great power to name the fields and member variables different things, for example.
Finally code can be written to create objects and write them to the database using the session object. The following snippet is an example of using the session object to write an object to the database.
Person p = new Person(); p.setFirstName(“John”); p.setLastName(“Doe”); Transaction tx = null; Session s1 = SessionFactory.getInstance().getCurrentSession(); Try { Tx = session.beginTransaction(); Session.save(p); Tx.commit(); } Catch(RuntimeException e) { Tx.rollback(); }
Comment on the ease of use compared to rails************
Python's Django
Python, presents an interesting case because it is an extremely similar language to Ruby in syntax and somewhat in philosophy. The most popular ORM extension in use for Python is part of an MVC web frame work built for Python called Django (could be considered the equivalent to Rails for Ruby). Much like Rails includes an ActiveRecord extension, Django includes built-in ORM support.
Following the Django programming syntax, each class in Python must subclass the ‘django.db.models.Model’ class. Additionally each variable or attribute of the class will represent a field inside of the database. Because Python is loosely typed, Django provides types inside of the ‘models’ extension for specifying the type that needs to be created inside of the database. The following code is an example of such a class:
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30)
This code would then create a table of people objects where the first and last names would be columns in the table with some kind of varchar type with a length limit of 30. In addition, Django creates a field named ‘id’ which acts as the primary key for the table and is auto-incremented with each row. It is easy to override using this column as the primary key, but careful consideration should be taken before doing so.
Creating a ‘Person’ object can then be done using the following code:
p = Person(first_name="John", last_name="Doe") p.save() p.first_name //will return the first name field
Like ActiveRecord in Ruby, Django also provides ways to create relationships among the models. Creating these relationships in Django isn’t quite as hidden as in Ruby as you actually have to specify that one model is a foreign key of another model for example (somewhat exposing the database nature of the persistence).
To then query the database to find the objects you can use the ‘objects’ variable on the model that was originally created which contains all the objects created in the particular table. The following example finds all the ‘Person’ objects with a first_name of John.
People = Person.objects.filter(first_name=”John”)
Comments about the ease of use compared to rails here.**********
Perl's DBlx::Class
Perl is yet another high-level dynamically typed language with ORM language extensions available and should make an interesting comparison to ActiveRecord in rails and the other languages presented. The most documented and most popular extension used for Perl is called DBIx::Class. The goal of this extension, like the other’s that we’ve discussed, is to abstract the developer away from writing sql code and allow them to synchronize objects with a database to ease the process of object persistence.
The first step in using the DBIx::Class extension is creating a connection with the schema object you intend to use. In Perl the schema object is a fairly simple class that the developer must write which contains information used to connect to the database and methods to retrieve result sets from the database.
Conclusion
The good news for developers is that there doesn’t exist a popular programming language that does not also have numerous ORM extensions and tools to accompany it. For the most part, the complexity of these tools and their integration into the language depend on the style of the language itself. Hibernate, for example, is more complicated to set up, but rewards the skilled java programmer with more control and a more exposed approach to ORM. ActiveRecord for Rails, on the other hand, is intuitive and doesn’t require extensive effort to get something working, but does so at the sacrifice of sometimes limited control. Ultimately, it can’t be said that any of these extensions are any ‘better’ than any other, but that it really depends on the developer’s style and requirements. Regardless of the developer’s final decision however, there is no doubt that using ORM and ActiveRecord for persistence of objects in an application is an easy solution to the otherwise difficult problem of object persistence.
Links