Professional Documents
Culture Documents
Zeos Firebird e Delphi
Zeos Firebird e Delphi
ZeosLib
+ Firebird
Although this article describes the usage of the ZEOS Library using Firebird,
all the basics can be used with other SQL servers / databases that are supported
by ZEOS.
Note: The Firebird Server can be downloaded from download section of
http://www.ibphoenix.com
http://www.firebirdsql.org
InstallingtheZEOSLibraryandadditionalstuff
TheinstallationoftheZEOSLibraryunderDelphi7professionalisnotthatcomplicated.Oncethecurrent
ZEOSversionandallthepatches(whilewritingthisarticleitwaslibraryversion6.1.5andthecorresponding
patches1&2)aredownloadedandunzippedintoadirectoryofyourchoicecompletelyyouonlyhaveto
followtheinstallationinstructions(note:FirebirddoesnotneedanyadditionalDLL's,here!):
OpenthedelphiprojectgroupZeosDbo.bpgfromsubdirectorypackages\delphi7ZeosDbo.bpgandinstall
thefollowingcomponentsingivenorder:
ZCore.bpl
ZParseSql.bpl
ZPlain.bpl
ZDbc.bpl
ZComponent.bpl
Note:Ifthereoccursomeerrorswhilecompilingthatsaythatacertaindcufilecouldnotbefoundthenjust
addthesubdirectorypackages\delphi7\buildtodelphislibrarypath.Alldcufilesthatarecreatedwhile
compilationarelocatedhere.
Attention:TheclientlibraryofFirebirdServerversion1.5.1(notembedded!)wasdeliveredas"gds32.dll"
andnot"fbclient.dll".ThiscausestroublewhileaccessingviaZEOSbecausetheprotocol"firebird1.5"
aassumesaDLLnamed"fbclient.dll".Aworkaroundistocopythe"gds32.dll"andrenamethiscopyto
"fbclient.dll".
Additive:EventAlertercomponent(TZIBEventAlerter)
Anadditionalcomponent,thatonlyexistsforFirebird(andalsoInterbase)interceptsall(registered)events,
thataretriggeredbyStoredProceduresofadatabase,andmakesitpossibletoreactuponthem.This
componentwaspostedbyAlexeyGilev(aka"GAF")intotheZEOSForumandmadeavailabletotheZEOS
Team.Theoriginalversioncanbedownloadedhere:
http://sourceforge.net/tracker/index.php?func=detail&aid=911233&group_id=35994&atid=415826
TheeventalerterisonlytestedforZEOSversion6.1.4butrunswithsomemodificationswithoutany
problemsinversion6.1.5(withPatch1&2).TheeventalerterthatwasportedtoZEOSversion6.1.5willbe
installedasfollows(pleaseconsidertheReadMefile!I'mnotgoingtogiveanywarrentythatthissolutionis
errorfree!Youuseitatyourownrisk!):
DownloadpatchforZIBEventAlerter(integrated)here
unzipthefilesanddistributethemtotheaccordingZEOSdirectories
ifnecessaryrecompileandreinstallZEOS(seeabove)
Basics:Transactions
SomemandatorybasicsabouttransactionshavetobetoldinordertounderstandhowtheTZConnection
componentworksinternallyandhowtoworkwithit.
Ingeneral:Youonlycanhaveaccesstoadatabasewithinthecontextofavalid("running")transaction.A
transactionhastofulfillthefollowingfourcharacteristicsthatareknownasACIDcharacteristicsofa
transaction.
Atomicity
Allactionsperformedonadatabasehavetobeexecutedsuccessfully.Ifonlyoneerroroccursthenthe
originalstateofthedatabasehastoberestored.Accordingtotheprinciple"allornothing".
Consitency
Atransaktiontransfersadatabasefromoneconsitentstatetoanotherconsistentstate.Ifanerroroccurs
thentheoriginalstateofthedatabasehastoberestored.
Isolation
Atransactionhastobehandledbytheserverasifitwastheonlyonerunning.Thismeansithastorun
indepentlyfromothertransactions.Ausermustnotnoticethechangesdonebyotherusers.
Durability
ChangesinthedatasetofadatabasethatarecausedbySQLstatementsthatareexecutedbetweenthe
startofatransactionandaCOMMIThavetobefixedirrevocably.
Transactionsencapsulateconsecutiveaccessesonadatabase.Adatabaseaccessmaybeofreadingor
writingnature(INSERT,UPDATE,DELETE)oritmaychangethestructureofadatabase.Transactionsare
terminatedbyCOMMITorROLLBACK.COMMITconfirmsallchangesinadatabasesincethestartofa
transaction.ROLLBACKresetsallchangesinadatabasesincethestartofatransaction.
Transactionsrunningontheserverhavetobeisolatedfromeachother.Sotheymayrunindependently.A
transactionhastobehandledbytheserverasitwastheonlyonethatiscurrentlyrunning.Thismeansfor
theuserthathemustneverseethechangesofotheruserswhileheisinarunningtransactionbecausethe
otherchangeshavenothingtodowithhistransaction.Thisiscalledtheisolationofatransaction.Byusing
differenttransactionisolationlevels(TILs)thedevelopermayprotectthedataofanSQLresultsetfrom
accessbyothertransactions.Thebehaviourdescribedaboveiscalledthestandardisolationlevelknownas
SERIALIZABLE.ThestandardisolationlevelofFirebirdiscalledSNAPSHOTandcomesverycloseto
SERIALIZABLE.
TZConnection
TheTZConnectioncomponentisacombinationofaBDETDatabaselikecomponentacomponentthat
handlesatransaction.ThiscombinationmakessensebecauseallaccesstoaFirebirddatabase(andalso
otherdatabases)isalwaysmadeinarunningtransaction.SuchatransactionisstartetbytheZEOSLibrary
wheneveraconnection(methodConnectofTZConnection)toadatabaseisopened.Thiscausesthateach
databaseaccessisdonewithinthecontextofarunningtransaction,automatically.Thesocalled
AutoCommitmodeisalwayson(setto"True").ThisisalsothestandardbehaviourofthecorrespondingBDE
component.IfAutoCommitisactivatedtheneverychangeofanSQLstatementwillbeconfirmedinthe
databasebyCOMMITateritssuccessfullexecution.Ifthisbehaviourshallbeturnedoffandanexplicit
transactionshallbestartedthenthemethodStartTransactionhastobecalled.Withinthisexplicittransaction
itispossibletoexecuteacoupleofSQLstatementsthatmakechangestothedatabase,insuccession.
Thesestatementsthencanbeconfirmedasa"group"byCOMMIT.Ifanexplicittransactionisactivethen
AutoCommitisalwaysturnedoff.ByCallingtheMethodCommitallthechangesmadewithinthisexplicit
transactionareconfirmed.CallingthemethodRollbackresetsthesechanges.InbothcasesAutoCommitwill
besettoTruewhenthemethodcall(CommitorRollback)isdone.Theexplicittransactionhasended.
Retaining
AfterconfirmingthechanesmadeinatransactionbyCOMMITorresettingthembyROLLLBACKthe
transactionnormallyisgoingtobeendedandanexistingresultsetofaqueryorstoredprocedurewillbe
discarded.TheseCOMMITsandROLLBACKsarecalled"hard"commitor"hard"rollback.Byusingthe
ZEOSlibrarythiswillbecomealittlebitdifferent.ZEOSkeepstheresultsetalive.Thisisachievedbyclosing
transactionwith"soft"commitsor"soft"rollbacks.AllthisisdonebytheTZConnectionobject.Thismethodis
calledretaining.TheCOMMITandROLLBACKcommandsareexecutedwiththeadditionRETAINING.
Retainingcausestheclosingofthecurrenttransactionandimmediatelyopeninganewtransactionwithall
thedataandresources(especiallytheresultset)ofthe"old"transaction.
Retainingbecomesaproblemifitisusesforhugetables.Itconstrainstheinternalcleanupmechanismof
firebird(garbagecollection).Thisleads(becauseoftheversioningandthemultigenerationalarchitectureof
Firebird)toalotofoldrecordsthathavetobekeptbutwillnotbeneededanymore.Thisinfluencesthe
server'sperformancedinanegativeway.Asocalledsweepwoulddiscardtheseoldversionsandimprove
theperformance.Thissweepwillonlybeexecutedbysendinga"hard"COMMITorROLLBACK.TheZEOS
Libraryonlyexecutesthese"hard"commandswhenendingthedatabaseconnection(closingconnection).It
isnotpossibletosendthemwhileadatabaseconnectionisactive.Sothedatabaseconnectionshouldbe
deactivatedandimmediatelyactivatedoccasionallytoachievethisperformanceimprovement.
TransactionIsolationLevelsofTZConnection
TheTZConnectioncomponentprovidesfourusefulandpredefinedTransactionIsolationLevels(TIL):
tiRepeatableRead
ItcorrespondstotheTIL"SNAPSHOT"whichisthestandardofFirebirdservers.Itisacombinationofthe
trasactionparameters"concurrency"and"nowait".Asnapshotofthecurrentdatabaseismade.Otherusers
areonlyinfluenced(constrained)iftwotransactionsworkononerecordsimultaneously.Ifconflictsarise
whileaccessingdataanerrormessagewillbereturned.Changeswithinothertransactionswillnotbe
noticed.ThisTILcoverstherequirementoftheSQLstandard(SERIALIZABLE)widely.
tiReadCommitted
ItcorrespondstotheTIL"READCOMMITTED".Itisacombinationofthetransactionparameters
"read_committed","rec_version"and"nowait".ThisTILrecognizesallchangesinothertransactionthathave
beenconfirmedbyCOMMIT.Theparameter"rec_version"isresponsibleforthebehaviourthatthemost
currentvaluesthatwerecommittedbyotheruserswillbeconsidered.theparameter"nowait"isresposiblefor
thebehaviourthatthereisnowaitingforthereleaseofalockedrecord.Sotheserverismorestressedthan
inTILtiRepeatableReadbecauseithastodoalltherefreshestogetthesevaluesagainandagain.
tiSerializable
ItcorrespondstotheTIL"SNAPSHOTTABLESTABILITY".Itisusedtogetanexclusiveaccesstotheresult
set.Realizedbytransactionparameter"consistency"itpreventsthat"foreign"transactionmayaccessthe
writtendata.Onlythetransactionwhichhaswrittenthedatamayaccessthem.Thispreventsalsoamulti
useraccesstothewrittendata.BecausethisTILisveryrestrictivebyaccessingwrittendataitshouldbe
appliedwithcautionandcare.
tiNone
NoTILisusedtoisolatethetransaction.
TheTILtiReadUncommittedisnotsupportedbyFirebird.IfthisTILisused,anerrorwillbetriggerdedand
thetransactionwillnotbeisolated(likeusingtiNone).
Recommendation
ItisadvisabletoisolatetransactionswithtransactionisolationleveltiRepeatableRead(theFirebirdstandard).
ThisTILcoverstherequirementoftheSQLstandard(SERIALIZABLE)widely.Itpreventsallproblems
concerningconsistencythatmayarisebyusingtransactions.SecondchoicewouldbetiReadCommittedbut
thisdependsontheapplicationandthenecessityifthereseultsetalwayshastobecurrent.
CustomizingTILs
IfyouwanttocustomizeyourTILsorexpandagivenTILthenyoucandothisbyusingtheparametersof
TZConnection.ThemostimportantthingaboutthisisthattheTILthatshouldbeexpandedhastobesetin
propertyIsolationLevel.IfitissettheTILparameters(seeIB/FBAPIreference)youwanttoaddmaybe
addedinsourcecode.ThefollowingexampleshowstheexpansionofaTILpresettotiNone:
:
ZConnection.TransactIsolationLevel := tiNone;
ZConnection.Properties.Add('isc_tpb_concurrency');
ZConnection.Properties.Add('isc_tpb_wait');
ZConnection.Connect;
:
Protocol
ThemostimportantsettinginaTZConnectionobjectistheserverprotocolitissetinpropertyProtocol.It
determineswhichprotocolshallbeusedandthuswhichSQLserverwillbeaccessed.Thismethodmakes
ZEOSsoflexible.Youdon'thavetoinstallspecialcomponentsforeachdatabaseyouwanttoaccessasit
wasinVersions5.xandearlier.Thecomponentswillbeinstalledonce.Thisisenough.Youonlychoosethe
protocolforthesupportedSQLserveryouwanttoaccessandyouaredone.Soyousetprotocol"firebird
1.5"toaccessFirebird1.5server.
ReadOnlyConnection
ThedatabaseconnectionmaintainedbyaTZConnectionobjectissettoreadonlybydefault(ReadOnly=
True).Thismeansthatnowritingaccesstotheconnecteddatabaseisallowed.Togetwritingaccesstothe
databaseyouhavetosetReadOnlytoFalse.
Codepages
Codepageswillbedeterminedbysettingparameter"lc_ctype"or"Codepage"inTZConnection.This
parametermustbeaddedtothepropertyProperties.E.g.:
ZConection.Properties.Add ('lc_ctype=ISO8859_1');
or
ZConnection.Properties.Add ('Codepage=ISO8859_1');
Note:TheCodepagesupportofFirebird(alsoembeddedversion)inversion1.5withZEOSisalittlebit
buggy.ThesebugsarecorrectedinFirebirdversion1.5.1.
FeaturesoftheFirebirdembeddedserver
NormallytheservernameorIPaddressoftheserverisgiveninpropertyHostName.ByusingaFirebird
embeddedserveryoumayleavethispropertyempty.OnlypropertyDatabasehastobedetermined.Here
youhavetospecifydrivepathandnameofthedatabaseincludingextension.
Anotherfeatureoftheembeddedserveristhatyoumayspecifyanyloginnamewithapaswordofyour
choice.Itdoesn'tmatterwhatyouchooseyouwillgetconnected.
SettingtheTZConnectionobjectpropertyConnectedtoTrueindesigntimeisextremelybadifyoudon'treset
ittoFalsebeforecompiling.IfyouthenstartthecompiledapplicationwithIDErunningyouwillgetanerror
thatsaysthatthedatabasecannotbeopenedbecauseitisalreadyinuse.Soyoushouldestablishthe
connectiontothedatabasewhenstartingtheapplication(e.g.inOnCreateeventofthemainform)andthen
opentheneededqueriesandtables.Deactivatingtheconnectionshouldalsobedoneinmainform(e.g.in
OnDestroy).Itisnotnecessarytocloseallopenqueriesandtables.Thiswillbedonewhenclosingthe
connectionoftheTZConnectionobject.Ifyouusedatamodelformsyouhavetotakecarethatthedatamodel
formiscreatedbeforethemainformiscreated(setintheIDE'sprojectoptions)..
UsefulTZConnectionparameters
AdditionalparametersforestablishingconnectionstoFirebirddatabasesare::
CreateNewDataBase:
AnewdatabasewillbecreatedbasedonthespecifiedCREATEDATABASEstatements.Whenthedatabase
iscreatedtheconnectionwillbeestablishedimmediately.AllthishappensbycallingtheConnectmethodof
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;
:
ToexecutethiscorrectlyyouhavetosettheDatabaseandProtocolpropertiesatminimum(alsopossiblein
objectinspector).
Dialect:
ThisparametersetsFirebird'sSQLdialiect.Tosetdialect"1"youhavetousethefollowingcode:
ZConnection.Properties.Add ('Dialect=1');
ThedialectofFirebird1.5.xissetto"3"bydefault.
Rolename:
Thisparametersetsarolename.Aloggedinuserthenworksinthecontextoftherole'srightsbutbeforethe
userhastobeassignedtothisrole.TheFirebirdembeddedserverdoesnotsupportthisfeature.
TZQuery
TheusageofTZQueryissimilartotheusageofBDE'sTQuerycomponent.
Recommandation:RequestLiveandTZUpdateSQL
IfanSQLdatasetshallbeupdatablethenRequestLivehastobesettotrueandyoushouldgenerallyuse
accordingupdateSQLstatementsthatwillbedefinedinTZUpdateSQL.Ifthisisdonejustassign
TZUpdateSQLtotheTZQueryobject.Nowallchangesthatwillbemadeintheresultsetwillbedonetothe
databasebyusingthedefinedstatementsofTZUpdateSQL.AccordingtoexperienceRequestLivemode
runsmoresmoothlybyusingTZUpdateSQL.
UsageofparametersinSQLstatements
UsingparametersinSELECTstatmentsisaseasyasusingthemwithBDE'sTQuery.IfTZQueryhasa
resultsetthenyouhavetousetheOpenmethod.IfyouwanttoexecuteanSQLstatementwhichhasno
resultset(e.g.:INSERTorUPDATE)youhavetouseExecSQL(seealso:TZStoredProc).
TZReadOnlyQuery
ThisisaQuerycomponentthatisquitesimilartotheTZQuerycomponent.Thereisjustonedifference:The
resultsetisreadonly.ThereisnopossibilitytoassignaTZUpdateSQLobject.
TZUpdateSQL
ATZUpdateSQLobjectprovidesstatementstomodifythedataofaresultsetthatisretrievedbyaTZQuery
object.TheTZUpdateSQLcomponentiscomparabletoBDE'sTUpdateSQLcomponent.Hereisanexample
howtodefinethestatementsofanSQLstatementwithcorrespondingupdatestatements(basedonadialect
3database):
SQL.Sql:
SELECT * FROM names
UpdateSQL.InsertSql:
INSERT INTO names (recno, name)
VALUES (:recno, :name)
UpdateSQL.ModifySql:
UPDATE names
SET recno = :RecNo,
name = :name
WHERE recno = :old_recno
UpdateSQL.DeleteSql:
DELETE FROM names
WHERE recno = :old_recno
The"OLD_"parameterprefixforSQLstatements
The"old_"prefixishandledaccordingtothehandlingwithBDEcomponents.Byusing"OLD_"asprefixfora
fieldnameyouareabletoaccessthevalueofthefieldbeforeitwaschanged.Thisisveryhelpfulifyouhave
tocomparefieldvaluesinaWHEREclause.
Querieswithreadonlyresultsets
IngenealaTZUpdateSQLobjectisassignedtoaTZQueryobjectthathasareadonlyresultset.Thismakes
itpossibletochangeitsdata.Suchreadonlyqueriesarequeriesthatjoinmultipletables.Butalsowith
"normal""RequestLive"resultsetsyoumayuseTZUpdateSQL(see:TZQuery).
MultiplestatementsinTZQueryandTZUpdateSQL
ThecomponentsTZQueryandTZUpdateSqlprovidethepossibilitytoexecutemultiplestatements,internally.
SoitispossibletoplacemultipleSQLstatements(evenwithparameters)forexecutioninSQLproperty.
Theyonlyhavetobeseparatedbysemicolon.Hereanexample:
:
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;
:
Thestatementswillbeexecutedingivenorder.Itisalsopossibletoexecutemultiplestatementsiftheyare
groupedinthismannerinsidemultipleTZUpdateSqlObjectsinordertoupdatemultipletables.
TZTable
TZTableactslikeBDE'sTTable.AsaprincipleyouonlyshoulduseTZTableinaC/Sapplicationifyouhave
verysmalltablesbecauseallrecordsofthetablewillbetransferredfromserverintoclient'smemoryby
openingtheTZTable.Thisisabahavioursimilartoa"SELECT*FROMXYZ"statement.Youshouldeven
preventastatementlikethisinaC/Sapplication.Theintensionistokeeptheresultsetthathastobe
transferredfromservertoclientassmallaspossible(perferablyonleonerecord).
TZStoredProc
TZStoredProcprovidesthepossiblitytoexecutestoredproceduresthataresavedinadatabase.Thereare
twokindsofstoredprocedures:Proceduresthatreturnaresultsetandproceduresthatdonotreturna
resultset.TZStoredProcworkssimilartoBDE'sTStoredProc.Theonlydifferencebetweenthemisthatyou
don'thavetocallPreparebeforeyoucalltheExecProcmethod.
StoredProcedureswithResultsets
IfastoredprocedurereturnsaresultsetthenitwillbeactivatedbycallingtheOpenmethod(whenall
existingparametershavegottheirvalues):
:
With spSumByName do Begin
Close;
ParamByName ('Name').Value := 'DontKnowHow';
Open;
End;
:
TheresultsetcanbeworkedonlikearesultsetofaTZQuery.
StoredProcedureswithoutResultsets
IfastoredprocedurehasnoresultsetthenitwillbeexecutedbycallingtheExecProcmethod(whenall
existingparametershavegottheirvalues).Hereisanexample(conConnection.AutoCommit=True):
:
With spDeleteByName do Begin
ParamByName ('Name').Value := 'DontKnowHow';
conConnection.StartTransaction
Try
// execute StoredProc
ExecProc;
Except
conConnection.Rollback;
End;
conConnection.Commit;
End;
:
Attention:BuginZEOSLibraryV6.1.5!
IfastoredprocedurewasexecutedviaExecProcatleastonetime,anexceptionwillbetriggeredwhen
disconnectingTZConnection(usedbyTZStoredProc)fromdatabase.Exception'sMessageis:"invalid
statementhandle".SQLerrornumberis901.ThisExceptionwillnotbetriggeredifthestoredprocedureis
executedusingaTZQueryorTZReadOnlyQuery.Executionofthestoredprocedurewillbedonebycalling
theprocedureusingthefollowingSQLcommand(putintotheSQLproperty):
[syntax="sql"]EXECUTEPROCEDUREDeleteByName(:Name)[/color][/font][/list]
ExecutingastoredprocedurethiswayissimilartoexecutioninTZStoredProc(conConnection.AutoCommit=
True):
:
// using a TZQuery object
With qryDeleteByName do Begin
ParamByName ('Name').Value := 'DontKnowHow';
conConnection.StartTransaction
Try
ExecSQL; // execute SQL statement in TZQuery
Except
conConnection.Rollback;
End;
conConnection.Commit;
End;
:
Info:Evenwithadialect3databaseitisnotneededtoputthenameofthestoredprocedureintoquotation
marksNamesofstoredproceduresinFirebirdarenotcasesensitive.
ProblemswithTZStoredProcwhenusingdialect1databases
Theproblemwhenusingdialect1databasesisthatthemetadataofstoredproecedureisnotproperly
transferredtotheTZStoredProcobjectswhenchoosingthestoredprocedureinobjectinspector.All
parapeterdataisnotproperlyinterpreted.Anupdatefromdialect1todialect3solvesthisproblem.Isthe
dialect1databasevitalfortheapplicationsystemthenaTZQueryorTZReadOnlyQueryhastobeusedas
workaround.
TZSQLProcessor
ThiscomponentprovidesthefunctiontoprocessSQLscriptsthatcanbeloadedbycallingthemethods
LoadFromStream()orLoadFromFile().TheloadedSQLScriptisputintoanaccordingpropertycalledScript.
Importanisthatthecorrectdelimiterforthescriptisset(PropertyisalsoDelimiter).Bydefault";"willbeset
asdelimiter.Thisissufficientforthemostscripts.Butifyouwanttocreatestoredproceduresortriggersvia
scriptyoushouldsetdelimiteraccordingtothesettingofthescript's"SETTERM>newDelimiter<
>oldDelimiter<"command(normally"^"isusedforthis).InadditiontothisthepropertyDelimiterTypehasto
besettodtSetTerm.HeresomelinesofcodethatshowhowtoprocessanSQLscript:
:
sqlScript.Script.Clear;
sqlScript.LoadFromFile('c:\temp\createdb.sql');
conConnection.StartTransaction;
Try
sqlScript.Execute;
Except
conConnection.Rollback;
End;
conConnection.Commit;
:
TheSQLscriptisprocessedwithinanexplicittransaktion(AutoCommitisturnedon).Ifexecutionsucceeds
thechangeswillbecommittedotherwisetheywillberolledback.
TZSQLMonitor
UsingtheTZSQLMonitorcomponentyoumaylogcertainactionsoreventsoftheZEOSdatabase
components.ThejournalmaybewrittenasfileorcollectedinaTMemoobjectorsomethinglikethat.
Writingtheactionsoreventstoalogfileonlyneedsafewsettings:
:
sqlMonitor.FileName := 'C:\Log\MyAppLog.log';
sqlMonitor.Active := True;
sqlMonitor.AutoSave := True;
:
TocollecttheloggedactionsoreventsinaTMemoobjectyouhavetoimplementtheOnLogTraceeventas
follows:
Procedure Tfrm_MyApp.sqlMonitorLogTrace (Sender: TObject;
Event: TZLoggingEvent);
Begin
If Trim (Event.Error) > '' Then
memMontor.Lines.Add (DateTimeToStr (Event.Timestamp) + ': ' +
Event.Message + #13#10 + ' Error: ' + Event.Error)
Else
memMontor.Lines.Add (DateTimeToStr (Event.Timestamp) + ': ' +
Event.Message);
End; // sqlMonitorLogTrace
TheOnLogTraceeventisalwaystriggeredwhenanactionoreventwasloggedbysqlMonitor.LogEvent
(oEvent).TheoEventparameterstandsforaninstanceofaTZLoggingEventclassobject.
PropertiesofTZLoggingEvent
Category(TZLoggingCategory):Standsforthecategoryoftheloggedactionorevent(lcConnect,
lcDisconnect,lcTransaction,lcExecuteorlcOther)
Protocol(String):Theprotocolthatisusedtoaccessthedatabase(see:TZConnection)
Message(String):Thetextthatislogged.
ErrorCode(Integer):Theerrorcodeincaseofanerror.
Error(String):Theerrortextincaseofanerror.
Timestamp(TDateTime):Dateandtimeoftheloggedactionorevent.
TZSQLMetadata
WiththisspecialTDataSetcomponentitispossibletoaccessthemetadataofadatabaseliketables,
columns,etc.(Thischapterisstilltobeexpanded!)
TZIBEventAlerter(AddOnonlyforFirebirdandInterbase)
ByusingthiscomponentyouareabletointercepteventstriggeredbystoredproceduresofaFirebird
databaseandreacttothem.Youonlyhavetoregistertheevent'stext(string)youwanttoreacttoinproperty
Eventswhichisastringlist.Youcandothisusingobjectinspectororinsidethesourcecode.Theeventalerter
isactivatedbyregisteringthelistedevents.TodothisyoushouldcalltheRegistereventsmethodbecause
thePropertiesAutoRegisterandRegistereddonotworkproperly.Ifyouoncehaveregisteredtheeventsthe
componentisabletoreacttothem.Alleventsareunregistered(deleted)bycallingmethodUnregisterEvents
andtheeventalertetisturnedoff.
Registrationofeventsand"activation"oftheeventalerter:
:
EventAlerter.Events.Add ('Minimum Stock Level Reached');
EventAlerter.Events.Add ('Credit Limit Exceeded');
EventAlerter.RegisterEvents;
:
Master/DetailwithZEOSLibrary
ZEOSDataSetcomponentscomewithtwokindsofmaster/detailconnections:thosewithaserversidedfilter
andthosewithaclientsidedfilter.BothkindsandonekindinadditiointhatisindependentfromZEOS(and
thuswithoutanycomfort)willbedescribedhere.
Note:Ifwetalkabout"master"or"detail"thenaTDataSetdescendant(TZQuery/TZReadOnlyQuery,
TZTableoraTZStoredProc)ismeantthataccesseseitheramasterresultsetoradetailresultsetofa
master/detailconnection.
Master/Detailwithserversidedfilters
ThismethodisthedefaultbehaviouroftheBDE'sTQuerycomponent.Amaster/detailconnectionoftwo
DataSetsisestablishedasfollows:
Themaster'sDataSourceisassignedtotheDataSourceofthedetail.
Allprimarykeyfieldsofthemasterhavetobecomparedwiththeforeignkeyfieldsofthedetailinthe
detailSQLstatement.
Thisisanexampleforasimplemaster/detailqueries.Requirement:WeuseTZQueryorTZReadOnlyQuery
toestablishthemaster/detailconnection:
MasterSQL:
SELECT id, feld1, feld2, feld3
FROM master
[*]DetailSQL:
SELECT feld1, feld2, master_id
FROM detail
WHERE master_id = :id
Parameter:idstandsforthecontentofmaster's"id"field(istheprimarykey)becausethemaster's
DataSourceisassignedtothepropertyDataSourceofthedetail.Sothisparameterreferencestothe"id"
fieldofthemaster.Therield"master_id"indetailqueryistheforeignkeyfieldofthedetailtablewhich
referencestheprimarykeyofthemaster.
Ifthecursorofthemasterchangesitspositionwhileserversidedfiltersareused,theSQLstatementofthe
detailisexecutedusingthecurrentkeyvalues.Sotheresultsetofthedetailisautomaticallyrefreshed.
Master/Detailwithclientsidedfilters
ThisisthedefaultbehaviourofaBDETTablecomponent.Hereamaster/detailconnectionbetweentwo
DataSetsisestablishedasfollows:
TheDataSourceofthemasterisassignedtothepropertyMasterDataSourceofthedetail.
TheprimarykeyfieldsofthemasterareassignedtopropertyMasterFieldofthedetail.
Theforeignkeyofthedetailwhichreferencestheprimarykeyofthemasterisassignedtopropety
IndexFieldNames.
Thisisanexampleforasimplemaster/detailqueries.Requirement:WeuseTZQueryorTZReadOnlyQuery
toestablishthemaster/detailconnection:
MasterSQL:
SELECT id, feld1, feld2, feld3
FROM master
DetailSQL:
SELECT feld1, feld2, master_id
FROM detail
WHERE master_id = :id
Parameter:idstandsforthecontentofmaster's"id"field(istheprimarykey)becausethemaster's
DataSourceisassignedtothepropertyDataSourceofthedetail.Sothisparameterreferencestothe"id"
fieldofthemaster.Therield"master_id"indetailqueryistheforeignkeyfieldofthedetailtablewhich
referencestheprimarykeyofthemaster.
Ifthecursorofthemasterchangesitspositionwhileserversidedfiltersareused,theSQLstatementofthe
detailisexecutedusingthecurrentkeyvalues.Sotheresultsetofthedetailisautomaticallyrefreshed.
Master/Detailwithclientsidedfilters
ThisisthedefaultbehaviourofaBDETTablecomponent.Hereamaster/detailconnectionbetweentwo
DataSetsisestablishedasfollows:
TheDataSourceofthemasterisassignedtothepropertyMasterDataSourceofthedetail.
TheprimarykeyfieldsofthemasterareassignedtopropertyMasterFieldofthedetail.
Theforeignkeyofthedetailwhichreferencestheprimarykeyofthemasterisassignedtopropety
IndexFieldNames.
WithclientsidedfiltersbothDataSetsfirsttransferalltablerowsfromservertoclient.Thedetailthensetsa
filter(onclientside)togetthedetailsaccordingtothecurrentmasterrecord.
Incaseofcreatinganewdetailrecordforamaster/detailconnectionwithaclientsidedfilterthereisakindof
automatism:TheForeignkeyfieldsofthedetail(setinpropertyIndexFieldNamesofthedetail)willbefilled
automaticallywiththeaccording(current)primarykeydataofthemaster(setinpropteryMasterFieldofthe
detail).Note:Withserversidedfiltersyouhavetocareaboutthisfunctionality,manuallyinyourprogram's
code.ThiscanbeachievedbyimplementingtheOnNewRecordeventofthedetail.Thiseventisalways
triggeredwhenannewrecordistobecreated(see:DelphionlinehelpforTDataSet).AccordingtotheSQL
statements,definedaboveyouonlyhavetoimplementthefollowing:
Procedure dmMasterDetail.qryDetailNewRecord (DataSet: TDataSet);
Begin
qryDetailMASTER_ID.Value := qryMasterID.Value;
End;
CorrespondingTFieldswerecreatedforthefields"master_id"ofthedetailand"id"ofthemasterusingthe
fieldeditor.
Formaster/detailconnectionsinZEOSthereisanadditionaloptionthatissetinpropertyOptions:Itis
doAlwaysResyncDetail.Ifthisoptionissetthentheresultsetofthedetailisonlyrefreshedwhenpostis
calledorarecordchanges(bothwithinmasterDataSet).
Master/Detail"byhand"
Normallyyouimplementamaster/detailconnectionaccordingtothemethodusedbyserversidedfilters.The
SQLstatementsforthislookexactlylikethat.Onlythepropertiesthataresetinmasteranddetail(see
above)willnotbesethere.BothTZQuerisareworkingindependently.Thismeans:ThedetailDataSetdoes
notrecognizeanychangesinmasterDataSet.Itssynchronzationhastobeimplemented,manually.Thiswill
bedoneintheOnChangeeventofthemaster.OnChangeistriggeredwhenchangingtoanewrecordor
fielddatahasbeenchanged(see:DelphionlinehelpforTDataSource).Synchronizationofthedetail
(accordingtotheexampleabove)wouldbeimplementedlikethis:
Procedure dmMasterDetail.dsMasterDataChange (
Sender: TObject; Field: TField);
Begin
With qryDetail do Begin
Close;
ParamByName('id').Value := qryMasterID.Value;
Open;
End;
End;
ThisisthesamefunctionthatZEOSexecutesautomaticallywhenusingserversidedfilters:Thedetailquery
isexecuted(agin)withthecurrentIDvalueofthemasterwhichwillbeassignedtoparameter":id"inthe
detailquery.Sotheresultsetofthedetailwillberefreshedaccordingtothecurrentmasterrecord.
IfanewrecordshallbecreatedindetailDataSetthenyouhavetoactthesamewayaswithserversided
filters(seeabove).
CachedUpdates
ThedevelopersoftheZEOSLibraryareintendedtoimplementthefunctionalityoftheBDEcomponentsas
goodaspossible.ThisiswhythereisalsothepossibilityofcachedupdateswithZEOS.Youonlyhavetoset
thepropertyCachedUpdatesofaDataSetdescendant(TZTable,TZQueryoderTZStoredProc)totrue.From
thistimeonallchangesintheresultsetwillbecachedandtheycanbeeasilycommittedtothedatabaseby
callingApplyUpdatesandCommitUpdatesoneaftertheothereitherautomaticallyinyourprogramcodeor
triggeredmanuallybyauser.CancelUpdatescausesthatthecangeswillnotbecommittedtothedatabase.
Inthiscaseallcachedchangeswillbereset(likeusingaROLLBACK).Hereyouwillfindalittlecodesnippet
thattriestoshowyouhowcachedupdatesareimplemented(don'targueaboutthesenseinthis...).
AutoCommitmodeofTZConnectinisturnedon(True):
:
With DataSet do Begin
bEverythingOK := True;
CachedUpdates := True;
DataSet.First;
While (not DataSet.EOF) and (bEverythingOK) do Begin
DataSet.Edit;
:
// process record
:
DataSet.Post;
DataSet.Next;
:
bEverythingOK := AFunctionForValidation;
End;
If bEverythingOK Then Begin
ApplyUpdates;
CommitUpdates;
End
Else
CancelUpdates;
CachedUpdates := False;
End;
:
BLOBFields
AccordingtoBDE'scomponentsthecomponentsoftheZEOSLibraryarecapableofhandlingBLOBfields.
HereisanexamplehowanewrecordwithaBLOBfieldiscreated.TheBLOBfieldisfilledwithabitmap.To
achievethiswehavetouseaStream:
:
Var TheStream : TMemoryStream;
Begin
TheStream := TMemoryStream.Create;
Try
Image1.Picture.Bitmap.Savetostream(TheStream);
With qryBlobInsert do Begin
Sql.Text := 'INSERT INTO EVENTS (EventNo,EVENT_PHOTO) ' +
'VALUES (100,:ThePicture)';
Params.Clear;
Params.CreateParam(ftBlob,'ThePicture', ptInput);
ParamByName('ThePicture').LoadfromStream(TheStream,ftBlob);
ExecSQL;
End;
Finally
TheStream.Free;
End;
End;
:
Thissectionaboutblobfieldsisnotcompleted,yet.PleasefeelfreetosendmeaprivatemessageoreMailif
youhaveanyissuesaboutblobsinordertoexpandthissection.
Sampleproject:"EasyQuery"
TopreventbeingtootheoreticalnowwewillcreateasmallsampleprojectthodemonstratehowtheZEOS
componentsareusedingeneral.Wewillcreateasmallapplicationthataccessesthetables"Customer"and
"Country"oftheFirebirdsampledatbase"Employee".Youshouldbeabletonavigateintable"Customer"
andedititsdata.AndnotbeintooboringwewillimplementaDBLookupComboboxforfield"Country"intable
"Customer".Thisfieldisaforeignkeythatreferencestofield"Country"intable"Country".
Solet'sgetstarted!
FirstofallwehavetocreateanewprojectinDelphi.Additionalytothedefaultformwehavetocreatea
DataModule.
ThefollowingpropertiesoftheDataModulehavetobeset:
Name:dmEasyQuery
ComponentsfortheDataModule
TZConnection
Database:Employee.fdb
Name:conEmployee
Password:"password"
Protocol:firebird1.5
ReadOnly:False
TransactionIsolationLevel:tiReadCommitted
User:"username"
TZQuery
Connection:conEmployee
Name:qryCustomer
RequestLive:True
SQL:SELECT*FROMcustomerORDERBYcustomer
UpdateObject:updCustomer
Note:YouhavetocreatepersistentTFieldsforalltablefieldsusingthefieldeditor!
TheOnAfterPosteventofqryCustomerwillbeimplementedlikethis:
procedure TdmEasyQuery.qryCustomerAfterPost(DataSet: TDataSet);
begin
// Refresh of resultset to actualize (sort) data shown in DBGrid.
qryCustomer.Refresh;
end;
TZReadOnlyQuery
Connection:conEmployee
Name:roqryCountry
SQL:SELECTcountryFROMcountryORDERBY1
Note:YouhavetocreatepersistentTFieldsforalltablefieldsusingthefieldeditor!
TZUpdateSQL
DeleteSQL:createdbyUpdateSqleditor
InsertSQL:createdbyUpdateSqleditor
ModifySQL:createdbyUpdateSqleditor
Name:updCustomer
Thedelete,insertandmodifyStatementsfortheTZUpdateSQLobjectarecreatedwiththeUpdateSQL
editor,automatically.TheeditorwillbeactivatedbydoubleclickingontheTZUpdateSQLcomponent.Askey
fieldyouhavetoselectthefieldCUST_NO.ThefieldsADDRESS_LINE1,ADDRESS_LINE2,
CITY,STATE_PROVINCE,COUNTRYundPOSTAL_CODEwillselectedasupdatefieldsinlist"Update
Fields".Nowthestatementswillbegeneratedbyclickingbutton"GenerateSQL".Ifyouonlyhavesucheasy
queriesyoucansavealotoftypingbyusingtheUpdateSQLeditor.Ifitgetsalittlemorcomplexyoushould
createtheneededstatements"byhand".
TDataSource
DataSet:sqlCustomer
Name:dsCustomer
TDataSource
DataSet:rosqlCountry
Name:dsCountry
NowthecreatedDataModulewillbesavedasdm_EasyQuery.pas.[/list]
Componentsfortheform
Themainformwillhavethefollowingcomponentsandisinitializedasfollows:
Properties
Caption:EasyQueryDemo
Name:frmEasyQuery
IntheUnit'sinterfaceyouhavetoadddm_EasyQuerytotheusesclausetogetaccesstothe
databasecomponents.
Thefollowingeventsofthemainformhavetobeimplemented::
OnCreate
Whencreatingtheformtheconnectiontothedatabasewillbeestablished.Afterconnectingtothe
databasethequerieswillbeopened:
procedure TForm1.FormCreate(Sender: TObject);
begin
dmEasyQuery.conEmployee.Connect;
dmEasyQuery.qryCustomer.Open;
dmEasyQuery.roqryCountry.Open;
end;
OnDestroy
Whenclosingtheapplication(destroyingthemainform)thedatabaseconnectionwillbecut.All
querieswillbeclosedautomaticallybeforedisconnecting.
procedure TForm1.FormDestroy(Sender: TObject);
begin
dmEasyQuery.conEmployee.Disconnect;
end;
TLabel
Caption:CUSTOMER
TDBGrid
DataSource:dmEasyQuery.dsCustomer
Options.dgTabs:False
IncolumneditorwewillcreateaTColumnsettingitspropertyFieldNameto"CUSTOMER".Afterthat
theaccordingcolumninDBGrid1willbeenlarged.
TDBEdit
(5x)
TLabel]
(5x)
TheseobjectswillbecreatedbyusingthecolumneditorofqryCustomer:Selectcolumns
ADDRESS_LINE1,ADDRESS_LINE2,CITY,STATE_PROVINCEandPOSTAL_CODEanddragand
dropthemontothenmainform.Alignthemandadaptthemtothelayoutyouseeinthescreenshot.
TLabel
Caption:COUNTRY
TDBLookUpComboBox
DataField:COUNTRY
DataSource:dmEasyQuery.dsCustomer
KeyField:COUNTRY
ListField:COUNTRY
ListSource:dmEasyQuery.dsCountry
TDBNavigator
DataSource:dmEasyQuery.dsCustomer
Nowthecreatedformwillbesavedasfrm_EasyQuery.pas.
Note:Thefollowingprojectoptionshavetobechanged:
dmEasyQueryhastobeplacedontopofthecreationorderinlist"Createautomatically".This
ensuresthatallobjectsoffrmEasyQuerymayaccessthedatabaseobjects.frmEasyQueryisstill
themainform.
AdditionalExamples
FishFact
ThisisthemostpopulardatabasedemoforDelphi.ItwasmigratedtouseZEOScomponents.
Transactions
Asampleapplicationconcerning"transactionswithZEOS".Itusesasmallselfmadetestdatabase.
StoredProc
Thisisasampleapplikationthatshowshowtousestoredproecedures.ThedatabaseistheFirebird
Employeesampledatabase.
MasterDetail
Asmallapplicationthatshowshowmaster/detailconnections(serverandclientsided)willbeimplemented.
EventDemo
AlsoaDelphidatabasesamplethatwasmigratedfromIBXtoZEOS.ItusesthecomponentTIBEventAlerter.
ThisapplicationneedsthedatabaseEvents(shippedwithDelphi)thathadtobemigratedtodialect3toget
thissamplerunning.
YouwillfindtheseExamplesherefordownload.
AdownloadablePDFVersionofthisTutorialisalsoonitsway!
MichaelSeeger
ZeosLibDevelopmentTeam