© John Kouraklis 2019
John KouraklisIntroducing Delphi ORMhttps://doi.org/10.1007/978-1-4842-5013-6_1

1. In the Land of ORM

John Kouraklis1 
(1)
London, UK
 

Object relational mapping (ORM) represents a set of techniques in computer programming, which attempt to make incompatible systems cooperate, communicate, and exchange information. At the same time, they attempt to make the life of developers easier.

The systems in discussion are database systems and systems that evolve from the dominant paradigm of object-oriented programming (OOP). Databases are designed to store and provide access to various data types in a persistent way. Data is stored in databases and outlives the execution of the applications that use it. On the other hand, developers that follow OOP principles think in a very different way when it comes to the representation of data in their applications.

The design of database systems has advanced considerably over the decades, and today databases represent both reliable and stable systems which are found in almost every type of applications. The widespread use of Internet, the domination of social media, and the ability to generate high volume of data in real time and in high velocity (big data) have led to a wide range of database systems with various degrees of sophistication and implementation complexity. As a result, database administrators enjoy a wealth of options – databases that follow the traditional relationship-based design (RDBMS/SQL) to systems without inherent structure like the NoSQL database.

The design of relational database systems has served the information world extremely well since their invention. Looking at the heart of relational database management systems (RDBMS), one can observe though that the fundamental elements have not changed drastically in terms of the way data is stored and considered. One of the most persistent designs of databases is the type of data those systems are capable of storing. Put simply, the vast majority of databases can manage data of simple types (scalar). Data types like integers, chars, strings, bytes, and dates are in their natural environment with databases.

On the other hand, developers in OOP see the world with the eyes of nonscalar concepts as they are represented by objects. Objects have attributes and properties (object-based design) but also provide mechanisms to manipulate the behavior of objects via the concepts of polymorphism and inheritance (object-oriented design). Polymorphism allows developers to change the behavior of an object or a function depending on the associated elements, and inheritance creates a form of hierarchy between objects allowing common attributes and behavior. Objects, although a common feature in modern programming languages, are quite complex in terms of implementation and are not compatible with the way databases manage data. Additionally, more complex structures like lists, maps, and dictionaries found in programming languages and used every day by developers cannot be mapped easily to the storage mechanism of databases (Mueller, 2013).

Communication Between Incompatible Systems

Consider the example of a blog site. Users register and create posts. Posts can have a number of tags or categories. From the database designer’s point of view, users can be represented by the table Users, posts by the table Posts, and tags and categories by the tables Tags and Categories, respectively. Concepts like “a user owns several posts” and “posts have many categories” are implemented by different types of relationships (one-to-one, one-to-many, many-to-many). Figure 1-1 shows a simplified database model for the blog site.
../images/481234_1_En_1_Chapter/481234_1_En_1_Fig1_HTML.jpg
Figure 1-1

Simplified database model for a blog site

This representation is only a conceptual model. At database level, database designers and administrators see the preceding model in terms of records (rows in a table) and columns (fields of a table). A more accurate representation would be Figure 1-2 where real content is being stored in a database and presented in the form of records (rows). The relationships that Figure 1-1 indicates are not visible at this level as they are forced by the database engine. Moreover, one can observe the simple data types the records hold. Although this is a simplified example, the values and, therefore, their data types are representative of more complex situations.
../images/481234_1_En_1_Chapter/481234_1_En_1_Fig2_HTML.jpg
Figure 1-2

Simplified database design and content for a blog site

This situation looks very different for a code developer who follows OOP principles. The problem of keeping track of the users and their posts becomes a problem of defining objects and the tags and categories of the posts becomes a problem of choosing the right data structure of the programming language that can hold this information and link it back to the posts. In Delphi, and in most of the developed languages, Users and Posts would be pure objects (TUser and TPost, respectively), whereas Tags and Categories can be a list of strings represented by properties in the TPost object . The following code demonstrates this approach:
type
  TUser = class
  private
    fName: string;
  public
    property Name: string read fName write fName;
  end;
  TPost = class
  private
    FCategories: TList<string>;
    FContent: string;
    FTags: TList<string>;
    FUser: TUser;
  public
    property Categories: TList<string> read fCategories write fCategories;
    property Content: string read fContent write fContent;
    property Tags: TList<string> read fTags write fTags;
    property User: TUser read fUser write fUser;
  end;

This snippet suggests that if a developer wants to store data in a database and, consequently, retrieve it, they must figure out how a TList<string> or a TUser data type in TPost translates efficiently and reliably to database tables. Moreover, the situation can increase in complexity if one considers that the tags and categories can be objects themselves (TTag and TCategory), which makes the TList<string> a TObjectList<TTag> and TObjectList<TCategory>, respectively.

