tiOPF
Free, Open Source Object Persistence Framework for
Free Pascal & Delphi

A quick guide to tiOPF

provided by Sean Cross of Source IT Software

This guide is designed to be read in conjunction with the tiOPF 2 and the associated demos.
tiOPF can be downloaded from the tiOPF Web Site

What is tiOPF

tiOPF is a Object Persistence Framework. That is, it is a framework based around saving your objects to, and loading them from, databases and/or flat files.

According to the website:
tiOPF is an Open Source framework for Delphi & Free Pascal that simplifies the mapping of an object oriented business model into a relational database. The framework is mature and robust having been in use on production sites since 1999. It is free, open source, and available for immediate download with full source code.

Key Features

From the website:
Some of the key features of the tiOPF include:

Support

What are the advantages?

What are the disadvantages?

 

How do I create a business object?

Business objects descend from TtiObject.

All TtiObject descendants have a unique Object ID with a property name of OID. This is of type integer, 64 bit integer or GUID. The OID is populated when a new object is created. The OID is saved and loaded automatically.

Other fields needing persistence are declared as published.

Object initialisation is done in CreateNew.

Example - taken from Demo_Collection:

  1. TClient = class(TtiObject)  
  2.   private  
  3.     FClientID: TClientID;  
  4.     FClientName: TClientName;  
  5.   public  
  6.     constructor CreateNew(const pDatabaseName: string = '';  
  7.                 const pPerLayerName: string = '');  
  8.                 override;  
  9.   published  
  10.     property ClientName: TClientName read FClientName write FClientName;  
  11.     property ClientID : TClientID read FClientID write FClientID;  
  12.   end;  
  13.   
  14.  ...  
  15.   
  16.   constructor TClient.CreateNew(  
  17.                const pDatabaseName: string = '';  
  18.                const pPerLayerName: string = '');  
  19.     begin  
  20.      inherited;  
  21.       // Set some default values for the demo  
  22.       ClientName:= 'TEST ' + DateTimeToStr(Now);  
  23.       ClientID:= IntToStr(GetTickCount);  
  24.     end;  

Business objects are normally stored in a TtiObjectList descendant.

This can be as simple a declaration as:

  1. TClients = class(TtiObjectList);  

Commonly however Add and Items are reintroduced to provide type safety

Example - taken from MastApp:

  1. TEmployees = class(TtiObjectList)  
  2.   private  
  3.   protected  
  4.     function GetItems(i: integer): TEmployee; reintroduce;  
  5.     procedure SetItems(i: integerconst Value: TEmployee); reintroduce;  
  6.   public  
  7.     property Items[i:integer]: TEmployee read GetItems write SetItems;  
  8.     procedure Add(pObject: TEmployee); reintroduce;  
  9.   published  
  10.   end;  

New items are created with CreateNew

Example - taken from Demo_Collection:

  1. procedure TFormCollection.btnInsertRowClick(Sender: TObject);  
  2.   var LClient: TClient;  
  3.    begin  
  4.      LClient:= TClient.CreateNew;  
  5.      FClients.Add(LClient);  
  6.      LV.Refresh(LClient);  
  7.    end;  

Items are deleted by setting the Deleted property to True.

Note that they are only removed when the object list is persisted.

 

How do I persist it?

Examples from MastApp, using an access database.

