This section introduces you with some of the basic concepts and features provided by FoxDataObjects, by using a few brief descriptions and short definitions complemented with simple code samples and walkthroughs.
The main goal for this document is to help you getting familiarized with Object-Relational Mapping concepts and technology. It explains what can we expect from an ORM tool and how it affects the way we develop and build software applications.
As software developers, we often build applications that normally require storing data into relational databases. On this context, Visual FoxPro give us a huge advantage over other development platforms because of its data-centric language that also provides cool object-oriented capabilities and a powerful and mature file based data storage engine. The language allows us to design the database, write programs to access/manage/ update data, and even build rich user interfaces with forms and reporting facilities.
However, there is a dominant trend, in software architecture terms, to design applications divided on independent logical blocks or layers like:
We will not bother you with details or theoretical concepts regarding n-Tier application design. We just want to review the current application development process to visualize the advantages on using an ORM tool like FoxDataObjects.
An n-Tier application design allows us to build and implement each application layer over the architecture and platform that best suits the solution requirements. As an example, the user interface layer could be built as Web pages or as a rich Win32 form application. Application data could be stored either in a file based local database or a remote RDBMS server.
Using these concepts, a well-designed n-Tier application, should allow us to bind the Business Rules Layer (application logic) to any database server and to different User Interfaces (even simultaneously).
This way, the Business-Rules layer carry with a very special responsibility: It must be able to isolate the User Interface layer from data storage details and implementation. Moreover, it must be able to isolate the Data Storage layer from all business-specific rules validation over the stored data.
Here is where object-oriented analysis and Business-Objects concepts come in action. We can design and build the Business Rules layer as an integral Object Model where entities from the real life are modeled in classes (Business Objects). Those objects not only expose Properties (data) but also Methods (behavior) that mimics real life entities. Therefore, in a Contacts application, as a very basic sample, we could find a Person class exposing properties like:
• Name
• Address
• Birthday
• Phone, etc.
and methods like:
• AddressChange()
This way, the User Interface layer only deal with business objects that expose properties and methods which, as an example, could be bound to form controls. The application can create object instances, set its properties and access its methods.
Therefore, one common need for virtually all business applications is the persistence (stable storage) of business objects. The options for persistent storage are file systems (VFP tables), object-oriented databases and relational SQL databases (RDBMS). For simple, non-mission critical applications requiring low data volume, a file system solution may be sufficient. For high performance, scalable, transactional and robust applications, a database management system is required for storing business objects.
Relational database management systems provide a popular and pragmatic repository for these business objects because of the maturity of the RDBMS technology and the availability of numerous third-party tools for analyzing and managing the relational data.
Now, in order to persist and retrieve our business objects to/from a relational database, we can find several approaches:
The first solution we find is to “add” special methods to our Business Classes that retrieve an instance from the database (the classic GetByID method), and save an object instance (the classic Save method). This way, each business class repeats this functionality according with the business entity its represents.
One-step further, leads us to keep our business classes “clear” from code that is not related with the business rules. This way we create an intermediary layer named “Data Access Layer” (closer to our business classes), which performs all the persistence jobs, encapsulating all the code related with the way our object instances are stored and retrieved from the repository. Therefore, if we need our business layer to be able to work with different data storages, we could replace the Data Access layer classes with code specialized for a completely different storage system, without needing to “touch” any business-rules related code.
This way, when the client layer calls the Person.Save method, it delegates the requirement to a specialized class that knows how to store a Person instance into the database, building and sending all the commands appropriate for the corresponding data store.
If we are working with just one data store brand, and looking for an improved performance, the next logical step is to build the most frequently used data access functionality directly into the database to be executed by the data store as Stored Procedures. Therefore, all of our GetByID, Save, Delete methods are replicated as Stored Procedures that receives the proper parameters for each entity.
This is great! Just adding some forms and binding them to our business objects and we have got all the application partitioned with each layer doing a specific part of the job.
Nevertheless, if we look back, we promptly realize that such degree of specialization carry a high cost in development time. Moreover, as we all know, development time is one of the variables we always try to reduce for economical reasons.
We where told that splitting our applications in specialized layers as exposed above allows us to change storage technology or change our Business Rules layer without any change in the User Interface layer. Now we know that this benefit is relative and in practice, such level of specialization increased the impact of “minor” changes over the whole application.
To show the situation, let us pretend we have a Person entry form (UI layer) with all of its controls bound to properties exposed by a Person class (Business Rules layer), including a button, that calls the Person.Save method. The Person class contains all the properties described on the first example and delegates on a Person_DataAccess class the code to implement the way a Person instance properties are persisted on an SQL Server database (Data Storage layer).
Let us pretend we decided to implement some of the most used data access functions directly into the database server as Stored Procedures. This way we have a set of stored procedures like PersonUpdate, PersonInsert, GetPersonByID, PersonDelete, etc. called by methods in the Person_DataAccess class.
Now let us pretend we need to add a new property and a new method to the Person class: we need to add the email property and the NotifyByeMail method.
To do so, we open a database management tool, connect to the data store, open the database schema and add a new column to the Person table with a proper data type. We need to check all of the Stored Procedures containing references to the Person table and ensure to add the eMail column to all of the involved SQL commands and parameter lists.
Then, we modify the Person class definition adding the eMail property and the NotifyByeMail method with the proper program code.
After that, we modify the Person_DataAccess class and check all methods referencing modified Stored Procedures, commands sent to the data server, Views definitions, local cursors and references to the Person instance properties to ensure the new property is now included.
At last, we modify the Person Entry form, by adding a new control for the eMail property and binding it to the new Person.eMail property.
Do not you think this is too much effort just to add a simple property to an existing entity?
Now the risk of failures due to omissions on any of the exposed steps is huge. Furthermore, there are other factors not directly related with the specialization that also increase those risks, like all of the activities required to reflect the database schema changes from the development environment into production environments.
Here is where an Object-Relational Mapping tool like FoxDataObjects comes in help.
FoxDataObjects assist you on the Business Rules layer development, by releasing you from the burden of mapping the object model to a relational model. It allows you to focus on your Business Classes and on the strictly functional aspects of the real world entities that you are modeling. In other words, it fully automates the Data Access layer in your application.
FoxDataObjects works at development time by analyzing your classes’ definitions and storing them on a mapping schema file along with the data dictionary or schema definitions needed to persist your objects instances into relational databases.
By default, FoxDataObjects maps each object-model class into a database table, and each class property into a table column, generating a classic and straightforward relational model, as we were doing manually since a while. All of these mappings are totally customizable to the developer taste or solution requirements.
At development time, FoxDataObjects provides the Schema Manager, an easy, powerful and intuitive graphical tool for mapping-file editing, that allows you to tweak every single mapping option and customize the proposed relational model, by adding or removing tables, columns, indexes and relationships.
At runtime, FoxDataObjects provides a powerful set of Persistence Services based on the mapping schema file/s generated at development time, easily saving your objects instances into the database and retrieving them either individually or collectively. On this context, now, you focus totally on your object model. Even Queries can be performed in terms of your object model, independently on how or on which tables your object model instances are stored.
It builds the tables and columns definitions required by your object model and even manages your database schema upgrades when your Business Layer connects to a Data Storage engine. It is done almost automatically and from any previous database schema version or state including the entire database schema creation.
FoxDataObjects is made up of two main components:
The core library, written in C++ as a Visual FoxPro Link Library (FLL), provides a natural and high performance extension to Visual FoxPro, and includes a set of class wrappers that form a complete, robust and easy to use persistence services object model.
The Schema Manager mapping tool allows you to map business classes to database tables and columns, and to specify object relationships at development time. It also can be used to define server side custom SQL, validity checks, and customized database configurations.
The mapping strategy is saved to a schema file that is used by the supporting core library at runtime. The core library provides the necessary API to allow your business objects to be persisted into the database. When FoxDataObjects is installed on your development machine, those components are copied into your local Program Files folder and a set of shortcuts are inserted into your Visual FoxPro IDE menu, giving you access to the Schema Manager and the product documentation right from your development environment.
FoxDataObjects assist you at development time, by creating and storing the mapping definitions on one or more schema mapping files, and at runtime, by persisting and retrieving your objects instances from the database.
Applications using FoxDataObjects only need to distribute the FDO library composed by a couple of lightweight files (FDO.FLL and FDO.FXP) plus one or more mapping files generated by the tool at development time.
By using the Schema Manager, you can create new mapping files and copy the FDO library files right into your project’s folder to begin working immediately.
In contrast to other ORM tools available for Java and .Net platforms, you get your object model mapped almost automatically. FDO analyzes your classes’ definitions right when you test your object model at development runtime, and generates all the basic mappings automatically for you, including the relational model definition (data dictionary).
As an introduction, let us pretend we start developing a new application and want to use the services provided by FoxDataObjects. The first step is to create a new folder where to put all of the project’s files.
You can implement the Business Rules layer as Business Object classes defined in one or more PRG or VCX files, as you prefer.
At the end of this section, you can find a set of tutorials and Walkthroughs that quickly show you how to begin working with FoxDataObjects.
Let us pretend you want to define your classes using a PRG based approach. You can create your first class library file with a small set of business classes’ definitions. When you are writing your classes definitions, you just focus on how to model the characteristics and behavior of your real life entities. You can forget about how your objects are stored or retrieved into a permanent storage like a relational database.
Here is when the first differences with our former development process appear. The main advantage of FDO is that it allows us to begin our application development right from the object model and not from the relational model used to store data as we have done before.
The relational model is just a consequence of the needing to store our object instances into Relational Database Management Systems (RDBMS)
Now, FoxDataObjects allows us to work directly on our classes and forget about the relational model needed to persist our instances. All this is done by FDO; nevertheless, we can change and customize every aspect of the process. As our object model evolves, the relational model follows it, because FoxDataObjects updates the relational model definitions as soon as we introduce changes into the object model (classes definitions). Moreover, all of this occurs almost transparently!
It implies a change on our Database-Centric development paradigm. For so many years, our applications inherited all the characteristics and limitations imposed by a relational storage model, either in normalization aspects as in Primary Key/Foreign Key relationships restrictions.
But in the real life, such limitations does not exist, and our Object Model, whose main objective is to model in classes real life entities, do not need to reflect the characteristics or limitations of the model we use to store our data, the relational model, whose main objective is to store data avoiding redundancy.
However, there is a genuine need to use relational databases to store business objects. Relational database management systems provide a popular and pragmatic repository for these business objects because of the maturity of the RDBMS technology and the availability of numerous third-party tools for analyzing and managing the relational data.
Now we can work directly in our object model because we have an “assistant” that take over of all the time consuming, repetitive and error prone tasks of mapping our object model into relational tables.
On this context, as developers, we can focus on the application specific requirements and business objects modeling, without the burden of defining and maintaining all of the relational structures required for object persistence. Now we focus solely on the application’s functional requirements and for the first time we can work entirely on the object realm.
FoxDataObjects generates an evident and classic relational model that is not limited. The Persistence Services, in addition to automate instances storing and retrieving, allows us to access and update the database in a traditional way, so we do not lose anything. If we need to, we can access all of our relational tables and data and update them as always we did.
As Visual FoxPro developers, we know that such a paradigm change represents an initial effort. We were developing our applications using a Data-Centric language for several years, so, for many of us; the relational model limitations are part of our “idiosyncrasy”. Furthermore, we do not consider it as limitations at all.
However, when we want to take advantage of the powerful object oriented features provided by the language to create our Business Objects layer, the “Impedance-Mismatch” problem between the object model and the relational model becomes evident.
Here is where all of our doubts appear and we start mixing concepts. As an example, we create our business objects classes but at the same time, we move data (like cursors in XML format) to and from the user interface layer so our business classes can validate all the records before sending them to the data store. Relational model objects (like a cursor or table) reach the user interface. Even though it is a valid way to implement layer separation, it propagates the limitations on the relational model into our object model (business layer) and even worst, to the User Interface layer. Furthermore, we are only moving data among layers and we are loosing the benefits that an object model provides: integrating data with behavior.
If a client receives a pointer to an object with data and behavior, the object can react to data changes immediately and can force the application of business rules in real time, so, its results are visible to the user interface immediately.
Note that this concept does not have anything to do with the genuine need to move data in XML format as the communication mechanism for disconnected applications, web services or Service Oriented Applications.
But coming back to our example, once we defined our first classes, and to begin working with FoxDataObjects, we need to copy the Persistence Engine library into the project’s folder and we need to create an empty mapping file where to store the definitions.
The Schema Manager mapping tool allows us to create the schema file and copy the library files into just one-step as described on the first tutorial at the end of this section.
After that, we can instruct FoxDataObjects to parse and analyze our classes’ definitions for the first time to get the initial mapping definitions. The “Parse class library“ menu option, analyze our classes definitions and build the default mappings.
FoxDataObjects completes the Schema Manager’s left panel tree with two main groups of nodes:
Clicking on a left panel node representing a Class, Member, Table or Column, the right panel shows a form that you can use to edit all of the selected item properties. The node representing the whole mapping file and the node representing the active session have their own properties edit form.
In summary, the tool shows both models:
To add or remove items on the Object Model, we just add or remove classes or properties on our class libraries (directly on the class definition code).
The relational model is composed by the items generated when FDO maps the object model, plus the items we manually add that are not mapped with Object Model items, (Not mapped Tables/Columns).
To prevent an object model item from generating definitions on the relational model, it must be marked as Not Persistent.
As you may observe on the relational model sub tree, all of the Primary Key / Foreign Key, row unique identifiers, indexes and relationships issues are defined automatically. Check the Documentation Manual for a complete description on how it works.
Changes over object model sub tree items properties may generate changes over the relational model that are reflected immediately. We can specify, among others, the mapping strategy for a class inheritance hierarchy, the table where class’s properties are mapped, desired concurrency control mode and options, and so on. All of this is detailed on the tutorials, the Documentation Manual and the Schema Manager help.
At this point, your project folder contains: your class library file with your first business classes’ definitions, the FoxDataObjects runtime library files and your mapping file containing all the information on how your object model relates with the relational model needed to persist your object instances.
But what about the database?
FoxDataObjects creates/updates the database schema automatically when it connects to a data store.
In order to know how to connect with a data source, FoxDataObjects uses a DataSources object schema that simplifies the use of all of the data stores and storage technologies supported by FDO. FDO can use from local native FoxPro databases to remote RDBMS like Microsoft SQL Server, MySQL, IBM DB2, PostgreSQL and others.
To access the persistence services, you need to instantiate a fdoServer object, that you can use to provide information about the data source object and the main schema mapping file to be used to get a valid Session object. The Session object represents a connection to a database and exposes all of the persistence services. It connects to the data store and verifies the state of the database schema, generating and sending the commands required to create/upgrade the database schema based on the definitions stored on the mapping file.
The Session object allows you to save and retrieve your object instances. The next tutorials will show how to begin working with persistence services.
As a preliminary example, to save a Person object is as simple as:
oSession.SaveObject(oPerson)
where oSession is our Session object and oPerson is a pointer to the object instance to store.
FoxDataObjects sends the commands required to insert and or update the instance records, based on the mapping information, and updates the database on a single operation protected by default with a Transaction.
At development time, while we test our object model using persistence services, FoxDataObjects can detect the changes we made over the classes’ definitions and generates the default mapping information automatically.
This way, the development cycle becomes significantly simplified: We edit our classes’ libraries, adding and removing classes and members. When we use the Persistence Services to test our changes during development, FoxDataObjects detects the changes that affect instances' persistence, documents them into the mapping file, generates the default mapping information and opens the Schema Manager GUI tool, highlighting all the changes and waiting for a confirmation. When we agree, all the changes over the relational model are reflected on the connected database and application execution resumes.
In other words, we have an assistant during development stage that keeps the relational model definitions updated for our object model and updates our development environment database automatically for us. When our application is deployed, FoxDataObjects can upgrade the Production database schema automatically from any previous version or state.
Note that the development cycle now is:
Step 2 is used by FoxDataObjects to detect object model changes and update the relational model definition automatically.
This development cycle, clearly contrast with the development cycle described on the beginning of this section. Now to add the eMail address to a Person entity is as simple as editing the class definition and adding a new property. When we test the change, FDO detects the new member, document it on the mapping file, generates a new column on the Person table definition and upgrades the development database schema automatically.
It represent a huge reduction in Business Rules layer coding times, allowing us to focus on class definition code rather that in database storage structures.
The initial design objective for our application to being able to connect the Business Rules layer with any data store, is now guaranteed by FoxDataObjects, furthermore, our Business Rules layer exposes a complete object model with data and behavior that can be easily accessed from any User Interface layer.
|
|
Home |
Send feedback on this topic to RunAhead Technologies
For Technical support and product issues please contact us at support@foxdataobjects.com or visit http://www.foxdataobjects.com
Copyright (c) 2000-2005 RunAhead Technologies