The preceding challenge of making two incompatible systems to seamlessly communicate is amplified when one realizes the wide range of database solutions that exist in the market both in the proprietary and open source domains. The vast majority of RDBMS databases today use SQL as the standard language to manipulate data. A closer look indicates that although SQL is a standardized language, different database vendors introduce their own SQL variations, constraints, and extensions apart from the basic set of commands. This, in turn, means that the developer who wants to manage TPost(s) in a database needs to be aware of the underlying database engine and perhaps make adjustments when the database is replaced by a different one.

Manipulating objects at database level introduces two more problems that need to be resolved if OOP and databases are expected to work reliably and correctly. One of the fundamental features of databases is their ability to facilitate the access of data by multiple users and at the same time (concurrency). For the OOP developer, the access of data by multiple users poses the need of synchronizing the changes being made at database level with any instances of the objects at programming level and vice versa. In the preceding example, an instance of TPost will have a set of tags in the Tags property. If a user adds a new tag and assigns it to the specific TPost instance, the communication between the OOP version of the model and the database should update the instance of TPost and perhaps the Tags property. Similarly, if the code that uses the TPost instance allows the user to add a new tag and assign it to the property Tags of this particular instance, the database tables should be updated accordingly.

Concurrency, on the other hand, opens the possibility of corrupted or partially saved data to the database. What may happen, especially in environments where multiple people access the same database assets at the same time, is that the users may edit the same piece of information for the same record at the same time. This situation, in conjunction with the previous one, can lead to challenges for the OOP developer.

ORM Frameworks

The previous discussion makes clear the fact that working out the mentioned issues requires a substantial effort at the coding side. People very often take the task of writing their own libraries to manage this situation but soon realize that the task is not a trivial one as the details can be quite complex and time-consuming. The solution to this problem is to use dedicated libraries known as object relational mapping (ORM) frameworks.

ORM frameworks provide a middle layer between object-oriented code and database operations (Hibernate, n.d.). They take the task of adapting typical objects to forms that can be understood by database engines, and they perform operations at both sides of the equation. These tools create a set of virtual object database that map classic database structures, can be understood by developers, and behave as expected in an OOP environment. They also expose a form of API that allows typical operations in a database to be performed at coding level and, in terms of database connectivity, they do a great job to abstract the underlying database engine.

In a typical three-tier application where there is a separation between the presentation, the business, and the data layers, ORM frameworks lie inside the data layer (Figure 1-3) and, contrary to the common presentation in books and articles, ORM frameworks can handle multiple database sources.
../images/481234_1_En_1_Chapter/481234_1_En_1_Fig3_HTML.jpg
Figure 1-3

The role of ORM frameworks in three-tier applications

The obvious advantage of ORM frameworks is to make the life of the developers easier as they now can focus on implementing the business logic their applications dictate rather than spending time on the technical side of the storage mechanism.

ORM approaches are not without criticism. Although they are valuable solutions, they have attracted negative comments by developers on the basis of the complexity ORM introduces in its own right and the fact that once an ORM framework is used, the code is tightly coupled with it and carries all the trade-offs a specific ORM solution brings. I encourage you to do your own investigation as there are many good posts on the merits and drawbacks of ORM (Atwood, 2006; Fowler, 2012). The more you know about the tools you use, the better you position yourself to take advantage of those tools; and this is a general advice that goes beyond the scope of ORM libraries.

The reality of the matter is that if your focus as developer is to produce applications that implement some form of business logic which provides value to your business and customers, you do not want to waste time, effort, and, from company’s perspective, human resources to develop your own solution that deals with the technicalities of databases. On the other hand, if you are an ORM framework developer, the perspective is totally different. ORM provides solutions to nearly 80% of the tasks you need to accomplish at database level. If the remaining 20% of the tasks make a significant difference to your business, then most likely you are part of a team that develops frameworks.

ORM Terminology

In the field of ORM, there are a number of terms that we come across repeatedly. Many terms originate from the ORM designers, and others are borrowed from the world of databases. This section provides a summary of the most commonly used terms in ORM solutions.

Entity

An entity is the complete set of data held by an application object as defined by the developer in order to serve the needs of a specific application. This data set replicates the data found in the underlying database. A customer, an employee, or the blog posts from the previous example are typical representatives of an entity. In the code, the objects may hold more data and exhibit additional functionality than what is required at database level, but the idea is that when you look at an entity in the code, you have at the very minimum access to all the data in the database that this entity is associated to. For the database administrator, entities usually match to tables in the database, and an instance of the object (entity) in the code corresponds to a row (record) in that table.