There are 6 steps:

  1. Include your persistence layer units and OID units:
    1. uses  
    2.     ...  
    3.     ,tiOID ,tiQuery       // always required  
    4.     ,tiOIDInteger ,tiQueryADOAccess         // use integer OID , and Access persistence  
  2. Set your default persistence layer:
    1. gTIOPFManager.DefaultPerLayerName:= 'ADOAccess';  
  3. Connect to the database:
    1. gTIOPFManager.ConnectDatabase('dbdemos.mdb''''');  
  4. Set your persistence mapping:

    This depends on what mapping you are using, Automapping, DB Independent or hard coded. This is covered in a later section. Automapping is the simplest.
    An object is mapped as follows:

    1. // Class, Table, Property, Column, Special Info  
    2.  gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, 'Employee''OID''EmpNo', [pktDB] );   
    3.  gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, 'Employee''LastName''LastName' );   
    4.  gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, 'Employee''FirstName''FirstName' ) ;   
    5.  gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, 'Employee''PhoneExt''PhoneExt' ) ;   
    6.  gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, 'Employee''HireDate''HireDate' ) ;   
    7.  gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TEmployee, 'Employee''Salary''Salary' ) ;   
    8.  gTIOPFManager.ClassDBMappingMgr.RegisterCollection(TEmployees, TEmployee);  

  5. Override Save and Read and make them public (for automapping it is sufficient to call the inherited handler):
    1.   Txxxx = class(TtiObjectList)  
    2.   public  
    3.     procedure Read; override;  
    4.     procedure Save; override;  
    5.   end;  
    6.     
    7. ...  
    8.     
    9. procedure Txxxx.Read;  
    10. begin  
    11.   inherited;  
    12. end;  
    13.   
    14. procedure Txxxx.Save;  
    15. begin  
    16.   inherited;  
    17. end;   
  6. Read/Save your object lists:
    1. FEmployees:= TEmployees.Create;   
    2.     FEmployees.Read;   
    3.     FEmployees.SortByProps(['LastName''FirstName']);  
    4.     ...  
    5.     FEmployees.Save;  

 

How do I create a database?

You can create a dataset as you would normally, and then map your objects to it.
However you can also create a database in code using the persistence layer.
Example - from Demo_CreateDatabase:

  1. procedure  
  2.   TFormMainCreateDatabase.btnDatabaseExistsClick(Sender: TObject);  
  3.   var  
  4.     LPerLayer: TtiPersistenceLayer;  
  5.   begin  
  6.     LPerLayer:= gTIOPFManager.PersistenceLayers.FindByPerLayerName(PersistenceLayerName);  
  7.     Assert(LPerLayer<>nil'"' + PersistenceLayerName + '" not registered');  
  8.     if LPerLayer.DatabaseExists(DatabaseName, UserName, Password) then  
  9.       ShowMessage('Database <' + DatabaseName + '> exists.')  
  10.     else  
  11.       ShowMessage('Database <' + DatabaseName + '> does not exist.');  
  12.   end;  
  13.   
  14.   procedure TFormMainCreateDatabase.btnCreateDatabaseClick(Sender: TObject);  
  15.   var  
  16.     LPerLayer: TtiPersistenceLayer;  
  17.   begin  
  18.     LPerLayer:= gTIOPFManager.PersistenceLayers.FindByPerLayerName(PersistenceLayerName);  
  19.     Assert(LPerLayer<>nil'"' + PersistenceLayerName + '" not registered');  
  20.     LPerLayer.CreateDatabase(DatabaseName, UserName, Password);  
  21.     ShowMessage('Database "' + DatabaseName + '" has been created.');  
  22.   end;  

Tables are created using TtiDBMetaDataTable.

Example - from Demo_CreateTable

  1. // Does a table exist?  
  2.   procedure TFormMainCreateTable.btnTableExistsClick(Sender: TObject);  
  3.   var  
  4.     lDBMetaData: TtiDBMetaData;  
  5.     lPooledDB : TPooledDB;  
  6.     lDatabase : TtiDatabase;  
  7.   begin  
  8.    lDBMetaData:= TtiDBMetaData.Create;  
  9.     try  
  10.       lPooledDB:= gTIOPFManager.DefaultDBConnectionPool.Lock;  
  11.       try  
  12.         lDatabase:= lPooledDB.Database;  
  13.         lDatabase.ReadMetaDataTables(lDBMetaData);  
  14.         if lDBMetaData.FindByTableName('Client') <> nil then  
  15.           ShowMessage('Table <Client> exists')  
  16.         else  
  17.           ShowMessage('Table <Client> does not exist');  
  18.       finally  
  19.         gTIOPFManager.DefaultDBConnectionPool.UnLock(lPooledDB);  
  20.       end;  
  21.     finally  
  22.       lDBMetaData.Free;  
  23.     end;  
  24.   end;  
  25.   
  26.   // Create table procedure  
  27.   TFormMainCreateTable.btnCreateTableClick(Sender: TObject);  
  28.   var   
  29.     lTableMetaData: TtiDBMetaDataTable;  
  30.   begin  
  31.     lTableMetaData:= TtiDBMetaDataTable.Create;  
  32.     try  
  33.       lTableMetaData.Name:= 'Client';  
  34.       lTableMetaData.AddField('OID', qfkString, 36);  
  35.       // Using GUID OIDs  
  36.       lTableMetaData.AddField('Client_Name', qfkString, 200);  
  37.       lTableMetaData.AddField('ACN', qfkString, 9);  
  38.       gTIOPFManager.CreateTable(lTableMetaData);  
  39.     finally  
  40.       lTableMetaData.Free;  
  41.     end;  
  42.     ShowMessage('Table ''Client'' created');  
  43.   end;  
  44.   
  45.   procedure TFormMainCreateTable.btnDropTableClick(Sender: TObject);   
  46.   begin   
  47.     gTIOPFManager.DropTable('Client');   
  48.     ShowMessage('Table ''Client'' dropped');   
  49.   end;  

 

How do I filter it.

The easiest way to filter an object list is by descending from TtiFilteredObjectList.

Example from Demo_CollectionWithCriteria

  1. TClients = class(TtiFilteredObjectList);  
  2.   
  3.   ...  
  4.   
  5.   FClients.Clear;  
  6.   FClients.Criteria.ClearAll;   
  7.   // needed for Db Independent mapping   
  8.   // note Automapping can use property name instead of field name   
  9.   // DB Independent can only use field name at the moment  
  10.   FClients.Criteria.AddLike('Client_Name', EditFilter.Text + '%');  
  11.     
  12.   ...  
  13.     
  14.   FClients.Read;  

An object list can also be filtered using Hard Coded Visitors.
See Demo_CollectionWithFilter

 

How do I build a GUI?

One of the main Gui elements is TtiVTListView. Columns are added using AddColumn, passing in the property name, type, display name and display size. To populate, the Data property is set to a TtiObjectList.

Example from Demo_EditDataInGui

  1. LV.AddColumn('ClientID', vttkString, 'Client ID'80);   
  2.   LV.AddColumn('ClientName', vttkString, 'Client name'200);  
  3.   
  4.   FClients:= TClients.Create;  
  5.   ...  
  6.   FClients.Read;   
  7.     
  8.   LV.Data:= FClients;  

Add, Edit and Delete capabilites are provided using the OnEdit, OnInsert and OnDelete events.

  1. procedure TForm2.LVItemEdit(pVT: TtiCustomVirtualTree; pData: TtiObject;  
  2.                     pItem: PVirtualNode);  
  3.   begin  
  4.    if TFormClientEdit.Execute(pData) then  
  5.      pVT.Refresh(pData);  
  6.   end;  
  7.   
  8.   procedure TForm2.LVItemInsert(pVT: TtiCustomVirtualTree; pData: TtiObject;  
  9.                     pItem: PVirtualNode);  
  10.   var  
  11.     lClient: TClient;  
  12.   begin  
  13.     lClient:= TClient.CreateNew;  
  14.     if TFormClientEdit.Execute(lClient) then  
  15.       begin  
  16.         FClients.Add(lClient);  
  17.         pVT.Refresh(lClient);  
  18.       end  
  19.     else  
  20.       lClient.Free;  
  21.   end;  
  22.   
  23.   procedure TForm2.LVItemDelete(pVT: TtiCustomVirtualTree; pData: TtiObject;  
  24.                     pItem: PVirtualNode);  
  25.   begin   
  26.     if tiPerObjAbsConfirmAndDelete(pData as TClient) then  
  27.       pVT.Refresh;  
  28.   end;  

tiOPF also comes with a number of object aware controls such as TtiPerAwareEdit. These use the LinkToData method to attach to a TtiObject

  1. property Databuffer : TtiObject read FDataBuffer write FDataBuffer;  
  2.     
  3.   ...  
  4.     
  5.   paeOID.Value:= DataBuffer.OID.AsString;   
  6.   paeClientName.LinkToData(DataBuffer, 'ClientName');   
  7.   paeClientID.LinkToData(DataBuffer,'ClientID');  

A GUI can also be built using standard non-db components and mediating views. Base edit and list mediator controls are provided in the GUI directory.

There is a demo in \tiOPF2_Demos\GenericMediatingViews.

 

How do I use data aware controls?

Use TTiDataset and TtiNestedDataset. These are included in the latest svn. They may not yet be available in the download version

TtiDataset is linked to an object list using the method LinkObject defined as follows:

  1. procedure LinkObject(AObjectList: TtiObjectList; AClass: TtiObjectClass);  
  2.   
  3. //Example of use:  
  4.   
  5. DatasetClients_.LinkObject(FClients, TClient);  

TtiNestedDataset is linked to an existing TtiDataset or TtiNestedDataset using the properties DataSetField and ObjectClass as follows:

  1. NestedDataset_PhoneNumbers.DataSetField:= DatasetClients_PhoneNumbers;   
  2. NestedDataset_PhoneNumbers.ObjectClass:= TPhoneNumber;  

See the dataset demo and unit tests for further information.

 

Useful units and functions.

Unit

Contains

tiObjectTtiObject and TtiObjectList
tiFilteredObjectListTtiFilteredObjectList
tiCriteriathe TPerCriteria objects used in TtiFilteredObjectList
tiOPFManagergTIOPFManager
tiAutoMapAutomapping
tiVisitorDBAutoGenTVisDBAutoGenRead and Update used in DB Independent Visitors
tiVisitorDBTVisOwnedQrySelect and Update used in Hard Coded Visitors
tiOIDbase OID. Needs to be included in your business model unit.
tiOIDGUIDGUID OID. Needs to be included in your project at least once if you require guid OIDs
tiOIDIntegerInteger OID. Needs to be included in your project at least once if you require int OIDs
tiQueryContains the base TtiQuery object

Standard Persistence layers Either include the required unit, or use the LINK_XXX conditional define.
See Demo_LoadPersistenceLayerIfDef or Demo_LoadPersistenceLayerUses.

Persistence

Conditional

tiQueryXMLLINK_XML
tiQueryIBXLINK_IBX
tiQueryBDEParadoxLINK_BDEPARADOX
tiQueryADOAccessLINK_ADOACCESS
tiQueryADOSQLServerLINK_ADOSQLSERVER
tiQueryCSVLINK_CSV
tiQueryTABLINK_TAB
tiQueryXMLLightLINK_XMLLIGHT
tiQueryDOALINK_DOA
tiQueryRemoteLINK_REMOTE
tiQuerySqldbIBLINK_SQLDB_IB
tiQuerySqldbPQLINK_SQLDB_PQ
tiQueryFBLLINK_FBL
tiQueryZeosIBFBLINK_ZEOS_FB

 

ADVANCED TOPICS

How do I encapsulate and associate objects?

Encapsulation is normally done by including an object or object list inside another.

A one to many relationship can be modelled by including a TtiObjectList containing the children, inside the parent. Alternately, the child can contain an instance of the parent.

A third possibility is to store just the OID of the associated object. This is useful when access to the object itself is not required.

If the encapsulated object is published, then it will be saved and loaded together with it's owner. If it is public, then the saving and loading will need to be handled in code.

Examples from MastApp:

  1. TOrder = class(TtiObject)   
  2.   private  
  3.   ...  
  4.   published   
  5.   //  property OID;  
  6.     property OrderNo: integer read GetOrderNo;  // resurfaces OID so we can get at it in the lists  
  7.     property CustNo: TOID read GetCustNo write SetCustNo;   
  8.     property SaleDate: TDateTime read FSaleDate write FSaleDate;   
  9.     property ShipDate: TDateTime read FShipDate write FShipDate;   
  10.     property EmpNo: TOID read GetEmpNo write SetEmpNo;  
  11.     ...  
  12.     property OrderItems: TOrderItems read FOrderItems;  // one order owns many items  
  13.   end;  
  14.   
  15.   TOrderItem = class(TtiObject)   
  16.     private  
  17.       ...  
  18.       function GetDescription: string;  
  19.       function GetPart: TPart;  
  20.       procedure SetPartNo(const Value: double);  
  21.       function GetListPrice: Currency;  
  22.       function GetTotalPrice: Currency;  
  23.     protected   
  24.       FOrderNo: TOID;   
  25.       FPartNo: double;   
  26.       FPart: Tpart;   
  27.       function GetCaption: string; override;   
  28.       procedure AssignClassProps(ASource: TtiObject); override;   
  29.     public  
  30.       ...  
  31.       property Part: TPart read GetPart; // encapsulated object � one part to many items   
  32.       property Description: string read GetDescription;   
  33.       property ListPrice: Currency read GetListPrice;   
  34.       property TotalPrice: Currency read GetTotalPrice;   
  35.     published   
  36.       property OrderNo: TOID read GetOrderNo write SetOrderNo; // OID of the owner object  
  37.       // need the following to provide db access to PartNo   
  38.       property PartNo: double read FPartNo write SetPartNo;  
  39.       ...  
  40.     end;  
  41.       
  42.     function TOrderItem.GetPart: TPart;   
  43.     begin   
  44.       if not assigned(FPart) then   
  45.       begin  
  46.         FPart:= TPart.Create;  
  47.         // FPart.OID.Assign(PartNo);  
  48.       end;  
  49.       if FPart.ObjectState = posEmpty then  
  50.       begin  
  51.         FPart.OID.AsString:= FloatToStr(FPartNo);  
  52.         FPart.Read();  
  53.       end;  
  54.       result:= FPart;   
  55.     end;   
  56.       
  57.     procedure TOrderItem.SetPartNo(const Value: double);   
  58.       begin   
  59.         if FPartNo <> Value then   
  60.         begin  
  61.           FPartNo:= Value;  
  62.             if assigned(FPart) then FPart.ObjectState:= posEmpty;  
  63.         end;  
  64.       end;  

 

Visitors

tiOPF uses the Visitor pattern extensively. Reading and Saving objects is done using visitors. An in depth knowledge of visitors is not required to use tiOPF successfully.

An executive summary is as follows:
Visitor objects iterate over a collection of objects and perform an operation on each acceptable object. In tiOPF terms, visitors descend from TtiVisitor and operate on descendants of TtiObject (this includes TtiObjectList).

Important features of TtiVisitor are:

Visited Property - the object currently being operated on.
AcceptVisitor virtual method - used to determine if the Visited object should be operated on.
Execute virtual method - that actually performs the operation on Visited.

For more information, see chapter 2 of the concepts manual.

 

Persistence mapping

As mentioned, there are 3 types of persistence mapping, AutoMapping, DB Independent Visitors and Hard Coded Visitors. Automapping will work with both flat files (xml, csv etc) and databases. DB Independent and hard Coded will only work with databases.

Most demos allow you to choose the mapping on start up so you can compare them.

 

Automapping:
Automapping is the simplest persistence mapping. Simply call;
gTIOPFManager.ClassDBMappingMgr.RegisterMapping for each field to be persisted,
and call
gTIOPFManager.ClassDBMappingMgr.RegisterCollection for each collection object.
this is done once, at start up.

Example from Demo_Collection

  1. // Class, Table, Property, Column, Special Info  
  2.   gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TClient, 'Client''OID''OID', [pktDB]);  
  3.   gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TClient, 'Client''ClientName''Client_Name' );  
  4.   gTIOPFManager.ClassDBMappingMgr.RegisterMapping(TClient, 'Client''ClientID''Client_ID' );  
  5.   gTIOPFManager.ClassDBMappingMgr.RegisterCollection(TClients, TClient);  

 

DB Independent Visitors:
DB Independent Visitors (DBIV) require creating 4 visitors for each class to be persisted; read, create, update and delete visitor objects. These objects need to be registered at start up. This provides more control than automapping at the cost of considerably more code.
  1. TVisClient_Read = class(TVisDBAutoGenRead)   
  2.   protected   
  3.     function AcceptVisitor: boolean; override;   
  4.     procedure Init ; override;   
  5.     procedure SetupParams ; override;   
  6.     procedure MapRowToObject; override;   
  7.   end;   
  8.     
  9.   TVisClient_Create = class(TVisDBAutoGenUpdate)   
  10.   protected   
  11.     function AcceptVisitor: boolean; override;   
  12.     procedure SetupParams ; override;   
  13.   end;   
  14.     
  15.   TVisClient_Update = class(TVisDBAutoGenUpdate)   
  16.   protected   
  17.     function AcceptVisitor: boolean; override;   
  18.     procedure SetupParams ; override;   
  19.   end;   
  20.     
  21.   TVisClient_Delete = class(TVisDBAutoGenDelete)   
  22.   protected   
  23.     function AcceptVisitor: boolean; override;   
  24.     procedure SetupParams ; override;   
  25.   end;  

See Demo_Collection, Client_DBIndependentVisitors_Svr.pas for implementation details.

Under the hood, the visitors create a sql statement based on the parameters supplied (table name and field names).

  1. function TVisClient_Create.AcceptVisitor: boolean;   
  2.   begin  
  3.     result:= (Visited is TClient) and (Visited.ObjectState = posCreate);   
  4.     Log([ClassName, Visited.ClassName, Visited.ObjectStateAsString, Result ]);  
  5.   end;  
  6.     
  7.   procedure TVisClient_Create.SetupParams;   
  8.   var   
  9.     LData: TClient;   
  10.   begin   
  11.     LData:= Visited as TClient;   
  12.     TableName:= 'Client';   
  13.     QueryType:= qtInsert;   
  14.     QueryParams.SetValueAsString('OID', LData.OID.AsString);   
  15.     QueryParams.SetValueAsString('Client_Name', LData.ClientName);   
  16.     QueryParams.SetValueAsString('Client_ID', LData.ClientID);   
  17.   end;  

The advantage over automapping is that you have more control over the makeup of the sql.
 
Hard Coded Visitors
Hard Coded Visitors also require the creation and registration of 4 visitors. However, they simply provide an empty Query object, and creation of the sql is left to the developer.
  1. TVisClient_Read = class(TVisOwnedQrySelect)   
  2.   protected   
  3.     function AcceptVisitor: boolean; override;   
  4.     procedure Init ; override;   
  5.     procedure SetupParams ; override;   
  6.     procedure MapRowToObject; override;   
  7.   end;   
  8.     
  9.   TVisClient_Create = class(TVisOwnedQryUpdate)   
  10.   protected   
  11.     function AcceptVisitor: boolean; override;   
  12.     procedure Init ; override;   
  13.     procedure SetupParams ; override;   
  14.   end;   
  15.     
  16.   TVisClient_Update = class(TVisOwnedQryUpdate)   
  17.   protected   
  18.     function AcceptVisitor: boolean; override;   
  19.     procedure Init ; override;   
  20.     procedure SetupParams ; override;   
  21.   end;   
  22.     
  23.   TVisClient_Delete = class(TVisOwnedQryUpdate)   
  24.   protected   
  25.     function AcceptVisitor: boolean; override;   
  26.     procedure Init ; override;   
  27.     procedure SetupParams ; override;   
  28.   end;  
  29.     
  30.   ...  
  31.     
  32.   function TVisClient_Create.AcceptVisitor: boolean;   
  33.   begin   
  34.     result:= (Visited is TClient) and (Visited.ObjectState = posCreate);   
  35.     Log([ClassName, Visited.ClassName, Visited.ObjectStateAsString, Result ]);   
  36.   end;   
  37.     
  38.   procedure TVisClient_Create.Init;   
  39.   begin   
  40.     Query.SQLText:= 'Insert into Client (OID, Client_Name, Client_ID) ' +   
  41.                               'Values ' + '(:OID,:Client_Name,:Client_ID)';  
  42.   end;   
  43.     
  44.   procedure TVisClient_Create.SetupParams;   
  45.   var   
  46.     lData: TClient;   
  47.   begin   
  48.     lData:= Visited as TClient;   
  49.     lData.OID.AssignToTIQuery('OID', Query);   
  50.     Query.ParamAsString['Client_Name']:= lData.ClientName;   
  51.     Query.ParamAsString['Client_ID']:= lData.ClientID;  
  52.   end;  

HCV should be used when you need precise control of the sql, or when you sql doesn't fall into the "Select * from ..." Table mould.

 

Which persistence mapping should I use?

For flat files (xml etc), use automapping.

Both Automapping and DBIV use "Select * from ..." queries, so if this is not suitable (bringing back too many unwanted fields) then use HCV.

Otherwise the difference in coding required is such that you are best off starting with automapping and then replacing it as required.

Note: it is possible to mix and match. E.g. use a HCV to select the data and then use Automapping to perform the updates.

 

Feature required

Automapping

DB Independent

Hard Coded

Flat files Yes No No
Use stored procedures No No Yes
Swap databases easily Yes Yes Maybe
Control over sql No Limited Complete
Performance None Limited Complete

 

More to come ... please stay tuned.