the developers are inteneded to
copy the functions and the behaviour of the corresponding BDE components as good
as possible.The intension is to minimize the learning courve for developers whomigrate from BDE
consists of the following nine components
which shall be introduced in the following:
•
TZConnection
•
TZQuery
•
TZReadOnlyQuery
•
TZUpdateSQL
•
TZTable
•
TZStoredProc
•
TZSQLProcessor
•
TZSQLMonitor• TZSQLMetadata
Installing the ZEOS Library
Open the delphi project group
ZeosDbo.bpg from subdirectory packages\delphi7 ZeosDbo.bpg and install
the following components in given order:
•
ZCore.bpl
•
ZParseSql.bpl
•
ZPlain.bpl
•
ZDbc.bpl• ZComponent.bpl
Note:
Attention:
The client library of Firebird Server version 1.5.1 (not embedded!) was delivered as "gds32.dll"
and not "fbclient.dll". This causes trouble while accessing via ZEOS because the protocol "firebird1.5"
aassumes a DLL named "fbclient.dll". A workaround is to copy the "gds32.dll" and rename this copy to
"fbclient.dll".
TZConnection
The TZConnection component is a combination of a BDE TDatabase like component a component that
handles a transaction.
Features of the Firebird embedded server
Normally the server name or IP address of the server is given in property HostName. By using a Firebird
embedded server you may leave this property empty. Only property Database has to be determined. Here
you have to specify drive path and name of the database including extension.
An other feature of the embedded server is that you may specify any login name with a pasword of your
choice. It doesn't matter what you choose you will get connected.
Useful TZConnection parameters
Additional parameters for establishing connections to Firebird databases are::
CreateNewDataBase:
A new database will be created based on the specified CREATE DATABASE statements. When the database
is created the connection will be established immediately. All this happens by calling the Connect method of
TZConnection.
:
ZConnection1.Database := 'd:\db1.fdb';
ZConnection1.Protocol := 'firebird-1.5';
ZConnection1.Properties.Add ('CreateNewDatabase=CREATE DATABASE ' +
QuotedStr ('d:\db1.fdb') + ' USER ' +
QuotedStr ('sysdba') + ' PASSWORD ' + QuotedStr ('masterkey') +
' PAGE_SIZE 4096 DEFAULT CHARACTER SET ISO8859_1');
ZConnection1.Connect;
:
TZQuery
The usage of TZQuery is similar to the usage of BDE's TQuery component.
Recommandation: RequestLive and TZUpdateSQL
If an SQL dataset shall be updatable then RequestLive has to be set to true and you should generally use
according update SQL statements that will be defined in TZUpdateSQL. If this is done just assign
TZUpdateSQL to the TZQuery object. Now all changes that will be made in the result set will be done to the
database by using the defined statements of TZUpdateSQL. According to experience RequestLive mode
runs more smoothly by using TZUpdateSQL.
Usage of parameters in SQL statements
Using parameters in SELECT statments is as easy as using them with BDE's TQuery.
TZReadOnlyQuery
This is a Query component that is quite similar to the TZQuery component. There is just one difference: The
result set is read only.
TZUpdateSQL
A TZUpdateSQL object provides statements to modify the data of a result set that is retrieved by a TZQuery
object. The TZUpdateSQL component is comparable to BDE's TUpdateSQL component.
Multiple statements in TZQuery and TZUpdateSQL
The components TZQuery and TZUpdateSql provide the possibility to execute multiple statements, internally.
So it is possible to place multiple SQL statements (even with parameters) for execution in SQL property.
They only have to be separated by semicolon. Here an example:
:
With Query do Begin
Sql.Clear;
Sql.Add('DELETE FROM table1;');
Sql.Add('INSERT INTO table1 VALUES (:Val1, :Val2);');
Sql.Add('INSERT INTO table2 VALUES (:Val3, :Val2);');
Sql.Add('UPDATE table3 SET field1 = :Val4;');
Params.ParamByName('Val1').AsInteger := 123;
:
ExecSql;
End;
:
TZTable
TZTable acts like BDE's TTable. As a principle you only should use TZTable in a C/S application if you have
very small tables because all records of the table will be transferred from server into client's memory by
opening the TZTable.
keep the resultset that has to be
transferred from server to client as small as possible (perferably onle one record).
TZStoredProc
TZStoredProc provides the possiblity to execute stored procedures that are saved in a database. There are
two kinds of stored procedures: Procedures that return a resultset and procedures that do not return a
resultset. TZStoredProc works similar to BDE's TStoredProc.
Stored Procedures with Resultsets
If a stored procedure returns a result set then it will be activated by calling the Open method (when all
existing parameters have got their values):
:
With spSumByName do Begin
Close;
ParamByName ('Name').Value := 'DontKnowHow';
Open;
End;
:
The resultset can be worked on like a resultset of a TZQuery.
Stored Procedures without Resultsets
If a stored procedure has no resultset then it will be executed by calling the ExecProc method (when all
existing parameters have got their values). Here is an example (conConnection.AutoCommit = True):
:
With spDeleteByName do Begin
ParamByName ('Name').Value := 'DontKnowHow';
conConnection.StartTransaction
Try
// execute StoredProc
ExecProc;
Except
conConnection.Rollback;
End;
conConnection.Commit;
End;
:
TZSQLProcessor
This component provides the function to process SQL scripts that can be loaded by calling the methods
LoadFromStream() or LoadFromFile(). The loaded SQLScript
is put into an according property called Script.
Importan is that the correct delimiter for the script is set (Property is also Delimiter). By default ";" will be set
as delimiter. This is sufficient for the most scripts. But if you want to create stored procedures or triggers via
script you should set delimiter according to the setting of the script's "SET TERM >newDelimiter<
>oldDelimiter<" command (normally "^" is used for this). In addition to this the property DelimiterType has to
be set to dtSetTerm. Here some lines of code that show how to process an SQL script:
:
sqlScript.Script.Clear;
sqlScript.LoadFromFile('c:\temp\createdb.sql');
conConnection.StartTransaction;
Try
sqlScript.Execute;
Except
conConnection.Rollback;
End;
conConnection.Commit;
:
The SQL script is processed within an explicit transaktion (AutoCommit is turned on). If execution succeeds
the changes will be committed otherwise they will be rolled back.
TZSQLMonitor
Using the TZSQLMonitor component you may log certain actions or events of the ZEOS database
components. The journal may be written as file or collected in a TMemo object or something like that.
Writing the actions or events to a logfile only needs a few settings:
:
sqlMonitor.FileName := 'C:\Log\MyAppLog.log';
sqlMonitor.Active := True;
sqlMonitor.AutoSave := True;
:
Master/Detail with ZEOS Library
ZEOS DataSet components come with two kinds of master/detail connections: those with a server sided filter
and those with a client sided filter. Both kinds and one kind in additioin that is independent from ZEOS (and
thus without any comfort)
Master/Detail with server sided filters
This method is the default behaviour of the BDE's TQuery component. A master/detail connection of two
DataSets is established as follows:
•
The master's DataSource is assigned to the DataSource of the detail.
•
All primary key fields of the master have to be compared with the foreign key fields of the detail in thedetail SQL statement.
This is an example for a simple master/detail queries. Requirement: We use TZQuery or TZReadOnlyQuery
to establish the master/detail connection:
•
Master SQL:
SELECT id, feld1, feld2, feld3
FROM master
[*]
DetailSQL:
SELECT feld1, feld2, master_id
FROM detail
WHERE master_id = :id
If the cursor of the master changes its position while server sided filters are used, the SQL statement of the
detail is executed using the current key values. So the result set of the detail is automatically refreshed.
Master/Detail with client sided filters
This is the default behaviour of a BDE TTable component. Here a master/detail connection between two
DataSets is established as follows:
•
The DataSource of the master is assigned to the property MasterDataSource of the detail.
•
The primary key fields of the master are assigned to property MasterField of the detail.
•
The foreign key of the detail which references the primary key of the master is assigned to propetyIndexFieldNames.
With client sided filters both DataSets first transfer all table rows from server to client. The detail then sets a
filter (on client side) to get the details according to the current master record.
In case of creating a new detail record for a master/detail connection with a client sided filter there is a kind of
automatism: The Foreign key fields of the detail (set in property IndexFieldNames of the detail) will be filled
automatically with the according (current) primary key data of the master (set in proptery MasterField of the
detail). Note: With server sided filters you have to care about this functionality, manually in your program's
code. This can be achieved by implementing the OnNewRecord event of the detail. This event is always
triggered when an new record is to be created (see: Delphi online help for TDataSet). According to the SQL
statements, defined above you only have to implement the following:
Procedure dmMasterDetail.qryDetailNewRecord (DataSet: TDataSet);
Begin
qryDetailMASTER_ID.Value := qryMasterID.Value;
End;
Corresponding TFields were created for the fields "master_id" of the detail and "id" of the master using the
fieldeditor.
For master/detail connections in ZEOS there is an additional option that is set in property Options: It is
doAlwaysResyncDetail. If this option is set then the resultset of the detail is only refreshed when post is
called or a record changes (both within master DataSet).
Master/Detail "
by hand"
Normally you implement a master/detail connection according to the method used by server sided filters. The
SQL statements for this look exactly like that. Only the properties that are set in master and detail (see
above) will not be set here. Both TZQueris are working independently. This means: The detail DataSet does
not recognize any changes in master DataSet. Its synchronzation has to be implemented, manually. This will
be done in the OnChange event of the master. OnChange is triggered when changing to a new record or
field data has been changed (see: Delphi online help for TDataSource). Synchronization of the detail
(according to the example above) would be implemented like this:
Procedure dmMasterDetail.dsMasterDataChange (
Sender: TObject; Field: TField);
Begin
With qryDetail do Begin
Close;
ParamByName('id').Value := qryMasterID.Value;
Open;
End;
End;
Michael Seeger
ZeosLib Development Team