Additionally, entities in code are used for other purposes than just to simply represent records in a table in the database. In databases, very often designers encapsulate a logical perspective of data that requires the combination of data found within different tables using keys and other database elements. Quite often, this logic also dictates the need for a range of calculations. In databases, this representation is implemented by views, and at code level entities are used for this purpose as well.

Properties

Entity’s data is stored in properties in the same way that classic objects use properties to hold data. As mentioned earlier, entities match database tables and table records; therefore, those instances should be uniquely identifiable by the ORM framework at the entity level. This is resolved by assigning a property to act as unique identifier. This concept is basically the same as the idea of primary keys in databases. One difference between properties in entities and the underlying data in databases is that the first ones can hold simple or complex data types.

Associations

Entities, like tables in databases, are generated in order to support a model that derives from a business problem. Entities make sense in a model when they form relationships that represent logical and conceptual notions. These relationships are called associations in ORM frameworks, and they are formed between one or more properties.

These properties in the ORM space are known as association endpoints, and depending on the data types they can define the different types of associations (cardinality) as represented by the common one-to-one, one-to-many, and many-to-many relationships. Although one-to-one and many-to-many relationships can be described in theoretical terms and implemented at database level, they hardly make business sense. Therefore, in most of the entity frameworks, all associations represent one-to-many (and vice versa) relationships, and if other relationship cardinalities (one-to-one) are required, it is left to the developer to enforce and filter them out.

Associations are always bidirectional so entities have full access to each other. In many ways, at database level, endpoint properties work in the same way as foreign keys do, and they behave in a way similar to joined table operations.

Criteria

When developers want to fetch data from databases, they create query statements using the relevant (SQL) language. The statements may or may not filter the results of the query. At ORM level, queries are built based on conditions that are passed to the underlying database engine. These conditions, which can be generic or specific, are formed by attaching criteria together. Most ORM frameworks provide a fluent interface (Ramsay, 2008) to manage criteria (meaning that the building of the query statement appears as a natural language to the user), and you can typically expect to have criteria for all the useful relational comparisons (e.g., greater than, equal, logical and, logical or, etc.) and sorting functions for properties.

Projections

Although you can retrieve all the properties (columns) of a table data from a database using ORM criteria, common programming practice indicates that you should only fetch the properties that are required in each situation. This type of queries is managed differently by ORM packages than the typical criteria-based queries, and they are called projections. Projections also allow programmers to drill down complicated data structures and even perform some (basic) mathematical calculations (e.g., average, summation, etc.).

Container

ORM frameworks create a buffer between the code and the database. When the user passes an operation to the ORM, the framework needs to have access to the status of all of the data in the database. The framework loads the relevant data in the memory in the form of entities or other relevant data structures, performs the instructed operations, and then, many times, pushes back the changes to the database. This snapshot of the data is managed by an entity container. Before the developer can interact with an entity, ORM libraries load any required data into a container. The containers are usually short-lived as their purpose is to serve a specific set of operations and lightweight as they need to be created and destroyed several times in the life of an application. In many aspects, a container is somewhat analogous to a database transaction.

The typical course of action in an ORM is as follows:
  • The developer creates an instance of the container. The container is also known as entity manager, and the instance of the manager is sometimes referred to as the context.

  • The developer uses the container to perform operations to entities. The operations may require interaction with the database as in the case of updating an entity or may only retrieve results like in the case of a query.

  • Once the operations are completed, the entity container (manager) is destroyed.

Putting It All Together

Figure 1-4 shows the several elements discussed earlier and their connection to the database structure based on the blog site example.
../images/481234_1_En_1_Chapter/481234_1_En_1_Fig4_HTML.jpg
Figure 1-4

Various elements of ORM frameworks and their relationship to database structure

Figure 1-5 pictures the ideas of criteria, projection, and entity container providing sample code.
../images/481234_1_En_1_Chapter/481234_1_En_1_Fig5_HTML.jpg
Figure 1-5

Sample code to demonstrate the concepts of criteria, projection, and entity container in ORM frameworks

Workflows

Depending on the available resources and information, several workflows can be devised that lead to a specific goal. In the area of ORM frameworks, there are three main workflow patterns1:
  • Code-first

  • Model-first

  • Database-first

Each of the preceding workflows becomes productive under specific conditions and meets specific needs. In general, these workflows attempt to make the work of the developer more structured and allow them to focus on how to build an application which adds business value rather than lock the developer in deep technical mazes indirectly linked to the goal of the application.

Code-First Workflow

This approach is relevant when you have an existing application and a point comes in the development life where persistence becomes a requirement. You typically follow OOP approaches and your application uses classes extensively. In the initial stages, the code does not consider any ORM framework requirements, conventions, or techniques. The database is created based on the class design, and the developer has full control on the way the ORM will generate the database structure. It offers flexibility but it requires more work to set the classes up correctly.

Code-first approach may be effective in small teams of developers or in the case of a sole developer, but it does not work well with large developer teams because it opens the possibilities for inconsistencies in class design. For this reason, it requires central management of the fundamental classes of the application. Additionally, code-first workflow can be used in applications that have a database already, but an additional step is required where the developer must reverse engineer the database and produce the appropriate classes.

As an example, the code snippet in the blog case represents the code-first approach. The developer defines the classes first and the ORM generates the database.

In this book, we are going to start with the code-first approach because it allows the exploration of different approaches and it encourages more in-depth understanding of the framework.

Model-First Workflow

The model-first approach assumes that the application requires a persistent medium and database support from the outset. When developers design the model first, they abstract the storage mechanism by focusing on the design of the database. Typically, a graphical environment is used as a tool to generate the underlying classes. Figure 1-1 represents the model of the blog application.

This approach is useful when someone works in a new application or in an application that does not have any databases yet. Additionally, the use of graphical elements creates a prototype of the database, and this proves to be a useful tool when it comes to sharing ideas and approaches between members of teams or between development groups.

As mentioned, code-first is the approach we are going to focus on in this book when we build our ORM entities. In Chapter 8 we are going to explore a separate tool called Data Modeler, which allows us to follow the model-first approach.

Database-First Workflow

This approach is based on existing databases. The framework can generate classes that match the database entities and relationships and, then, developers interact with these classes. This is appropriate when a new application needs to be developed based on existing data. This situation is common in software development and leads to the model-first approach.

Database-first development does not put any constraints in terms of the number of databases the framework uses. Although the workflow comes with the assumption of one database in use, the approach can be used with multiple databases. ORM frameworks are capable of managing more than one databases.

The biggest advantage of this approach is the consistency in the generation of the classes in the ORM framework (Smartbear, 2013). This makes the approach suitable to large teams where different groups may design and work in different parts of a database. This approach will generate a very consistent class tree that can be shared among the team members. On the other hand, it offers limited flexibility as the developers do not have the opportunity to alter the class design because the next update to the database and, consequently, to the classes will overwrite any changes.

As with the case of the model-first design, we are going to look at this approach with Data Modeler in Chapter 8.

Choosing Workflows

The available workflows are useful in many different circumstances, but they generally describe a dedicated approach to achieving a goal. This means that, in theory, one would choose a workflow and stick with it. However, real-life work environments are hardly pure (Microsoft, 2016), and it is typical to mix and match workflows. For example, you may start a new application with the code-first approach and as the complexity increases you may find it more productive to move to a model-first workflow.

Summary

This chapter provides an introduction to object relational mapping frameworks. A discussion of the fundamental ideas and concepts puts light to the way these frameworks are designed, and different workflows associated with ORM libraries are discussed. This chapter offers the basic knowledge that allows us to, firstly, explore TMS Aurelius ORM in Delphi and, secondly, to work in developing an example application.

References

Atwood, J., 2006. Object-Relational Mapping is the Vietnam of Computer Science. [Online] Available at: https://blog.codinghorror.com/object-relational-mapping-is-the-vietnam-of-computer-science/ [Accessed 28 02 2019].

Fowler, M., 2012. Martin Fowler on ORM Hate. [Online] Available at: https://dzone.com/articles/martin-fowler-orm-hate [Accessed 28 02 2019].

Hibernate, n.d.. What is Object/Relational Mapping. [Online] Available at: http://hibernate.org/orm/what-is-an-orm/ [Accessed 28 02 2019].

Microsoft, 2016. Get started with Entity Framework 6. [Online] Available at: https://docs.microsoft.com/en-us/ef/ef6/get-started [Accessed 28 02 2019].

Mueller, J. P., 2013. Microsoft ADO.NET Entity Framework. California: O’Reilly Media, Inc..

Ramsay, C., 2008. NHibernate: Optimising Queries with Projections. [Online] Available at: http://colinramsay.co.uk/nhibernate/2008/01/15/nhibernate-optimising-queries-with-projections.html [Accessed 28 02 2019].

Smartbear, 2013. Choosing the Right Entity Framework Workflow. [Online] Available at: https://smartbear.com/blog/develop/choosing-the-right-entity-framework-workflow/ [Accessed 29 02 2019].

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.217.198